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, };