Commit Graph

133 Commits

Author SHA1 Message Date
Window Maker b714cb783d x11: fix menu rendering, balloon tooltips, clip arrows, and event handling
Menu entry painting:
- Add drawFrame() calls for 3D beveled entries (WTEX_SOLID)
- Add menu_text_clearance to text Y positioning
- Add left-side indicator drawing (radio/check/snap pixmaps)
- Add indicator X offset (MENU_INDICATOR_SPACE)
- Fix disabled+selected text color
- Fix texture-aware background clearing
- Add cascade arrow for submenu entries

Balloon tooltips:
- Implement x11_balloon_show() with text and mini-preview rendering
- Previously was a no-op stub

Clip arrows:
- Draw workspace-switching triangles on clip icon
- Previously stubbed out

Event handling:
- Add MapRequest, ConfigureRequest, ReparentNotify translation to
  xev_to_wme() in WINGs/wevent_x11.c
- These WM-critical events were silently dropped, preventing new X
  clients (e.g. xterm) from being managed
2026-05-25 17:04:30 +02:00
Window Maker f878117013 WINGs: deliver maskless events (selection, mapping) to all handlers
Selection and mapping events have mask 0 in the type table (matching
X11 semantics where they bypass event masks). Change the dispatch
check to deliver events with mask==0 to all registered handlers,
so these events are not silently dropped.
2026-05-25 13:39:18 +02:00
Window Maker 1954fa2a12 WINGs: fix WME_CLIENT_MESSAGE mask so close events reach handlers
The wmeMaskForType table had WME_CLIENT_MESSAGE mapped to 0, so
WMHandleEvent never dispatched client messages to registered handlers.
The window close handler (registered with ClientMessageMask) was never
called. Set the correct mask so the deleteWindow protocol works.
2026-05-25 13:37:32 +02:00
Window Maker d522fc87c5 wayland: use non-blocking read_events for client dispatch
The poll-based wlclient_dispatch with timeout 0 missed events that
were already buffered. Use prepare_read/read_events/dispatch_pending
which reads whatever is available on the socket without polling.
2026-05-25 13:33:30 +02:00
Window Maker fe2ec1fb9e wayland: pump client display in main loop for close button support
In-process WINGs clients have their own wl_display connection that
needs to be dispatched for xdg_toplevel.close to be received. Add
W_WLClientDispatchPending() and call it each main loop iteration
so protocol events reach the client-side handlers promptly.
2026-05-25 13:32:59 +02:00
Window Maker 36c2396130 WINGs/wayland: defer redisplay during W_RealizeView to prevent crash
During W_RealizeView, child views with mapWhenRealized triggered
wl_map which called wl_redisplay on the toplevel. This ran expose
handlers on a partially-realized widget tree, causing backing image
corruption from resizes during redisplay.

