Kaynağa Gözat

wayland: Refactor the cursor event thread into a general purpose event thread

No functional changes, just tidies the mouse code, and allows for future generalized use of the event handling thread, if required.
Frank Praznik 2 ay önce
ebeveyn
işleme
9961fb80a7

+ 210 - 0
src/video/wayland/SDL_waylandeventthread.c

@@ -0,0 +1,210 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifdef SDL_VIDEO_DRIVER_WAYLAND
+
+#include "../../core/unix/SDL_poll.h"
+#include "SDL_waylandeventthread.h"
+#include "SDL_waylandvideo.h"
+
+#include <errno.h>
+
+struct Wayland_EventThreadContext
+{
+    SDL_VideoData *wayland_data;
+    SDL_Thread *thread;
+    struct wl_event_queue *queue;
+    SDL_Mutex *dispatch_lock;
+    bool should_exit;
+};
+
+static void handle_event_thread_exit(void *data, struct wl_callback *wl_callback, uint32_t callback_data)
+{
+    Wayland_EventThreadContext *context = (Wayland_EventThreadContext *)data;
+    wl_callback_destroy(wl_callback);
+    context->should_exit = true;
+}
+
+static const struct wl_callback_listener event_thread_exit_listener = {
+    handle_event_thread_exit
+};
+
+static int SDLCALL Wayland_EventThreadFunc(void *data)
+{
+    Wayland_EventThreadContext *context = (Wayland_EventThreadContext *)data;
+    struct wl_display *display = context->wayland_data->display;
+    const int display_fd = WAYLAND_wl_display_get_fd(display);
+    int ret;
+
+    /* The lock must be held whenever dispatching to avoid a race condition when adding
+     * or destroying objects using the queue.
+     *
+     * Any error other than EAGAIN is fatal and causes the thread to exit.
+     */
+    while (!context->should_exit) {
+        if (WAYLAND_wl_display_prepare_read_queue(display, context->queue) == 0) {
+            Sint64 timeoutNS = -1;
+
+            ret = WAYLAND_wl_display_flush(display);
+
+            if (ret < 0) {
+                if (errno == EAGAIN) {
+                    // If the flush failed with EAGAIN, don't block as not to inhibit other threads from reading events.
+                    timeoutNS = SDL_MS_TO_NS(1);
+                } else {
+                    WAYLAND_wl_display_cancel_read(display);
+                    return -1;
+                }
+            }
+
+            // Wait for a read/write operation to become possible.
+            ret = SDL_IOReady(display_fd, SDL_IOR_READ, timeoutNS);
+
+            if (ret <= 0) {
+                WAYLAND_wl_display_cancel_read(display);
+                if (ret < 0) {
+                    return -1;
+                }
+
+                // Nothing to read, and woke to flush; try again.
+                continue;
+            }
+
+            ret = WAYLAND_wl_display_read_events(display);
+            if (ret == -1) {
+                return -1;
+            }
+        }
+
+        SDL_LockMutex(context->dispatch_lock);
+        ret = WAYLAND_wl_display_dispatch_queue_pending(display, context->queue);
+        SDL_UnlockMutex(context->dispatch_lock);
+
+        if (ret < 0) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+Wayland_EventThreadContext *Wayland_CreateEventThread(SDL_VideoData *data, const char *queue_name)
+{
+    Wayland_EventThreadContext *context = SDL_calloc(1, sizeof(Wayland_EventThreadContext));
+    if (!context) {
+        return NULL;
+    }
+
+    context->wayland_data = data;
+    context->queue = Wayland_DisplayCreateQueue(data->display, queue_name);
+    if (!context->queue) {
+        goto cleanup;
+    }
+
+    context->dispatch_lock = SDL_CreateMutex();
+    if (!context->dispatch_lock) {
+        goto cleanup;
+    }
+
+    context->thread = SDL_CreateThread(Wayland_EventThreadFunc, "wl_event_thread", context);
+    if (!context->thread) {
+        goto cleanup;
+    }
+
+    return context;
+
+cleanup:
+    if (context->dispatch_lock) {
+        SDL_DestroyMutex(context->dispatch_lock);
+    }
+
+    if (context->queue) {
+        WAYLAND_wl_event_queue_destroy(context->queue);
+    }
+
+    SDL_free(context);
+    return NULL;
+}
+
+void Wayland_DestroyEventThread(Wayland_EventThreadContext *context)
+{
+    if (context->thread) {
+        // Dispatch the exit event to unblock the thread and signal it to exit.
+        struct wl_display *display = context->wayland_data->display;
+        struct wl_proxy *display_wrapper = Wayland_CreateEventThreadProxyWrapper(context, display);
+
+        SDL_LockMutex(context->dispatch_lock);
+        struct wl_callback *cb = wl_display_sync((struct wl_display *)display_wrapper);
+        wl_callback_add_listener(cb, &event_thread_exit_listener, context);
+        SDL_UnlockMutex(context->dispatch_lock);
+
+        WAYLAND_wl_proxy_wrapper_destroy(display_wrapper);
+
+        int ret = WAYLAND_wl_display_flush(display);
+        while (ret == -1 && errno == EAGAIN) {
+            // Shutting down the thread requires a successful flush.
+            ret = SDL_IOReady(WAYLAND_wl_display_get_fd(display), SDL_IOR_WRITE, -1);
+            if (ret >= 0) {
+                ret = WAYLAND_wl_display_flush(display);
+            }
+        }
+
+        // Avoid a warning if the flush failed due to a broken connection.
+        if (ret < 0) {
+            wl_callback_destroy(cb);
+        }
+
+        // Wait for the thread to return; it will exit automatically on a broken connection.
+        SDL_WaitThread(context->thread, NULL);
+
+        WAYLAND_wl_event_queue_destroy(context->queue);
+        SDL_DestroyMutex(context->dispatch_lock);
+        SDL_free(context);
+    }
+}
+
+void *Wayland_CreateEventThreadProxyWrapper(Wayland_EventThreadContext *context, void *obj)
+{
+    struct wl_proxy *wrapper = WAYLAND_wl_proxy_create_wrapper(obj);
+    if (wrapper) {
+        WAYLAND_wl_proxy_set_queue(wrapper, context->queue);
+        return wrapper;
+    }
+
+    return NULL;
+}
+
+void Wayland_LockEventThread(Wayland_EventThreadContext *context)
+{
+    if (context) {
+        SDL_LockMutex(context->dispatch_lock);
+    }
+}
+
+void Wayland_UnlockEventThread(Wayland_EventThreadContext *context)
+{
+    if (context) {
+        SDL_UnlockMutex(context->dispatch_lock);
+    }
+}
+
+#endif // SDL_VIDEO_DRIVER_WAYLAND

+ 36 - 0
src/video/wayland/SDL_waylandeventthread.h

@@ -0,0 +1,36 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifndef SDL_waylandeventthread_h_
+#define SDL_waylandeventthread_h_
+
+#include "../SDL_sysvideo.h"
+
+typedef struct Wayland_EventThreadContext Wayland_EventThreadContext;
+
+extern Wayland_EventThreadContext *Wayland_CreateEventThread(SDL_VideoData *data, const char *queue_name);
+extern void Wayland_DestroyEventThread(Wayland_EventThreadContext *context);
+extern void *Wayland_CreateEventThreadProxyWrapper(Wayland_EventThreadContext *context, void *obj);
+extern void Wayland_LockEventThread(Wayland_EventThreadContext *context);
+extern void Wayland_UnlockEventThread(Wayland_EventThreadContext *context);
+
+#endif // SDL_waylandeventthread_h_

+ 13 - 175
src/video/wayland/SDL_waylandmouse.c

@@ -324,168 +324,6 @@ static struct wl_buffer *Wayland_CursorStateGetFrame(SDL_WaylandCursorState *sta
     return NULL;
     return NULL;
 }
 }
 
 
-static struct CursorThreadContext
-{
-    SDL_Thread *thread;
-    struct wl_event_queue *queue;
-    struct wl_proxy *compositor_wrapper;
-    SDL_Mutex *lock;
-    bool should_exit;
-} cursor_thread_context;
-
-static void handle_cursor_thread_exit(void *data, struct wl_callback *wl_callback, uint32_t callback_data)
-{
-    wl_callback_destroy(wl_callback);
-    cursor_thread_context.should_exit = true;
-}
-
-static const struct wl_callback_listener cursor_thread_exit_listener = {
-    handle_cursor_thread_exit
-};
-
-static int SDLCALL Wayland_CursorThreadFunc(void *data)
-{
-    struct wl_display *display = data;
-    const int display_fd = WAYLAND_wl_display_get_fd(display);
-    int ret;
-
-    /* The lock must be held whenever dispatching to avoid a race condition when setting
-     * or destroying cursor frame callbacks, as adding the callback followed by setting
-     * the listener is not an atomic operation, and the callback proxy must not be
-     * destroyed while in the callback handler.
-     *
-     * Any error other than EAGAIN is fatal and causes the thread to exit.
-     */
-    while (!cursor_thread_context.should_exit) {
-        if (WAYLAND_wl_display_prepare_read_queue(display, cursor_thread_context.queue) == 0) {
-            Sint64 timeoutNS = -1;
-
-            ret = WAYLAND_wl_display_flush(display);
-
-            if (ret < 0) {
-                if (errno == EAGAIN) {
-                    // If the flush failed with EAGAIN, don't block as not to inhibit other threads from reading events.
-                    timeoutNS = SDL_MS_TO_NS(1);
-                } else {
-                    WAYLAND_wl_display_cancel_read(display);
-                    return -1;
-                }
-            }
-
-            // Wait for a read/write operation to become possible.
-            ret = SDL_IOReady(display_fd, SDL_IOR_READ, timeoutNS);
-
-            if (ret <= 0) {
-                WAYLAND_wl_display_cancel_read(display);
-                if (ret < 0) {
-                    return -1;
-                }
-
-                // Nothing to read, and woke to flush; try again.
-                continue;
-            }
-
-            ret = WAYLAND_wl_display_read_events(display);
-            if (ret == -1) {
-                return -1;
-            }
-        }
-
-        SDL_LockMutex(cursor_thread_context.lock);
-        ret = WAYLAND_wl_display_dispatch_queue_pending(display, cursor_thread_context.queue);
-        SDL_UnlockMutex(cursor_thread_context.lock);
-
-        if (ret < 0) {
-            return -1;
-        }
-    }
-
-    return 0;
-}
-
-static bool Wayland_StartCursorThread(SDL_VideoData *data)
-{
-    if (!cursor_thread_context.thread) {
-        cursor_thread_context.queue = Wayland_DisplayCreateQueue(data->display, "SDL Cursor Surface Queue");
-        if (!cursor_thread_context.queue) {
-            goto cleanup;
-        }
-
-        cursor_thread_context.compositor_wrapper = WAYLAND_wl_proxy_create_wrapper(data->compositor);
-        if (!cursor_thread_context.compositor_wrapper) {
-            goto cleanup;
-        }
-        WAYLAND_wl_proxy_set_queue(cursor_thread_context.compositor_wrapper, cursor_thread_context.queue);
-
-        cursor_thread_context.lock = SDL_CreateMutex();
-        if (!cursor_thread_context.lock) {
-            goto cleanup;
-        }
-
-        cursor_thread_context.thread = SDL_CreateThread(Wayland_CursorThreadFunc, "wl_cursor_surface", data->display);
-        if (!cursor_thread_context.thread) {
-            goto cleanup;
-        }
-
-        return true;
-    }
-
-cleanup:
-    if (cursor_thread_context.lock) {
-        SDL_DestroyMutex(cursor_thread_context.lock);
-    }
-
-    if (cursor_thread_context.compositor_wrapper) {
-        WAYLAND_wl_proxy_wrapper_destroy(cursor_thread_context.compositor_wrapper);
-    }
-
-    if (cursor_thread_context.queue) {
-        WAYLAND_wl_event_queue_destroy(cursor_thread_context.queue);
-    }
-
-    SDL_zero(cursor_thread_context);
-
-    return false;
-}
-
-static void Wayland_DestroyCursorThread(SDL_VideoData *data)
-{
-    if (cursor_thread_context.thread) {
-        // Dispatch the exit event to unblock the animation thread and signal it to exit.
-        struct wl_proxy *display_wrapper = WAYLAND_wl_proxy_create_wrapper(data->display);
-        WAYLAND_wl_proxy_set_queue(display_wrapper, cursor_thread_context.queue);
-
-        SDL_LockMutex(cursor_thread_context.lock);
-        struct wl_callback *cb = wl_display_sync((struct wl_display *)display_wrapper);
-        wl_callback_add_listener(cb, &cursor_thread_exit_listener, NULL);
-        SDL_UnlockMutex(cursor_thread_context.lock);
-
-        WAYLAND_wl_proxy_wrapper_destroy(display_wrapper);
-
-        int ret = WAYLAND_wl_display_flush(data->display);
-        while (ret == -1 && errno == EAGAIN) {
-            // Shutting down the thread requires a successful flush.
-            ret = SDL_IOReady(WAYLAND_wl_display_get_fd(data->display), SDL_IOR_WRITE, -1);
-            if (ret >= 0) {
-                ret = WAYLAND_wl_display_flush(data->display);
-            }
-        }
-
-        // Avoid a warning if the flush failed due to a broken connection.
-        if (ret < 0) {
-            wl_callback_destroy(cb);
-        }
-
-        // Wait for the thread to return; it will exit automatically on a broken connection.
-        SDL_WaitThread(cursor_thread_context.thread, NULL);
-
-        WAYLAND_wl_proxy_wrapper_destroy(cursor_thread_context.compositor_wrapper);
-        WAYLAND_wl_event_queue_destroy(cursor_thread_context.queue);
-        SDL_DestroyMutex(cursor_thread_context.lock);
-        SDL_zero(cursor_thread_context);
-    }
-}
-
 static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time);
 static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time);
 static const struct wl_callback_listener cursor_frame_listener = {
 static const struct wl_callback_listener cursor_frame_listener = {
     cursor_frame_done
     cursor_frame_done
@@ -545,24 +383,26 @@ static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time)
 
 
 void Wayland_CursorStateSetFrameCallback(SDL_WaylandCursorState *state, void *userdata)
 void Wayland_CursorStateSetFrameCallback(SDL_WaylandCursorState *state, void *userdata)
 {
 {
-    SDL_LockMutex(cursor_thread_context.lock);
+    SDL_VideoData *viddata = SDL_GetVideoDevice()->internal;
+    Wayland_LockEventThread(viddata->event_thread_context);
 
 
     state->frame_callback = wl_surface_frame(state->surface);
     state->frame_callback = wl_surface_frame(state->surface);
     wl_callback_add_listener(state->frame_callback, &cursor_frame_listener, userdata);
     wl_callback_add_listener(state->frame_callback, &cursor_frame_listener, userdata);
 
 
-    SDL_UnlockMutex(cursor_thread_context.lock);
+    Wayland_UnlockEventThread(viddata->event_thread_context);
 }
 }
 
 
 void Wayland_CursorStateDestroyFrameCallback(SDL_WaylandCursorState *state)
 void Wayland_CursorStateDestroyFrameCallback(SDL_WaylandCursorState *state)
 {
 {
-    SDL_LockMutex(cursor_thread_context.lock);
+    SDL_VideoData *viddata = SDL_GetVideoDevice()->internal;
+    Wayland_LockEventThread(viddata->event_thread_context);
 
 
     if (state->frame_callback) {
     if (state->frame_callback) {
         wl_callback_destroy(state->frame_callback);
         wl_callback_destroy(state->frame_callback);
         state->frame_callback = NULL;
         state->frame_callback = NULL;
     }
     }
 
 
-    SDL_UnlockMutex(cursor_thread_context.lock);
+    Wayland_UnlockEventThread(viddata->event_thread_context);
 }
 }
 
 
 static void Wayland_CursorStateResetAnimationState(SDL_WaylandCursorState *state)
 static void Wayland_CursorStateResetAnimationState(SDL_WaylandCursorState *state)
@@ -575,9 +415,10 @@ static void Wayland_CursorStateResetAnimationState(SDL_WaylandCursorState *state
 static void Wayland_CursorStateResetAnimation(SDL_WaylandCursorState *state, bool lock)
 static void Wayland_CursorStateResetAnimation(SDL_WaylandCursorState *state, bool lock)
 {
 {
     if (lock) {
     if (lock) {
-        SDL_LockMutex(cursor_thread_context.lock);
+        SDL_VideoData *viddata = SDL_GetVideoDevice()->internal;
+        Wayland_LockEventThread(viddata->event_thread_context);
         Wayland_CursorStateResetAnimationState(state);
         Wayland_CursorStateResetAnimationState(state);
-        SDL_UnlockMutex(cursor_thread_context.lock);
+        Wayland_UnlockEventThread(viddata->event_thread_context);
     } else {
     } else {
         Wayland_CursorStateResetAnimationState(state);
         Wayland_CursorStateResetAnimationState(state);
     }
     }
@@ -1175,8 +1016,10 @@ static void Wayland_CursorStateSetCursor(SDL_WaylandCursorState *state, const Wa
         state->current_cursor = cursor_data;
         state->current_cursor = cursor_data;
 
 
         if (!state->surface) {
         if (!state->surface) {
-            if (cursor_thread_context.compositor_wrapper) {
-                state->surface = wl_compositor_create_surface((struct wl_compositor *)cursor_thread_context.compositor_wrapper);
+            if (viddata->event_thread_context) {
+                struct wl_compositor *compositor_wrapper = Wayland_CreateEventThreadProxyWrapper(viddata->event_thread_context, viddata->compositor);
+                state->surface = wl_compositor_create_surface(compositor_wrapper);
+                WAYLAND_wl_proxy_wrapper_destroy(compositor_wrapper);
             } else {
             } else {
                 state->surface = wl_compositor_create_surface(viddata->compositor);
                 state->surface = wl_compositor_create_surface(viddata->compositor);
             }
             }
@@ -1541,10 +1384,6 @@ void Wayland_InitMouse(SDL_VideoData *data)
     mouse->SetRelativeMouseMode = Wayland_SetRelativeMouseMode;
     mouse->SetRelativeMouseMode = Wayland_SetRelativeMouseMode;
     mouse->GetGlobalMouseState = Wayland_GetGlobalMouseState;
     mouse->GetGlobalMouseState = Wayland_GetGlobalMouseState;
 
 
-    if (!Wayland_StartCursorThread(data)) {
-        SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "wayland: Failed to start cursor animation event thread");
-    }
-
     SDL_HitTestResult r = SDL_HITTEST_NORMAL;
     SDL_HitTestResult r = SDL_HITTEST_NORMAL;
     while (r <= SDL_HITTEST_RESIZE_LEFT) {
     while (r <= SDL_HITTEST_RESIZE_LEFT) {
         switch (r) {
         switch (r) {
@@ -1604,7 +1443,6 @@ void Wayland_FiniMouse(SDL_VideoData *data)
         sys_cursors[i] = NULL;
         sys_cursors[i] = NULL;
     }
     }
 
 
-    Wayland_DestroyCursorThread(data);
     Wayland_FreeCursorThemes(data);
     Wayland_FreeCursorThemes(data);
 
 
 #ifdef SDL_USE_LIBDBUS
 #ifdef SDL_USE_LIBDBUS

+ 9 - 2
src/video/wayland/SDL_waylandvideo.c

@@ -1684,6 +1684,11 @@ bool Wayland_VideoInit(SDL_VideoDevice *_this)
 {
 {
     SDL_VideoData *data = _this->internal;
     SDL_VideoData *data = _this->internal;
 
 
+    data->event_thread_context = Wayland_CreateEventThread(data, "SDL Event Thread Queue");
+    if (!data->event_thread_context) {
+        SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "wayland: Failed to create event thread context");
+    }
+
     data->xkb_context = WAYLAND_xkb_context_new(0);
     data->xkb_context = WAYLAND_xkb_context_new(0);
     if (!data->xkb_context) {
     if (!data->xkb_context) {
         return SDL_SetError("Failed to create XKB context");
         return SDL_SetError("Failed to create XKB context");
@@ -1787,6 +1792,10 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this)
     }
     }
 
 
     Wayland_FiniMouse(data);
     Wayland_FiniMouse(data);
+    Wayland_QuitKeyboard(_this);
+
+    Wayland_DestroyEventThread(data->event_thread_context);
+    data->event_thread_context = NULL;
 
 
     if (data->pointer_constraints) {
     if (data->pointer_constraints) {
         zwp_pointer_constraints_v1_destroy(data->pointer_constraints);
         zwp_pointer_constraints_v1_destroy(data->pointer_constraints);
@@ -1813,8 +1822,6 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this)
         data->key_inhibitor_manager = NULL;
         data->key_inhibitor_manager = NULL;
     }
     }
 
 
-    Wayland_QuitKeyboard(_this);
-
     if (data->text_input_manager) {
     if (data->text_input_manager) {
         zwp_text_input_manager_v3_destroy(data->text_input_manager);
         zwp_text_input_manager_v3_destroy(data->text_input_manager);
         data->text_input_manager = NULL;
         data->text_input_manager = NULL;

+ 2 - 0
src/video/wayland/SDL_waylandvideo.h

@@ -30,6 +30,7 @@
 #include "../SDL_sysvideo.h"
 #include "../SDL_sysvideo.h"
 #include "../../core/linux/SDL_dbus.h"
 #include "../../core/linux/SDL_dbus.h"
 #include "../../core/linux/SDL_ime.h"
 #include "../../core/linux/SDL_ime.h"
+#include "SDL_waylandeventthread.h"
 
 
 struct xkb_context;
 struct xkb_context;
 struct SDL_WaylandSeat;
 struct SDL_WaylandSeat;
@@ -97,6 +98,7 @@ struct SDL_VideoData
     struct SDL_WaylandSeat *last_implicit_grab_seat;
     struct SDL_WaylandSeat *last_implicit_grab_seat;
     struct SDL_WaylandSeat *last_incoming_data_offer_seat;
     struct SDL_WaylandSeat *last_incoming_data_offer_seat;
     struct SDL_WaylandSeat *last_incoming_primary_selection_seat;
     struct SDL_WaylandSeat *last_incoming_primary_selection_seat;
+    Wayland_EventThreadContext *event_thread_context;
 
 
     SDL_DisplayData **output_list;
     SDL_DisplayData **output_list;
     int output_count;
     int output_count;