This MR introduces the driver mechanisms to handle dynamic events from the Wayland compositor, using wl_output events as the guiding use case (i.e., we want to update the win32u display settings when the host settings change).
In this design we create a dedicated thread to read and dispatch Wayland events received from the compositor. If a Wayland event handler wants some code to be run in the context of a particular HWND's thread, it can add an internal event to a custom queue we have for each (GUI enabled) thread. The ProcessEvents driver callback processes internal events from that queue. In order to wake up waiting threads we use a pipe to notify about new internal events, with the read end acting as the thread's host queue fd. This is similar to how winemac.drv works.
We use the aforementioned mechanisms to queue win32u display device updates to the desktop window thread. Since there are many pieces that need to fall into place, this MR gradually reaches the final design:
1. We first introduce the dedicated read/dispatch thread and handle events (and also display device updates if in the desktop process) in that thread. 2. We ensure access to Wayland output information is thread-safe and consistent (since in step 3 we will need to access it from a different thread). 3. We finally introduce per-thread internal event queues and, if we are in the desktop process, queue the display device update to the desktop window thread internal event queue. Note that the main portion of the wl_output event code is still handled in the dedicated read/dispatch thread.
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Create a dedicated thread from which we read and dispatch Wayland events (beyond the initial ones).
Signed-off-by: Alexandros Frantzis alexandros.frantzis@collabora.com --- dlls/winewayland.drv/dllmain.c | 15 +++++++++++++++ dlls/winewayland.drv/unixlib.h | 1 + dlls/winewayland.drv/waylanddrv_main.c | 12 ++++++++++++ 3 files changed, 28 insertions(+)
diff --git a/dlls/winewayland.drv/dllmain.c b/dlls/winewayland.drv/dllmain.c index 89d981a4314..9e303931556 100644 --- a/dlls/winewayland.drv/dllmain.c +++ b/dlls/winewayland.drv/dllmain.c @@ -20,8 +20,20 @@
#include "waylanddrv_dll.h"
+static DWORD WINAPI wayland_read_events_thread(void *arg) +{ + WAYLANDDRV_UNIX_CALL(read_events, NULL); + /* This thread terminates only if an unrecoverable error occurred + * during event reading (e.g., the connection to the Wayland + * compositor is broken). */ + TerminateProcess(GetCurrentProcess(), 1); + return 0; +} + BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) { + DWORD tid; + if (reason != DLL_PROCESS_ATTACH) return TRUE;
DisableThreadLibraryCalls(instance); @@ -30,5 +42,8 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) if (WAYLANDDRV_UNIX_CALL(init, NULL)) return FALSE;
+ /* Read wayland events from a dedicated thread. */ + CreateThread(NULL, 0, wayland_read_events_thread, NULL, 0, &tid); + return TRUE; } diff --git a/dlls/winewayland.drv/unixlib.h b/dlls/winewayland.drv/unixlib.h index 427837523e3..dc3bfdf8893 100644 --- a/dlls/winewayland.drv/unixlib.h +++ b/dlls/winewayland.drv/unixlib.h @@ -26,6 +26,7 @@ enum waylanddrv_unix_func { waylanddrv_unix_func_init, + waylanddrv_unix_func_read_events, waylanddrv_unix_func_count, };
diff --git a/dlls/winewayland.drv/waylanddrv_main.c b/dlls/winewayland.drv/waylanddrv_main.c index a9297edc500..da5770ef5e7 100644 --- a/dlls/winewayland.drv/waylanddrv_main.c +++ b/dlls/winewayland.drv/waylanddrv_main.c @@ -49,9 +49,20 @@ err: return STATUS_UNSUCCESSFUL; }
+static NTSTATUS waylanddrv_unix_read_events(void *arg) +{ + while (wl_display_dispatch_queue(process_wl_display, + process_wayland->wl_event_queue) != -1) + continue; + /* This function only returns on a fatal error, e.g., if our connection + * to the Wayland server is lost. */ + return STATUS_UNSUCCESSFUL; +} + const unixlib_entry_t __wine_unix_call_funcs[] = { waylanddrv_unix_init, + waylanddrv_unix_read_events, };
C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == waylanddrv_unix_func_count); @@ -61,6 +72,7 @@ C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == waylanddrv_unix_func_count); const unixlib_entry_t __wine_unix_call_wow64_funcs[] = { waylanddrv_unix_init, + waylanddrv_unix_read_events, };
C_ASSERT(ARRAYSIZE(__wine_unix_call_wow64_funcs) == waylanddrv_unix_func_count);
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Handle Wayland output events sent after process initialization, and update the win32u display devices when handling these events in the desktop window process.
Signed-off-by: Alexandros Frantzis alexandros.frantzis@collabora.com --- dlls/winewayland.drv/display.c | 14 +++++++++++--- dlls/winewayland.drv/wayland.c | 5 ++++- dlls/winewayland.drv/wayland_output.c | 24 ++++++++++++++++++++++++ dlls/winewayland.drv/waylanddrv.h | 3 ++- 4 files changed, 41 insertions(+), 5 deletions(-)
diff --git a/dlls/winewayland.drv/display.c b/dlls/winewayland.drv/display.c index 5234a1f03aa..0e1c7f17d1e 100644 --- a/dlls/winewayland.drv/display.c +++ b/dlls/winewayland.drv/display.c @@ -34,9 +34,15 @@
WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
-void wayland_init_display_devices(void) +BOOL force_display_devices_refresh = FALSE; + +void wayland_init_display_devices(BOOL force) { UINT32 num_path, num_mode; + + TRACE("force=%d\n", force); + + if (force) force_display_devices_refresh = TRUE; /* Trigger refresh in win32u */ NtUserGetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &num_path, &num_mode); } @@ -284,9 +290,11 @@ BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manage struct wl_array output_info_array; struct output_info *output_info;
- if (!force) return TRUE; + if (!force && !force_display_devices_refresh) return TRUE;
- TRACE("force=%d\n", force); + TRACE("force=%d force_refresh=%d\n", force, force_display_devices_refresh); + + force_display_devices_refresh = FALSE;
wl_array_init(&output_info_array);
diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index 22a7a8cc63f..c6ebbe9aeb9 100644 --- a/dlls/winewayland.drv/wayland.c +++ b/dlls/winewayland.drv/wayland.c @@ -34,6 +34,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
struct wl_display *process_wl_display = NULL; struct wayland *process_wayland = NULL; +BOOL process_wayland_initialized = FALSE;
/********************************************************************** * Registry handling @@ -140,7 +141,9 @@ BOOL wayland_process_init(void) wl_display_roundtrip_queue(process_wl_display, process_wayland->wl_event_queue); wl_display_roundtrip_queue(process_wl_display, process_wayland->wl_event_queue);
- wayland_init_display_devices(); + wayland_init_display_devices(FALSE); + + process_wayland_initialized = TRUE;
return TRUE; } diff --git a/dlls/winewayland.drv/wayland_output.c b/dlls/winewayland.drv/wayland_output.c index 5cd6e74f24a..7d49c6d81fe 100644 --- a/dlls/winewayland.drv/wayland_output.c +++ b/dlls/winewayland.drv/wayland_output.c @@ -97,6 +97,26 @@ static void wayland_output_add_mode(struct wayland_output *output, if (current) output->current_mode = mode; }
+static void maybe_init_display_devices(void) +{ + DWORD desktop_pid = 0; + HWND desktop_hwnd; + + /* Right after process init we initialize all the display devices, so there + * is no need to react to each individual event at that time. This check + * also helps us avoid calling NtUserGetDesktopWindow() (see below) at + * process init time, since it may not be safe. */ + if (!process_wayland_initialized) return; + + desktop_hwnd = NtUserGetDesktopWindow(); + NtUserGetWindowThread(desktop_hwnd, &desktop_pid); + + /* We only update the display devices from the desktop process. */ + if (GetCurrentProcessId() != desktop_pid) return; + + wayland_init_display_devices(TRUE); +} + static void wayland_output_done(struct wayland_output *output) { struct wayland_output_mode *mode; @@ -111,6 +131,8 @@ static void wayland_output_done(struct wayland_output *output) mode->width, mode->height, mode->refresh, output->current_mode == mode ? "*" : ""); } + + maybe_init_display_devices(); }
static void output_handle_geometry(void *data, struct wl_output *wl_output, @@ -283,6 +305,8 @@ void wayland_output_destroy(struct wayland_output *output) wl_output_destroy(output->wl_output); free(output->name); free(output); + + maybe_init_display_devices(); }
/********************************************************************** diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index e1ea66f3b33..6316079019a 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -41,6 +41,7 @@
extern struct wl_display *process_wl_display DECLSPEC_HIDDEN; extern struct wayland *process_wayland DECLSPEC_HIDDEN; +extern BOOL process_wayland_initialized DECLSPEC_HIDDEN;
/********************************************************************** * Definitions for wayland types @@ -80,7 +81,7 @@ struct wayland_output */
BOOL wayland_process_init(void) DECLSPEC_HIDDEN; -void wayland_init_display_devices(void) DECLSPEC_HIDDEN; +void wayland_init_display_devices(BOOL force) DECLSPEC_HIDDEN;
/********************************************************************** * Wayland output
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Store all incoming Wayland output state as pending and make it current in a thread-safe way when the "done" event arrives. This enables other threads, with proper locking, to safely read consistent and complete Wayland output information.
Signed-off-by: Alexandros Frantzis alexandros.frantzis@collabora.com --- dlls/winewayland.drv/Makefile.in | 2 +- dlls/winewayland.drv/display.c | 10 ++- dlls/winewayland.drv/wayland.c | 12 ++- dlls/winewayland.drv/wayland_output.c | 115 +++++++++++++++++++------- dlls/winewayland.drv/waylanddrv.h | 18 +++- 5 files changed, 120 insertions(+), 37 deletions(-)
diff --git a/dlls/winewayland.drv/Makefile.in b/dlls/winewayland.drv/Makefile.in index 614ce8fe6e1..3f9c052f2b0 100644 --- a/dlls/winewayland.drv/Makefile.in +++ b/dlls/winewayland.drv/Makefile.in @@ -1,7 +1,7 @@ MODULE = winewayland.drv UNIXLIB = winewayland.so UNIX_CFLAGS = $(WAYLAND_CLIENT_CFLAGS) -UNIX_LIBS = -lwin32u $(WAYLAND_CLIENT_LIBS) +UNIX_LIBS = -lwin32u $(WAYLAND_CLIENT_LIBS) $(PTHREAD_LIBS)
SOURCES = \ display.c \ diff --git a/dlls/winewayland.drv/display.c b/dlls/winewayland.drv/display.c index 0e1c7f17d1e..5fab28dc871 100644 --- a/dlls/winewayland.drv/display.c +++ b/dlls/winewayland.drv/display.c @@ -50,7 +50,7 @@ void wayland_init_display_devices(BOOL force) struct output_info { int x, y; - struct wayland_output *output; + struct wayland_output_state *output; };
static int output_info_cmp_primary_x_y(const void *va, const void *vb) @@ -298,11 +298,13 @@ BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manage
wl_array_init(&output_info_array);
+ pthread_mutex_lock(&process_wayland->output_mutex); + wl_list_for_each(output, &process_wayland->output_list, link) { - if (!output->current_mode) continue; + if (!output->current.current_mode) continue; output_info = wl_array_add(&output_info_array, sizeof(*output_info)); - if (output_info) output_info->output = output; + if (output_info) output_info->output = &output->current; else ERR("Failed to allocate space for output_info\n"); }
@@ -321,5 +323,7 @@ BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manage
wl_array_release(&output_info_array);
+ pthread_mutex_unlock(&process_wayland->output_mutex); + return TRUE; } diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index c6ebbe9aeb9..561ef68166b 100644 --- a/dlls/winewayland.drv/wayland.c +++ b/dlls/winewayland.drv/wayland.c @@ -36,6 +36,15 @@ struct wl_display *process_wl_display = NULL; struct wayland *process_wayland = NULL; BOOL process_wayland_initialized = FALSE;
+static void wayland_mutex_init(pthread_mutex_t *mutex, int type) +{ + pthread_mutexattr_t mutexattr; + pthread_mutexattr_init(&mutexattr); + pthread_mutexattr_settype(&mutexattr, type); + pthread_mutex_init(mutex, &mutexattr); + pthread_mutexattr_destroy(&mutexattr); +} + /********************************************************************** * Registry handling */ @@ -76,7 +85,7 @@ static void registry_handle_global_remove(void *data, struct wl_registry *regist { if (output->global_id == id) { - TRACE("removing output->name=%s\n", output->name); + TRACE("removing output->name=%s\n", output->current.name); wayland_output_destroy(output); return; } @@ -131,6 +140,7 @@ BOOL wayland_process_init(void) }
wl_list_init(&process_wayland->output_list); + wayland_mutex_init(&process_wayland->output_mutex, PTHREAD_MUTEX_NORMAL);
/* Populate registry */ wl_registry_add_listener(process_wayland->wl_registry, ®istry_listener, diff --git a/dlls/winewayland.drv/wayland_output.c b/dlls/winewayland.drv/wayland_output.c index 7d49c6d81fe..ed6bf6c2068 100644 --- a/dlls/winewayland.drv/wayland_output.c +++ b/dlls/winewayland.drv/wayland_output.c @@ -35,6 +35,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv); static const int32_t default_refresh = 60000; static uint32_t next_output_id = 0;
+#define WAYLAND_OUTPUT_CHANGED_MODES 0x01 +#define WAYLAND_OUTPUT_CHANGED_NAME 0x02 +#define WAYLAND_OUTPUT_CHANGED_LOGICAL_XY 0x04 +#define WAYLAND_OUTPUT_CHANGED_LOGICAL_WH 0x08 + /********************************************************************** * Output handling */ @@ -62,9 +67,9 @@ static int wayland_output_mode_cmp_rb(const void *key, return 0; }
-static void wayland_output_add_mode(struct wayland_output *output, - int32_t width, int32_t height, - int32_t refresh, BOOL current) +static void wayland_output_state_add_mode(struct wayland_output_state *state, + int32_t width, int32_t height, + int32_t refresh, BOOL current) { struct rb_entry *mode_entry; struct wayland_output_mode *mode; @@ -75,7 +80,7 @@ static void wayland_output_add_mode(struct wayland_output *output, .refresh = refresh, };
- mode_entry = rb_get(&output->modes, &key); + mode_entry = rb_get(&state->modes, &key); if (mode_entry) { mode = RB_ENTRY_VALUE(mode_entry, struct wayland_output_mode, entry); @@ -91,10 +96,10 @@ static void wayland_output_add_mode(struct wayland_output *output, mode->width = width; mode->height = height; mode->refresh = refresh; - rb_put(&output->modes, mode, &mode->entry); + rb_put(&state->modes, mode, &mode->entry); }
- if (current) output->current_mode = mode; + if (current) state->current_mode = mode; }
static void maybe_init_display_devices(void) @@ -117,19 +122,65 @@ static void maybe_init_display_devices(void) wayland_init_display_devices(TRUE); }
+static void wayland_output_mode_free_rb(struct rb_entry *entry, void *ctx) +{ + free(RB_ENTRY_VALUE(entry, struct wayland_output_mode, entry)); +} + static void wayland_output_done(struct wayland_output *output) { struct wayland_output_mode *mode;
+ /* Update current state from pending state. */ + pthread_mutex_lock(&process_wayland->output_mutex); + + if (output->pending_flags & WAYLAND_OUTPUT_CHANGED_MODES) + { + RB_FOR_EACH_ENTRY(mode, &output->pending.modes, struct wayland_output_mode, entry) + { + wayland_output_state_add_mode(&output->current, + mode->width, mode->height, mode->refresh, + mode == output->pending.current_mode); + } + rb_destroy(&output->pending.modes, wayland_output_mode_free_rb, NULL); + rb_init(&output->pending.modes, wayland_output_mode_cmp_rb); + } + + if (output->pending_flags & WAYLAND_OUTPUT_CHANGED_NAME) + { + free(output->current.name); + output->current.name = output->pending.name; + output->pending.name = NULL; + } + + if (output->pending_flags & WAYLAND_OUTPUT_CHANGED_LOGICAL_XY) + { + output->current.logical_x = output->pending.logical_x; + output->current.logical_y = output->pending.logical_y; + } + + if (output->pending_flags & WAYLAND_OUTPUT_CHANGED_LOGICAL_WH) + { + output->current.logical_w = output->pending.logical_w; + output->current.logical_h = output->pending.logical_h; + } + + if (wl_list_empty(&output->link)) + wl_list_insert(process_wayland->output_list.prev, &output->link); + + output->pending_flags = 0; + + pthread_mutex_unlock(&process_wayland->output_mutex); + TRACE("name=%s logical=%d,%d+%dx%d\n", - output->name, output->logical_x, output->logical_y, - output->logical_w, output->logical_h); + output->current.name, output->current.logical_x, output->current.logical_y, + output->current.logical_w, output->current.logical_h);
- RB_FOR_EACH_ENTRY(mode, &output->modes, struct wayland_output_mode, entry) + RB_FOR_EACH_ENTRY(mode, &output->current.modes, struct wayland_output_mode, entry) { TRACE("mode %dx%d @ %d %s\n", mode->width, mode->height, mode->refresh, - output->current_mode == mode ? "*" : ""); + output->current.current_mode == mode ? "*" : ""); }
maybe_init_display_devices(); @@ -153,8 +204,10 @@ static void output_handle_mode(void *data, struct wl_output *wl_output, /* Windows apps don't expect a zero refresh rate, so use a default value. */ if (refresh == 0) refresh = default_refresh;
- wayland_output_add_mode(output, width, height, refresh, - (flags & WL_OUTPUT_MODE_CURRENT)); + wayland_output_state_add_mode(&output->pending, width, height, refresh, + (flags & WL_OUTPUT_MODE_CURRENT)); + + output->pending_flags |= WAYLAND_OUTPUT_CHANGED_MODES; }
static void output_handle_done(void *data, struct wl_output *wl_output) @@ -187,8 +240,9 @@ static void zxdg_output_v1_handle_logical_position(void *data, { struct wayland_output *output = data; TRACE("logical_x=%d logical_y=%d\n", x, y); - output->logical_x = x; - output->logical_y = y; + output->pending.logical_x = x; + output->pending.logical_y = y; + output->pending_flags |= WAYLAND_OUTPUT_CHANGED_LOGICAL_XY; }
static void zxdg_output_v1_handle_logical_size(void *data, @@ -198,8 +252,9 @@ static void zxdg_output_v1_handle_logical_size(void *data, { struct wayland_output *output = data; TRACE("logical_w=%d logical_h=%d\n", width, height); - output->logical_w = width; - output->logical_h = height; + output->pending.logical_w = width; + output->pending.logical_h = height; + output->pending_flags |= WAYLAND_OUTPUT_CHANGED_LOGICAL_WH; }
static void zxdg_output_v1_handle_done(void *data, @@ -218,8 +273,9 @@ static void zxdg_output_v1_handle_name(void *data, { struct wayland_output *output = data;
- free(output->name); - output->name = strdup(name); + free(output->pending.name); + output->pending.name = strdup(name); + output->pending_flags |= WAYLAND_OUTPUT_CHANGED_NAME; }
static void zxdg_output_v1_handle_description(void *data, @@ -259,14 +315,15 @@ BOOL wayland_output_create(uint32_t id, uint32_t version) wl_output_add_listener(output->wl_output, &output_listener, output);
wl_list_init(&output->link); - rb_init(&output->modes, wayland_output_mode_cmp_rb); + rb_init(&output->pending.modes, wayland_output_mode_cmp_rb); + rb_init(&output->current.modes, wayland_output_mode_cmp_rb);
/* Have a fallback while we don't have compositor given name. */ name_len = snprintf(NULL, 0, "WaylandOutput%d", next_output_id); - output->name = malloc(name_len + 1); - if (output->name) + output->current.name = malloc(name_len + 1); + if (output->current.name) { - snprintf(output->name, name_len + 1, "WaylandOutput%d", next_output_id++); + snprintf(output->current.name, name_len + 1, "WaylandOutput%d", next_output_id++); } else { @@ -277,8 +334,6 @@ BOOL wayland_output_create(uint32_t id, uint32_t version) if (process_wayland->zxdg_output_manager_v1) wayland_output_use_xdg_extension(output);
- wl_list_insert(process_wayland->output_list.prev, &output->link); - return TRUE;
err: @@ -286,9 +341,10 @@ err: return FALSE; }
-static void wayland_output_mode_free_rb(struct rb_entry *entry, void *ctx) +static void wayland_output_state_deinit(struct wayland_output_state *state) { - free(RB_ENTRY_VALUE(entry, struct wayland_output_mode, entry)); + rb_destroy(&state->modes, wayland_output_mode_free_rb, NULL); + free(state->name); }
/********************************************************************** @@ -298,12 +354,15 @@ static void wayland_output_mode_free_rb(struct rb_entry *entry, void *ctx) */ void wayland_output_destroy(struct wayland_output *output) { - rb_destroy(&output->modes, wayland_output_mode_free_rb, NULL); + pthread_mutex_lock(&process_wayland->output_mutex); wl_list_remove(&output->link); + pthread_mutex_unlock(&process_wayland->output_mutex); + + wayland_output_state_deinit(&output->pending); + wayland_output_state_deinit(&output->current); if (output->zxdg_output_v1) zxdg_output_v1_destroy(output->zxdg_output_v1); wl_output_destroy(output->wl_output); - free(output->name); free(output);
maybe_init_display_devices(); diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 6316079019a..77efbd238e3 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -25,6 +25,7 @@ # error You must include config.h to use this header #endif
+#include <pthread.h> #include <wayland-client.h> #include "xdg-output-unstable-v1-client-protocol.h"
@@ -53,6 +54,8 @@ struct wayland struct wl_registry *wl_registry; struct zxdg_output_manager_v1 *zxdg_output_manager_v1; struct wl_list output_list; + /* Protects the output_list and the wayland_output.current states. */ + pthread_mutex_t output_mutex; };
struct wayland_output_mode @@ -63,17 +66,24 @@ struct wayland_output_mode int32_t refresh; };
-struct wayland_output +struct wayland_output_state { - struct wl_list link; - struct wl_output *wl_output; - struct zxdg_output_v1 *zxdg_output_v1; struct rb_tree modes; struct wayland_output_mode *current_mode; char *name; int logical_x, logical_y; int logical_w, logical_h; +}; + +struct wayland_output +{ + struct wl_list link; + struct wl_output *wl_output; + struct zxdg_output_v1 *zxdg_output_v1; uint32_t global_id; + unsigned int pending_flags; + struct wayland_output_state pending; + struct wayland_output_state current; };
/**********************************************************************
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Introduce per-thread data for GUI enabled threads. For now we create the per-thread data only for the desktop window.
Signed-off-by: Alexandros Frantzis alexandros.frantzis@collabora.com --- dlls/winewayland.drv/Makefile.in | 1 + dlls/winewayland.drv/waylanddrv.h | 12 +++++++ dlls/winewayland.drv/waylanddrv_main.c | 44 +++++++++++++++++++++++++ dlls/winewayland.drv/window.c | 45 ++++++++++++++++++++++++++ 4 files changed, 102 insertions(+) create mode 100644 dlls/winewayland.drv/window.c
diff --git a/dlls/winewayland.drv/Makefile.in b/dlls/winewayland.drv/Makefile.in index 3f9c052f2b0..454a7e5a4b4 100644 --- a/dlls/winewayland.drv/Makefile.in +++ b/dlls/winewayland.drv/Makefile.in @@ -10,4 +10,5 @@ SOURCES = \ wayland.c \ wayland_output.c \ waylanddrv_main.c \ + window.c \ xdg-output-unstable-v1.xml diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 77efbd238e3..b33efaa153d 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -86,12 +86,23 @@ struct wayland_output struct wayland_output_state current; };
+struct wayland_thread_data +{ + DWORD dummy; +}; + /********************************************************************** * Wayland initialization */
BOOL wayland_process_init(void) DECLSPEC_HIDDEN; void wayland_init_display_devices(BOOL force) DECLSPEC_HIDDEN; +struct wayland_thread_data *wayland_init_thread_data(void) DECLSPEC_HIDDEN; + +static inline struct wayland_thread_data *wayland_thread_data(void) +{ + return (struct wayland_thread_data *)(UINT_PTR)NtUserGetThreadInfo()->driver_data; +}
/********************************************************************** * Wayland output @@ -105,6 +116,7 @@ void wayland_output_use_xdg_extension(struct wayland_output *output) DECLSPEC_HI * USER driver functions */
+BOOL WAYLAND_CreateWindow(HWND hwnd) DECLSPEC_HIDDEN; BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manager, BOOL force, void *param) DECLSPEC_HIDDEN;
diff --git a/dlls/winewayland.drv/waylanddrv_main.c b/dlls/winewayland.drv/waylanddrv_main.c index da5770ef5e7..3c6a30d3015 100644 --- a/dlls/winewayland.drv/waylanddrv_main.c +++ b/dlls/winewayland.drv/waylanddrv_main.c @@ -29,8 +29,52 @@
#include "waylanddrv.h"
+#include "wine/debug.h" + +#include <stdlib.h> + +WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv); + +/*********************************************************************** + * Initialize per thread data + */ +struct wayland_thread_data *wayland_init_thread_data(void) +{ + struct wayland_thread_data *thread = wayland_thread_data(); + + if (thread) return thread; + + if (!(thread = calloc(1, sizeof(*thread)))) + { + ERR("could not create thread data\n"); + NtTerminateProcess(0, 1); + } + + NtUserGetThreadInfo()->driver_data = (UINT_PTR)thread; + + return thread; +} + +/*********************************************************************** + * ThreadDetach (WAYLAND.@) + */ +static void WAYLAND_ThreadDetach(void) +{ + struct wayland_thread_data *thread = wayland_thread_data(); + + if (thread) + { + free(thread); + /* Clear data in case we get re-entered from user32 before the thread + * is truly dead */ + NtUserGetThreadInfo()->driver_data = 0; + } +} + static const struct user_driver_funcs waylanddrv_funcs = { + .pCreateWindow = WAYLAND_CreateWindow, + .pThreadDetach = WAYLAND_ThreadDetach, .pUpdateDisplayDevices = WAYLAND_UpdateDisplayDevices, };
diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c new file mode 100644 index 00000000000..039a27fbecf --- /dev/null +++ b/dlls/winewayland.drv/window.c @@ -0,0 +1,45 @@ +/* + * Wayland driver + * + * Copyright 2020 Alexandros Frantzis for Collabora Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include "waylanddrv.h" + +#include "wine/debug.h" + +#include "ntuser.h" + +WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv); + +/********************************************************************** + * WAYLAND_CreateWindow + */ +BOOL WAYLAND_CreateWindow(HWND hwnd) +{ + TRACE("%p\n", hwnd); + + if (hwnd == NtUserGetDesktopWindow()) wayland_init_thread_data(); + + return TRUE; +}
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Introduce per-thread event queues and handle their events from the ProcessEvents driver callback, taking the event mask into account. We use unix pipe fds to notify about new events.
Use the per-thread queues to dispatch updates to display devices from the desktop window thread.
Signed-off-by: Alexandros Frantzis alexandros.frantzis@collabora.com --- dlls/winewayland.drv/wayland.c | 183 +++++++++++++++++++++++++ dlls/winewayland.drv/wayland_output.c | 8 +- dlls/winewayland.drv/waylanddrv.h | 15 +- dlls/winewayland.drv/waylanddrv_main.c | 35 +++++ 4 files changed, 239 insertions(+), 2 deletions(-)
diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index 561ef68166b..4974bd4e3b3 100644 --- a/dlls/winewayland.drv/wayland.c +++ b/dlls/winewayland.drv/wayland.c @@ -28,7 +28,10 @@
#include "wine/debug.h"
+#include <errno.h> +#include <fcntl.h> #include <stdlib.h> +#include <unistd.h>
WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
@@ -36,6 +39,16 @@ struct wl_display *process_wl_display = NULL; struct wayland *process_wayland = NULL; BOOL process_wayland_initialized = FALSE;
+static pthread_mutex_t thread_data_mutex = PTHREAD_MUTEX_INITIALIZER; +static struct wl_list thread_data_list = {&thread_data_list, &thread_data_list}; + +struct wayland_event +{ + struct wl_list link; + DWORD mask; + void (*handle)(void); +}; + static void wayland_mutex_init(pthread_mutex_t *mutex, int type) { pthread_mutexattr_t mutexattr; @@ -157,3 +170,173 @@ BOOL wayland_process_init(void)
return TRUE; } + +BOOL wayland_thread_init(struct wayland_thread_data *thread) +{ + wl_list_init(&thread->link); + + thread->thread_id = GetCurrentThreadId(); + + /* Thread wayland data have notification pipes to get informed when + * there might be new events in their queues. The read part of the + * pipe is also used as the wine server queue fd. */ + if (pipe(thread->event_notification_pipe) == -1 || + fcntl(thread->event_notification_pipe[0], F_SETFD, FD_CLOEXEC) == -1 || + fcntl(thread->event_notification_pipe[0], F_SETFL, O_NONBLOCK) == -1 || + fcntl(thread->event_notification_pipe[1], F_SETFD, FD_CLOEXEC) == -1 || + fcntl(thread->event_notification_pipe[1], F_SETFL, O_NONBLOCK) == -1) + return FALSE; + + wl_list_init(&thread->event_list); + wayland_mutex_init(&thread->event_mutex, PTHREAD_MUTEX_NORMAL); + + pthread_mutex_lock(&thread_data_mutex); + wl_list_insert(&thread_data_list, &thread->link); + pthread_mutex_unlock(&thread_data_mutex); + + return TRUE; +} + +void wayland_thread_deinit(struct wayland_thread_data *thread) +{ + struct wayland_event *event, *tmp; + + pthread_mutex_lock(&thread_data_mutex); + wl_list_remove(&thread->link); + pthread_mutex_unlock(&thread_data_mutex); + + pthread_mutex_lock(&thread->event_mutex); + wl_list_for_each_safe(event, tmp, &thread->event_list, link) + { + wl_list_remove(&event->link); + free(event); + } + pthread_mutex_unlock(&thread->event_mutex); + + pthread_mutex_destroy(&thread->event_mutex); + + close(thread->event_notification_pipe[0]); + close(thread->event_notification_pipe[1]); +} + +void wayland_queue_thread_event(HWND hwnd, DWORD mask, void (*handle)(void)) +{ + struct wayland_thread_data *thread; + DWORD tid; + struct wayland_event *event; + BOOL queued = FALSE; + + event = malloc(sizeof(*event)); + if (!event) + { + ERR("Failed to allocate space for wayland event\n"); + return; + } + wl_list_init(&event->link); + event->mask = mask; + event->handle = handle; + + tid = NtUserGetWindowThread(hwnd, NULL); + + pthread_mutex_lock(&thread_data_mutex); + + wl_list_for_each(thread, &thread_data_list, link) + { + if (thread->thread_id == tid) + { + int ret; + + pthread_mutex_lock(&thread->event_mutex); + wl_list_insert(thread->event_list.prev, &event->link); + pthread_mutex_unlock(&thread->event_mutex); + + queued = TRUE; + + do + { + ret = write(thread->event_notification_pipe[1], "a", 1); + } while (ret < 0 && errno == EINTR); + + if (ret < 0 && errno != EAGAIN) + ERR("Failed to write to notification pipe: %s\n", strerror(errno)); + + break; + } + } + + pthread_mutex_unlock(&thread_data_mutex); + + if (!queued) + { + ERR("Failed to queue thread event for hwnd=%p\n", hwnd); + free(event); + } +} + +static void wayland_consume_thread_notifications(struct wayland_thread_data *thread) +{ + int ret; + char buf[512]; + + do + { + ret = read(thread->event_notification_pipe[0], buf, sizeof(buf)); + } while (ret > 0 || (ret < 0 && errno == EINTR)); + + if (ret == 0) + ERR("Failed to read from notification pipe: pipe is closed\n"); + else if (ret == -1 && errno != EAGAIN) + ERR("Failed to read from notification pipe: %s\n", strerror(errno)); +} + +static BOOL wayland_dispatch_thread_events(struct wayland_thread_data *thread, + DWORD mask) +{ + struct wayland_event *event, *tmp; + DWORD dispatched = 0; + struct wl_list event_list; + + TRACE("mask=0x%lx\n", (long)mask); + + wl_list_init(&event_list); + + /* Copy matching events to local event list. */ + pthread_mutex_lock(&thread->event_mutex); + wl_list_for_each_safe(event, tmp, &thread->event_list, link) + { + if (event->mask & mask) + { + wl_list_remove(&event->link); + wl_list_insert(event_list.prev, &event->link); + } + } + pthread_mutex_unlock(&thread->event_mutex); + + /* Now handle matching events without any lock held. */ + wl_list_for_each_safe(event, tmp, &event_list, link) + { + event->handle(); + wl_list_remove(&event->link); + dispatched |= event->mask; + free(event); + } + + wl_display_flush(process_wl_display); + + TRACE("=> dispatched=0x%lx\n", (long)dispatched); + + return dispatched != 0; +} + +/*********************************************************************** + * WAYLAND_ProcessEvents + */ +BOOL WAYLAND_ProcessEvents(DWORD mask) +{ + struct wayland_thread_data *thread = wayland_thread_data(); + + if (!thread) return FALSE; + + wayland_consume_thread_notifications(thread); + return wayland_dispatch_thread_events(thread, mask); +} diff --git a/dlls/winewayland.drv/wayland_output.c b/dlls/winewayland.drv/wayland_output.c index ed6bf6c2068..487a629e198 100644 --- a/dlls/winewayland.drv/wayland_output.c +++ b/dlls/winewayland.drv/wayland_output.c @@ -102,6 +102,11 @@ static void wayland_output_state_add_mode(struct wayland_output_state *state, if (current) state->current_mode = mode; }
+static void handle_init_display_devices(void) +{ + wayland_init_display_devices(TRUE); +} + static void maybe_init_display_devices(void) { DWORD desktop_pid = 0; @@ -119,7 +124,8 @@ static void maybe_init_display_devices(void) /* We only update the display devices from the desktop process. */ if (GetCurrentProcessId() != desktop_pid) return;
- wayland_init_display_devices(TRUE); + wayland_queue_thread_event(desktop_hwnd, QS_SENDMESSAGE, + handle_init_display_devices); }
static void wayland_output_mode_free_rb(struct rb_entry *entry, void *ctx) diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index b33efaa153d..88dc31967be 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -88,7 +88,11 @@ struct wayland_output
struct wayland_thread_data { - DWORD dummy; + struct wl_list link; + DWORD thread_id; + pthread_mutex_t event_mutex; + struct wl_list event_list; + int event_notification_pipe[2]; };
/********************************************************************** @@ -98,6 +102,8 @@ struct wayland_thread_data BOOL wayland_process_init(void) DECLSPEC_HIDDEN; void wayland_init_display_devices(BOOL force) DECLSPEC_HIDDEN; struct wayland_thread_data *wayland_init_thread_data(void) DECLSPEC_HIDDEN; +BOOL wayland_thread_init(struct wayland_thread_data *thread) DECLSPEC_HIDDEN; +void wayland_thread_deinit(struct wayland_thread_data *thread) DECLSPEC_HIDDEN;
static inline struct wayland_thread_data *wayland_thread_data(void) { @@ -112,11 +118,18 @@ BOOL wayland_output_create(uint32_t id, uint32_t version) DECLSPEC_HIDDEN; void wayland_output_destroy(struct wayland_output *output) DECLSPEC_HIDDEN; void wayland_output_use_xdg_extension(struct wayland_output *output) DECLSPEC_HIDDEN;
+/********************************************************************** + * Wayland event dispatch + */ + +void wayland_queue_thread_event(HWND hwnd, DWORD mask, void (*handle)(void)) DECLSPEC_HIDDEN; + /********************************************************************** * USER driver functions */
BOOL WAYLAND_CreateWindow(HWND hwnd) DECLSPEC_HIDDEN; +BOOL WAYLAND_ProcessEvents(DWORD mask) DECLSPEC_HIDDEN; BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manager, BOOL force, void *param) DECLSPEC_HIDDEN;
diff --git a/dlls/winewayland.drv/waylanddrv_main.c b/dlls/winewayland.drv/waylanddrv_main.c index 3c6a30d3015..31b1368795b 100644 --- a/dlls/winewayland.drv/waylanddrv_main.c +++ b/dlls/winewayland.drv/waylanddrv_main.c @@ -30,11 +30,39 @@ #include "waylanddrv.h"
#include "wine/debug.h" +#include "wine/server.h"
#include <stdlib.h>
WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
+static void set_queue_fd(int fd) +{ + HANDLE handle; + int ret; + + if (wine_server_fd_to_handle(fd, GENERIC_READ | SYNCHRONIZE, 0, &handle)) + { + ERR("Can't allocate handle for wayland fd\n"); + NtTerminateProcess(0, 1); + } + + SERVER_START_REQ(set_queue_fd) + { + req->handle = wine_server_obj_handle(handle); + ret = wine_server_call(req); + } + SERVER_END_REQ; + + if (ret) + { + ERR("Can't store handle for wayland fd %x\n", ret); + NtTerminateProcess(0, 1); + } + + NtClose(handle); +} + /*********************************************************************** * Initialize per thread data */ @@ -50,6 +78,11 @@ struct wayland_thread_data *wayland_init_thread_data(void) NtTerminateProcess(0, 1); }
+ if (!wayland_thread_init(thread)) + NtTerminateProcess(0, 1); + + set_queue_fd(thread->event_notification_pipe[0]); + NtUserGetThreadInfo()->driver_data = (UINT_PTR)thread;
return thread; @@ -64,6 +97,7 @@ static void WAYLAND_ThreadDetach(void)
if (thread) { + wayland_thread_deinit(thread); free(thread); /* Clear data in case we get re-entered from user32 before the thread * is truly dead */ @@ -74,6 +108,7 @@ static void WAYLAND_ThreadDetach(void) static const struct user_driver_funcs waylanddrv_funcs = { .pCreateWindow = WAYLAND_CreateWindow, + .pProcessEvents = WAYLAND_ProcessEvents, .pThreadDetach = WAYLAND_ThreadDetach, .pUpdateDisplayDevices = WAYLAND_UpdateDisplayDevices, };
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Signed-off-by: Alexandros Frantzis alexandros.frantzis@collabora.com --- dlls/winewayland.drv/wayland.c | 10 ++++++++++ dlls/winewayland.drv/waylanddrv.h | 1 + 2 files changed, 11 insertions(+)
diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index 4974bd4e3b3..7e303c19e4e 100644 --- a/dlls/winewayland.drv/wayland.c +++ b/dlls/winewayland.drv/wayland.c @@ -312,6 +312,8 @@ static BOOL wayland_dispatch_thread_events(struct wayland_thread_data *thread, } pthread_mutex_unlock(&thread->event_mutex);
+ thread->processing_events = TRUE; + /* Now handle matching events without any lock held. */ wl_list_for_each_safe(event, tmp, &event_list, link) { @@ -321,6 +323,8 @@ static BOOL wayland_dispatch_thread_events(struct wayland_thread_data *thread, free(event); }
+ thread->processing_events = FALSE; + wl_display_flush(process_wl_display);
TRACE("=> dispatched=0x%lx\n", (long)dispatched); @@ -337,6 +341,12 @@ BOOL WAYLAND_ProcessEvents(DWORD mask)
if (!thread) return FALSE;
+ if (thread->processing_events) + { + wl_display_flush(process_wl_display); + return FALSE; + } + wayland_consume_thread_notifications(thread); return wayland_dispatch_thread_events(thread, mask); } diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 88dc31967be..c693034b013 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -93,6 +93,7 @@ struct wayland_thread_data pthread_mutex_t event_mutex; struct wl_list event_list; int event_notification_pipe[2]; + BOOL processing_events; };
/**********************************************************************
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Update the desktop window size to match the current virtual screen rect.
Signed-off-by: Alexandros Frantzis alexandros.frantzis@collabora.com --- dlls/winewayland.drv/wayland_output.c | 1 + dlls/winewayland.drv/waylanddrv.h | 2 ++ dlls/winewayland.drv/window.c | 25 +++++++++++++++++++++++++ 3 files changed, 28 insertions(+)
diff --git a/dlls/winewayland.drv/wayland_output.c b/dlls/winewayland.drv/wayland_output.c index 487a629e198..0fd1c6d3df5 100644 --- a/dlls/winewayland.drv/wayland_output.c +++ b/dlls/winewayland.drv/wayland_output.c @@ -105,6 +105,7 @@ static void wayland_output_state_add_mode(struct wayland_output_state *state, static void handle_init_display_devices(void) { wayland_init_display_devices(TRUE); + wayland_resize_desktop(); }
static void maybe_init_display_devices(void) diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index c693034b013..f83477548d2 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -105,6 +105,7 @@ void wayland_init_display_devices(BOOL force) DECLSPEC_HIDDEN; struct wayland_thread_data *wayland_init_thread_data(void) DECLSPEC_HIDDEN; BOOL wayland_thread_init(struct wayland_thread_data *thread) DECLSPEC_HIDDEN; void wayland_thread_deinit(struct wayland_thread_data *thread) DECLSPEC_HIDDEN; +void wayland_resize_desktop(void) DECLSPEC_HIDDEN;
static inline struct wayland_thread_data *wayland_thread_data(void) { @@ -130,6 +131,7 @@ void wayland_queue_thread_event(HWND hwnd, DWORD mask, void (*handle)(void)) DEC */
BOOL WAYLAND_CreateWindow(HWND hwnd) DECLSPEC_HIDDEN; +LRESULT WAYLAND_DesktopWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) DECLSPEC_HIDDEN; BOOL WAYLAND_ProcessEvents(DWORD mask) DECLSPEC_HIDDEN; BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manager, BOOL force, void *param) DECLSPEC_HIDDEN; diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index 039a27fbecf..f8e3a80e68b 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -43,3 +43,28 @@ BOOL WAYLAND_CreateWindow(HWND hwnd)
return TRUE; } + +void wayland_resize_desktop(void) +{ + RECT virtual_rect = NtUserGetVirtualScreenRect(); + NtUserSetWindowPos(NtUserGetDesktopWindow(), 0, + virtual_rect.left, virtual_rect.top, + virtual_rect.right - virtual_rect.left, + virtual_rect.bottom - virtual_rect.top, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_DEFERERASE); +} + +/********************************************************************** + * WAYLAND_DesktopWindowProc + */ +LRESULT WAYLAND_DesktopWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) +{ + switch (msg) + { + case WM_DISPLAYCHANGE: + wayland_resize_desktop(); + break; + } + + return NtUserMessageCall(hwnd, msg, wp, lp, 0, NtUserDefWindowProc, FALSE); +}
Rémi Bernon (@rbernon) commented about dlls/winewayland.drv/dllmain.c:
if (WAYLANDDRV_UNIX_CALL(init, NULL)) return FALSE;
- /* Read wayland events from a dedicated thread. */
- CreateThread(NULL, 0, wayland_read_events_thread, NULL, 0, &tid);
```suggestion:-0+0 CloseHandle(CreateThread(NULL, 0, wayland_read_events_thread, NULL, 0, &tid)); ```
Rémi Bernon (@rbernon) commented about dlls/winewayland.drv/display.c:
WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
-void wayland_init_display_devices(void) +BOOL force_display_devices_refresh = FALSE;
```suggestion:-0+0 static BOOL force_display_devices_refresh; ```
Unless it will be used elsewhere later, in which case it should probably also be declared in the header? Might still be better to make it static here and change it then.
Rémi Bernon (@rbernon) commented about dlls/winewayland.drv/dllmain.c:
#include "waylanddrv_dll.h"
+static DWORD WINAPI wayland_read_events_thread(void *arg) +{
- WAYLANDDRV_UNIX_CALL(read_events, NULL);
- /* This thread terminates only if an unrecoverable error occurred
* during event reading (e.g., the connection to the Wayland
* compositor is broken). */
- TerminateProcess(GetCurrentProcess(), 1);
You probably should print an `ERR` message.
Rémi Bernon (@rbernon) commented about dlls/winewayland.drv/wayland.c:
struct wayland *process_wayland = NULL; BOOL process_wayland_initialized = FALSE;
+static void wayland_mutex_init(pthread_mutex_t *mutex, int type) +{
- pthread_mutexattr_t mutexattr;
- pthread_mutexattr_init(&mutexattr);
- pthread_mutexattr_settype(&mutexattr, type);
- pthread_mutex_init(mutex, &mutexattr);
- pthread_mutexattr_destroy(&mutexattr);
+}
The helper doesn't seem to add much value, as you use normal mutexes. We use pthread_mutex_init directly in other places, with direct pthread_mutexattr_settype calls for some very rare recursive mutexes.
Fwiw you don't need to sign-off your patches anymore.
I'm also not sure why `process_wayland` is dynamically allocated? If it's going to be the only `struct wayland` instance, maybe having it static would make a few things simpler, like for instance its mutex initialization.