Fix: track realization depth with W_WL_Realizing counter. wl_map
skips redisplay when realization is in progress. After the outermost
W_RealizeView completes, trigger a single redisplay of the toplevel
to paint everything correctly.
2026-05-25 13:29:46 +02:00
Window Maker 6e18e1fe0d WINGs/wayland: use backing image dimensions for fill_rectangles
The fill_rectangles call used view->size which can exceed the backing
image dimensions when a view is resized during realization without its
backing being reallocated. This wrote past the allocated buffer,
corrupting the heap and causing later pixman_image_composite32 crashes.
Use pixman_image_get_width/height of the actual backing instead.
2026-05-25 13:22:41 +02:00
Window Maker 7a93b1d389 WINGs/wayland: remove diagnostic fprintf, keep bounds clipping
Remove the verbose COMPOSITE fprintf and BAD composite warning that
were used for debugging. Keep the source/destination bounds clipping
as it correctly handles the case where view->size diverges from the
backing image dimensions during realization.
2026-05-25 13:21:14 +02:00
Window Maker ef98cc3c64 WINGs/wayland: prevent infinite redisplay recursion during resize
The font prefs panel crashed because wl_resize called wl_redisplay
while already inside a redisplay. This caused expose handlers to
trigger more resizes, creating unbounded recursion and memory growth
(backing images growing to 1024+ pixels wide). Skip the redisplay
from wl_resize when wl_redisplay_depth > 0 — the ongoing parent
redisplay will composite the resized child at its new size.
2026-05-25 13:19:39 +02:00
Window Maker 4a6c5330a3 WINGs/wayland: also clip composite to destination bounds and check data
The previous source-only clamp was insufficient. Also check that both
src and dst have non-NULL pixel data, and clip the composite region to
the destination image dimensions to prevent out-of-bounds writes.
2026-05-25 13:12:58 +02:00
Window Maker 53b0b65b3b WINGs/wayland: clamp pixman composite to actual backing image size
During W_RealizeView the WME_EXPOSE handler can trigger child resizes
that make view->size exceed the backing image dimensions. Clamp the
composite width/height to the source image actual size to prevent
out-of-bounds reads in pixman_image_composite32.
2026-05-25 13:12:17 +02:00
Window Maker 9c83db6977 WINGs/wayland: guard pixman composite against zero-sized views
Font prefs panel crashed in pixman_image_composite32 during W_MapView
because a child view had zero width/height. Skip compositing for
zero-sized views.
2026-05-25 13:09:59 +02:00
Window Maker 2aeb76971a WINGs: guard WMRegisterViewForDraggedTypes against NULL X display
In Wayland mode the X11 display is NULL. WMCreateColorWell calls
WMRegisterViewForDraggedTypes which calls XInternAtom, crashing.
Bail early when display is NULL since X11 DnD is not available.
2026-05-25 13:07:55 +02:00
Window Maker e2f73e90e5 WINGs/wayland: handle xdg_toplevel close by synthesizing WM_DELETE_WINDOW
The close button press from the compositor sends xdg_toplevel.close,
but the handler only set a flag without notifying the WINGs event
system. Synthesize a WME_CLIENT_MESSAGE with deleteWindowAtom so
the window closeAction callback fires and the app shuts down
gracefully.
2026-05-25 13:03:31 +02:00
Window Maker 7982e8155d WINGs/wayland: flush display after surface commit for immediate updates
Without an explicit wl_display_flush after wl_surface_commit, protocol
requests were buffered until the next event loop iteration. This caused
visual updates (like the icon position preview in WPrefs) to appear one
interaction behind.
2026-05-25 12:25:43 +02:00
Window Maker b94bc2ce95 WPrefs: drop SIGUSR2, rely on inotify for defaults reload
SIGUSR2 is fundamentally unsafe in a multi-threaded compositor: library
threads (pango fontconfig) created after pthread_sigmask receive the
signal and crash. The Wayland event loop already has inotify watching
the defaults directory, so file changes are detected automatically
without any explicit signaling.

The early pthread_sigmask(SIG_BLOCK, SIGUSR2) is retained as a safety
measure to prevent stray SIGUSR2 from terminating the compositor.
2026-05-25 12:02:26 +02:00
Window Maker 9dee4f7a65 WPrefs: fix SIGUSR2 targeting wmaker instead of parent shell
The previous getppid() approach sent SIGUSR2 to the terminal process
(foot) rather than wmaker, killing the shell. Now wmaker exports
WMAKER_PID at startup and wl_send_wm_command reads it to signal the
correct process.
2026-05-25 11:55:14 +02:00
Window Maker 35226c8588 WPrefs: send SIGUSR2 to wmaker after saving settings
The Wayland event backend stub for send_wm_command was a no-op, so
WPrefs settings changes never triggered a reread in the running wmaker.
Now sends SIGUSR2 to the parent process (wmaker) on "Reconfigure",
causing wDefaultsCheckDomains to run and apply changes immediately.
2026-05-25 11:51:25 +02:00
Window Maker ffc4806f0b WINGs: implement keysym/keycode/modifier stubs and window_set_title
- wl_keysym_to_string: xkb_keysym_get_name
- wl_keysym_to_keycode: iterate keymap to find keycode
- wl_keycode_to_keysym: xkb_keymap_key_get_syms_by_level
- wl_keysym_convert_case: xkb_keysym_to_lower/upper
- wl_get_modifier_keysyms: standard XKB_KEY_* for 8 modifiers
- wl_window_set_title: calls xdg_toplevel_set_title
- W_WLClientGetKeymap accessor for xkb_keymap
2026-05-24 22:25:36 +02:00
Window Maker b7fb4725c4 WINGs/wview_wl: implement wl_balloon_draw for tooltip rendering 2026-05-24 22:19:53 +02:00
Window Maker b52ca0747c WINGs: commit all mapped toplevels on every event loop flush
Widgets that paint directly to their view backing (fill_rect,
draw_line, W_DrawRelief, W_PaintText) without going through the
DOUBLE_BUFFER pixmap_copy path never triggered a surface commit.
The painted content stayed in the pixman buffer but was never
submitted to the compositor — making popup menus, radio buttons,
text fields, sliders, and other controls appear empty/stale.

