wayland: implement pointer warp via pointer-constraints hint

Native Wayland clients (e.g. plan9port acme/devdraw) warp the pointer
by locking it via zwp_pointer_constraints_v1, setting a
cursor_position_hint, then immediately destroying the lock. The
compositor is expected to warp the cursor to the hint on unlock.

Our handler just activated the constraint and ignored the hint. Now we
listen for the constraint destroy event and warp the cursor to the
hint position (converted from surface-local to global coordinates).
This commit is contained in:
lkn 2026-06-25 07:20:05 +02:00
parent 1ef79265e7
commit 9da620d443
1 changed files with 43 additions and 4 deletions

View File

@ -9,6 +9,7 @@
#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/types/wlr_primary_selection.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_seat.h>
@ -99,16 +100,54 @@ handle_pointer_axis(struct wl_listener *listener, void *data)
* In headless mode the pointer is virtual so there is nothing physically
* to lock, but activating keeps the client happy.
*/
struct wl_constraint_ctx {
struct wlr_pointer_constraint_v1 *constraint;
struct wl_listener destroy;
};
static void
handle_pointer_constraint_destroy(struct wl_listener *listener, void *data)
{
struct wl_constraint_ctx *ctx =
wl_container_of(listener, ctx, destroy);
struct wlr_pointer_constraint_v1 *constraint = ctx->constraint;
(void)data;
if (constraint->current.cursor_hint.enabled && constraint->surface) {
/* Convert surface-local hint to global coordinates */
double sx = constraint->current.cursor_hint.x;
double sy = constraint->current.cursor_hint.y;
int gx = 0, gy = 0;
/* Find the toplevel view owning this surface to get global pos */
struct wl_toplevel_view *v;
wl_list_for_each(v, &wl_state.toplevel_list, link) {
if (v->xdg_surface && v->xdg_surface->surface == constraint->surface) {
gx = v->x;
gy = v->y;
break;
}
}
wl_pointer_warp_to(gx + (int)sx, gy + (int)sy);
}
wl_list_remove(&ctx->destroy.link);
free(ctx);
}
void
handle_new_pointer_constraint(struct wl_listener *listener, void *data)
{
(void)listener;
struct wlr_pointer_constraint_v1 *constraint = data;
(void)constraint;
/* Activate immediately — in nested mode the real pointer is bounded
* by the host compositor's window anyway. */
wlr_pointer_constraint_v1_send_activated(constraint);
wwarning("wl_backend: pointer constraint activated");
struct wl_constraint_ctx *ctx = calloc(1, sizeof(*ctx));
if (ctx) {
ctx->constraint = constraint;
ctx->destroy.notify = handle_pointer_constraint_destroy;
wl_signal_add(&constraint->events.destroy, &ctx->destroy);
}
}
/*