WINGs/wclient_wl: implement implicit pointer grab for drag operations

On X11, a button press creates an implicit pointer grab — all
subsequent motion and release events go to the window that received
the press, even if the pointer moves outside it. This is essential
for scroller knob dragging, window resize handles, etc.

Implement the same for Wayland client: on button press, record the
target view as wlc_ptr_grab. Motion events during the grab are
delivered to the grabbed view with coordinates translated relative
to it. On button release, deliver to the grabbed view and clear
the grab. Enter/leave synthesis is suppressed during grabs.
This commit is contained in:
Window Maker 2026-05-24 17:33:50 +02:00
parent d6767530e4
commit 7efede0274
1 changed files with 39 additions and 3 deletions

View File

@ -544,6 +544,7 @@ static WLClientToplevel *wlc_ptr_focused;
static unsigned int wlc_ptr_button_state;
static unsigned int wlc_ptr_mods;
static W_View *wlc_ptr_child_focus;
static W_View *wlc_ptr_grab; /* implicit grab: deliver events here while button held */
/*
* Hit-test: find the deepest child view at (x,y) relative to the toplevel,
@ -640,10 +641,24 @@ pointer_handle_motion(void *data, struct wl_pointer *p, uint32_t time,
if (!wlc_ptr_focused) return;
int lx, ly;
W_View *target = wlc_hit_test(wlc_ptr_focused, (int)wlc_ptr_x, (int)wlc_ptr_y, &lx, &ly);
W_View *target;
/* During implicit grab (button held), deliver to grabbed view */
if (wlc_ptr_grab) {
target = wlc_ptr_grab;
int ox = 0, oy = 0;
for (W_View *v = target; v && v != wlc_ptr_focused->view; v = v->parent) {
ox += v->pos.x;
oy += v->pos.y;
}
lx = (int)wlc_ptr_x - ox;
ly = (int)wlc_ptr_y - oy;
} else {
target = wlc_hit_test(wlc_ptr_focused, (int)wlc_ptr_x, (int)wlc_ptr_y, &lx, &ly);
}
/* Synthesize enter/leave when pointer moves between child views */
if (target != wlc_ptr_child_focus) {
if (!wlc_ptr_grab && target != wlc_ptr_child_focus) {
if (wlc_ptr_child_focus) {
WMEvent lev;
memset(&lev, 0, sizeof(lev));
@ -717,7 +732,28 @@ pointer_handle_button(void *data, struct wl_pointer *p, uint32_t serial,
? WME_BUTTON_PRESS : WME_BUTTON_RELEASE;
int lx, ly;
W_View *target = wlc_hit_test(wlc_ptr_focused, (int)wlc_ptr_x, (int)wlc_ptr_y, &lx, &ly);
W_View *target;
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
target = wlc_hit_test(wlc_ptr_focused, (int)wlc_ptr_x, (int)wlc_ptr_y, &lx, &ly);
wlc_ptr_grab = target;
} else {
/* Deliver release to the grabbed view */
target = wlc_ptr_grab;
if (target) {
/* Compute local coords relative to grabbed view */
int ox = 0, oy = 0;
for (W_View *v = target; v && v != wlc_ptr_focused->view; v = v->parent) {
ox += v->pos.x;
oy += v->pos.y;
}
lx = (int)wlc_ptr_x - ox;
ly = (int)wlc_ptr_y - oy;
} else {
target = wlc_hit_test(wlc_ptr_focused, (int)wlc_ptr_x, (int)wlc_ptr_y, &lx, &ly);
}
wlc_ptr_grab = NULL;
}
ev.window = target ? target->window : wlc_ptr_focused->view->window;
ev.u.button.window = ev.window;