diff --git a/meson.build b/meson.build
index 37c14687..ce386a4c 100644
--- a/meson.build
+++ b/meson.build
@@ -46,6 +46,7 @@ have_funcs = [
'memfd_create',
'mremap',
'strndup',
+ 'gettid',
]
foreach f: have_funcs
config_h.set('HAVE_' + f.underscorify().to_upper(), cc.has_function(f))
diff --git a/protocol/wayland.xml b/protocol/wayland.xml
index bee74a10..707af108 100644
--- a/protocol/wayland.xml
+++ b/protocol/wayland.xml
@@ -165,9 +165,13 @@
the global using the bind request, the client should now
destroy that object.
- The object remains valid and requests to the object will be
- ignored until the client destroys it, to avoid races between
- the global going away and a client sending a request to it.
+ Until the client destroys the object, the compositor will
+ ignore other requests to the object except that, for any
+ request that adds new objects to the compositor state,
+ the compositor will continue to add the new objects to
+ its state. These new objects transitively follow the same
+ rules.
+
diff --git a/src/connection.c b/src/connection.c
index 593f52f3..2d1e8d1d 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -26,6 +26,8 @@
#define _GNU_SOURCE
+#include "../config.h"
+
#include
#include
#include
@@ -1491,11 +1493,56 @@ wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection)
return result;
}
+bool
+wl_check_env_token(const char *env, const char *token)
+{
+ const char *ptr = env;
+ size_t token_len;
+
+ if (env == NULL)
+ return false;
+
+ token_len = strlen(token);
+
+ // Scan the string for comma-separated tokens and look for a match.
+ while (true) {
+ const char *end;
+ size_t len;
+
+ // Skip over any leading separators.
+ while (*ptr == ',')
+ ptr++;
+
+ if (*ptr == '\x00')
+ return false;
+
+ end = strchr(ptr + 1, ',');
+
+ // If there isn't another separarator, then the rest of the string
+ // is one token.
+ if (end == NULL)
+ return (strcmp(ptr, token) == 0);
+
+ len = end - ptr;
+ if (len == token_len && memcmp(ptr, token, len) == 0) {
+ return true;
+ }
+
+ // Skip to the next token.
+ ptr += len;
+ }
+
+ return false;
+}
+
void
wl_closure_print(struct wl_closure *closure, struct wl_object *target,
int send, int discarded, uint32_t (*n_parse)(union wl_argument *arg),
const char *queue_name, int color)
{
+#if defined(HAVE_GETTID)
+ static int include_tid = -1;
+#endif // defined(HAVE_GETTID)
int i;
struct argument_details arg;
const char *signature = closure->message->signature;
@@ -1516,6 +1563,18 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target,
color ? WL_DEBUG_COLOR_GREEN : "",
time / 1000, time % 1000);
+#if defined(HAVE_GETTID)
+ if (include_tid < 0) {
+ include_tid = wl_check_env_token(getenv("WAYLAND_DEBUG"), "thread_id");
+ }
+
+ if (include_tid) {
+ fprintf(f, "%sTID#%d ",
+ color ? WL_DEBUG_COLOR_CYAN : "",
+ (int) gettid());
+ }
+#endif
+
if (queue_name) {
fprintf(f, "%s{%s} ",
color ? WL_DEBUG_COLOR_YELLOW : "",
diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h
index 970e6254..e0523e49 100644
--- a/src/wayland-client-core.h
+++ b/src/wayland-client-core.h
@@ -268,9 +268,16 @@ int
wl_display_dispatch_queue_pending(struct wl_display *display,
struct wl_event_queue *queue);
+int
+wl_display_dispatch_queue_pending_single(struct wl_display *display,
+ struct wl_event_queue *queue);
+
int
wl_display_dispatch_pending(struct wl_display *display);
+int
+wl_display_dispatch_pending_single(struct wl_display *display);
+
int
wl_display_get_error(struct wl_display *display);
diff --git a/src/wayland-client.c b/src/wayland-client.c
index c8633046..ed686b5c 100644
--- a/src/wayland-client.c
+++ b/src/wayland-client.c
@@ -1236,7 +1236,7 @@ wl_display_connect_to_fd(int fd)
no_color = getenv("NO_COLOR");
force_color = getenv("FORCE_COLOR");
debug = getenv("WAYLAND_DEBUG");
- if (debug && (strstr(debug, "client") || strstr(debug, "1"))) {
+ if (debug && (wl_check_env_token(debug, "client") || wl_check_env_token(debug, "1"))) {
debug_client = 1;
if (isatty(fileno(stderr)))
debug_color = 1;
@@ -1882,6 +1882,34 @@ err:
return -1;
}
+
+static int
+dispatch_queue_single(struct wl_display *display, struct wl_event_queue *queue)
+{
+ if (display->last_error)
+ goto err;
+
+ while (!wl_list_empty(&display->display_queue.event_list)) {
+ dispatch_event(display, &display->display_queue);
+ if (display->last_error)
+ goto err;
+ }
+
+ if (!wl_list_empty(&queue->event_list)) {
+ dispatch_event(display, queue);
+ if (display->last_error)
+ goto err;
+ return 1;
+ } else {
+ return 0;
+ }
+
+err:
+ errno = display->last_error;
+
+ return -1;
+}
+
/** Prepare to read events from the display's file descriptor to a queue
*
* \param display The display context object
@@ -2212,6 +2240,34 @@ wl_display_dispatch_queue_pending(struct wl_display *display,
return ret;
}
+/** Dispatch at most one pending event in an event queue
+ *
+ * \param display The display context object
+ * \param queue The event queue to dispatch
+ * \return The number of dispatched events (0 or 1) on success or -1 on failure
+ *
+ * Dispatch at most one pending event for objects assigned to the given
+ * event queue. On failure -1 is returned and errno set appropriately.
+ * If there are no events queued, this function returns immediately.
+ *
+ * \memberof wl_display
+ * \since 1.25.0
+ */
+WL_EXPORT int
+wl_display_dispatch_queue_pending_single(struct wl_display *display,
+ struct wl_event_queue *queue)
+{
+ int ret;
+
+ pthread_mutex_lock(&display->mutex);
+
+ ret = dispatch_queue_single(display, queue);
+
+ pthread_mutex_unlock(&display->mutex);
+
+ return ret;
+}
+
/** Process incoming events
*
* \param display The display context object
@@ -2272,6 +2328,25 @@ wl_display_dispatch_pending(struct wl_display *display)
&display->default_queue);
}
+/** Dispatch at most one pending event in the default event queue.
+ *
+ * \param display The display context object
+ * \return The number of dispatched events (0 or 1) on success or -1 on failure
+ *
+ * Dispatch at most one pending event for objects assigned to the default
+ * event queue. On failure -1 is returned and errno set appropriately.
+ * If there are no events queued, this function returns immediately.
+ *
+ * \memberof wl_display
+ * \since 1.25.0
+ */
+WL_EXPORT int
+wl_display_dispatch_pending_single(struct wl_display *display)
+{
+ return wl_display_dispatch_queue_pending_single(display,
+ &display->default_queue);
+}
+
/** Retrieve the last error that occurred on a display
*
* \param display The display context object
diff --git a/src/wayland-private.h b/src/wayland-private.h
index d7ba9dae..d0e4cfc6 100644
--- a/src/wayland-private.h
+++ b/src/wayland-private.h
@@ -237,6 +237,9 @@ wl_closure_send(struct wl_closure *closure, struct wl_connection *connection);
int
wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection);
+bool
+wl_check_env_token(const char *env, const char *token);
+
void
wl_closure_print(struct wl_closure *closure,
struct wl_object *target, int send, int discarded,
diff --git a/src/wayland-server.c b/src/wayland-server.c
index 482743b3..c81d98f1 100644
--- a/src/wayland-server.c
+++ b/src/wayland-server.c
@@ -1198,7 +1198,7 @@ wl_display_create(void)
no_color = getenv("NO_COLOR");
force_color = getenv("FORCE_COLOR");
debug = getenv("WAYLAND_DEBUG");
- if (debug && (strstr(debug, "server") || strstr(debug, "1"))) {
+ if (debug && (wl_check_env_token(debug, "server") || wl_check_env_token(debug, "1"))) {
debug_server = 1;
if (isatty(fileno(stderr)))
debug_color = 1;
diff --git a/tests/display-test.c b/tests/display-test.c
index 89606c73..fe78b521 100644
--- a/tests/display-test.c
+++ b/tests/display-test.c
@@ -1695,6 +1695,75 @@ TEST(global_remove)
display_destroy(d);
}
+static void
+dispatch_single_read_events(struct wl_display *d)
+{
+ if (wl_display_prepare_read(d) < 0) {
+ return;
+ }
+
+ int ret = 0;
+ do {
+ ret = wl_display_flush(d);
+ } while (ret < 0 && (errno == EINTR || errno == EAGAIN));
+ assert(ret >= 0);
+
+ struct pollfd pfd[1];
+ pfd[0].fd = wl_display_get_fd(d);
+ pfd[0].events = POLLIN;
+
+ do {
+ ret = poll(pfd, 1, -1);
+ } while (ret < 0 && errno == EINTR);
+ assert(ret > 0);
+
+ wl_display_read_events(d);
+}
+
+static void
+dispatch_single_client(void)
+{
+ struct client *c = client_connect();
+
+ assert(wl_display_dispatch_pending_single(c->wl_display) == 0);
+
+ struct wl_registry *registry = wl_display_get_registry(c->wl_display);
+
+ dispatch_single_read_events(c->wl_display);
+
+ // [1815110.061] {Default Queue} wl_registry#3.global(1, "test", 1)
+ assert(wl_display_dispatch_pending_single(c->wl_display) == 1);
+
+ dispatch_single_read_events(c->wl_display);
+
+ // [1815110.067] {Default Queue} wl_registry#3.global(2, "wl_seat", 1)
+ assert(wl_display_dispatch_pending_single(c->wl_display) == 1);
+
+ // No more events
+ assert(wl_display_dispatch_pending_single(c->wl_display) == 0);
+
+ wl_registry_destroy(registry);
+
+ client_disconnect(c);
+}
+
+TEST(dispatch_single)
+{
+ struct display *d = display_create();
+
+ struct wl_global *global = wl_global_create(d->wl_display,
+ &wl_seat_interface,
+ 1, d, bind_seat);
+
+ client_create_noarg(d, dispatch_single_client);
+
+ display_run(d);
+
+ wl_global_destroy(global);
+
+ display_destroy(d);
+}
+
static void
terminate_display(void *arg)
{