blob: 662f38ba4a7191952b415fe3d07bd5d69dac3384 [file] [log] [blame]
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lloyd Pique <lpique@google.com>
Date: Fri, 29 Jan 2021 14:26:03 -0800
Subject: [PATCH 1/3] client: Support client protocol loggers
This is very much based on commit 450f06e2 which added server protocol
loggers.
Adds a new pair of public API functions:
* wl_display_add_protocol_logger_client allows the client to register a
function to be called to log each message that is sent or received.
* wl_protocol_logger_client_destroy allows the client to unregister the
function.
As with the server protocol loggers, this is akin to setting
WAYLAND_DEBUG=1, but allows the client code to choose how the messages
are logged, and it can also enable and disable logging at run time.
The logging logic for the client was also changed to log all events, not
just the ones that have listeners or dispatchers.
Signed-off-by: Lloyd Pique <lpique@google.com>
diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h
index 0cd96e0..547ae04 100644
--- a/src/wayland-client-core.h
+++ b/src/wayland-client-core.h
@@ -267,6 +267,32 @@ wl_display_read_events(struct wl_display *display);
void
wl_log_set_handler_client(wl_log_func_t handler);
+enum wl_protocol_logger_client_type {
+ WL_PROTOCOL_LOGGER_CLIENT_REQUEST,
+ WL_PROTOCOL_LOGGER_CLIENT_EVENT,
+};
+
+struct wl_protocol_logger_client_message {
+ struct wl_proxy *proxy;
+ int message_opcode;
+ const struct wl_message *message;
+ int arguments_count;
+ const union wl_argument *arguments;
+};
+
+typedef void (*wl_protocol_logger_client_func_t)(
+ void *user_data,
+ enum wl_protocol_logger_client_type direction,
+ const struct wl_protocol_logger_client_message *message);
+
+struct wl_protocol_logger_client *
+wl_display_add_protocol_logger_client(struct wl_display *display,
+ wl_protocol_logger_client_func_t,
+ void *user_data);
+
+void
+wl_protocol_logger_client_destroy(struct wl_protocol_logger_client *logger);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/wayland-client.c b/src/wayland-client.c
index 21d4606..7f5a651 100644
--- a/src/wayland-client.c
+++ b/src/wayland-client.c
@@ -107,12 +107,47 @@ struct wl_display {
int reader_count;
uint32_t read_serial;
pthread_cond_t reader_cond;
+
+ struct wl_list protocol_loggers;
};
/** \endcond */
+struct wl_protocol_logger_client {
+ struct wl_list link;
+ wl_protocol_logger_client_func_t func;
+ void *user_data;
+};
+
static int debug_client = 0;
+static void
+log_closure(struct wl_closure *closure, struct wl_proxy* proxy, int send)
+{
+ struct wl_display *display = proxy->display;
+ struct wl_protocol_logger_client *protocol_logger;
+ struct wl_protocol_logger_client_message message;
+
+ if (debug_client)
+ wl_closure_print(closure, &proxy->object, send);
+
+ if (!wl_list_empty(&display->protocol_loggers)) {
+ message.proxy = proxy;
+ message.message_opcode = closure->opcode;
+ message.message = closure->message;
+ message.arguments_count = closure->count;
+ message.arguments = closure->args;
+ wl_list_for_each(protocol_logger, &display->protocol_loggers,
+ link) {
+ protocol_logger->func(
+ protocol_logger->user_data,
+ send ? WL_PROTOCOL_LOGGER_CLIENT_REQUEST :
+ WL_PROTOCOL_LOGGER_CLIENT_EVENT,
+ &message);
+ }
+ }
+}
+
/**
* This helper function wakes up all threads that are
* waiting for display->reader_cond (i. e. when reading is done,
@@ -751,8 +786,7 @@ wl_proxy_marshal_array_constructor_versioned(struct wl_proxy *proxy,
goto err_unlock;
}
- if (debug_client)
- wl_closure_print(closure, &proxy->object, true);
+ log_closure(closure, proxy, true);
if (wl_closure_send(closure, proxy->display->connection)) {
wl_log("Error sending request: %s\n", strerror(errno));
@@ -1056,6 +1090,7 @@ wl_display_connect_to_fd(int fd)
pthread_mutex_init(&display->mutex, NULL);
pthread_cond_init(&display->reader_cond, NULL);
display->reader_count = 0;
+ wl_list_init(&display->protocol_loggers);
wl_map_insert_new(&display->objects, 0, NULL);
@@ -1177,6 +1212,7 @@ wl_display_disconnect(struct wl_display *display)
wl_map_release(&display->objects);
wl_event_queue_release(&display->default_queue);
wl_event_queue_release(&display->display_queue);
+ wl_list_remove(&display->protocol_loggers);
pthread_mutex_destroy(&display->mutex);
pthread_cond_destroy(&display->reader_cond);
close(display->fd);
@@ -1439,16 +1475,12 @@ dispatch_event(struct wl_display *display, struct wl_event_queue *queue)
pthread_mutex_unlock(&display->mutex);
- if (proxy->dispatcher) {
- if (debug_client)
- wl_closure_print(closure, &proxy->object, false);
+ log_closure(closure, proxy, false);
+ if (proxy->dispatcher) {
wl_closure_dispatch(closure, proxy->dispatcher,
&proxy->object, opcode);
} else if (proxy->object.implementation) {
- if (debug_client)
- wl_closure_print(closure, &proxy->object, false);
-
wl_closure_invoke(closure, WL_CLOSURE_INVOKE_CLIENT,
&proxy->object, opcode, proxy->user_data);
}
@@ -2280,3 +2312,60 @@ wl_log_set_handler_client(wl_log_func_t handler)
{
wl_log_handler = handler;
}
+
+/** Adds a new protocol client logger.
+ *
+ * When a new protocol message arrives or is sent from the client
+ * all the protocol logger functions will be called, carrying the
+ * \a user_data pointer, the type of the message (request or
+ * event) and the actual message.
+ * The lifetime of the messages passed to the logger function ends
+ * when they return so the messages cannot be stored and accessed
+ * later.
+ *
+ * \a errno is set on error.
+ *
+ * \param display The display object
+ * \param func The function to call to log a new protocol message
+ * \param user_data The user data pointer to pass to \a func
+ *
+ * \return The protocol logger object on success, NULL on failure.
+ *
+ * \sa wl_protocol_logger_client_destroy
+ *
+ * \memberof wl_display
+ */
+WL_EXPORT struct wl_protocol_logger_client *
+wl_display_add_protocol_logger_client(struct wl_display *display,
+ wl_protocol_logger_client_func_t func,
+ void *user_data)
+{
+ struct wl_protocol_logger_client *logger;
+
+ logger = malloc(sizeof *logger);
+ if (!logger)
+ return NULL;
+
+ logger->func = func;
+ logger->user_data = user_data;
+ wl_list_insert(&display->protocol_loggers, &logger->link);
+
+ return logger;
+}
+
+/** Destroys a protocol client logger.
+ *
+ * This function destroys a protocol client logger and removes it from the
+ * display it was added to with \a wl_display_add_protocol_logger_client.
+ * The \a logger object becomes invalid after calling this function.
+ *
+ * \sa wl_display_add_protocol_logger_client
+ *
+ * \memberof wl_protocol_logger_client
+ */
+WL_EXPORT void
+wl_protocol_logger_client_destroy(struct wl_protocol_logger_client *logger)
+{
+ wl_list_remove(&logger->link);
+ free(logger);
+}
diff --git a/tests/protocol-logger-test.c b/tests/protocol-logger-test.c
index 80c74aa..e409368 100644
--- a/tests/protocol-logger-test.c
+++ b/tests/protocol-logger-test.c
@@ -52,6 +52,12 @@ struct compositor {
struct wl_client *client;
};
+struct client {
+ struct wl_display *display;
+ struct wl_callback *cb;
+ int message;
+};
+
struct message {
enum wl_protocol_logger_type type;
const char *class;
@@ -82,6 +88,36 @@ struct message {
},
};
+struct client_message {
+ enum wl_protocol_logger_client_type type;
+ const char *class;
+ int opcode;
+ const char *message_name;
+ int args_count;
+} client_messages[] = {
+ {
+ .type = WL_PROTOCOL_LOGGER_CLIENT_REQUEST,
+ .class = "wl_display",
+ .opcode = 0,
+ .message_name = "sync",
+ .args_count = 1,
+ },
+ {
+ .type = WL_PROTOCOL_LOGGER_CLIENT_EVENT,
+ .class = "wl_display",
+ .opcode = 1,
+ .message_name = "delete_id",
+ .args_count = 1,
+ },
+ {
+ .type = WL_PROTOCOL_LOGGER_CLIENT_EVENT,
+ .class = "wl_callback",
+ .opcode = 0,
+ .message_name = "done",
+ .args_count = 1,
+ },
+};
+
static void
logger_func(void *user_data, enum wl_protocol_logger_type type,
const struct wl_protocol_logger_message *message)
@@ -98,6 +134,20 @@ logger_func(void *user_data, enum wl_protocol_logger_type type,
c->client = wl_resource_get_client(message->resource);
}
+static void
+client_logger_func(void *user_data, enum wl_protocol_logger_client_type type,
+ const struct wl_protocol_logger_client_message *message)
+{
+ struct client *c = user_data;
+ struct client_message *msg = &client_messages[c->message++];
+
+ assert(msg->type == type);
+ assert(strcmp(msg->class, wl_proxy_get_class(message->proxy)) == 0);
+ assert(msg->opcode == message->message_opcode);
+ assert(strcmp(msg->message_name, message->message->name) == 0);
+ assert(msg->args_count == message->arguments_count);
+}
+
static void
callback_done(void *data, struct wl_callback *cb, uint32_t time)
{
@@ -114,11 +164,9 @@ TEST(logger)
const char *socket;
struct compositor compositor = { 0 };
- struct {
- struct wl_display *display;
- struct wl_callback *cb;
- } client;
+ struct client client = { 0 };
struct wl_protocol_logger *logger;
+ struct wl_protocol_logger_client *logger_client;
require_xdg_runtime_dir();
@@ -130,6 +178,8 @@ TEST(logger)
logger_func, &compositor);
client.display = wl_display_connect(socket);
+ logger_client = wl_display_add_protocol_logger_client(
+ client.display, client_logger_func, &client);
client.cb = wl_display_sync(client.display);
wl_callback_add_listener(client.cb, &callback_listener, NULL);
wl_display_flush(client.display);
@@ -142,6 +192,7 @@ TEST(logger)
wl_display_dispatch(client.display);
wl_display_disconnect(client.display);
+ wl_protocol_logger_client_destroy(logger_client);
wl_client_destroy(compositor.client);
wl_protocol_logger_destroy(logger);
wl_display_destroy(compositor.display);