Fix: W_WLClientCommitAllToplevels() iterates all mapped toplevels
and commits their surfaces. Called from wl_flush() which runs once
per event loop iteration (in waitForEvent). This ensures any
direct-paint operations become visible on the next frame regardless
of which render path was used.
2026-05-24 22:17:17 +02:00
Window Maker db915281e0 WINGs/wevent_wl: implement translate_coordinates and get_window_geometry
Both were no-op stubs returning 0/False.  Editmenu uses these to
compute the original screen position of an item before drag-and-drop,
then animates the item back to that position via slideWindow on
revert.  With both returning 0, the slide animation moved popups
from where they were to (0, 0) instead of back to their origin —
producing the 'menus moving on their own' effect.

translate_coordinates: walk up from src view accumulating pos.x/y
to compute the position relative to the toplevel.

get_window_geometry: return the view's pos/size directly.
2026-05-24 19:27:33 +02:00
Window Maker 8c8293acc9 debug: trace Z-order in wmaker_popup_apply; remove noisy debug 2026-05-24 19:23:43 +02:00
Window Maker 01508ef31a debug: trace commit_locked 2026-05-24 19:17:00 +02:00
Window Maker eb93d30fd9 debug: trace popup expose 2026-05-24 19:13:57 +02:00
Window Maker 54c8d585c3 WINGs/wpopupbutton: paint all items in expose, not just highlighted
paintMenuEntry was only called for the highlighted item on expose.
On X11 the X server auto-filled the rest of the window with the
background pixel, so unpainted items looked grey-but-empty.  On
Wayland the buffer starts transparent — unpainted items showed
through to whatever was behind, making the popup invisible.

Iterate all items in the expose handler so the menu's full content
is visible when it first appears.
2026-05-24 19:08:52 +02:00
Window Maker bf356c2567 WINGs/wclient_wl: clear input focus refs on toplevel destroy
When a popup toplevel is destroyed (e.g., menu editor submenu closes),
its WLClientToplevel struct is freed.  If wlc_kbd_focused or
wlc_ptr_focused pointed to it, subsequent input events dereferenced
the dangling pointer and crashed (saw it in keyboard_handle_leave).

Clear both refs in wl_client_view_destroy_backing before freeing.
2026-05-24 19:05:35 +02:00
Window Maker 27802e6d43 wmaker-popup-v1: add set_parent for parent-relative positioning
The previous version had set_position(x, y) in compositor-global
coordinates, but Wayland clients don't know their toplevel's screen
position — the editmenu and popup button code naturally compute
positions relative to the parent toplevel via accumulated view->pos.

Add set_parent(xdg_toplevel) request. When a parent is set,
set_position(x, y) is interpreted as offsets from the parent
toplevel's top-left corner.

Compositor side resolves the parent's wl_toplevel_view via
wlr_xdg_toplevel_from_resource → wl_find_view_by_xdg_surface,
then adds parent->x and parent->y to the requested offsets.

