Commit Graph

207 Commits

Author SHA1 Message Date
lkn 2cfbd7d34b wayland: fix dockapp drag-to-dock and pointer input routing
- 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)
2026-06-28 01:49:50 +02:00
lkn 53d8d82e3a wayland: use PIXMAN_OP_SRC for fill_rect to replace stale content
fill_rect used PIXMAN_OP_OVER which blends over existing pixels.
Uninitialized view backing images retained stale pixel data (e.g.
from icon tiles previously in that heap region), causing visual
artifacts like appicons showing through editmenu entries in WPrefs.

SRC fully replaces destination pixels, eliminating bleed-through.
2026-06-26 07:29:02 +02:00
lkn 144b089dff fix UAF: NULL last_hovered_view when view is destroyed
W_ViewDestroyedFn hook clears last_hovered_view when the pointed-to
view is destroyed. Prevents use-after-free in motion handler when
the hovering over a view inside a dialog that is then closed.
2026-06-24 00:20:07 +02:00
lkn 7f5dd5f682 text fields: i-beam cursor via W_SetCursorImageFn hook
Implement wl_set_cursor() to forward cursor name strings to the
compositor via a function pointer. Initialize textCursor/defaultCursor/
invisibleCursor in wl_screen_init with xcursor name strings.
2026-06-23 23:13:06 +02:00
lkn 03eede810b fix crash: resolve frame_buf IDs via W_GetFrameBufImageFn
After removing frame_bufs from the view_table, policy code like
update_icon_title still passes frame_buf IDs to WMDrawString.
These IDs (0x10000000 range) were being treated as raw pointers,
causing a segfault. Add W_GetFrameBufImageFn hook that resolves
frame_buf IDs to pixman images via frame_buf_find().
2026-06-23 22:40:49 +02:00
lkn 49a0b50a6d fix widget rendering: stop polluting view_table with frame_buf IDs
Frame_buf IDs (0x10000000-0x1FFFFFFF) were registered in the 256-entry
view_table alongside WINGs views, filling it with tombstones until
lookups failed. Now frame_buf IDs are excluded from the table; the 3
callers that passed frame_buf IDs to WMDrawString pass raw pixman
pointers instead.
2026-06-23 21:22:14 +02:00
lkn 143b8906bb wayland: fix view_table probe chain corruption on widget reopen
The WINGs view_table (open-addressing hash, size 256) used id=0 as both
"empty" and "deleted". Zeroing a slot on removal broke probe chains for
entries that hashed past that slot. After two dialog open/close cycles
(~40 views created and destroyed), enough chains were broken that
W_GetViewForWindow/W_GetViewBacking returned NULL for valid IDs.

Fix: use a tombstone marker (id=1) for deleted slots. Lookups skip
tombstones (continue probing), inserts reuse them. This is the standard
approach for deletion in open-addressing hash tables (Knuth Vol.3 §6.4).

Also:
- wl_destroy now calls view_table_remove + frees backing pixman image
- wl_frame_destroy cleans up child frame_bufs
- client_reparent existing path re-links backing and recreates scene_buf
- client_reparent to root destroys the child frame_buf
2026-06-22 00:21:42 +02:00
lkn a3413a8402 WINGs/wayland: commit correct topLevel view after child widget repaint
The upward compositing walk in render_wl_pixmap_copy, render_wl_fill_rect,
and wl_draw_string traverses from the repainted widget to the root view.
Previously, the commit at the end targeted the root view or searched its
direct children for a topLevel — this found the main window instead of
the dialog containing the widget that actually changed.

Fix: track the topLevel ancestor during the upward walk and commit that
specific view. This ensures dialog widgets (buttons, lists) have their
visual feedback rendered to the correct scene_buf.
2026-06-21 17:55:35 +02:00
lkn c027f092c0 cleanup: remove debug wwarning/fprintf output
Remove diagnostic output from wl_framebuf.c (hit debug), wl_handlers.c
(button routing), wl_grabstate.c (grab/ungrab), wl_client.c (reparent),
wl_xdg.c (toplevel unmap), window.c (wWindowMap), wview_wl.c
(map/unmap), superfluous.c (MakeGhostIcon), and appicon.c (move loop).

