summaryrefslogtreecommitdiff
path: root/src/platform/wayland/pointer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/wayland/pointer.c')
-rw-r--r--src/platform/wayland/pointer.c131
1 files changed, 128 insertions, 3 deletions
diff --git a/src/platform/wayland/pointer.c b/src/platform/wayland/pointer.c
index 83f6e2f..cdc5780 100644
--- a/src/platform/wayland/pointer.c
+++ b/src/platform/wayland/pointer.c
@@ -152,8 +152,10 @@ static uint32_t cursor_shape_of(enum yasl_cursor shape)
}
}
-static void seat_set_cursor(struct seat *s, struct wayland_surface *w, enum yasl_cursor shape)
+/* Relative mode hides the pointer but keeps w->cursor for the return to NORMAL. */
+static void seat_set_cursor(struct seat *s, struct wayland_surface *w)
{
+ enum yasl_cursor shape = w->relative ? YASL_CURSOR_HIDDEN : w->cursor;
if (!s->pointer || !s->pointer_serial || s->pointer_focus != w ||
s->cursor_shape == (int)shape)
return;
@@ -183,7 +185,7 @@ void wayland_surface_set_cursor(struct wayland_surface *w, enum yasl_cursor shap
if (!w || !w->conn || w->destroyed || (unsigned)shape > YASL_CURSOR_HIDDEN) return;
w->cursor = shape;
for (struct seat *s = w->conn->seats; s; s = s->next) {
- seat_set_cursor(s, w, shape);
+ seat_set_cursor(s, w);
if (s->tool && s->tablet_focus == w)
tool_set_cursor(s->tool, shape);
}
@@ -279,7 +281,7 @@ static void pointer_enter(void *data, struct wl_pointer *pointer,
s->pointer_pos = device_from_fixed(w, surface_x, surface_y);
s->pointer_serial = serial;
s->cursor_shape = -1;
- seat_set_cursor(s, w, w->cursor);
+ seat_set_cursor(s, w);
s->motion_pending = true;
pointer_event_done(s);
}
@@ -448,6 +450,105 @@ static void pointer_axis_relative_direction(void *data, struct wl_pointer *point
static void pointer_warp(void *data, struct wl_pointer *pointer, wl_fixed_t x, wl_fixed_t y)
{ (void)data; (void)pointer; (void)x; (void)y; }
+static void relative_motion(void *data, struct zwp_relative_pointer_v1 *pointer,
+ uint32_t utime_hi, uint32_t utime_lo, wl_fixed_t dx, wl_fixed_t dy,
+ wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel)
+{
+ (void)pointer; (void)utime_hi; (void)utime_lo; (void)dx_unaccel; (void)dy_unaccel;
+ struct seat *s = data;
+ struct wayland_surface *w = s->pointer_focus;
+ /* Unlocked movement arrives as wl_pointer.motion; the queue derives its delta. */
+ if (!w || !w->pointer_locked || w->lock_seat != s) return;
+ flush_pending_motion(s, w);
+ push_pointer(s, w, YASL_EVENT_POINTER_MOTION, (struct yasl_pointer_event){
+ .pos = s->pointer_pos,
+ .delta = {wl_fixed_to_double(dx) * w->scale_factor,
+ wl_fixed_to_double(dy) * w->scale_factor},
+ });
+}
+
+static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
+ .relative_motion = relative_motion,
+};
+
+static void pointer_locked(void *data, struct zwp_locked_pointer_v1 *lock)
+{
+ (void)lock;
+ struct wayland_surface *w = data;
+ w->pointer_locked = true;
+ surface_root(w)->input.relative = true;
+}
+
+static void pointer_unlocked(void *data, struct zwp_locked_pointer_v1 *lock)
+{
+ (void)lock;
+ struct wayland_surface *w = data;
+ w->pointer_locked = false;
+ surface_root(w)->input.relative = false;
+}
+
+static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = {
+ .locked = pointer_locked,
+ .unlocked = pointer_unlocked,
+};
+
+/* A persistent lock re-engages each time the compositor focuses the surface. */
+static void surface_lock_pointer(struct wayland_surface *w, struct seat *s)
+{
+ struct wayland_display *c = w->conn;
+ if (!w->relative || w->pointer_lock || w->destroyed || !s->pointer)
+ return;
+ w->pointer_lock = zwp_pointer_constraints_v1_lock_pointer(c->pointer_constraints,
+ w->surface, s->pointer, NULL, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
+ if (!w->pointer_lock)
+ return;
+ w->lock_seat = s;
+ zwp_locked_pointer_v1_add_listener(w->pointer_lock, &locked_pointer_listener, w);
+}
+
+void surface_unlock_pointer(struct wayland_surface *w)
+{
+ if (!w->pointer_lock)
+ return;
+ zwp_locked_pointer_v1_destroy(w->pointer_lock);
+ w->pointer_lock = NULL;
+ w->lock_seat = NULL;
+ if (w->pointer_locked) {
+ w->pointer_locked = false;
+ surface_root(w)->input.relative = false;
+ }
+}
+
+static void seat_lock_surfaces(struct seat *s)
+{
+ for (struct wayland_surface *w = s->conn->surfaces; w; w = w->conn_next)
+ surface_lock_pointer(w, s);
+}
+
+bool wayland_surface_set_pointer_mode(struct wayland_surface *w, enum yasl_pointer_mode mode)
+{
+ if (!w || !w->conn || w->destroyed)
+ return false;
+ struct wayland_display *c = w->conn;
+ bool relative = mode == YASL_POINTER_RELATIVE;
+ if (relative && (!c->relative_pointer_mgr || !c->pointer_constraints))
+ 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);
+ } else {
+ surface_unlock_pointer(w);
+ }
+ for (struct seat *s = c->seats; s; s = s->next)
+ seat_set_cursor(s, w);
+ return true;
+}
+
static const struct wl_pointer_listener pointer_listener = {
.axis_relative_direction = pointer_axis_relative_direction,
.warp = pointer_warp,
@@ -984,6 +1085,18 @@ void seat_clipboard_ensure(struct seat *s)
static void seat_drop_pointer(struct seat *s)
{
+ struct wayland_display *c = s->conn;
+ bool unlocked = false;
+ for (struct wayland_surface *w = c->surfaces; w; w = w->conn_next) {
+ if (w->lock_seat == s) {
+ surface_unlock_pointer(w);
+ unlocked = true;
+ }
+ }
+ if (s->relative_pointer) {
+ zwp_relative_pointer_v1_destroy(s->relative_pointer);
+ s->relative_pointer = NULL;
+ }
if (s->cursor_shape_dev) {
wp_cursor_shape_device_v1_destroy(s->cursor_shape_dev);
s->cursor_shape_dev = NULL;
@@ -998,6 +1111,9 @@ static void seat_drop_pointer(struct seat *s)
seat_pointer_reset(s);
s->pointer_focus = s->ptr_left = NULL;
s->ptr_presses.n = 0;
+ for (struct seat *o = c->seats; unlocked && o; o = o->next)
+ if (o != s && o->pointer)
+ seat_lock_surfaces(o);
}
static void seat_drop_keyboard(struct seat *s)
@@ -1057,6 +1173,15 @@ static void seat_capabilities(void *data, struct wl_seat *proxy, uint32_t caps)
if (s->pointer && c->cursor_shape_mgr)
s->cursor_shape_dev = wp_cursor_shape_manager_v1_get_pointer(
c->cursor_shape_mgr, s->pointer);
+ if (s->pointer && c->relative_pointer_mgr) {
+ s->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(
+ c->relative_pointer_mgr, s->pointer);
+ if (s->relative_pointer)
+ zwp_relative_pointer_v1_add_listener(s->relative_pointer,
+ &relative_pointer_listener, s);
+ }
+ if (s->pointer)
+ seat_lock_surfaces(s);
}
} else if (s->pointer) {
seat_drop_pointer(s);