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. --- 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 34aa8bec04e..2f2e865f54a 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);
@@ -38,6 +41,16 @@ struct wayland process_wayland = .output_mutex = PTHREAD_MUTEX_INITIALIZER };
+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); +}; + /********************************************************************** * Registry handling */ @@ -142,3 +155,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); + pthread_mutex_init(&thread->event_mutex, NULL); + + 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_wayland.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 5ea04340347..c0f23413162 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 c05a1818211..47754dcdadc 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 2afdea23332..e4deec282d6 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, };