Also simplify the wl_unmap logic in wview_wl.c.
2026-06-21 17:25:06 +02:00
lkn f2fa6f0529 WME_BUTTON_PRESS on menuView: do not set insideMenu (only motion initiates drag) 2026-06-07 17:47:20 +02:00
lkn 0cba6c4bda popup: insideMenu set by motion/click on menuView, not enter; release guard uses insideMenu only
Three changes to fix press-drag-release vs simple click semantics:

1. WME_ENTER no longer sets insideMenu. On Wayland, the enter event for
the menu surface fires too early (menu overlaps button), incorrectly
marking simple clicks as drag interactions.

2. WME_MOTION on menuView sets insideMenu=1 on the first motion,
initiating drag interaction. This matches the desired semantics:
moving the pointer into the menu after press establishes intent.

3. BUTTON_RELEASE close/select guard checks insideMenu only, not
event->window == menuView. A release on the menu surface without
prior motion (simple click on button where menu happens to be under
the pointer) no longer triggers selection.
2026-06-07 17:42:06 +02:00
lkn cfddd2ebfc BUTTON_RELEASE: capture selection before popDownMenu clears insideMenu/highlightedItem
popDownMenu() resets insideMenu=0 and highlightedItem=-1. When called before
the selection logic, the guard (insideMenu && highlightedItem >= 0) is always
false, so WMSetPopUpButtonSelectedItem is never called and the closed-button
display never updates.

Fix: capture the selection state (insideMenu, highlightedItem, enabled item)
into local variables BEFORE calling popDownMenu. Apply the selection
(WMSetPopUpButtonSelectedItem + action callback) AFTER popDownMenu.
2026-06-07 17:38:05 +02:00
lkn 874f9b1ae0 fix popup reopen: shrink view size on 1x1 unmap; fix cascade commit target
Two concrete bugs fixed:

1. Popup reopen with 1x1 buffer: W_ResizeView returns early when view size
matches requested size. After 1x1 unmap, view->size.width/height was still the
original correct size, so resizeMenu never triggered wl_client_toplevel_resize.
Fix: set view size to 1x1 after replacing the buffer.

2. Selected item not updating button display: render_wl_pixmap_copy cascade
walks up to rootView (no WLClientToplevel) and commits it (no-op). After
paintPopUpButton, the mainAppView backing is updated but never committed.
Fix: when the cascade reaches rootView, fall back to committing the first
topLevel child that has a client backing.
2026-06-07 17:32:44 +02:00
lkn 1229f15a8e wl_client_view_unmap: replace buffer with 1x1 transparent instead of NULL to avoid xdg unmap
NULL-buffer unmap requires a configure/ack cycle before the next real buffer
commit. The compositor may not send a configure event for a NULL unmap,
leaving the surface stuck at configured=0 on reopen.

Fix: replace the popup buffer with a 1x1 transparent buffer on close.
This hides the popup visually without triggering an xdg unmap. The surface
stays configured and alive. On reopen, resizeMenu (now always called in
popUpMenu) creates a correct-size buffer via wl_client_toplevel_resize.
wl_redisplay then paints into the new buffer and commits immediately.
No configure/ack cycle needed.
2026-06-07 17:25:27 +02:00
lkn 93d69639dc add targeted diagnostics to commit_locked, configure, and wl_map dispatch paths 2026-06-07 17:13:24 +02:00
lkn 0b80bc4efb cleanup: remove dispatch from popUpMenu, keep in wl_map 2026-06-07 17:02:32 +02:00
lkn af4d33032a popUpMenu: dispatch pending Wayland events after W_MapView to process configure 2026-06-07 16:59:25 +02:00
lkn 88596112f3 wl_client_view_unmap: NULL buffer without configured=0; surface stays configured after first configure 2026-06-07 16:55:32 +02:00
lkn a6a692baf1 restore NULL-buffer unmap + dispatch pending events in wl_map after redisplay
Revert the 1x1-buffer approach which caused pointer focus issues. Return
to NULL-buffer unmap which correctly hides the popup. The configure event
from the NULL unmap is dispatched in wl_map via W_WLClientDispatchPending
after wl_redisplay and before returning, ensuring the surface is
configured before the user can interact with the popup.
2026-06-07 16:53:06 +02:00
lkn 96dc7fa6a8 WME_BUTTON_PRESS: do not set insideMenu on main button open - only menuView press/enter sets it 2026-06-07 16:49:18 +02:00
lkn 0e9df79182 WME_ENTER: only set insideMenu for real menuView enter, not main button 2026-06-07 16:46:48 +02:00
lkn 6b5b015cd8 popDownMenu: reset insideMenu and highlightedItem to prevent stale state on reopen 2026-06-07 16:42:25 +02:00
lkn 230eefd8d3 wl_client_view_unmap: use 1x1 transparent buffer instead of NULL to avoid xdg unmap
NULL-buffer unmap triggers a wlroots xdg_surface unmap, which requires a
configure/ack cycle before the next buffer attach. This creates a race
window where the popup reopen cannot commit its real buffer yet, causing
the click to hit the main window instead of the popup.