Client side (wclient_wl.c) tracks the first non-popup toplevel as
ctx->main_toplevel and calls set_parent on every popup wrapper to
anchor it to the application's main window.
2026-05-24 18:37:48 +02:00
Window Maker 1cf271932a debug: trace wmaker-popup-v1 protocol flow 2026-05-24 18:31:37 +02:00
Window Maker b5cc53a8a8 .gitignore: exclude generated wmaker-popup-v1 files; keep protocols/
Generated *-protocol.c and *-server-protocol.h files were accidentally
committed in the previous commit. The .gitignore was also excluding
the protocols/ source directory (where the XML lives). Fix both.
2026-05-24 18:28:51 +02:00
Window Maker 60348da411 Implement wmaker-popup-v1 Wayland protocol for client-driven popup positioning
Standard xdg_shell does not allow Wayland clients to position toplevels —
the compositor decides placement.  WINGs UI patterns require client-driven
positioning (e.g., a dropdown popup must appear directly below its trigger
button; menu editor items must be placed by the client at specific screen
coordinates).

This protocol lets the client wrap an xdg_toplevel with a wmaker_popup
object and call set_position(x, y) before the surface is mapped.  The
compositor honours the requested position when the surface becomes visible
and on subsequent set_position calls.

Files:
- protocols/wmaker-popup-v1.xml: protocol definition
- src/Makefile.am, src/Makefile: server-protocol.h + protocol.c generation
- WINGs/Makefile.am, WINGs/Makefile: client-protocol.h + protocol.c generation
- src/backend/wayland/wl_backend.c: wmaker_popup_manager global, get_popup
  handler that wraps an xdg_toplevel; set_position handler that updates
  view->popup_x/y and calls wmaker_popup_apply to move the frame_buf
  scene tree.  Apply runs at toplevel_map time too for pre-map positions.
- WINGs/wclient_wl.c: bind manager in registry, wrap popup toplevels with
  wmaker_popup, expose W_WLClientPopupSetPosition.
- WINGs/wview_wl.c: wl_move calls W_WLClientPopupSetPosition for popup
  toplevels so WMMoveWidget propagates to the compositor.
- WINGs/wevent_wl.c: wl_move_window also routes to popup positioning.
2026-05-24 18:27:55 +02:00
Window Maker fd73e94a58 WINGs/wview_wl: stub pointer_grab/ungrab; mark unmanaged toplevels popup
WMPopUpButton's popUpMenu calls viewBackend->pointer_grab which was
NULL on Wayland — calling NULL function pointer crashed WPrefs when
clicking the popup dropdown. Provide no-op stubs (Wayland uses implicit
grab on button press).

Also mark W_CreateUnmanagedTopView views with flags.popup=1 so they
go through the same borderless/skip-taskbar path as editmenu standalone
windows.
2026-05-24 18:05:36 +02:00
Window Maker 04e6b348b2 Implement popup-style toplevels for menu editor on Wayland
The WPrefs menu editor creates standalone draggable windows for each
menu item via W_CreateTopView. On X11 these are override-redirect
windows. On Wayland they were becoming framed XDG toplevels with
taskbar entries, cluttering the desktop with phantom 'WINGs...' tabs.

Implementation:
1. New W_View flags.popup field, set by editmenu's standalone mode
2. wclient_wl: when realizing a popup view, set xdg_toplevel app_id
   to 'wings-popup' as a hint to the compositor
3. wmaker compositor: detect 'wings-popup' app_id in toplevel_map,
   set borderless + skip-taskbar + skip-switchpanel flags, then call
   wWindowConfigureBorders to rebuild the frame without decorations
4. New W_EventBackend->window_begin_move vtable method:
   - X11: returns False (caller runs manual drag loop)
   - Wayland: calls xdg_toplevel_move, returns True (compositor drives drag)
5. editmenu's dragMenu calls window_begin_move first; on Wayland the
   compositor handles the entire drag, so the manual loop is skipped
2026-05-24 17:58:58 +02:00
Window Maker e01dedb833 WINGs/wclient_wl: set button.x/y in motion events for widget compat
WMEButton.x is at offset 40, WMEMotion.x is at offset 48. The
scroller widget reads event->u.button.x for motion events (legacy
X11 code where XButtonEvent and XMotionEvent have x at the same
offset). Set both union members so widgets reading either get
correct coordinates.
2026-05-24 17:37:24 +02:00
Window Maker 7efede0274 WINGs/wclient_wl: implement implicit pointer grab for drag operations
On X11, a button press creates an implicit pointer grab — all
subsequent motion and release events go to the window that received
the press, even if the pointer moves outside it. This is essential
for scroller knob dragging, window resize handles, etc.

