Commit Graph

181 Commits

Author SHA1 Message Date
lkn d29f2a29c9 wayland: fix close/iconify buttons and miniwindow rendering
Three bugs fixed:

1. Close and iconify buttons broken after frame rendering model change
   Buttons are composited into the titlebar buffer, so their scene_buf
   nodes carry no committed buffer and are invisible to wlr_scene_node_at.
   frame_buf_at() returned the titlebar fb for all button-area clicks,
   routing them to titlebarMouseDown instead of buttonMouseDown.

   Fix: add frame_buf_button_at() -- coordinate walk of frame_list entries
   with scene_buf->buffer == NULL -- and frame_buf_hit() which tries button
   resolution first then falls back to the scene graph. Replace all
   frame_buf_at() calls in button and motion handlers with frame_buf_hit().
   Also emit synthetic WME_LEAVE/WME_ENTER events on sub-window boundary
   crossings so buttonMouseDown's press-track loop works correctly.

2. Iconify crash (SIGSEGV in update_icon_title)
   WMDrawString() was passed icon->core->window (a frame_buf synthetic ID,
   0x10000000+) as the drawable. wl_font_resolve_drawable() only recognised
   WINGs view IDs (0x20000000+), so it cast the frame_buf ID directly as a
   pixman_image_t* -- wild pointer -- SIGSEGV.

   Fix: add W_RegisterBacking()/W_UnregisterBacking() to wview_wl.c.
   Register each frame_buf's pixman image in the backing table on creation
   and unregister on destroy. Extend the ID range check in wfont_wl.c and
   wrender_wl.c from 0x20000000 down to 0x10000000 so frame_buf IDs resolve
   correctly via W_GetViewBacking().

3. Resizebar visible after iconify + miniwindow not rendering
   wl_window_unmap() disabled fb->scene_buf->node individually, but a
   subsequent wl_frame_paint() could re-enable and re-commit it while the
   window was still iconified.

   Fix: for top-level frames, disable fb->tree->node -- hides the entire
   scene tree (frame buffer, titlebar, resizebar, client surface) atomically.
   Symmetric enable in wl_window_map().

   Miniwindow icons were invisible: icon_paint was a noop so the frame_buf
   image was never committed to the scene graph. Add wl_icon_paint() mirroring
   wl_appicon_paint(): tile + file_image composite + title text, committed
   via wlr_scene_buffer_set_buffer(). Wire into wIconPaint() in icon.c so
   the backend finalises the surface after policy-layer setup.
2026-05-15 09:49:06 +02:00
Window Maker a961c86d4b wayland: fix click-through and focus-toggle on overlapping windows
view_surface_at iterated toplevel_list in reverse using geometry alone
to find the view under the cursor. toplevel_list order is maintained
separately from scene Z-order: handle_pointer_button updated it on click
but stacking operations (wl_stacking_restack, wl_stacking_raise/lower)
did not. After any stacking operation the list order and visual order
diverged, so successive clicks at the same position returned different
views, causing focus to toggle between overlapping windows.

Fix: use wlr_scene_node_at as the authority on what is visually topmost,
then walk up to the fb->tree / window-root level (stopping when the
parent is a level_trees[] bucket), and scan toplevel_list only for the
ID-to-surface mapping of the matched node. This is the same approach
already used by frame_buf_at after the level-tree rewrite.

The scene graph is the single source of truth for Z-order.
2026-05-15 09:14:18 +02:00
Window Maker 245d0077b5 wayland: implement correct stacking model with scene-graph level trees
Three interlocking problems were present in the Wayland stacking
implementation.

1. Wrong scene-graph nodes targeted by stacking operations

Each managed window groups its frame pixels and client surface under a
single wlr_scene_tree (fb->tree):

  fb->tree
    fb->scene_buf       (frame decoration pixels)
    child_frame_bufs    (titlebar / resizebar sub-frames)
    v->scene_node       (client surface, reparented in)

wl_stacking_restack was raising fb->scene_buf -- a child inside fb->tree
-- instead of fb->tree itself, a no-op for inter-window ordering. Three
redundant loops then tried to separately raise the client surface and child
frames, also to no effect. wl_stacking_lower had the same bug.

Fix: raise/lower fb->tree in both functions so the entire window assembly
moves as one atomic unit.

2. Flat scene graph -- no structural level ordering

