wayland: implement pointer grab for icon drag-and-move
Implement wl_pointer_grab (type 3 WM policy grab) that suppresses client pointer focus delivery during WM-internal drags while letting WME_MOTION events flow to WMMaskEvent. Implement wl_pointer_ungrab and wl_pointer_change_cursor (maps WCUR_* to xcursor names). Fix button release handler to not terminate type 3 grabs early — the policy code calls pointer_ungrab itself. Fix overlay surface blocking hit-testing: the iconify animation's full-screen overlay (WL_LAYER_OVERLAY) was left enabled after the animation completed, causing wlr_scene_node_at to hit it instead of frame_bufs. Now server_ungrab disables the overlay node after flush.
This commit is contained in:
parent
6d42151d2e
commit
37ecea7c07
|
|
@ -806,6 +806,7 @@ Bool wHandleAppIconMove(WAppIcon *aicon, WMEvent *event)
|
|||
while (1) {
|
||||
WMMaskEvent(scr->wmscreen, PointerMotionMask | ButtonReleaseMask | ButtonPressMask
|
||||
| ButtonMotionMask | ExposureMask | EnterWindowMask, &ev);
|
||||
wwarning("wHandleAppIconMove: got event type=%d", ev.type);
|
||||
switch (ev.type) {
|
||||
case WME_EXPOSE:
|
||||
WMHandleEvent(&ev);
|
||||
|
|
|
|||
|
|
@ -409,8 +409,12 @@ struct wl_compositor_state {
|
|||
int sigfd;
|
||||
struct wl_event_source *sigfd_source;
|
||||
/* Phase 22: interactive move/resize grab state.
|
||||
* grab_type: 0=none, 1=move, 2=resize.
|
||||
* grab_view: the toplevel being moved/resized (NULL when grab_type==0).
|
||||
* grab_type: 0=none, 1=move, 2=resize, 3=WM policy grab.
|
||||
* Type 3 is used by wl_pointer_grab for WM-internal drags (dock icons,
|
||||
* app icons, menus). It suppresses wl_update_pointer_focus (so client
|
||||
* surfaces don't steal pointer events) but does NOT intercept motion —
|
||||
* WME_MOTION events flow normally to the WMMaskEvent loop.
|
||||
* grab_view: the toplevel being moved/resized (NULL when grab_type==0 or 3).
|
||||
* grab_start_ptr_{x,y}: pointer position when grab started.
|
||||
* grab_start_win_{x,y,w,h}: window geometry when grab started.
|
||||
* grab_resize_edges: WL_SHELL_SURFACE_RESIZE_* bitmask (resize only). */
|
||||
|
|
@ -5130,10 +5134,12 @@ handle_pointer_button(struct wl_listener *listener, void *data)
|
|||
struct wlr_pointer_button_event *ev = data;
|
||||
if (!wl_state.seat)
|
||||
return;
|
||||
/* Phase 22: any button release ends an active grab. */
|
||||
if (wl_state.grab_type &&
|
||||
/* Phase 22: any button release ends an active compositor grab (move/resize).
|
||||
* Type 3 (WM policy grab) is NOT ended here — the policy code calls
|
||||
* pointer_ungrab itself after processing the button release event. */
|
||||
if (wl_state.grab_type && wl_state.grab_type != 3 &&
|
||||
ev->state == WLR_BUTTON_RELEASED) {
|
||||
wwarning("wl_backend: grab ended (button release)");
|
||||
wwarning("wl_backend: grab ended (button release) grab_type=%d", wl_state.grab_type);
|
||||
wl_grab_end();
|
||||
wl_update_pointer_focus(ev->time_msec);
|
||||
return;
|
||||
|
|
@ -5233,12 +5239,33 @@ wl_pointer_grab(WNativeWindow grab_win, WNativeWindow confine_to,
|
|||
unsigned int event_mask, int cursor_id,
|
||||
int owner_events, unsigned long timestamp)
|
||||
{
|
||||
(void)grab_win; (void)confine_to; (void)event_mask;
|
||||
(void)cursor_id; (void)owner_events; (void)timestamp;
|
||||
return 0;
|
||||
(void)confine_to; (void)event_mask;
|
||||
(void)timestamp; (void)grab_win;
|
||||
wwarning("wl_pointer_grab: win=0x%lx owner_events=%d cursor_id=%d grab_type_before=%d",
|
||||
grab_win, owner_events, cursor_id, wl_state.grab_type);
|
||||
/* Only set up a WM policy grab (type 3) for actual drags (owner_events=True).
|
||||
* Brief protective grabs (e.g., during icon creation) don't need to suppress
|
||||
* pointer focus — they just need to return success. */
|
||||
if (owner_events) {
|
||||
wl_state.grab_type = 3;
|
||||
wl_state.grab_view = NULL;
|
||||
if (wl_state.seat)
|
||||
wlr_seat_pointer_clear_focus(wl_state.seat);
|
||||
}
|
||||
if (cursor_id > 0)
|
||||
wl_set_cursor_image("left_ptr");
|
||||
return 1; /* success */
|
||||
}
|
||||
|
||||
static void wl_pointer_ungrab(unsigned long timestamp) { (void)timestamp; }
|
||||
static void wl_pointer_ungrab(unsigned long timestamp)
|
||||
{
|
||||
(void)timestamp;
|
||||
wwarning("wl_pointer_ungrab: grab_type=%d", wl_state.grab_type);
|
||||
if (wl_state.grab_type == 3) {
|
||||
wl_state.grab_type = 0;
|
||||
wl_state.grab_view = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Phase 13: pointer focus delivery to Wayland surfaces */
|
||||
|
|
@ -5690,6 +5717,8 @@ overlay_flush(void)
|
|||
wm_shm_surface_commit(s, _op, 0, 0);
|
||||
}
|
||||
if (s->scene_buf) {
|
||||
/* Ensure the node is visible for the animation frame. */
|
||||
wlr_scene_node_set_enabled(&s->scene_buf->node, true);
|
||||
/* Notify wlroots that the pixel data has changed. */
|
||||
wlr_scene_buffer_set_buffer(s->scene_buf, &s->wlr_buf);
|
||||
}
|
||||
|
|
@ -5704,7 +5733,16 @@ overlay_flush(void)
|
|||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void wl_server_grab(void) {} /* no-op: Wayland double-buffers for us */
|
||||
static void wl_server_ungrab(void) { overlay_flush(); }
|
||||
static void wl_server_ungrab(void)
|
||||
{
|
||||
overlay_flush();
|
||||
/* Disable the overlay scene node so it doesn't intercept hit-testing.
|
||||
* The XOR animation has already erased its pixels; leaving the node
|
||||
* enabled blocks frame_buf_at (wlr_scene_node_at hits the overlay
|
||||
* before any frame_buf). */
|
||||
if (wl_state.overlay_surf && wl_state.overlay_surf->scene_buf)
|
||||
wlr_scene_node_set_enabled(&wl_state.overlay_surf->scene_buf->node, false);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Stacking */
|
||||
|
|
@ -7931,7 +7969,20 @@ static int wl_noop_cursor_create_from_files(WScreen *scr, const char *bitmap_pat
|
|||
{ (void)scr; (void)bitmap_path; (void)mask_path; (void)cursor_out; return 0; }
|
||||
static void wl_noop_cursor_free(Cursor cursor) { (void)cursor; }
|
||||
static void wl_noop_cursor_set(WNativeWindow window, Cursor cursor) { (void)window; (void)cursor; }
|
||||
static void wl_noop_pointer_change_cursor(int cursor_id) { (void)cursor_id; }
|
||||
static void wl_pointer_change_cursor(int cursor_id)
|
||||
{
|
||||
const char *name;
|
||||
switch (cursor_id) {
|
||||
case 1: /* WCUR_MOVE */ name = "fleur"; break;
|
||||
case 2: /* WCUR_RESIZE */ name = "bottom_right_corner"; break;
|
||||
case 7: /* WCUR_WAIT */ name = "watch"; break;
|
||||
case 8: /* WCUR_ARROW */ name = "left_ptr"; break;
|
||||
case 9: /* WCUR_QUESTION */name = "question_arrow"; break;
|
||||
case 10:/* WCUR_TEXT */ name = "xterm"; break;
|
||||
default: name = "left_ptr"; break;
|
||||
}
|
||||
wl_set_cursor_image(name);
|
||||
}
|
||||
static void wl_noop_appicon_paint_dock_dots(WAppIcon *aicon, void *wapp)
|
||||
{ (void)aicon; (void)wapp; }
|
||||
|
||||
|
|
@ -8217,7 +8268,7 @@ const WMBackend wl_backend = {
|
|||
.cursor_create_from_files = wl_noop_cursor_create_from_files,
|
||||
.cursor_free = wl_noop_cursor_free,
|
||||
.cursor_set = wl_noop_cursor_set,
|
||||
.pointer_change_cursor = wl_noop_pointer_change_cursor,
|
||||
.pointer_change_cursor = wl_pointer_change_cursor,
|
||||
.appicon_paint_dock_dots = wl_noop_appicon_paint_dock_dots,
|
||||
.pixmap_get_size = wl_noop_pixmap_get_size,
|
||||
.icon_paint_title_bg = wl_noop_icon_paint_title_bg,
|
||||
|
|
|
|||
|
|
@ -3899,6 +3899,10 @@ static void iconMouseDown(WObjDescriptor *desc, WBackendEvent *event)
|
|||
WDock *dock = aicon->dock;
|
||||
WScreen *scr = aicon->icon->core->screen_ptr;
|
||||
|
||||
wwarning("iconMouseDown: aicon=%p dock=%p button=%d xindex=%d yindex=%d",
|
||||
(void*)aicon, (void*)dock, event->u.button.button,
|
||||
aicon->xindex, aicon->yindex);
|
||||
|
||||
if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
|
||||
return;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue