From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lloyd Pique <lpique@google.com>
Date: Fri, 29 Jan 2021 17:24:56 -0800
Subject: [PATCH 2/3] tests: Add demo real-world protocol logging

Adds a real-world sample function for protocol message loging,
duplicating the same output produced by the internal wl_closure_print.

Signed-off-by: Lloyd Pique <lpique@google.com>

diff --git a/tests/protocol-logger-test.c b/tests/protocol-logger-test.c
index e409368..d0bca41 100644
--- a/tests/protocol-logger-test.c
+++ b/tests/protocol-logger-test.c
@@ -29,10 +29,12 @@
 #include <string.h>
 #include <stdio.h>
 #include <sys/un.h>
+#include <time.h>
 #include <unistd.h>
 
 #include "wayland-client.h"
 #include "wayland-server.h"
+#include "wayland-util.h"
 #include "test-runner.h"
 
 /* Ensure the connection doesn't fail due to lack of XDG_RUNTIME_DIR. */
@@ -148,6 +150,116 @@ client_logger_func(void *user_data, enum wl_protocol_logger_client_type type,
 	assert(msg->args_count == message->arguments_count);
 }
 
+// A slightly simplified version of  get_next_argument() from src/connection.c
+static const char*
+get_next_argument_type(const char *signature, char* type)
+{
+	for (; *signature; ++signature) {
+		assert(strchr("iufsonah?", *signature) != NULL);
+		switch (*signature) {
+		case 'i':
+		case 'u':
+		case 'f':
+		case 's':
+		case 'o':
+		case 'n':
+		case 'a':
+		case 'h':
+			*type = *signature;
+			return signature + 1;
+		case '?':
+			break;
+
+		}
+	}
+	*type = 0;
+	return signature;
+}
+
+// This duplicates what the internal wl_closure_print function does, and can be
+// used as a starting point for a client or server that wants to log messages.
+static void
+client_log_to_stderr_demo(void *user_data,
+			  enum wl_protocol_logger_client_type type,
+			  const struct wl_protocol_logger_client_message *message) {
+	int i;
+	char arg_type;
+	const char *signature = message->message->signature;
+	const union wl_argument* args = message->arguments;
+	struct wl_proxy* arg_proxy;
+	const char* arg_class;
+	struct timespec tp;
+	unsigned int time;
+
+	clock_gettime(CLOCK_REALTIME, &tp);
+	time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000);
+
+	// Note: server logger will be given message->resource, and should
+	// use wl_resource_get_class and wl_resolurce_get_id.
+	fprintf(stderr, "[%10.3f] %s%s@%u.%s(",
+		time / 1000.0,
+		(type == WL_PROTOCOL_LOGGER_CLIENT_REQUEST) ? " -> " : "",
+		wl_proxy_get_class(message->proxy), wl_proxy_get_id(message->proxy),
+		message->message->name);
+
+	for (i = 0; i < message->arguments_count; i++) {
+		signature = get_next_argument_type(signature, &arg_type);
+		if (i > 0)
+			fprintf(stderr, ", ");
+
+		switch (arg_type) {
+		case 'u':
+			fprintf(stderr, "%u", args[i].u);
+			break;
+		case 'i':
+			fprintf(stderr, "%d", args[i].i);
+			break;
+		case 'f':
+			fprintf(stderr, "%f", wl_fixed_to_double(args[i].f));
+			break;
+		case 's':
+			if (args[i].s)
+				fprintf(stderr, "\"%s\"", args[i].s);
+			else
+				fprintf(stderr, "nil");
+			break;
+		case 'o':
+			if (args[i].o) {
+				// Note: server logger should instead cast to
+				// wl_resource, and use wl_resource_get_class
+				// and wl_resource_get_id.
+				arg_proxy = (struct wl_proxy *)(args[i].o);
+				arg_class = wl_proxy_get_class(arg_proxy);
+
+				fprintf(stderr, "%s@%u",
+					arg_class ? arg_class : "[unknown]",
+					wl_proxy_get_id(arg_proxy));
+			} else {
+				fprintf(stderr, "nil");
+			}
+			break;
+		case 'n':
+			fprintf(stderr, "new id %s@",
+				  (message->message->types[i]) ?
+				   message->message->types[i]->name :
+				    "[unknown]");
+			if (args[i].n != 0)
+				fprintf(stderr, "%u", args[i].n);
+			else
+				fprintf(stderr, "nil");
+			break;
+		case 'a':
+			fprintf(stderr, "array");
+			break;
+		case 'h':
+			fprintf(stderr, "fd %d", args[i].h);
+			break;
+		}
+	}
+
+	fprintf(stderr, ")\n");
+}
+
 static void
 callback_done(void *data, struct wl_callback *cb, uint32_t time)
 {
@@ -167,6 +279,7 @@ TEST(logger)
 	struct client client = { 0 };
 	struct wl_protocol_logger *logger;
 	struct wl_protocol_logger_client *logger_client;
+	struct wl_protocol_logger_client *logger_client_demo;
 
 	require_xdg_runtime_dir();
 
@@ -180,6 +293,8 @@ TEST(logger)
 	client.display = wl_display_connect(socket);
 	logger_client = wl_display_add_protocol_logger_client(
 		client.display, client_logger_func, &client);
+	logger_client_demo = wl_display_add_protocol_logger_client(
+		client.display, client_log_to_stderr_demo, &client);
 	client.cb = wl_display_sync(client.display);
 	wl_callback_add_listener(client.cb, &callback_listener, NULL);
 	wl_display_flush(client.display);
@@ -193,6 +308,7 @@ TEST(logger)
 	wl_display_disconnect(client.display);
 
 	wl_protocol_logger_client_destroy(logger_client);
+	wl_protocol_logger_client_destroy(logger_client_demo);
 	wl_client_destroy(compositor.client);
 	wl_protocol_logger_destroy(logger);
 	wl_display_destroy(compositor.display);