All nodes were direct children of scene->tree, so creation order and
raise_to_top calls determined Z-order. A normal window could accidentally
render above a floating window after any CommitStacking call.

Fix: introduce 9 wlr_scene_tree level buckets created at scene init time
in bottom-to-top order:

  0 WL_LAYER_BACKGROUND  background rect / wallpaper
  1 WL_LAYER_DESKTOP     WMDesktopLevel
  2 WL_LAYER_SUNKEN      WMSunkenLevel
  3 WL_LAYER_NORMAL      WMNormalLevel  (default for new frames)
  4 WL_LAYER_FLOATING    WMFloatingLevel / WINGs dialogs
  5 WL_LAYER_MENU        WMMainMenuWindowLevel / OR-redirect windows
  6 WL_LAYER_DOCK        WMStatusWindowLevel / dock panel surface
  7 WL_LAYER_FULLSCREEN  WMFullscreenLevel
  8 WL_LAYER_OVERLAY     WMModalPanel / WMPopUp / XOR overlay

Every scene node is assigned to the correct bucket at creation.
A new stacking_set_level vtable slot called from AddToStackList moves
fb->tree to the right bucket whenever a window is added or its level
changes via ChangeStackingLevel. X11 backend implements it as a no-op.

3. XDG client surface not reparented into fb->tree

For XDG toplevels the map handler called wManageWindow before creating
view->scene_node, so the reparent inside wl_client_reparent was a no-op.
The client surface then lived as a flat sibling of fb->tree in
WL_LAYER_NORMAL, never moving with its frame during stacking operations.

Fix: after creating view->scene_node, check if frame_id is already set
and do the deferred reparent immediately.

4. Hit-testing broken by level-tree hierarchy

frame_buf_at walked up from the hit scene node to the first direct child
of scene->tree. With level buckets that stopped at the level_tree node
instead of fb->tree, making every window in the same bucket appear to be
the same window. Client surfaces were not blocking clicks on frames
beneath them, and titlebar sub-frames were not distinguished from the
top-level frame, breaking move and resize initiation.

Fix: rewrite frame_buf_at with a two-pass approach:
  Pass 1 -- exact match: check if the topmost node is a child frame_buf
    scene_buf such as titlebar, resizebar, or button; return it directly.
  Pass 2 -- walk up stopping when parent is a level_tree bucket, then
    match that window-root node to a top-level frame_buf tree.
  Return NULL if the hit was on a client surface.

ROADMAP: mark A1 done.
2026-05-15 09:11:46 +02:00
lkn 0e6f2f60e8 wl_backend: fix frame button click dispatch (close/iconify)
frame_buf_at was preferring parent frame_bufs over smaller children
(buttons), so clicks on the close/iconify button area were dispatched
to the titlebar instead. Fix by preferring the smallest frame_buf
that contains the pointer position.
2026-05-14 23:36:34 +02:00
lkn a32ad9a11e wl_backend: fix button positioning on frame resize
wl_window_move for frame_bufs was setting absolute x,y instead of
updating rel_x/rel_y and computing absolute position from parent.
This caused the close button to stay at its old position when the
frame was resized (e.g. on maximize).

Fix: set rel_x/rel_y and compute absolute position from parent,
matching the logic in wl_frame_configure.
2026-05-14 23:29:50 +02:00
lkn 72d972a719 wl_backend: fix frame decoration repaint on maximize/unmaximize
frame_paint was never called after wWindowConfigure during maximize
because the X11 path relies on expose events to trigger repaints.
In Wayland there are no expose events, so explicitly call frame_paint
after wWindowConfigure in wMaximizeWindow and wUnmaximizeWindow.

Also:
- Guard client scene_node repositioning in frame_configure (NULL check)
- Move dispatch+flush+frame_done into wl_window_resize with reentrancy
  guard and output frame listener disabled to avoid nested render crashes
- Remove debug wwarnings
2026-05-14 23:22:46 +02:00
lkn 98f3175895 wl_backend: fix client resize and frame lifecycle
- Send frame_done after configure so clients can render at new size
- Set activated state on toplevel configure
- Dispatch idle callbacks to flush configure immediately
- Add client_set_maximized vtable entry for XDG toplevel state
- Re-enable frame_buf scene node on unshade
- Raise client scene node on window_map (fixes unshade visibility)
- Reposition client and children in frame_configure
- Schedule output frame after frame_paint
- Remove debug wwarnings from wl_window_resize