Fix: replace the popup buffer with a 1x1 transparent ARGB buffer instead
of attaching NULL. This hides the popup visually (the tiny buffer is
invisible) without unmapping the xdg_surface. The surface stays configured
and alive, so the next popup open can commit a real buffer immediately
without waiting for a configure event.

Also: always call resizeMenu() in popUpMenu to ensure the backing buffer
is recreated at the correct dimensions after the 1x1 replacement.
2026-06-07 16:39:34 +02:00
lkn 2a335646ea add diagnostics to handleActionEvents for popup close-reopen debugging 2026-06-07 16:31:24 +02:00
lkn bbef21785b popup: all popDownMenu calls in BUTTON_RELEASE now guarded by menuView/insideMenu check 2026-06-07 16:03:50 +02:00
lkn 8c7b201823 popup: only close on BUTTON_RELEASE from menuView or when insideMenu active 2026-06-07 16:00:42 +02:00
lkn 1fce95d0cc add comments clarifying the xdg configure lifecycle after NULL-buffer unmap 2026-06-07 15:54:38 +02:00
lkn 931094cce8 wl_client_view_unmap: reset configured flag so remap waits for configure 2026-06-07 15:52:40 +02:00
lkn d923d9fb83 wl_unmap: send NULL to wl_surface when W_ViewSceneNodeSetEnabledFn unavailable 2026-06-07 15:48:38 +02:00
lkn f95cd3d6d3 Revert "wire W_ViewSceneNodeSetEnabledFn in WINGs client path via recycle/remap"
This reverts commit 340a60927e.
2026-06-07 15:46:53 +02:00
lkn 340a60927e wire W_ViewSceneNodeSetEnabledFn in WINGs client path via recycle/remap
In WINGs client mode (e.g. WPrefs), W_ViewSceneNodeSetEnabledFn was never
set because the compositor-side wl_backend.c does not run in the client
process. When popup close calls wl_unmap, the function pointer was NULL,
so the scene node was never disabled and the popup remained visible.

Fix: during W_WLClientInit, if W_ViewSceneNodeSetEnabledFn is still NULL
(unset by compositor), set it to wl_client_scene_node_set_enabled which
calls recycle (destroy xdg_toplevel + wl_surface) on disable and remap
(recreate from scratch) on enable. This ensures the client-side popup
lifecycle correctly removes and recreates the surface, eliminating stale
pixels without depending on compositor-side function pointers.
2026-06-07 15:44:22 +02:00
lkn 405c481e38 clean up diagnostics in wl_unmap and wl_view_scene_node_set_enabled
Remove duplicate W_ViewSceneNodeSetEnabledFn call, fix malformed debug code.
wl_unmap now logs cleanup diagnostics once. wl_view_scene_node_set_enabled
logs proxy ID lookup, match result, and no-match case properly.
2026-06-07 15:38:19 +02:00
lkn 2fca1e1df3 add diagnostics to wl_unmap and wl_view_scene_node_set_enabled for popup stale-pixel debugging 2026-06-07 15:34:51 +02:00
lkn 118a7c30a9 wl_unmap: recycle WINGs client wl_surface on popup close to eliminate stale pixels
The stale popup content visible under the main window after close comes from
the wl_redisplay composite of the popup into the parent backing (rootView).
Even though rootView is not committed to any wl_surface, the popup surface
itself retains its last committed buffer. Disabling the scene node is
insufficient for completely eliminating visual artifacts.

