diff --git a/src/wayland-server-core.h b/src/wayland-server-core.h index 00a54433..31888249 100644 --- a/src/wayland-server-core.h +++ b/src/wayland-server-core.h @@ -342,6 +342,10 @@ struct wl_listener * wl_client_get_destroy_late_listener(struct wl_client *client, wl_notify_func_t notify); +void +wl_client_add_event_dispatch_listener(struct wl_client *client, + struct wl_listener *listener); + struct wl_resource * wl_client_get_object(struct wl_client *client, uint32_t id); diff --git a/src/wayland-server.c b/src/wayland-server.c index 1e079a3b..7af2ec83 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -80,6 +80,7 @@ struct wl_client { struct wl_map objects; struct wl_priv_signal destroy_signal; struct wl_priv_signal destroy_late_signal; + struct wl_priv_signal event_dispatch_signal; pid_t pid; uid_t uid; gid_t gid; @@ -214,12 +215,13 @@ handle_array(struct wl_resource *resource, uint32_t opcode, { struct wl_closure *closure; struct wl_object *object = &resource->object; + struct wl_client *client = resource->client; - if (resource->client->error) + if (client->error) return; if (!verify_objects(resource, opcode, args)) { - resource->client->error = true; + client->error = true; return; } @@ -227,14 +229,17 @@ handle_array(struct wl_resource *resource, uint32_t opcode, &object->interface->events[opcode]); if (closure == NULL) { - resource->client->error = true; + client->error = true; return; } log_closure(resource, closure, true); - if (send_func(closure, resource->client->connection)) - resource->client->error = true; + if (send_func(closure, client->connection)) { + client->error = true; + } else { + wl_priv_signal_final_emit(&client->event_dispatch_signal, client); + } wl_closure_destroy(closure); } @@ -551,6 +556,7 @@ wl_client_create(struct wl_display *display, int fd) wl_priv_signal_init(&client->destroy_signal); wl_priv_signal_init(&client->destroy_late_signal); + wl_priv_signal_init(&client->event_dispatch_signal); if (bind_display(client, display) < 0) goto err_map; @@ -930,6 +936,27 @@ wl_client_get_destroy_late_listener(struct wl_client *client, return wl_priv_signal_get(&client->destroy_late_signal, notify); } +/** Add a listener to be called after successful event dispatch. + * + * \param client The client object. + * \param listener The listener to be added. + * + * After an event has been dispatched to this client, the listener will + * be notified. + * + * There is no requirement to remove the link of the wl_listener when the + * signal is emitted. + * + * \memberof wl_client + * \since 1.22.0 + */ +WL_EXPORT void +wl_client_add_event_dispatch_listener(struct wl_client *client, + struct wl_listener *listener) +{ + wl_priv_signal_add(&client->event_dispatch_signal, listener); +} + WL_EXPORT void wl_client_destroy(struct wl_client *client) { diff --git a/tests/client-test.c b/tests/client-test.c index 659bac10..0148caa1 100644 --- a/tests/client-test.c +++ b/tests/client-test.c @@ -158,3 +158,47 @@ TEST(client_destroy_listener) wl_display_destroy(display); } +struct event_dispatch_listener { + struct wl_listener listener; + bool done; +}; + +static void +event_dispatch_notify(struct wl_listener *l, void *data) +{ + struct event_dispatch_listener *listener = + wl_container_of(l, listener, listener); + listener->done = true; +} + +TEST(event_dispatch_listener) +{ + struct wl_display *display; + struct wl_client *client; + struct wl_resource *resource; + struct event_dispatch_listener dispatch_listener; + int s[2]; + + assert(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, s) == 0); + display = wl_display_create(); + assert(display); + client = wl_client_create(display, s[0]); + assert(client); + + dispatch_listener.listener.notify = event_dispatch_notify; + dispatch_listener.done = false; + wl_client_add_event_dispatch_listener(client, &dispatch_listener.listener); + + resource = wl_resource_create(client, &wl_seat_interface, 4, 0); + assert(resource); + wl_seat_send_name(resource, "default"); + + wl_event_loop_dispatch(wl_display_get_event_loop(display), 0); + + assert(dispatch_listener.done); + + close(s[1]); + wl_client_destroy(client); + wl_display_destroy(display); +} +