This fixes:
- Window shade/unshade (resizebar + client surface visibility)
- Client surface receiving configure events and rendering
- Frame decoration updates on resize/maximize
2026-05-14 23:08:37 +02:00
lkn 859c4d8e78 wl_backend: fix unshade — re-enable and raise client surface and frame
On shade, the client scene node was lowered and disabled, and the
frame_buf scene node was disabled. On unshade, the frame_buf node
was not re-enabled, and the client node was enabled but remained
behind the frame (at bottom of Z-order from stacking_lower).

Fix: re-enable frame_buf node when show_resizebar is true, and
raise the client scene node to top on window_map so it's above
the frame background.
2026-05-14 22:26:07 +02:00
lkn 34ed9fc1b2 wayland: hide resizebar when window is shaded
On Wayland, scene buffer nodes are independently composited and not
clipped to their parent frame bounds. When shading collapses the frame
to titlebar height, the resizebar scene node remained visible because
nothing disabled it.

Two changes:

1. wl_frame_paint: skip resizebar rendering and disable both the
   resizebar own scene node and the main frame scene buffer when the
   frame height is <= the titlebar height (shaded). Re-enable both
   when unshaded.

2. actions.c: call wm_backend->frame_paint() after wFrameWindowResize
   in both wShadeWindow and wUnshadeWindow. This triggers the scene
   graph update on Wayland and is a harmless decoration refresh on X11.
2026-05-14 21:35:50 +02:00
lkn 42e07acc4f x11: resize fake leader window to panel dimensions before reparenting
x11_fake_leader_create() creates a 10x10 parent window, but the
original code created each parent at the correct pwidth x pheight.
When the WINGs panel is reparented into a 10x10 parent, X11 clips
child windows to parent bounds, making dialog contents invisible.
wWindowConfigure never resizes the client because client.width is
pre-set to the correct value, so resize evaluates to false.

Add wm_backend->window_resize() on the parent immediately after
fake_leader_create in all 9 callers that reparent content into it:
dialog.c (7), winspector.c (1), dockedapp.c (1).

The window.c group-leader caller is unaffected (size irrelevant).
2026-05-14 21:25:51 +02:00
Levi Neely 95b0b8ee7c WINGs/wevent: remove _x11_raw from WMHandleEvent dispatch path
WMHandleEvent is the middle layer — it must not contain any
backend-specific code. Move view lookup to _view_target field
set by each event backend during fetch. Move XRefreshKeyboardMapping
and XFilterEvent into x11_fetch. Selection and DND handling removed
from middle layer (handled by extraEventHandler in the WM layer).

The _x11_raw field remains in WMEvent for now (used by src/event.c
and other WM-layer code that still needs the raw XEvent), but the
middle layer no longer touches it.
2026-05-14 20:24:05 +02:00
Levi Neely ca06f1c847 x11_backend: guard x11_clip_icon_paint against NULL icon
Clip icon paint is called during dock creation before the icon
is fully initialized. Guard against NULL dereference.
2026-05-14 19:59:59 +02:00
Levi Neely 509a59ec81 wl_backend: stacking_raise raises frame + client + children together
When raising a frame_buf, also raise its children (titlebar,
resizebar) and the client surface inside it. When raising a
client surface, also raise its containing frame. This ensures
click-on-titlebar raises the entire window assembly.
2026-05-14 19:56:52 +02:00
Levi Neely 30f8fe51c8 menu: include window_menu in findMenu hit-testing
findMenu only checked root_menu and switch_menu, so the window
menu (right-click titlebar) was never found during mouse tracking.
Add window_menu and its cascades to the search.
2026-05-14 19:25:42 +02:00
Levi Neely a01895763b wl_backend: raise frame_bufs to top on map
Menus and other WM chrome (frame_bufs) must render above client
surfaces. Raise them to the top of the scene graph when mapped.
2026-05-14 19:18:19 +02:00
Levi Neely 1037fc0630 wl_backend: respect XKB_DEFAULT_* env vars for keyboard layout
Read XKB_DEFAULT_LAYOUT, XKB_DEFAULT_VARIANT, XKB_DEFAULT_OPTIONS,
XKB_DEFAULT_MODEL, and XKB_DEFAULT_RULES from environment when
creating the default keymap. Set these to match your layout
(e.g. XKB_DEFAULT_LAYOUT=us XKB_DEFAULT_VARIANT=dvorak).
2026-05-14 19:08:24 +02:00
Levi Neely d883166d4c wl_backend: fall back to modalView for key event routing
When focusInfo has no target, route key events to the modal view
so text fields in modal dialogs (run dialog) receive input.
2026-05-14 19:02:36 +02:00
Levi Neely 81f60abca7 wl_backend: safely re-wire input listeners on VT switch
When the session resumes after a VT switch, wlroots may destroy and
recreate input devices. Remove old listeners before adding new ones
to avoid assertion failure in wlr_keyboard_finish. Initialize all
listener links at startup so the first remove is safe.
2026-05-14 18:56:23 +02:00
Levi Neely 0801fb7d86 wl_backend: handle Ctrl+Alt+Fn for VT switching in standalone mode
Intercept XF86Switch_VT_N keysyms and call wlr_session_change_vt()
to switch virtual terminals when running on DRM/KMS.
2026-05-14 18:50:52 +02:00
Levi Neely 90cd607b1e wl_backend: fix standalone VT switching and relative pointer motion
- Store wlr_session from wlr_backend_autocreate.
- Skip frame commit when session is inactive (VT switched away).
- Wire ptr->events.motion (relative) for libinput devices so cursor
  moves in standalone DRM/KMS mode.
