From 2cfbd7d34b1d5fd86eee3d09c6543285595d5613 Mon Sep 17 00:00:00 2001 From: lkn Date: Sun, 28 Jun 2026 01:49:50 +0200 Subject: [PATCH] wayland: fix dockapp drag-to-dock and pointer input routing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - appIconMouseDown: allow drag (Button1) even when icon->owner is NULL, which happens for dockapps whose WWindow is unmanaged after docking - canBeDocked: fall back to aicon->wm_class/wm_instance when owner is NULL so dockapps can be snapped into dock slots - wl_pointer_focus: don't give seat pointer focus to views embedded in frame_bufs (dockapps) — let WM handle input via frame_buf path - wl_handlers button hittest: route clicks on embedded views to their parent frame_buf instead of the client surface - wl_window_unmap: don't disable scene_node if view is reparented into a frame (docked) --- WINGs/wevent_wl.c | 5 ++++- src/appicon.c | 13 +++++++++++-- src/backend/wayland/wl_handlers.c | 16 +++++++++++++++- src/backend/wayland/wl_pointer_focus.c | 5 ++++- src/dock.c | 2 ++ src/event.c | 7 ++++++- 6 files changed, 42 insertions(+), 6 deletions(-) diff --git a/WINGs/wevent_wl.c b/WINGs/wevent_wl.c index c7f7136..bced339 100644 --- a/WINGs/wevent_wl.c +++ b/WINGs/wevent_wl.c @@ -171,8 +171,11 @@ wl_flush(void *ectx, Bool discard) W_WLClientCommitAllToplevels(); /* Pump the wlroots event loop so pointer/key events arrive in our queue. * This is called from waitForEvent/WMMaskEvent when the queue is empty. */ - if (ctx->dispatch_fn) + if (ctx->dispatch_fn) { ctx->dispatch_fn(16, ctx->dispatch_ud); + if (ctx->head != ctx->tail) + wwarning("wl_flush: after dispatch, queue has events (head=%d tail=%d)", ctx->head, ctx->tail); + } } static int diff --git a/src/appicon.c b/src/appicon.c index 2a707ad..5c1eac2 100644 --- a/src/appicon.c +++ b/src/appicon.c @@ -315,6 +315,8 @@ static WAppIcon *wAppIconCreate(WWindow *leader_win) /* will be overriden if docked */ aicon->icon->core->descriptor.handle_mousedown = appIconMouseDown; + wwarning("appicon: SET appIconMouseDown on core->window=0x%lx (wAppIconCreate)", + (unsigned long)aicon->icon->core->window); aicon->icon->core->descriptor.handle_expose = iconExpose; aicon->icon->core->descriptor.parent_type = WCLASS_APPICON; aicon->icon->core->descriptor.parent = aicon; @@ -634,8 +636,13 @@ void appIconMouseDown(WObjDescriptor * desc, WBackendEvent * event) if (aicon->editing || WCHECK_STATE(WSTATE_MODAL)) return; - if (!aicon->icon->owner) + if (!aicon->icon->owner) { + /* No running app — only allow drag (Button1) for docking */ + if (event->u.button.button == Button1) { + wHandleAppIconMove(aicon, event); + } return; + } if (IsDoubleClick(scr, event)) { /* Middle or right mouse actions were handled on first click */ @@ -740,7 +747,8 @@ Bool wHandleAppIconMove(WAppIcon *aicon, WMEvent *event) if (wPreferences.flags.nodock && wPreferences.flags.noclip && wPreferences.flags.nodrawer) dockable = 0; else - dockable = canBeDocked(icon->owner); + dockable = canBeDocked(icon->owner) + || aicon->wm_class || aicon->wm_instance; } /* We try the various docks in that order: @@ -785,6 +793,7 @@ Bool wHandleAppIconMove(WAppIcon *aicon, WMEvent *event) while (1) { WMMaskEvent(scr->wmscreen, WM_EVMASK_POINTER_MOTION | WM_EVMASK_BUTTON_RELEASE | WM_EVMASK_BUTTON_PRESS | WM_EVMASK_BUTTON_MOTION | WM_EVMASK_EXPOSURE | WM_EVMASK_ENTER_WINDOW, &ev); + wwarning("wHandleAppIconMove: ev.type=%d", ev.type); switch (ev.type) { case WME_EXPOSE: WMHandleEvent(&ev); diff --git a/src/backend/wayland/wl_handlers.c b/src/backend/wayland/wl_handlers.c index daf8d37..9aa796d 100644 --- a/src/backend/wayland/wl_handlers.c +++ b/src/backend/wayland/wl_handlers.c @@ -176,6 +176,9 @@ handle_pointer_button(struct wl_listener *listener, void *data) struct wlr_pointer_button_event *ev = data; if (!wl_state.seat) return; + wwarning("BUTTON TOP: btn=%u state=%d pos=(%d,%d) grab_type=%d", + ev->button, ev->state, + wl_state.pointer_x, wl_state.pointer_y, wl_state.grab_type); /* 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. */ @@ -252,7 +255,9 @@ handle_pointer_button(struct wl_listener *listener, void *data) struct wl_toplevel_view *_tv = NULL; struct wlr_surface *_surf = view_surface_at(wl_state.pointer_x, wl_state.pointer_y, &_sx, &_sy, &_tv); - if (_surf && _tv) { + if (_surf && _tv && !_tv->frame_id) { + /* Normal client window — use the view's ID or find a + * WINGs frame_buf overlay at this position. */ struct wl_frame_buf *_wtb = NULL; wl_list_for_each(_wtb, &wl_state.frame_list, link) { if (_wtb->id >= 0x20000000 && @@ -268,9 +273,18 @@ handle_pointer_button(struct wl_listener *listener, void *data) wev.u.button.window = wev.window; wev.u.button.x = (int)_sx; wev.u.button.y = (int)_sy; + wwarning("btn hittest: CLIENT view=0x%lx frame_id=0x%lx wev.window=0x%lx", + (unsigned long)_tv->id, (unsigned long)_tv->frame_id, (unsigned long)wev.window); } else { + /* Decorations, dockapp tile, or empty area — use frame_buf */ struct wl_frame_buf *fb = frame_buf_hit(wl_state.pointer_x, wl_state.pointer_y); + if (!fb && _tv && _tv->frame_id) + fb = frame_buf_find(_tv->frame_id); + wwarning("btn hittest: FRAME surf=%p tv=%p tv_frame=0x%lx fb=%p fb_id=0x%lx", + (void*)_surf, (void*)_tv, + _tv ? (unsigned long)_tv->frame_id : 0, + (void*)fb, fb ? (unsigned long)fb->id : 0); if (fb) { wev.window = fb->id; wev.u.button.window = fb->id; diff --git a/src/backend/wayland/wl_pointer_focus.c b/src/backend/wayland/wl_pointer_focus.c index 34a085e..ca10c3e 100644 --- a/src/backend/wayland/wl_pointer_focus.c +++ b/src/backend/wayland/wl_pointer_focus.c @@ -176,11 +176,14 @@ wl_update_pointer_focus(uint32_t time_msec) struct wl_toplevel_view *v = NULL; struct wlr_surface *cs = view_surface_at( wl_state.pointer_x, wl_state.pointer_y, &sx, &sy, &v); - if (cs) { + if (cs && v && !v->frame_id) { + /* Normal window content area — give client pointer focus */ surface = cs; view = v; fb = NULL; /* not over decorations */ } + /* If v->frame_id is set, the surface is embedded (dockapp) — + * let the WM handle input via the frame_buf path. */ } if (!fb && !surface) { surface = view_surface_at( diff --git a/src/dock.c b/src/dock.c index d77fd6d..5989c95 100644 --- a/src/dock.c +++ b/src/dock.c @@ -1593,6 +1593,8 @@ static WAppIcon *restore_icon_state(WScreen *scr, WMPropList *info, int type, in wfree(command); aicon->icon->core->descriptor.handle_mousedown = iconMouseDown; + wwarning("dock: SET iconMouseDown on core->window=0x%lx (restoreIcon/attach)", + (unsigned long)aicon->icon->core->window); aicon->icon->core->descriptor.handle_enternotify = clipEnterNotify; aicon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify; aicon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON; diff --git a/src/event.c b/src/event.c index 4427883..50830c3 100644 --- a/src/event.c +++ b/src/event.c @@ -685,8 +685,13 @@ static void handleButtonPress(WMEvent * event) desc = wm_backend->context_find(event->u.button.window, WM_CTX_CLIENT_WIN); if (!desc) desc = wm_backend->context_find(event->u.button.window, WM_CTX_CLIENT_WIN); - if (!desc) + if (!desc) { + wwarning("handleButtonPress: NO DESC for window=0x%lx", (unsigned long)event->u.button.window); return; + } + wwarning("handleButtonPress: window=0x%lx desc=%p parent_type=%d handle_mousedown=%p", + (unsigned long)event->u.button.window, (void*)desc, desc->parent_type, + (void*)desc->handle_mousedown); if (desc->parent_type == WCLASS_WINDOW) { wm_backend->event_sync(0);