Fix: add wl_client_toplevel_recycle which destroys the xdg_toplevel,
xdg_surface, wl_surface, and SHM buffer but preserves the WLClientToplevel
struct and its WINGs window ID mapping. On unmap (wl_unmap), call recycle
for topLevel popups. On remap (wl_map), call wl_client_toplevel_remap which
recreates all Wayland protocol objects from scratch, triggering a fresh
configure cycle. This ensures no stale buffer or state persists between
open/close cycles.
2026-06-07 15:29:33 +02:00
lkn 60efe4db78 wl_view_scene_node_set_enabled: find WINGs client toplevels via wl_proxy_get_id
The function only handled wl_frame_buf entries. WINGs client popup menus have
their own xdg_toplevel surface with a scene_node but no wl_frame_buf. Their
WINGs view window IDs (0x20000000+) differ from compositor view IDs
(0x10000000+), so wl_find_view_by_id could not find them.

Fix: add wlc_top_get_surface_id() in wclient_wl.c which returns the
libwayland-client proxy ID for the wl_surface. In
wl_view_scene_node_set_enabled, use this ID to iterate the compositor
toplevel_list and find the matching wlr_surface resource by object ID.
Then enable/disable the view scene_node directly.
2026-06-07 15:00:08 +02:00
lkn b30cfa31d9 Revert "wl_unmap: send NULL buffer to WINGs client toplevel surfaces on unmap"
This reverts commit 38a0009e03.
2026-06-07 14:56:02 +02:00
lkn 38a0009e03 wl_unmap: send NULL buffer to WINGs client toplevel surfaces on unmap
When a WINGs client popup is unmapped (popDownMenu), the scene node is
disabled via W_ViewSceneNodeSetEnabledFn. But for WINGs client toplevels
(which have their own wl_surface + xdg_toplevel via libwayland-client),
the surface still holds its last committed buffer. Disabling the scene
node is not enough - the compositor still sees the surface as mapped.

Fix: add wl_client_toplevel_unmap which attaches NULL to the wl_surface
and commits, properly unmapping it from the compositor. Wire through
W_WLClientUnmapFn function pointer in wview_wl.c, called from wl_unmap
for topLevel views.
2026-06-07 14:51:30 +02:00
lkn 1b04023591 Revert "wl_unmap: force commit sibling toplevels to clear stale popup pixels"
This reverts commit a9397b8291.
2026-06-07 14:50:58 +02:00
lkn a9397b8291 wl_unmap: force commit sibling toplevels to clear stale popup pixels
When a topLevel popup is unmapped, wl_redisplay(view-parent) clears stale popup pixels from the parent backing. But for rootView (common parent), the commit at the end of wl_redisplay is a no-op because rootView has no WLClientToplevel. The cleared backing was never pushed to any surface. Fix: iterate parent child list and force-commit every topLevel sibling, pushing the cleared backing to their wl_buffers.
2026-06-07 14:46:02 +02:00
lkn ff86d8055f wl_map: raise popup to top on remap via wmaker-popup-v1
When a WINGs popup is unmapped (popDownMenu) and remapped (popUpMenu),
the scene node is re-enabled via W_ViewSceneNodeSetEnabledFn, but its
Z-order position stays at its original insertion point. Other surfaces
mapped while the popup was disabled may now sit above it, making the
popup invisible when it reappears.

handle_toplevel_map (which calls wmaker_popup_apply to raise the
popup) only fires on the initial xdg_surface map event, not on
scene-node re-enable. Fix by calling W_WLClientPopupSetPosition on
every topLevel popup map, which triggers wmaker_popup_v1_set_position
→ wmaker_popup_apply → wlr_scene_node_raise_to_top.
2026-06-07 13:42:25 +02:00
lkn 10a22a8484 wpopupbutton: only clear menu state on leave from menuView, not on bPtr->view
When the pointer leaves the main button view to enter the popup surface,
the WME_LEAVE event fires for bPtr->view (which shares the same
handleActionEvents handler). This unconditionally cleared insideMenu=0
and highlightedItem=-1, causing the subsequent button release to fail
the selection check and not fire the action.

Fix: guard the leave handler with event->window == bPtr->menuView->window
so that clearing only happens when the pointer actually leaves the menu
view, not when it just leaves the main button on its way to the popup.
2026-06-07 13:38:46 +02:00
lkn d43ceae081 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.
2026-06-07 13:33:19 +02:00
lkn a63c97638d 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.
2026-06-07 13:31:16 +02:00
lkn d2c9a59344 popUpMenu: init highlight from pointer position on open, not selectedItemIndex
When the popup opens via ButtonPress on the main button, compute the
highlighted item from the pointer position relative to the menu view,
matching upstream WINGs semantics. Previously highlightedItem was set
to selectedItemIndex, which might be -1 (no prior selection), causing
the popup to open with no visible highlight until the first motion event.