2026-05-14 18:49:32 +02:00
Levi Neely 62b8e4254e wl_backend: standalone DRM/KMS mode, fix pointer focus and keyboard delivery
- Use wlr_backend_autocreate for standalone mode (from TTY) instead of
  headless-only. Gives real DRM/KMS output + libinput.
- wl_update_pointer_focus: skip client surface focus when pointer is
  over an internal frame_buf (menu, titlebar). Prevents client from
  stealing cursor/events from WM chrome.
- wl_focus_set: ensure seat keyboard is set and flush clients before
  sending keyboard.enter so client has processed wl_seat.capabilities.
- Renderer and allocator use the autocreated backend in standalone mode.
2026-05-14 18:46:04 +02:00
Levi Neely 3edf25e0b4 fix build: add missing WINGs vtable fields, fix double-free and linker errors
- Add selection_acquire, selection_release, platform_init to
  W_ViewBackend struct (were used but not declared).
- Add lookup_string to W_FontBackend struct (same issue).
- Guard selection_acquire call with NULL check in selection.c.
- Remove pixman_image_unref in wl_frame_configure resize path —
  wlroots manages the buffer lifecycle via scene refcounting.
  LSan confirms no leak.
- Remove #include of wmspec.c/xdnd.c/xmodifier.c from x11_backend.c
  — they are already compiled as standalone objects, causing
  multiple-definition linker errors on clean builds.
2026-05-14 16:24:59 +02:00
Levi Neely 57536b9991 moveres: replace XEvent with WMEvent in move/resize functions
wMouseMoveWindow and wMouseResizeWindow took XEvent* but were
being passed WMEvent* via casts. The XEvent union layout differs
completely from WMEvent, so ev->xmotion.x_root read garbage —
causing the window to jump to a wrong position and the move to
become uncontrollable.

Changed both functions to take WMEvent*, accessing ev->u.button
fields directly. Removes all XEvent references from the core
policy layer (zero backend-specific types in the middle).
2026-05-14 13:15:34 +02:00
Levi Neely c47fc49290 wayland: restore cursor when pointer leaves client surface
When a client (e.g. foot) sets its own cursor surface via
wl_pointer.set_cursor, the cursor disappears once the pointer
moves off the client surface because nobody resets it. Now
wl_update_pointer_focus resets to left_ptr when the pointer
leaves all surfaces or enters a new one (before the client
gets a chance to override via request_set_cursor).
2026-05-14 13:04:30 +02:00
Levi Neely 6dfdc5a108 wayland: fix frame/client desync and titlebar offset
Three issues fixed:

1. Frame and client surface moved independently during interactive
   window move. The client surface (wl_toplevel_view) and frame
   decorations (wl_frame_buf) are independent scene graph nodes —
   unlike X11 where reparenting creates an implicit parent-child
   relationship. Added frame_id/frame_rel_x/frame_rel_y to
   wl_toplevel_view so wl_window_move keeps them in sync.

