summaryrefslogtreecommitdiff
path: root/src/platform/wayland/backend.c
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-14 14:16:42 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-14 14:17:37 +0300
commit7e8bf8d2a81f37303e0c9ae430f1acbff106da22 (patch)
tree274cc707238f930395f87e74322c7c139811fe46 /src/platform/wayland/backend.c
parent26f644f51709cc9f6cdd8060e7561498f2bb465d (diff)
Wayland: fix GL thread races, reconnect ordering and sizing bugs
- GL swaps on a rendering thread no longer touch frame callbacks or presentation feedback. They set a per-surface flag and wake the loop; the owner thread arms both at its next flush and commits, so the frame callback fires without waiting for the application's next swap. - Clear the offline flag only after the reconnect rebuild succeeds, and not when the connection was lost again during it. - make_current fails with ENOTCONN for a window without a context. Detaching is a no-op when nothing is current or the EGL display is gone. - Window move, resize, window menu and drag start need a pointer button or tablet tip that is still held; otherwise they fail with ESTALE and use that press's serial. Popups fail with ESTALE without an input serial and with ENOTCONN while the display is lost. - An oversized device size shrinks to the buffer limits, keeping the aspect ratio, instead of closing the window. - FIXED_SIZE windows pin xdg min and max on every set_size. The 64px minimum applies only to compositor-suggested sizes, not to application sizes. - The keyboard-enter serial becomes the input serial. Clipboard copy with no keyboard focus fails with ESTALE. - Relative-pointer windows relock after a reconnect rebuild. - errno: EBUSY for an active drag, ENOTSUP for place_under_pointer and for GL without EGL, EINVAL for drag carry without a drag. - Default window title and app ID are "YASL". - Pointer motion and button events carry unrounded device-pixel positions in precise_x/precise_y with precise_position set. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Diffstat (limited to 'src/platform/wayland/backend.c')
-rw-r--r--src/platform/wayland/backend.c74
1 files changed, 63 insertions, 11 deletions
diff --git a/src/platform/wayland/backend.c b/src/platform/wayland/backend.c
index 6b7758f..3ff696c 100644
--- a/src/platform/wayland/backend.c
+++ b/src/platform/wayland/backend.c
@@ -171,10 +171,18 @@ static struct yasl_surface *impl_surface_create_gl(struct yasl_private_display *
{
struct wayland_display *d = (struct wayland_display *)display;
struct yasl_private_gl_options o;
- if (!spec || !yasl_private_gl_parse_options(attributes, &o) || !d || !d->egl_initialized)
+ if (!spec || !d || !yasl_private_gl_parse_options(attributes, &o)) {
+ errno = EINVAL;
return NULL;
- if (share && (share->display != display || !((struct wayland_surface *)share)->egl_context))
+ }
+ if (!d->egl_initialized) {
+ errno = ENOTSUP;
return NULL;
+ }
+ if (share && (share->display != display || !((struct wayland_surface *)share)->egl_context)) {
+ errno = EINVAL;
+ return NULL;
+ }
struct yasl_private_window_spec gl_spec = *spec;
gl_spec.external_gpu = true;
gl_spec.flags &= ~YASL_PRIVATE_SURFACE_CPU;
@@ -190,15 +198,44 @@ static struct yasl_surface *impl_surface_create_gl(struct yasl_private_display *
return &w->base;
}
+/* An EGL call failed: a lost context means the display is gone, anything else is a bad argument. */
+static int egl_failure_errno(void)
+{
+ return eglGetError() == EGL_CONTEXT_LOST ? ENOTCONN : EINVAL;
+}
+
static bool impl_gl_make_current(struct yasl_private_display *display, struct yasl_surface *surface)
{
struct wayland_display *d = (struct wayland_display *)display;
struct wayland_surface *w = (struct wayland_surface *)surface;
- if (!d) return false;
- if (!w)
- return eglMakeCurrent(d->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_TRUE;
- return w->conn == d && w->base.presenter == YASL_PRIVATE_PRESENTER_GL &&
- eglMakeCurrent(d->egl_display, w->egl_surface, w->egl_surface, w->egl_context) == EGL_TRUE;
+ if (!d) {
+ errno = EINVAL;
+ return false;
+ }
+ if (!w) {
+ /* Nothing to detach when no context is current or the display is gone. */
+ if (eglGetCurrentContext() == EGL_NO_CONTEXT || d->egl_display == EGL_NO_DISPLAY)
+ return true;
+ if (eglMakeCurrent(d->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) != EGL_TRUE) {
+ errno = egl_failure_errno();
+ return false;
+ }
+ return true;
+ }
+ if (w->conn != d || w->base.presenter != YASL_PRIVATE_PRESENTER_GL) {
+ errno = EINVAL;
+ return false;
+ }
+ /* A window whose rebuild failed has no context and binds nothing. */
+ if (!w->egl_context) {
+ errno = ENOTCONN;
+ return false;
+ }
+ if (eglMakeCurrent(d->egl_display, w->egl_surface, w->egl_surface, w->egl_context) != EGL_TRUE) {
+ errno = egl_failure_errno();
+ return false;
+ }
+ return true;
}
static yasl_private_gl_proc impl_gl_get_proc_address(struct yasl_private_display *display, const char *name)
@@ -223,10 +260,20 @@ static bool impl_gl_swap_buffers(struct yasl_private_display *display, struct ya
w->base.presenter != YASL_PRIVATE_PRESENTER_GL)
return false;
if (!impl_gl_make_current(display, surface)) return false;
- wayland_surface_arm_frame(w);
- surface_request_presentation_feedback(w);
- if (!eglSwapBuffers(d->egl_display, w->egl_surface)) return false;
- return true;
+ /* Frame callbacks and feedback belong to the owner thread. Another thread only
+ * flags the surface and wakes the loop; the owner arms them at its next flush. */
+ bool owner = pthread_equal(pthread_self(), d->owner);
+ if (owner) {
+ wayland_surface_arm_frame(w);
+ surface_request_presentation_feedback(w);
+ }
+ bool swapped = eglSwapBuffers(d->egl_display, w->egl_surface) == EGL_TRUE;
+ if (!swapped) errno = egl_failure_errno();
+ if (!owner) {
+ atomic_store(&w->frame_deferred, true);
+ if (d->loop) yasl_private_loop_kick(d->loop);
+ }
+ return swapped;
}
#endif
@@ -263,6 +310,11 @@ static bool impl_surface_set_size(struct yasl_surface *s, struct yasl_private_ve
/* Floating windows choose their own size; constrained states keep it for later. */
w->floating_width = size.x;
w->floating_height = size.y;
+ /* A fixed-size window is pinned at its size, so the compositor must not stretch it. */
+ if ((w->base.creation_flags & YASL_PRIVATE_SURFACE_FIXED_SIZE) && w->toplevel && !w->conn->lost) {
+ xdg_toplevel_set_min_size(w->toplevel, size.x, size.y);
+ xdg_toplevel_set_max_size(w->toplevel, size.x, size.y);
+ }
if (w->conn->lost ||
(w->base.state & (YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_TILED)))
return true;