The fix uses event->u.button.y_root - menuView->pos.y divided by item
height to determine which item the pointer is over when the menu appears.
This item is painted as highlighted immediately on open.
2026-06-07 13:08:21 +02:00
lkn 3a2f5e96db wl_pointer_grab: replace no-op stub with compositor-grab delegation
The WINGs view backend wl_pointer_grab was a no-op, meaning the
press-drag-release sequence for popup menus had no effect on compositor
state. On X11 this would call XGrabPointer to redirect events to the
menu window; the equivalent on Wayland is to activate the compositor
wl_pointer_grab which sets wl_state.grab_type and suppresses unwanted
cursor resets and axis leaks during the interactive drag.

Implementation: add function pointers W_WLPointerGrabFn and
W_WLPointerUngrabFn in wview_wl.c, initialized to NULL. The compositor
wires them to wl_pointer_grab / wl_pointer_ungrab at startup.
The WINGs view backend now delegates to the compositor instead of
silently returning.
2026-06-07 13:05:57 +02:00
lkn 7285ca6286 paintMenuEntry: redraw full row on non-highlight instead of just gray fill
The non-highlight path in paintMenuEntry only filled the row with gray
and returned early, erasing the item text, relief border, and indicator
pixmap. On Wayland this was immediately visible because the cascade+commit
on fill_rect commits every draw operation to the wl_surface.

Fix: after filling the gray background, redraw the relief border, the
item text (in black or darkGray depending on enabled state), and the
indicator pixmap for the selected item — matching what the highlight path
and makeMenuPixmap do.
2026-06-07 12:53:46 +02:00
lkn 3ccf87ff4f popup: fix click selection and blank second-open
Bug 1: Clicks on menu items did not select the correct item.
The WME_BUTTON_PRESS handler always called popUpMenu() which re-mapped
the already-open menu and reset highlightedItem to selectedItemIndex,
ignoring the click position. Since no motion event follows a
click-without-drag, the highlight was never corrected.
Fix: distinguish menuView vs main button events via event->window.
For menuView clicks, compute highlightedItem from click.y directly
without re-opening the menu.

Bug 2: Second popup open showed a blank gray box.
wl_unmap disabled the compositor scene node via W_ViewSceneNodeSetEnabledFn
but wl_map never re-enabled it. On re-map, the buffer was committed
to the wl_surface but the scene node stayed disabled.
Fix: re-enable scene node in wl_map before redisplay.
2026-06-07 12:47:24 +02:00
lkn 2c3a1764a4 popUpMenu: map before render, use makeMenuPixmap for robust initial content
Reorder popUpMenu to:
1. Resize menuView before map (correct backing dimensions)
2. W_MapView first (wl_map calls wl_redisplay which fills background
   and dispatches WME_EXPOSE — the old redisplay() before map was a
   no-op on Wayland because wl_redisplay returns early when !mapped)
3. Copy makeMenuPixmap() output into menuView->window backing as a
   safety net, ensuring the buffer has complete menu content
   independent of expose-timing correctness
4. paintMenuEntry for initial highlighted item

This ensures the first visible popup buffer has all menu items
rendered regardless of the order in which the Wayland configure
and commit events arrive.
2026-06-07 11:32:23 +02:00
lkn e063d8fa87 WINGs Wayland: fix popup dropdown blank gray box and motion highlight rendering
Three related fixes for WINGs popup menus on Wayland:

1. wl_client_toplevel_resize: update pending_w/h after resize
   (wclient_wl.c:545-546). Without this, the configure handler sees
   stale pending dimensions and creates a wrong-sized buffer,
   discarding the one containing the menu text.

2. Export wl_redisplay_depth (wview_wl.c:288) so render and font
   backends can detect when they are called from within a full
   redisplay cycle vs. from an event handler.

3. Add cascade+commit to render_wl_fill_rect (wrender_wl.c:162-183)
   and wl_draw_string (wfont_wl.c:183-208) when not inside a
   redisplay cycle. This makes motion-driven highlight changes
   (text color, background fill) immediately visible by compositing
   up the view tree and committing the toplevel wl_surface.
2026-06-07 11:24:00 +02:00
lkn 57a8973bc2 fix: wlclient_dispatch stops spinning when compositor closes the socket (NULLs ctx->display, making subsequent calls a no-op) 2026-06-06 16:53:13 +02:00