2. window_move(client_win, x, y) treated coordinates as absolute,
   but the WM core passes relative-to-frame coordinates (matching
   X11 child window semantics). Now detected via frame_id and
   converted to absolute scene positions.

3. Titlebar scene node was offset by left_button_width because
   updateTitlebar() configures it for X11 layout (separate
   overlapping windows). Since the Wayland backend composites
   buttons directly into the titlebar buffer, override the
   titlebar position to frame origin during wl_frame_paint.
2026-05-14 12:59:39 +02:00
Levi Neely 4be05c799c Phase 84: eliminate all backend guards from policy code
Remove every is_wayland / !is_wayland guard from WM policy files.
All backend-specific logic now lives exclusively in the backends:

- main.c: display_post_open vtable slot handles fatal check, visual ID,
  display name, and multiHead parsing (X11) or no-ops (Wayland)
- event.c: EventLoop() unconditionally calls event_loop_run(); the X11
  backend's implementation contains the WMNextEvent+inotify loop
- screen.c: screen_init_display vtable slot handles RContext/WMScreen/
  color creation (each backend owns its full init path)
- monitor.c: show_crash_dialog vtable slot (X11 shows panel, Wayland
  returns abort)
- startup.c: InitializeSwitchMenu called unconditionally (it's
  backend-agnostic); defaults polling timer no longer guarded
- shutdown.c: wScreenSaveState called unconditionally
- window.c: client_reparent called unconditionally (Wayland no-ops);
  dead frameless-window code removed

Policy code is now completely backend-agnostic. The only references to
is_wayland are the vtable field definitions in the two backend files.
2026-05-14 12:41:01 +02:00
Levi Neely ac1dbd3b8e Phase 83: frame decorations for native Wayland clients
- Create WM frames (titlebar, buttons, resizebar) for native XDG
  toplevels — remove the is_wayland guard that skipped frame creation
- Set decoration mode to SERVER_SIDE so clients don't draw their own
  titlebar (CSD)
- Skip client_reparent for Wayland (no X11 reparenting needed; the
  client scene node is independent of the frame scene buffers)
- Set frame_border_width=0 in wl_screen_create_internals (Wayland has
  no X11-style window border; the frame_buf renders all decoration)
- Use wlr_scene_subsurface_tree_create for client surface (avoids XDG
  geometry offset issues)

Known issue: transient size mismatch on initial map when saved session
state or size constraints differ from the client's committed buffer.
Resolves after the client processes the configure event.
2026-05-14 12:18:13 +02:00
Levi Neely 288d2ad3a7 Phase 82: wlroots 0.19 compatibility + native Wayland client support
- Add wlr_subcompositor global (required by all modern Wayland clients)
- Switch from xdg_shell new_surface to new_toplevel signal (wlroots 0.19
  fires new_surface with role=NONE before get_toplevel assigns the role)
- Defer initial xdg_surface configure to first client commit via
  surface->events.commit listener (surface must be initialized first)
- Defer xdg_decoration set_mode to first commit (set_mode internally
  calls schedule_configure which asserts initialized)
- Wire destroy_listener to toplevel->events.destroy instead of
  xdg_surface->events.destroy (wlroots asserts toplevel event lists
  are empty before the toplevel is freed)
- Log DISPLAY and WAYLAND_DISPLAY env vars at startup
- Add WAYLAND_ROADMAP.md

Native Wayland clients (foot, etc.) can now connect, render, and
disconnect without crashing the compositor.
2026-05-14 11:57:15 +02:00
Levi Neely 71c4bfdfbc Fix xkb_state NULL and respect host keyboard layout
- Only apply default XKB keymap as fallback when the host compositor
  hasn't provided one yet (kb->xkb_state == NULL)
- The nested backend receives the host's keymap (Dvorak, etc.) via
  wl_keyboard.keymap — don't overwrite it with QWERTY default
2026-05-13 22:55:39 +02:00
Levi Neely 8f0ec30759 Fix keyboard input: don't forward keys to seat without focused surface
wlr_seat_keyboard_notify_key was being called unconditionally, which
caused re-entrancy that consumed WINGs events from the queue before
the modal loop could fetch them. Now only forwards to the seat when
there's an actual focused client surface.

