wclient_wl: hit-test child views on pointer enter/leave during drag

pointer_handle_enter always sent WME_ENTER to the toplevel root view
with _view_target=NULL, so child views (e.g. menuView) never received
proper WME_ENTER with correct local coordinates when the pointer entered
the popup surface during a held drag.

Fix: hit-test against the newly-focused toplevel on enter, find the
deepest child at the pointer position, and deliver WME_ENTER to that
child view with proper local coordinates and _view_target set.

Also fix pointer_handle_leave to deliver WME_LEAVE to the deepest
child (via wlc_ptr_child_focus) instead of the toplevel root view,
matching the enter side.
This commit is contained in:
lkn 2026-06-07 13:33:19 +02:00
parent a63c97638d
commit d43ceae081
1 changed files with 19 additions and 6 deletions

View File

@ -711,18 +711,27 @@ pointer_handle_enter(void *data, struct wl_pointer *p, uint32_t serial,
wlc_ptr_y = wl_fixed_to_double(sy);
if (!wlc_ptr_focused) return;
/* Hit-test to find the deepest child view at the enter point,
* consistent with motion handling. This ensures popup child views
* (e.g. menuView) receive proper WME_ENTER with correct local
* coordinates during drag-selection. */
int hit_lx, hit_ly;
W_View *hit = wlc_hit_test(wlc_ptr_focused, (int)wlc_ptr_x,
(int)wlc_ptr_y, &hit_lx, &hit_ly);
wlc_ptr_child_focus = hit;
WMEvent ev;
memset(&ev, 0, sizeof(ev));
ev.type = WME_ENTER;
ev.window = wlc_ptr_focused->view->window;
ev.window = hit ? hit->window : wlc_ptr_focused->view->window;
ev.u.crossing.window = ev.window;
ev.u.crossing.x = (int)wlc_ptr_x;
ev.u.crossing.y = (int)wlc_ptr_y;
ev.u.crossing.x = hit_lx;
ev.u.crossing.y = hit_ly;
ev.u.crossing.x_root = (int)wlc_ptr_x;
ev.u.crossing.y_root = (int)wlc_ptr_y;
ev.u.crossing.mode = WM_NOTIFY_NORMAL;
ev.u.crossing.detail = WM_NOTIFY_ANCESTOR;
W_WL_EnqueueEvent(ctx->scr, &ev, NULL);
W_WL_EnqueueEvent(ctx->scr, &ev, hit);
}
static void
@ -736,11 +745,15 @@ pointer_handle_leave(void *data, struct wl_pointer *p, uint32_t serial,
WMEvent ev;
memset(&ev, 0, sizeof(ev));
ev.type = WME_LEAVE;
ev.window = wlc_ptr_focused->view->window;
/* Deliver leave to the deepest child that was under the pointer,
* consistent with the child that received the matching enter. */
ev.window = wlc_ptr_child_focus
? wlc_ptr_child_focus->window
: wlc_ptr_focused->view->window;
ev.u.crossing.window = ev.window;
ev.u.crossing.mode = WM_NOTIFY_NORMAL;
ev.u.crossing.detail = WM_NOTIFY_ANCESTOR;
W_WL_EnqueueEvent(ctx->scr, &ev, NULL);
W_WL_EnqueueEvent(ctx->scr, &ev, wlc_ptr_child_focus);
wlc_ptr_focused = NULL;
wlc_ptr_child_focus = NULL;
}