summaryrefslogtreecommitdiff
path: root/src/platform/wayland/pointer.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/pointer.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/pointer.c')
-rw-r--r--src/platform/wayland/pointer.c85
1 files changed, 63 insertions, 22 deletions
diff --git a/src/platform/wayland/pointer.c b/src/platform/wayland/pointer.c
index 0cd8c5b..9f8ca49 100644
--- a/src/platform/wayland/pointer.c
+++ b/src/platform/wayland/pointer.c
@@ -35,21 +35,33 @@ struct tablet_pad {
struct zwp_tablet_pad_v2 *proxy;
};
-static struct yasl_private_vector2i device_from_fixed(const struct wayland_surface *w,
- wl_fixed_t x, wl_fixed_t y)
+static struct yasl_private_vector2d device_precise(const struct wayland_surface *w,
+ wl_fixed_t x, wl_fixed_t y)
{
- return (struct yasl_private_vector2i){
- .x = (int)lrint(wl_fixed_to_double(x) * w->scale_factor),
- .y = (int)lrint(wl_fixed_to_double(y) * w->scale_factor),
+ return (struct yasl_private_vector2d){
+ .x = wl_fixed_to_double(x) * w->scale_factor,
+ .y = wl_fixed_to_double(y) * w->scale_factor,
};
}
+static struct yasl_private_vector2i device_round(struct yasl_private_vector2d p)
+{
+ return (struct yasl_private_vector2i){ .x = (int)lrint(p.x), .y = (int)lrint(p.y) };
+}
+
void seat_note_input(struct seat *s, uint32_t serial)
{
s->input_serial = serial;
s->conn->active_seat = s;
}
+uint32_t seat_held_press_serial(const struct seat *s)
+{
+ if (!s) return 0;
+ bool held = s->pointer_buttons != 0 || (s->tool && s->tool->in_contact);
+ return held ? s->press_serial : 0;
+}
+
static void push_pointer(struct seat *s, struct wayland_surface *w,
enum yasl_private_event_type type, struct yasl_private_pointer_event ev)
{
@@ -220,6 +232,9 @@ static void press_queue_flush(struct seat *s, struct press_queue *q,
*buttons = (*buttons & ~mask) | ((uint64_t)(!p.release) << p.button);
struct yasl_private_pointer_event ev = like;
ev.pos = p.pos;
+ ev.precise_x = p.precise.x;
+ ev.precise_y = p.precise.y;
+ ev.precise_position = true;
ev.button = (enum yasl_private_pointer_button)p.button;
enum yasl_private_event_type type = p.release ? YASL_PRIVATE_EVENT_POINTER_UP : YASL_PRIVATE_EVENT_POINTER_DOWN;
push_pointer(s, p.surface, type, ev);
@@ -229,14 +244,15 @@ static void press_queue_flush(struct seat *s, struct press_queue *q,
}
static void press_queue_push(struct press_queue *q, struct wayland_surface *w,
- struct yasl_private_vector2i pos, int button, bool release)
+ struct yasl_private_vector2i pos, struct yasl_private_vector2d precise,
+ int button, bool release)
{
if (q->n == 8) {
q->head = (q->head + 1) & 7;
q->n--;
}
q->ev[(q->head + q->n) & 7] = (struct pending_press){
- .surface = w, .pos = pos, .button = button, .release = release,
+ .surface = w, .pos = pos, .precise = precise, .button = button, .release = release,
};
q->n++;
}
@@ -282,6 +298,9 @@ static void flush_pending_motion(struct seat *s, struct wayland_surface *w)
s->motion_pending = false;
push_pointer(s, w, YASL_PRIVATE_EVENT_POINTER_MOTION, (struct yasl_private_pointer_event){
.pos = s->pointer_pos,
+ .precise_x = s->pointer_precise.x,
+ .precise_y = s->pointer_precise.y,
+ .precise_position = true,
});
}
@@ -295,7 +314,8 @@ static void pointer_enter(void *data, struct wl_pointer *pointer,
(struct wayland_surface *)wl_surface_get_user_data(surface) : NULL;
s->pointer_focus = w;
if (!w) return;
- s->pointer_pos = device_from_fixed(w, surface_x, surface_y);
+ s->pointer_precise = device_precise(w, surface_x, surface_y);
+ s->pointer_pos = device_round(s->pointer_precise);
s->pointer_serial = serial;
s->cursor_shape = -1;
seat_set_cursor(s, w);
@@ -315,6 +335,7 @@ static void pointer_leave(void *data, struct wl_pointer *pointer,
pointer_frame(s, s->pointer);
s->ptr_left = w;
s->ptr_left_pos = s->pointer_pos;
+ s->ptr_left_precise = s->pointer_precise;
s->ptr_left_motion = s->motion_pending;
s->motion_pending = false;
pointer_event_done(s);
@@ -327,7 +348,8 @@ static void pointer_motion(void *data, struct wl_pointer *pointer,
struct seat *s = data;
struct wayland_surface *w = s->pointer_focus;
if (!w) return;
- s->pointer_pos = device_from_fixed(w, surface_x, surface_y);
+ s->pointer_precise = device_precise(w, surface_x, surface_y);
+ s->pointer_pos = device_round(s->pointer_precise);
s->motion_pending = true;
pointer_event_done(s);
}
@@ -360,7 +382,7 @@ static void pointer_button(void *data, struct wl_pointer *pointer,
s->press_serial = serial;
s->press_surface = w;
}
- press_queue_push(&s->ptr_presses, w, s->pointer_pos, b, release);
+ press_queue_push(&s->ptr_presses, w, s->pointer_pos, s->pointer_precise, b, release);
pointer_event_done(s);
}
@@ -464,17 +486,22 @@ static void pointer_frame(void *data, struct wl_pointer *pointer)
if (s->ptr_left_motion)
push_pointer(s, left, YASL_PRIVATE_EVENT_POINTER_MOTION, (struct yasl_private_pointer_event){
.pos = s->ptr_left_pos,
+ .precise_x = s->ptr_left_precise.x,
+ .precise_y = s->ptr_left_precise.y,
+ .precise_position = true,
});
press_queue_flush(s, &s->ptr_presses, left, &s->pointer_buttons, (struct yasl_private_pointer_event){0});
push_pointer(s, left, YASL_PRIVATE_EVENT_POINTER_LEAVE, (struct yasl_private_pointer_event){
.pos = s->ptr_left_pos,
});
struct yasl_private_vector2i pos = s->pointer_pos;
+ struct yasl_private_vector2d precise = s->pointer_precise;
bool motion = s->motion_pending;
uint32_t serial = s->pointer_focus ? s->pointer_serial : 0;
int shape = s->pointer_focus ? s->cursor_shape : -1;
seat_pointer_reset(s);
s->pointer_pos = pos;
+ s->pointer_precise = precise;
s->motion_pending = motion;
s->pointer_serial = serial;
s->cursor_shape = shape;
@@ -508,6 +535,9 @@ static void relative_motion(void *data, struct zwp_relative_pointer_v1 *pointer,
flush_pending_motion(s, w);
push_pointer(s, w, YASL_PRIVATE_EVENT_POINTER_MOTION, (struct yasl_private_pointer_event){
.pos = s->pointer_pos,
+ .precise_x = s->pointer_precise.x,
+ .precise_y = s->pointer_precise.y,
+ .precise_position = true,
.delta = {wl_fixed_to_double(dx) * w->scale_factor,
wl_fixed_to_double(dy) * w->scale_factor},
});
@@ -571,6 +601,17 @@ static void seat_lock_surfaces(struct seat *s)
surface_lock_pointer(w, s);
}
+void surface_lock_preferred(struct wayland_surface *w)
+{
+ struct wayland_display *c = w->conn;
+ /* Prefer the seat that last delivered input; others lock once it goes away. */
+ struct seat *seat = c->active_seat && c->active_seat->pointer ? c->active_seat : NULL;
+ for (struct seat *s = c->seats; s && !seat; s = s->next)
+ if (s->pointer) seat = s;
+ if (seat)
+ surface_lock_pointer(w, seat);
+}
+
bool wayland_surface_set_pointer_mode(struct wayland_surface *w, enum yasl_private_pointer_mode mode)
{
if (!w || !w->conn || w->destroyed)
@@ -581,12 +622,7 @@ bool wayland_surface_set_pointer_mode(struct wayland_surface *w, enum yasl_priva
return false;
w->relative = relative;
if (relative) {
- /* Prefer the seat that last delivered input; others lock once it goes away. */
- struct seat *seat = c->active_seat && c->active_seat->pointer ? c->active_seat : NULL;
- for (struct seat *s = c->seats; s && !seat; s = s->next)
- if (s->pointer) seat = s;
- if (seat)
- surface_lock_pointer(w, seat);
+ surface_lock_preferred(w);
} else {
surface_unlock_pointer(w);
}
@@ -859,6 +895,7 @@ static void tablet_tool_proximity_in(void *data,
if (!w) return;
t->x = s->tool_pos.x;
t->y = s->tool_pos.y;
+ s->tool_precise = (struct yasl_private_vector2d){ s->tool_pos.x, s->tool_pos.y };
if (s->conn->cursor_shape_mgr && !t->shape_dev)
t->shape_dev = wp_cursor_shape_manager_v1_get_tablet_tool_v2(
s->conn->cursor_shape_mgr, t->proxy);
@@ -903,7 +940,7 @@ static void tablet_tool_down(void *data, struct zwp_tablet_tool_v2 *tool, uint32
seat_note_input(s, serial);
s->press_serial = serial;
s->press_surface = w;
- press_queue_push(&s->tool_presses, w, s->tool_pos, 0, false);
+ press_queue_push(&s->tool_presses, w, s->tool_pos, s->tool_precise, 0, false);
t->in_contact = true;
pen_action_push(t, YASL_PRIVATE_PEN_DOWN, 0);
}
@@ -916,7 +953,7 @@ static void tablet_tool_up(void *data, struct zwp_tablet_tool_v2 *tool)
struct wayland_surface *w = s->tablet_focus;
if (!w) return;
s->pen.pressure = 0;
- press_queue_push(&s->tool_presses, w, s->tool_pos, 0, true);
+ press_queue_push(&s->tool_presses, w, s->tool_pos, s->tool_precise, 0, true);
t->pressure = 0;
t->in_contact = false;
pen_action_push(t, YASL_PRIVATE_PEN_UP, 0);
@@ -929,10 +966,11 @@ static void tablet_tool_motion(void *data, struct zwp_tablet_tool_v2 *tool, wl_f
struct seat *s = ((struct tablet_tool *)data)->seat;
struct wayland_surface *w = s->tablet_focus;
if (!w) return;
- s->tool_pos = device_from_fixed(w, x, y);
+ s->tool_precise = device_precise(w, x, y);
+ s->tool_pos = device_round(s->tool_precise);
struct tablet_tool *owned = data;
- owned->x = wl_fixed_to_double(x) * w->scale_factor;
- owned->y = wl_fixed_to_double(y) * w->scale_factor;
+ owned->x = s->tool_precise.x;
+ owned->y = s->tool_precise.y;
s->tool_motion = true;
}
@@ -953,7 +991,7 @@ static void tablet_tool_button(void *data, struct zwp_tablet_tool_v2 *tool,
default: return;
}
bool release = state == ZWP_TABLET_TOOL_V2_BUTTON_STATE_RELEASED;
- press_queue_push(&s->tool_presses, w, s->tool_pos, b, release);
+ press_queue_push(&s->tool_presses, w, s->tool_pos, s->tool_precise, b, release);
pen_action_push(t, YASL_PRIVATE_PEN_BUTTON, (unsigned)b);
}
@@ -1032,6 +1070,9 @@ static void tool_frame(void *d, struct zwp_tablet_tool_v2 *t, uint32_t time)
if (s->tool_motion) {
struct yasl_private_pointer_event ev = like;
ev.pos = s->tool_pos;
+ ev.precise_x = s->tool_precise.x;
+ ev.precise_y = s->tool_precise.y;
+ ev.precise_position = true;
push_pointer(s, w, YASL_PRIVATE_EVENT_POINTER_MOTION, ev);
s->tool_motion = false;
}