Also use 16ms timeout in wl_flush so keyboard events from the nested
compositor are actually received during modal loop dispatch.
2026-05-13 22:48:13 +02:00
Levi Neely df7f681c14 Fail loudly when xkb_state is NULL in handle_keyboard_key
Warn and drop the event rather than silently swallowing it. This makes
the root cause visible — key events arriving before keyboard is fully
initialized.
2026-05-13 21:45:04 +02:00
Levi Neely 4e9043ad6d Guard xkb_state against NULL in handle_keyboard_key
Key events can arrive before the keyboard's xkb_state is initialized.
2026-05-13 21:44:30 +02:00
Levi Neely 76080cd2eb Implement keyboard input via font backend abstraction
- WMEKey: add keysym, utf8[8], utf8_len fields
- handle_keyboard_key: translate keycode via xkb_state, fill keysym/utf8,
  route event to focused WINGs view
- W_FontBackend: add lookup_string slot
- wfont_x11.c: implement via W_LookupString (wraps XLookupString)
- wfont_wl.c: implement by reading pre-translated keysym/utf8 from event
- wtextfield.c: use font backend lookup_string (no X11 guards)
2026-05-13 21:43:24 +02:00
Levi Neely bf9a0a9cf5 Fix dialog titlebar and background rendering
- window.c: always call frame_paint for internal windows (title=NULL
  skipped wFrameWindowChangeTitle which was the only paint trigger)
- wview_wl.c: fill view backing with background color before expose
  (X11 server fills background automatically, Wayland needs explicit fill)
