From 9fdc425931888ea70bc095e53cc006fca8ccb703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 30 Nov 2023 02:43:48 +0100 Subject: [PATCH] Wayland: Use Wayland to wait for libdecor to init Much of libdecor is initialized only after certain events have been received from the compositor and some parts of libdecor 0.1 are unsafe to use until this delayed initialization has completed. Since libdecor does not provide an API to query if or be notified when this has happened, GLFW processed events until its newly created libdecor frame had created its XDG shell objects. This commit switches to using a generic Wayland sync point created just after libdecor (and presumably its plugin) has set up its delayed initialization, instead of relying on the more specific implementation detail mentioned above. It also makes this wait mandatory before the first libdecor frame is created instead of a pre-condition for certain libdecor frame calls, hopefully removing even more dependence on implementation details. --- src/wl_init.c | 32 +++++++++++++++++++++++++++++--- src/wl_platform.h | 2 ++ src/wl_window.c | 10 ++++------ 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/wl_init.c b/src/wl_init.c index 66d77dca..0ec65900 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "wayland-client-protocol.h" #include "wayland-xdg-shell-client-protocol.h" @@ -216,6 +217,22 @@ static const struct libdecor_interface libdecorInterface = libdecorHandleError }; +static void libdecorReadyCallback(void* userData, + struct wl_callback* callback, + uint32_t time) +{ + _glfw.wl.libdecor.ready = GLFW_TRUE; + + assert(_glfw.wl.libdecor.callback == callback); + wl_callback_destroy(_glfw.wl.libdecor.callback); + _glfw.wl.libdecor.callback = NULL; +} + +static const struct wl_callback_listener libdecorReadyListener = +{ + libdecorReadyCallback +}; + // Create key code translation tables // static void createKeyTables(void) @@ -775,10 +792,17 @@ int _glfwInitWayland(void) if (_glfw.wl.libdecor.handle) { _glfw.wl.libdecor.context = libdecor_new(_glfw.wl.display, &libdecorInterface); - - // Allow libdecor to receive its globals before proceeding if (_glfw.wl.libdecor.context) - libdecor_dispatch(_glfw.wl.libdecor.context, 1); + { + // Perform an initial dispatch and flush to get the init started + libdecor_dispatch(_glfw.wl.libdecor.context, 0); + + // Create sync point to "know" when libdecor is ready for use + _glfw.wl.libdecor.callback = wl_display_sync(_glfw.wl.display); + wl_callback_add_listener(_glfw.wl.libdecor.callback, + &libdecorReadyListener, + NULL); + } } #ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION @@ -822,6 +846,8 @@ void _glfwTerminateWayland(void) _glfwTerminateEGL(); _glfwTerminateOSMesa(); + if (_glfw.wl.libdecor.callback) + wl_callback_destroy(_glfw.wl.libdecor.callback); if (_glfw.wl.libdecor.context) libdecor_unref(_glfw.wl.libdecor.context); diff --git a/src/wl_platform.h b/src/wl_platform.h index e9dd0b4a..d00e28fe 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -550,6 +550,8 @@ typedef struct _GLFWlibraryWayland struct { void* handle; struct libdecor* context; + struct wl_callback* callback; + GLFWbool ready; PFN_libdecor_new libdecor_new_; PFN_libdecor_unref libdecor_unref_; PFN_libdecor_get_fd libdecor_get_fd_; diff --git a/src/wl_window.c b/src/wl_window.c index fc4a671a..c7dd62b0 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -735,6 +735,10 @@ static const struct libdecor_frame_interface libdecorFrameInterface = static GLFWbool createLibdecorFrame(_GLFWwindow* window) { + // Allow libdecor to finish initialization of itself and its plugin + while (!_glfw.wl.libdecor.ready) + _glfwWaitEventsWayland(); + window->wl.libdecor.frame = libdecor_decorate(_glfw.wl.libdecor.context, window->wl.surface, &libdecorFrameInterface, @@ -776,12 +780,6 @@ static GLFWbool createLibdecorFrame(_GLFWwindow* window) if (window->monitor) { - // HACK: Allow libdecor to finish initialization of itself and its - // plugin so it will create the xdg_toplevel for the frame - // This needs to exist when setting the frame to fullscreen - while (!libdecor_frame_get_xdg_toplevel(window->wl.libdecor.frame)) - _glfwWaitEventsWayland(); - libdecor_frame_set_fullscreen(window->wl.libdecor.frame, window->monitor->wl.output); setIdleInhibitor(window, GLFW_TRUE);