fix: WINGs widget clicks on Wayland (child view hit-testing)

On Wayland, all WINGs child widgets composite into a single scene_buf
for the toplevel dialog. Clicks always arrived with the toplevel window
ID, so WMHandleEvent delivered them to the toplevel view which has no
ButtonPressMask handler. Child widget handlers never fired.

Fix: add W_FindViewAtPoint() to walk the view tree and find the deepest
mapped child at click coordinates. In handle_pointer_button, resolve the
child view for WINGs surfaces and pass it as the direct target to
W_WL_EnqueueEvent. WMHandleEvent then delivers directly to the widget.
This commit is contained in:
lkn 2026-05-23 13:50:00 +02:00
parent 68d4301b9f
commit ee8489e3a1
2 changed files with 33 additions and 1 deletions

View File

@ -328,6 +328,25 @@ static void wl_redisplay(void *vctx, W_View *view)
W_ViewCommitBackingFn((unsigned long)top->window);
}
/* Find the deepest child view at (x, y) relative to the given view. */
W_View *W_FindViewAtPoint(W_View *parent, int x, int y)
{
if (!parent) return NULL;
W_View *child = parent->childrenList;
while (child) {
if (child->flags.mapped && child->flags.realized &&
x >= child->pos.x && x < child->pos.x + child->size.width &&
y >= child->pos.y && y < child->pos.y + child->size.height) {
/* Recurse into this child with local coordinates */
W_View *deeper = W_FindViewAtPoint(child,
x - child->pos.x, y - child->pos.y);
return deeper ? deeper : child;
}
child = child->nextSister;
}
return NULL;
}
static WMPoint wl_get_screen_position(void *vctx, W_View *view)
{
W_Screen *scr = (W_Screen *)vctx;

View File

@ -5248,7 +5248,20 @@ handle_pointer_button(struct wl_listener *listener, void *data)
case 0x112: wev.u.button.button = 2; break;
default: wev.u.button.button = (ev->button - 0x110) + 1; break;
}
W_WL_EnqueueEvent(wmscr, &wev, NULL);
/* For WINGs views, resolve the child widget at click coordinates */
struct W_View *target = NULL;
if (fb && fb->id >= 0x20000000) {
extern W_View *W_GetViewForWindow(unsigned long id);
extern struct W_View *W_FindViewAtPoint(struct W_View *parent, int x, int y);
W_View *toplevel = W_GetViewForWindow((unsigned long)fb->id);
if (toplevel) {
target = W_FindViewAtPoint(toplevel,
wev.u.button.x, wev.u.button.y);
if (!target)
target = toplevel;
}
}
W_WL_EnqueueEvent(wmscr, &wev, target);
}
wlr_seat_pointer_notify_button(wl_state.seat, ev->time_msec,