wclient_wl: fix cross-surface motion/release during implicit grab
When a popup menu is opened during a button-held drag, the compositor changes pointer focus to the popup surface as the pointer enters it. But pointer_handle_motion always delivered events to the originally grabbed view (wlc_ptr_grab), never hit-testing inside the popup. This meant wpopupbutton.c received motion events targeted at the main button view with coordinates relative to that view, not the menu. Fix: in both pointer_handle_motion and pointer_handle_button (release), check whether the grab target is within the currently focused toplevel. If not (the pointer entered a different surface), use wlc_hit_test against wlc_ptr_focused to find the correct child view with proper local coordinates. Also enable enter/leave synthesis during grabs when target changes, so child views receive proper WME_ENTER/WME_LEAVE as the pointer moves between them across the cross-surface boundary.
This commit is contained in:
parent
d2c9a59344
commit
a63c97638d
|
|
@ -758,22 +758,39 @@ pointer_handle_motion(void *data, struct wl_pointer *p, uint32_t time,
|
||||||
int lx, ly;
|
int lx, ly;
|
||||||
W_View *target;
|
W_View *target;
|
||||||
|
|
||||||
/* During implicit grab (button held), deliver to grabbed view */
|
/* During implicit grab (button held), deliver to grabbed view.
|
||||||
|
* If the pointer has entered a different toplevel surface (e.g. a
|
||||||
|
* popup menu opened during the drag), hit-test against the focused
|
||||||
|
* surface instead so motion events reach the correct child view
|
||||||
|
* (e.g. menuView) with proper local coordinates. */
|
||||||
if (wlc_ptr_grab) {
|
if (wlc_ptr_grab) {
|
||||||
target = wlc_ptr_grab;
|
/* Check if grab target is within the currently focused toplevel */
|
||||||
int ox = 0, oy = 0;
|
int grab_in_focused = 0;
|
||||||
for (W_View *v = target; v && v != wlc_ptr_focused->view; v = v->parent) {
|
for (W_View *v = wlc_ptr_grab; v; v = v->parent) {
|
||||||
ox += v->pos.x;
|
if (v == wlc_ptr_focused->view) {
|
||||||
oy += v->pos.y;
|
grab_in_focused = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (grab_in_focused) {
|
||||||
|
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 {
|
||||||
|
/* Pointer entered a different toplevel — hit-test there */
|
||||||
|
target = wlc_hit_test(wlc_ptr_focused, (int)wlc_ptr_x, (int)wlc_ptr_y, &lx, &ly);
|
||||||
}
|
}
|
||||||
lx = (int)wlc_ptr_x - ox;
|
|
||||||
ly = (int)wlc_ptr_y - oy;
|
|
||||||
} else {
|
} else {
|
||||||
target = wlc_hit_test(wlc_ptr_focused, (int)wlc_ptr_x, (int)wlc_ptr_y, &lx, &ly);
|
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 */
|
/* Synthesize enter/leave when pointer moves between child views */
|
||||||
if (!wlc_ptr_grab && target != wlc_ptr_child_focus) {
|
if (target != wlc_ptr_child_focus) {
|
||||||
if (wlc_ptr_child_focus) {
|
if (wlc_ptr_child_focus) {
|
||||||
WMEvent lev;
|
WMEvent lev;
|
||||||
memset(&lev, 0, sizeof(lev));
|
memset(&lev, 0, sizeof(lev));
|
||||||
|
|
@ -859,17 +876,29 @@ pointer_handle_button(void *data, struct wl_pointer *p, uint32_t serial,
|
||||||
target = wlc_hit_test(wlc_ptr_focused, (int)wlc_ptr_x, (int)wlc_ptr_y, &lx, &ly);
|
target = wlc_hit_test(wlc_ptr_focused, (int)wlc_ptr_x, (int)wlc_ptr_y, &lx, &ly);
|
||||||
wlc_ptr_grab = target;
|
wlc_ptr_grab = target;
|
||||||
} else {
|
} else {
|
||||||
/* Deliver release to the grabbed view */
|
/* Deliver release to the grabbed view. If the pointer has moved
|
||||||
|
* to a different toplevel (e.g. popup menu), hit-test there. */
|
||||||
target = wlc_ptr_grab;
|
target = wlc_ptr_grab;
|
||||||
if (target) {
|
if (target) {
|
||||||
/* Compute local coords relative to grabbed view */
|
int grab_in_focused = 0;
|
||||||
int ox = 0, oy = 0;
|
for (W_View *v = target; v; v = v->parent) {
|
||||||
for (W_View *v = target; v && v != wlc_ptr_focused->view; v = v->parent) {
|
if (v == wlc_ptr_focused->view) {
|
||||||
ox += v->pos.x;
|
grab_in_focused = 1;
|
||||||
oy += v->pos.y;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (grab_in_focused) {
|
||||||
|
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 {
|
||||||
|
/* Release on a different surface — hit-test there */
|
||||||
|
target = wlc_hit_test(wlc_ptr_focused, (int)wlc_ptr_x, (int)wlc_ptr_y, &lx, &ly);
|
||||||
}
|
}
|
||||||
lx = (int)wlc_ptr_x - ox;
|
|
||||||
ly = (int)wlc_ptr_y - oy;
|
|
||||||
} else {
|
} else {
|
||||||
target = wlc_hit_test(wlc_ptr_focused, (int)wlc_ptr_x, (int)wlc_ptr_y, &lx, &ly);
|
target = wlc_hit_test(wlc_ptr_focused, (int)wlc_ptr_x, (int)wlc_ptr_y, &lx, &ly);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue