diff --git a/client/displayservers/Wayland/wayland.c b/client/displayservers/Wayland/wayland.c index 3f9483d1..16dbe7ac 100644 --- a/client/displayservers/Wayland/wayland.c +++ b/client/displayservers/Wayland/wayland.c @@ -119,6 +119,8 @@ static bool waylandInit(const LG_DSInitParams params) atomic_init(&wlWm.cmCanDoHDR, false); atomic_init(&wlWm.hdrPQWhiteLevel, 203); atomic_init(&wlWm.hdrScRGBWhiteLevel, 80); + atomic_init(&wlWm.pendingResize, 0); + wlWm.resizeEventFd = -1; wlWm.display = wl_display_connect(NULL); if (!wlWm.display) @@ -184,7 +186,10 @@ static bool waylandInit(const LG_DSInitParams params) if (!waylandEGLInit(waylandScaleMulInt(wlWm.scale, width), waylandScaleMulInt(wlWm.scale, height))) + { + waylandWindowFree(); return false; + } app_handleResizeEvent(width, height, waylandScaleToDouble(wlWm.scale), (struct Border) {0, 0, 0, 0}); @@ -193,7 +198,10 @@ static bool waylandInit(const LG_DSInitParams params) #ifdef ENABLE_OPENGL if (params.opengl && !waylandOpenGLInit()) + { + waylandWindowFree(); return false; + } #endif return true; diff --git a/client/displayservers/Wayland/wayland.h b/client/displayservers/Wayland/wayland.h index 6c872631..fdb9e6a0 100644 --- a/client/displayservers/Wayland/wayland.h +++ b/client/displayservers/Wayland/wayland.h @@ -148,6 +148,9 @@ struct WaylandDSState bool warpSupport; struct WlMotion motion; + _Atomic(uint64_t) pendingResize; // 0 = none, else (width << 32) | height + int resizeEventFd; // wakes the event loop to apply it + #if defined(ENABLE_EGL) || defined(ENABLE_OPENGL) struct wl_egl_window * eglWindow; struct SwapWithDamageData swapWithDamage; diff --git a/client/displayservers/Wayland/window.c b/client/displayservers/Wayland/window.c index d5c80cb9..05079411 100644 --- a/client/displayservers/Wayland/window.c +++ b/client/displayservers/Wayland/window.c @@ -23,6 +23,10 @@ #include #include +#include +#include +#include +#include #include #include "app.h" @@ -132,6 +136,29 @@ static const struct wp_fractional_scale_v1_listener fractionalScaleListener = { .preferred_scale = fractionalScalePreferredScale, }; +static void resizePollCallback(uint32_t events, void * opaque) +{ + eventfd_t value; + eventfd_read(wlWm.resizeEventFd, &value); + + const uint64_t pending = atomic_exchange_explicit(&wlWm.pendingResize, 0, + memory_order_acquire); + if (!pending || !app_isRunning()) + return; + + wlWm.desktop->shellResize((int)(pending >> 32), (int)(uint32_t)pending); +} + +static void resizeEventFdFree(void) +{ + if (wlWm.resizeEventFd < 0) + return; + + waylandPollUnregister(wlWm.resizeEventFd); + close(wlWm.resizeEventFd); + wlWm.resizeEventFd = -1; +} + bool waylandWindowInit(const char * title, const char * appId, bool fullscreen, bool maximize, bool borderless, bool resizable) { wlWm.scale = waylandScaleFromInt(1); @@ -142,11 +169,29 @@ bool waylandWindowInit(const char * title, const char * appId, bool fullscreen, DEBUG_ERROR("Failed to initialize event for waitFrame"); return false; } + waylandSignalFrame(LG_DS_WAIT_FRAME_INTERRUPTED); + wlWm.resizeEventFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (wlWm.resizeEventFd < 0) + { + DEBUG_ERROR("Failed to create the resize eventfd: %s", strerror(errno)); + return false; + } + + if (!waylandPollRegister(wlWm.resizeEventFd, resizePollCallback, NULL, + EPOLLIN)) + { + DEBUG_ERROR("Failed to register the resize eventfd"); + close(wlWm.resizeEventFd); + wlWm.resizeEventFd = -1; + return false; + } + if (!wlWm.compositor) { DEBUG_ERROR("Compositor missing wl_compositor (version 3+), will not proceed"); + resizeEventFdFree(); return false; } @@ -154,6 +199,7 @@ bool waylandWindowInit(const char * title, const char * appId, bool fullscreen, if (!wlWm.surface) { DEBUG_ERROR("Failed to create wl_surface"); + resizeEventFdFree(); return false; } @@ -176,7 +222,10 @@ bool waylandWindowInit(const char * title, const char * appId, bool fullscreen, if (!wlWm.desktop->shellInit(wlWm.display, wlWm.surface, title, appId, fullscreen, maximize, borderless, resizable)) + { + resizeEventFdFree(); return false; + } INTERLOCKED_SECTION(wlWm.surfaceLock, { @@ -190,6 +239,7 @@ bool waylandWindowInit(const char * title, const char * appId, bool fullscreen, if (wl_display_roundtrip(wlWm.display) < 0) { DEBUG_ERROR("Failed waiting for the initial Wayland configure"); + resizeEventFdFree(); return false; } } @@ -199,6 +249,8 @@ bool waylandWindowInit(const char * title, const char * appId, bool fullscreen, void waylandWindowFree(void) { + resizeEventFdFree(); + struct SurfaceOutput * output; struct SurfaceOutput * temp; wl_list_for_each_safe(output, temp, &wlWm.surfaceOutputs, link) @@ -217,7 +269,10 @@ void waylandWindowFree(void) void waylandSetWindowSize(int x, int y) { - wlWm.desktop->shellResize(x, y); + // The shell resize must run on the event-loop thread; see resizePollCallback + atomic_store_explicit(&wlWm.pendingResize, + (uint64_t)(uint32_t)x << 32 | (uint32_t)y, memory_order_release); + eventfd_write(wlWm.resizeEventFd, 1); } bool waylandIsValidPointerPos(int x, int y)