blob: 54ad6f158b8804ccca55ef28988e4b393df1c938 [file] [log] [blame]
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lloyd Pique <lpique@google.com>
Date: Fri, 11 Mar 2022 17:57:37 -0800
Subject: [PATCH 3/6] protocol-logger-test: Demonstrate logging
Client message observers 3/6
Adds code demonstrating how to replicate the output produced by the internal
wl_closure_print() using the client message observer interface.
If you run protocol-logger-test with "WAYLAND_DEBUG=client", you can see the
client messages logged to stderr twice, with the same strings.
Signed-off-by: Lloyd Pique <lpique@google.com>
diff --git a/tests/protocol-logger-test.c b/tests/protocol-logger-test.c
index 3b9dc3e..082f055 100644
--- a/tests/protocol-logger-test.c
+++ b/tests/protocol-logger-test.c
@@ -80,6 +80,7 @@ struct client {
struct wl_display *display;
struct wl_callback *cb;
struct wl_client_observer *sequence_observer;
+ struct wl_client_observer *stderr_logger;
struct expected_client_message *expected_msg;
int expected_msg_count;
@@ -190,6 +191,130 @@ client_sequence_observer_func(
"arg count mismatch: %s", details_msg);
}
+// 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_client_message_type type,
+ const struct wl_client_observed_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 long long time;
+ FILE *f;
+ char *buffer;
+ size_t buffer_length;
+
+ f = open_memstream(&buffer, &buffer_length);
+ if (f == NULL)
+ return;
+
+ clock_gettime(CLOCK_REALTIME, &tp);
+ time = (tp.tv_sec * 1000000LL) + (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(f, "[%7llu.%03llu] %s%s%s%s%s@%u.%s(", time / 1000, time % 1000,
+ (message->discarded_reason_str ? "discarded[" : ""),
+ (message->discarded_reason_str ? message->discarded_reason_str
+ : ""),
+ (message->discarded_reason_str ? "] " : ""),
+ (type == WL_CLIENT_MESSAGE_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(f, ", ");
+
+ switch (arg_type) {
+ case 'u':
+ fprintf(f, "%u", args[i].u);
+ break;
+ case 'i':
+ fprintf(f, "%d", args[i].i);
+ break;
+ case 'f':
+ fprintf(f, "%f", wl_fixed_to_double(args[i].f));
+ break;
+ case 's':
+ if (args[i].s)
+ fprintf(f, "\"%s\"", args[i].s);
+ else
+ fprintf(f, "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(f, "%s@%u",
+ arg_class ? arg_class : "[unknown]",
+ wl_proxy_get_id(arg_proxy));
+ } else {
+ fprintf(f, "nil");
+ }
+ break;
+ case 'n':
+ fprintf(f, "new id %s@",
+ (message->message->types[i])
+ ? message->message->types[i]->name
+ : "[unknown]");
+ if (args[i].n != 0)
+ fprintf(f, "%u", args[i].n);
+ else
+ fprintf(f, "nil");
+ break;
+ case 'a':
+ fprintf(f, "array");
+ break;
+ case 'h':
+ fprintf(f, "fd %d", args[i].h);
+ break;
+ }
+ }
+
+ fprintf(f, ")\n");
+
+ if (fclose(f) == 0) {
+ fprintf(stderr, "%s", buffer);
+ free(buffer);
+ }
+}
+
static void
callback_done(void *data, struct wl_callback *cb, uint32_t time)
{
@@ -218,12 +343,15 @@ logger_setup(struct compositor *compositor, struct client *client)
client->display = wl_display_connect(socket);
client->sequence_observer = wl_display_create_client_observer(
client->display, client_sequence_observer_func, client);
+ client->stderr_logger = wl_display_create_client_observer(
+ client->display, client_log_to_stderr_demo, client);
}
static void
logger_teardown(struct compositor *compositor, struct client *client)
{
wl_client_observer_destroy(client->sequence_observer);
+ wl_client_observer_destroy(client->stderr_logger);
wl_display_disconnect(client->display);
wl_client_destroy(compositor->client);