diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-10-14 14:16:42 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-10-14 14:17:37 +0300 |
| commit | 7e8bf8d2a81f37303e0c9ae430f1acbff106da22 (patch) | |
| tree | 274cc707238f930395f87e74322c7c139811fe46 /src/platform/wayland/backend.c | |
| parent | 26f644f51709cc9f6cdd8060e7561498f2bb465d (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.c | 74 |
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; |
