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/buffer.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/buffer.c')
| -rw-r--r-- | src/platform/wayland/buffer.c | 75 |
1 files changed, 63 insertions, 12 deletions
diff --git a/src/platform/wayland/buffer.c b/src/platform/wayland/buffer.c index 029b684..b68621d 100644 --- a/src/platform/wayland/buffer.c +++ b/src/platform/wayland/buffer.c @@ -1,8 +1,14 @@ #include "internal.h" #include "common/utf8.h" #include <errno.h> +#include <math.h> #include <sys/resource.h> +/* Largest device side a window may use, and the largest pixel count whose + * 4-byte rows still fit an INT32_MAX buffer. */ +#define DEVICE_SIDE_MAX 32767 +#define DEVICE_AREA_MAX (INT32_MAX / 4) + static long minor_faults(void) { struct rusage usage; @@ -28,6 +34,28 @@ int device_px(int logical, uint32_t scale_120) return px > 0 && px <= INT_MAX ? (int)px : -1; } +/* Unclamped device pixels of a logical extent; zero when it is not positive. */ +static int64_t device_extent(int logical, uint32_t scale_120) +{ + if (logical <= 0 || !scale_120) return 0; + return ((int64_t)logical * scale_120 + YASL_PRIVATE_SCALE_UNIT / 2) / YASL_PRIVATE_SCALE_UNIT; +} + +/* Shrinks a device size to fit the limits, keeping its aspect ratio. */ +static void device_size_fit(int64_t *width, int64_t *height) +{ + double w = (double)*width, h = (double)*height; + double k = 1.0; + if (*width > DEVICE_SIDE_MAX) k = DEVICE_SIDE_MAX / w; + if (*height > DEVICE_SIDE_MAX) k = fmin(k, DEVICE_SIDE_MAX / h); + if (w * h > DEVICE_AREA_MAX) k = fmin(k, sqrt(DEVICE_AREA_MAX / (w * h))); + if (k >= 1.0) return; + *width = (int64_t)floor(w * k); + *height = (int64_t)floor(h * k); + if (*width < 1) *width = 1; + if (*height < 1) *height = 1; +} + bool buffer_size(int width, int height, size_t *bytes) { if (width <= 0 || height <= 0 || width > INT32_MAX / 4 || @@ -140,26 +168,35 @@ int apply_pending_geometry(struct wayland_surface *w) (YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_TILED)); int free_w = floating && w->floating_width > 0 ? w->floating_width : w->logical_width; int free_h = floating && w->floating_height > 0 ? w->floating_height : w->logical_height; - int logical_w = !w->parent && w->req_width > 0 ? w->req_width : free_w; - int logical_h = !w->parent && w->req_height > 0 ? w->req_height : free_h; - if (!w->parent) { - if (logical_w < YASL_PRIVATE_MIN_LOGICAL_PX) logical_w = YASL_PRIVATE_MIN_LOGICAL_PX; - if (logical_h < YASL_PRIVATE_MIN_LOGICAL_PX) logical_h = YASL_PRIVATE_MIN_LOGICAL_PX; - } + /* Compositor-suggested sizes are raised to the minimum; application sizes are used as given. */ + bool suggested_w = !w->parent && w->req_width > 0; + bool suggested_h = !w->parent && w->req_height > 0; + int logical_w = suggested_w ? w->req_width : free_w; + int logical_h = suggested_h ? w->req_height : free_h; + if (suggested_w && logical_w < YASL_PRIVATE_MIN_LOGICAL_PX) logical_w = YASL_PRIVATE_MIN_LOGICAL_PX; + if (suggested_h && logical_h < YASL_PRIVATE_MIN_LOGICAL_PX) logical_h = YASL_PRIVATE_MIN_LOGICAL_PX; if (!(w->base.state & YASL_PRIVATE_STATE_FULLSCREEN)) { if (w->bound_width > 0 && logical_w > w->bound_width) logical_w = w->bound_width; if (w->bound_height > 0 && logical_h > w->bound_height) logical_h = w->bound_height; } - int width = device_px(logical_w, w->pending_scale_120); - int height = device_px(logical_h, w->pending_scale_120); - size_t bytes; - if (width <= 0 || height <= 0 || - (w->base.presenter == YASL_PRIVATE_PRESENTER_CPU && - !buffer_size(width, height, &bytes))) { + /* An oversized window is shrunk to what a buffer holds rather than closed. */ + int64_t device_w = device_extent(logical_w, w->pending_scale_120); + int64_t device_h = device_extent(logical_h, w->pending_scale_120); + if (device_w <= 0 || device_h <= 0) return -1; + if (device_w > DEVICE_SIDE_MAX || device_h > DEVICE_SIDE_MAX || + (double)device_w * (double)device_h > DEVICE_AREA_MAX) { + device_size_fit(&device_w, &device_h); + logical_w = yasl_private_scale_to_logical((int)device_w, w->pending_scale_120); + logical_h = yasl_private_scale_to_logical((int)device_h, w->pending_scale_120); } + int width = (int)device_w; + int height = (int)device_h; + size_t bytes; + if (w->base.presenter == YASL_PRIVATE_PRESENTER_CPU && !buffer_size(width, height, &bytes)) + return -1; bool rescaled = w->scale_120 != w->pending_scale_120; bool resized = width != w->width || height != w->height; w->size_pending = false; @@ -424,6 +461,20 @@ void surface_request_presentation_feedback(struct wayland_surface *w) wp_presentation_feedback_add_listener(fb, &feedback_listener, feedback); } +/* A swap on a rendering thread only sets frame_deferred. The owner arms the frame + * callback and feedback here, then commits so that the callback can fire without + * waiting for the application's next swap. */ +void surface_arm_deferred(struct wayland_surface *w) +{ + if (!atomic_exchange(&w->frame_deferred, false) || w->closed) + return; + bool armed = !w->frame_cb; + wayland_surface_arm_frame(w); + surface_request_presentation_feedback(w); + if (armed && w->frame_cb && !w->closed) + wl_surface_commit(w->surface); +} + static void commit_buffer(struct wayland_surface *w, int idx) { wl_surface_attach(w->surface, w->buffers[idx].proxy, 0, 0); |