2026-05-13 21:34:07 +02:00
Levi Neely 4440476a35 Fix dialog position: update children when reparenting frame_buf
When fake_leader is reparented into the frame, its children (the WINGs
view frame_buf) weren't being repositioned. Now cascades position
updates to children of the reparented frame_buf.
2026-05-13 21:29:46 +02:00
Levi Neely 3a0ca5444e Fix NULL gview crash: create geometry view in Wayland screen path
The Wayland screen init path had 'goto screen_done' which skipped
gview creation. Now creates it before the goto. Also added safety
guard in moveGeometryDisplayCentered.
2026-05-13 20:05:17 +02:00
Levi Neely f348b3a04b Guard moveGeometryDisplayCentered against NULL gview 2026-05-13 20:01:13 +02:00
Levi Neely 4870c72c78 Fix dialog visibility: trigger paint on map, enable scene node on commit
- wl_map: set mapped=1 before calling wl_redisplay (was bailing because
  mapped flag wasn't set yet)
- wl_view_commit_backing_impl: enable scene node when content is committed
  (frame_buf was created disabled, never enabled before frame was mapped)
2026-05-13 19:51:22 +02:00
Levi Neely 6d9450ea8b Make dialogs visible: fake_leader, recursive reparent/map/move
- fake_leader_create: return a real frame_buf ID (was returning 0)
- client_reparent: handle frame_buf-to-frame_buf reparenting
- client_reparent: get WINGs view size from view table
- window_map: enable grandchildren (dialog content is grandchild of frame)
- window_move: move grandchildren when parent moves
- dialog.c: guard scr->w_visual for Wayland mode in Info panel
2026-05-13 19:45:41 +02:00
Levi Neely 9191f12913 Fix render backend crashes: resolve view IDs in all drawing functions
All WINGs render backend functions (draw_line, draw_rect, fill_rect,
pixmap_copy, composite_pixmap) now use resolve_drawable() to handle
WINGs view IDs (0x20000000+) by looking up the backing pixman image.

Also fixes:
- wfont_wl.c: resolve view IDs in draw_string/draw_image_string
- wtextfield.c paintCursor: use render backend draw_line (not XDrawLine)
- dialog.c wShowInfoPanel: guard scr->w_visual for Wayland mode
2026-05-13 19:40:31 +02:00
Levi Neely 1d0b82eed5 Implement WINGs dialog rendering infrastructure
- wl_client_reparent: create child frame_buf for WINGs views, connect
  backing pixman image so widgets paint into the scene graph
- View lookup table: map synthetic IDs to W_View* for event dispatch
- render_wl_pixmap_copy: detect view IDs and copy into backing image
- W_ViewCommitBacking callback: commit frame_buf after WINGs paint
- WMHandleEvent: use view table in Wayland mode (was always NULL)
- wtextfield.c paintCursor: use render backend draw_line instead of
  XDrawLine (crash fix for Run dialog)
2026-05-13 19:33:26 +02:00
Levi Neely 531dcf8235 Fix text rendering and titlebar positioning
- wl_draw_string: remove ascent subtraction, y is TOP position (matches
  X11 Pango behavior where pango_xft_render_layout takes top-left)
- Menu entry text: pass top position (y+3) not baseline (y+3+fh-1)
- Icon title: pass y=0 (top) not y=fh (baseline)
- frame_paint_titlebar: pass centered top not baseline
- frame_configure: compute absolute position from parent for children
  (was setting absolute pos to relative coords, warping to 0,0)
- Titlebar buffer: always render full frame width, resize if needed
- Frame scene node: disable when no resizebar (transparent, not needed)
- Always call frame_paint after show/hide button (TS_NEW was skipping it)
2026-05-13 19:03:49 +02:00
Levi Neely 3a48a0eb30 Fix close button: always call frame_paint after show/hide button
wFrameWindowShowButton was only calling updateTitlebar (not frame_paint)
when new_style==TS_NEW. Now always calls frame_paint so the close button
icon is rendered into the frame buffer after pinning a menu.
2026-05-13 18:28:45 +02:00
Levi Neely 147b9d0694 Fix hit-testing: prefer child frame_bufs over parents
frame_buf_at was returning the parent frame instead of child windows
(titlebar, content area) because the parent was later in the list.
Now prefers children (frame_bufs with parent_id set) over parents.

This fixes menu titlebar click detection — clicking the titlebar now
dispatches to titlebarMouseDown/menuTitleMouseDown instead of
menuMouseDown, enabling menu pinning and drag-to-move.
2026-05-13 18:21:07 +02:00
Levi Neely 08c0e07f6c Fix clean shutdown: remove pointer listeners, reset signal handlers
On SIGINT/SIGTERM:
- signalfd handler sets terminate flag and exits the event loop
- After loop exit, remove wlroots pointer listeners to prevent
  assertion failure in wlr_pointer_finish
- Reset SIGTERM/SIGINT/SIGHUP to SIG_DFL after signalfd takes over
  to prevent the old handleExitSig from competing
2026-05-13 18:06:01 +02:00
Levi Neely 8922525d3c Fix crash: guard wRootMenuPerformShortcut against NULL _x11_raw
handleKeyPress passed event->_x11_raw (NULL in Wayland mode) to
wRootMenuPerformShortcut which dereferences it immediately. Skip
the call when _x11_raw is NULL.
2026-05-13 18:01:07 +02:00
Levi Neely fb2d0ab2ef Implement pointer_get_position: return tracked cursor coordinates
pointer_get_position was a noop returning (0,0), causing findMenu to
always return NULL. This made the menu close on every button release
regardless of pointer position. Now returns wl_state.pointer_x/y which
are updated on every pointer motion event.

With this fix, the root menu stays open when the pointer is over it on
release (matching X11 behavior), allowing entry selection or titlebar
pinning.
2026-05-13 17:52:17 +02:00
Levi Neely bfa248ae29 Fix findMenu y_ret: subtract top_width for content-relative coords
findMenu was returning y relative to the frame top (including titlebar).
getEntryAt expects y relative to the content area. Subtracting top_width
means pointer in titlebar gives y<0 → entry_no=-1 (no selection), which
is correct behavior matching the X11 path.
2026-05-13 17:48:08 +02:00
Levi Neely 784fcfbfb2 Implement frame button rendering (close/iconify icons)
Add wl_frame_paint_button which renders button icons from XPM data
into the parent frame buffer. Called both from the vtable slot and
from wl_frame_paint during titlebar rendering. Uses PRED_CLOSE_XPM
and PRED_ICONIFY_XPM parsed lazily via RGetImageFromXPMData.

Also removes XEvent from all WFrameWindow callbacks (on_click_left,
on_click_right, on_dblclick_right, on_mousedown_resizebar) — they
now take WMEvent*.
2026-05-13 17:46:21 +02:00
Levi Neely 1af95d539f Fix menu unmap: hide children when parent is unmapped
wl_window_unmap now disables child scene nodes when the parent is
hidden. This fixes the menu entries staying visible after the menu
frame (titlebar) was unmapped. The entire menu now disappears
together, matching X11 behavior where the brother menu closes on
button release.
2026-05-13 17:32:49 +02:00