summaryrefslogtreecommitdiff
path: root/src/platform/wayland/buffer.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/buffer.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/buffer.c')
-rw-r--r--src/platform/wayland/buffer.c75
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);