Implement the same for Wayland client: on button press, record the
target view as wlc_ptr_grab. Motion events during the grab are
delivered to the grabbed view with coordinates translated relative
to it. On button release, deliver to the grabbed view and clear
the grab. Enter/leave synthesis is suppressed during grabs.
2026-05-24 17:33:50 +02:00
Window Maker d6767530e4 WINGs/wview_wl: fix configure event to trigger SubstructureNotify
The viewport's event handler is registered for SUBSTRUCTURE_NOTIFY.
WMHandleEvent switches from STRUCTURE_NOTIFY to SUBSTRUCTURE_NOTIFY
when event.window != configure.event. The event was incorrectly
setting both to the parent window. Fix: set event.window to the
child (resized view) so the mismatch triggers the SubstructureNotify
path.
2026-05-24 17:28:20 +02:00
Window Maker 7b8a6cca3f WINGs/wview_wl: send WME_CONFIGURE to parent on child resize
On X11, resizing a child window generates a ConfigureNotify event
delivered to the parent. Scroll views rely on this to update scroller
proportions when the content view is resized. On Wayland, wl_resize
never generated this event, so the scroller's documentFullyVisible
flag stayed at 1 (set during initial setup when content was small)
and paintScroller drew only a stippled 'full' indicator instead of
the knob/track.

Fix: when a mapped non-toplevel view is resized, dispatch a
WME_CONFIGURE event to its parent synchronously.
2026-05-24 17:27:03 +02:00
Window Maker 4abb21dcf6 debug: log scroller child in scroll view 2026-05-24 17:23:33 +02:00
Window Maker 3337bc110b WINGs: implement XDG toplevel min/max size to prevent resize
WMSetWindowMinSize/MaxSize were no-ops on Wayland. Implement
wl_window_set_geometry to call xdg_toplevel_set_min_size/max_size.
WPrefs sets min=max=520x390, making it non-resizable.
2026-05-24 17:22:53 +02:00
Window Maker b4dbf15817 WINGs: dispatch WME_CONFIGURE synchronously before redisplay on resize
The configure event was enqueued for later processing but the redisplay
happened immediately, painting children at their old positions. Now
WMHandleEvent is called directly with the configure event so widget
delegates relayout children before the redisplay paints them.

Also removed scroller debug logging.
2026-05-24 17:15:36 +02:00
Window Maker 53b23a582c debug: check for unmapped children in scroll view 2026-05-24 17:13:26 +02:00
Window Maker 577e2d6c45 WINGs: send WME_CONFIGURE on resize + debug scroller iteration
- Send WME_CONFIGURE event after xdg_surface configure resize so
  widget delegates can relayout children.
- Add temporary debug to identify why scroller isn't rendering.
2026-05-24 17:12:02 +02:00
Window Maker 1038146e94 WINGs: repaint content after XDG surface configure resize
When the compositor sends a new size via xdg_surface.configure, the
client buffer was reallocated but content was never repainted into it.
The surface was committed with an empty buffer, making the window
content disappear after resize.

Fix: after resizing the buffer in xdg_surface_handle_configure, call
wl_redisplay on the toplevel view to repaint all widget content into
the new buffer before committing.
2026-05-24 17:09:13 +02:00
Window Maker 85216efeea WINGs/wview_wl: redisplay toplevel when non-toplevel view is mapped
Non-toplevel views mapped after the toplevel (e.g., icon buttons added
during Init* calls) need to trigger a redisplay to become visible.
Instead of redisplaying the view itself (which crashed during
realization), find the toplevel ancestor and redisplay from there.

