From 7efede0274c2621511238b0a35cd37b28d8af86e Mon Sep 17 00:00:00 2001 From: Window Maker Date: Sun, 24 May 2026 17:33:50 +0200 Subject: [PATCH] WINGs/wclient_wl: implement implicit pointer grab for drag operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- WINGs/wclient_wl.c | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/WINGs/wclient_wl.c b/WINGs/wclient_wl.c index e3af02b..6cc5c1d 100644 --- a/WINGs/wclient_wl.c +++ b/WINGs/wclient_wl.c @@ -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;