Guards:
- All ancestors must be realized (prevents crash during W_RealizeView)
- Toplevel must be mapped (prevents premature redisplay)
- wl_redisplay_depth==0 (prevents re-entrancy)
2026-05-24 17:02:13 +02:00
Window Maker 9873adf689 WINGs/wview_wl: only trigger wl_redisplay from wl_map for toplevels
The parent-backing-check approach was too fragile. Simplify: wl_map
only triggers wl_redisplay for toplevel views. Non-toplevel views
become visible through:
1. render_wl_pixmap_copy composite-up (when widget paints itself)
2. wl_map_subviews (which redisplays the parent after mapping children)
3. Parent's recursive wl_redisplay (which iterates all mapped children)

This avoids crashes during W_RealizeView recursion where children are
mapped before the widget tree is fully constructed.
2026-05-24 16:58:58 +02:00
Window Maker 66f9d19106 WINGs/wview_wl: don't redisplay from wl_map if parent lacks backing
During W_RealizeView recursion, children are realized and mapped
before their parent's backing pixman image exists. wl_map triggered
wl_redisplay which sent an expose event, the widget painted itself,
and render_wl_pixmap_copy tried to composite up through the parent
chain — hitting a NULL pixman image in pixman_image_composite32.

Fix: before triggering wl_redisplay from wl_map, walk up the parent
chain and verify all ancestors have valid backings. If any ancestor
lacks a backing (still being realized), skip the redisplay — the
parent's wl_map will handle it once the full tree is ready.
2026-05-24 16:56:08 +02:00
Window Maker 50bc0d35aa WINGs/wview_wl: prevent re-entrant wl_redisplay from wl_map
When a panel's createWidgets callback maps child widgets, each
WMMapWidget triggered wl_redisplay which sent expose events. Those
expose handlers could create/destroy more widgets, causing re-entrant
modification of the view tree during iteration — segfault.

Fix: track wl_redisplay call depth. wl_map only triggers wl_redisplay
when depth==0 (not already inside a redisplay). Child views mapped
during a redisplay cycle will be painted by the parent's recursive
child iteration instead.

Also save nextSister before recursing into children to handle list
modifications during iteration.
2026-05-24 16:20:43 +02:00
Window Maker 8d77acc349 WINGs: guard against destroyed views in redisplay and composite-up
When a button click triggers changeSection which destroys widgets
(nameL, versionL, statusL) and then maps new ones, the recursive
wl_redisplay and the composite-up in render_wl_pixmap_copy could
encounter views marked alreadyDead or with freed backings.

Add alreadyDead checks to:
- wl_redisplay entry (skip dead views)
- wl_redisplay child iteration (skip dead children)
- render_wl_pixmap_copy composite-up loop (stop at dead views)
2026-05-24 16:17:06 +02:00
Window Maker 7e21ae259a WINGs/wrender_wl: composite up to toplevel after pixmap_copy to view
On X11, painting to a window pixmap (via XCopyArea in the DOUBLE_BUFFER
path) makes content immediately visible because the X server handles
display. On Wayland, writing to a view's backing pixman image has no
effect until it's composited up through the parent chain to the toplevel
surface and committed.

When render_wl_pixmap_copy detects the destination is a registered view
backing (ID in 0x10000000-0x2FFFFFFF range), it now walks up the parent
chain compositing each view into its parent, then commits the toplevel.
This makes widget paint operations (labels, buttons, frames) immediately
visible without requiring a full wl_redisplay cycle.
2026-05-24 15:55:12 +02:00
Window Maker 50096c7487 WINGs/wview_wl,wclient_wl: fix rendering and input for child views
Rendering: wl_map only triggered wl_redisplay for topLevel views.
Non-toplevel views (buttons, frames, labels) were mapped but never
painted. Now wl_map calls wl_redisplay for any realized view.
wl_redisplay already recurses into children, so wl_map_subviews is
simplified to just set mapped flags then redisplay the parent.

Input: pointer events were delivered to the toplevel view because
Wayland has a single surface per window (unlike X11 where each widget
has its own window). Added hit-testing via W_FindViewAtPoint to route
button press/release and motion events to the correct child view.
Also synthesizes enter/leave events as the pointer moves between
child views (needed for button highlight on hover).
2026-05-24 15:49:05 +02:00