Commit Graph

284 Commits

Author SHA1 Message Date
Window Maker ee434b024a wayland: commit scene after dispatch to fix one-frame-behind rendering
Client surface commits arrive during wl_event_loop_dispatch but may be
processed after the frame event has already rendered the old state. Add
an additional wlr_scene_output_commit after dispatch to immediately
pick up any new client content, fixing the one-interaction-behind
visual update issue in WINGs clients like WPrefs.
2026-05-25 12:43:55 +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 d8592f3b57 wayland: block SIGUSR2 early so library threads inherit the mask
SIGUSR2 was being delivered to pango fontconfig threads instead of the
signalfd because those threads were created before the signal mask was
set in wl_event_loop_run. Block SIGUSR2 at the very start of real_main
so all threads (including library-spawned ones) inherit the blocked
mask and the signalfd is the sole recipient.
2026-05-25 12:00:13 +02:00
Window Maker 8630a5ce95 wayland: handle SIGUSR2 via signalfd to reload defaults without dying
SIGUSR2 was not in the signalfd set, so it was delivered asynchronously
to the old signal handler which set WSTATE_NEED_REREAD. But the Wayland
event loop never checked that state flag, and the signal delivery itself
could crash the compositor.

Add SIGUSR2 to the signalfd set and handle it synchronously within the
event loop by calling wDefaultsCheckDomains directly.
2026-05-25 11:58:14 +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 9a4438fb65 cursor: respect XCURSOR_THEME and XCURSOR_SIZE environment variables
The xcursor manager was hardcoded to NULL theme with size 24. Now reads
XCURSOR_THEME and XCURSOR_SIZE from the environment, matching the
behavior of other Wayland compositors like Sway.
2026-05-25 11:48:23 +02:00
Window Maker 703872bcca xwayland: route keyboard events via wlr_seat_keyboard_notify_enter
The XWayland focus path only called wlr_xwayland_surface_activate (X11
focus) but did not notify the wlr_seat about keyboard focus. Without
wlr_seat_keyboard_notify_enter, key events were never forwarded to the
XWayland surface via the Wayland transport.

Now wl_focus_set for XWayland surfaces sets the seat keyboard and calls
wlr_seat_keyboard_notify_enter, matching the XDG toplevel path.
2026-05-25 11:45:47 +02:00
Window Maker 5bff28efcd xwayland: fix rendering by switching from map_request to associate+surface.map
The map_request signal was never firing for managed XWayland surfaces,
preventing scene node creation and window management. Switch to the
standard wlroots 0.19 lifecycle: listen for associate (surface attached),
then hook surface->events.map (content committed) before creating the
scene node and calling wManageWindow.

Also fix wl_window_get_client_attrs to report VIEWABLE based on surface
existence rather than surface->mapped, since X11 geometry (xw->width/height)
is available before the wlr_surface maps.
2026-05-25 11:43:18 +02:00
Window Maker 9bb2d02254 debug: trace xwayland scene node creation 2026-05-25 10:35:07 +02:00
Window Maker b67d3a04fb wl_backend: send 640x480 configure for xwayland surfaces with tiny initial size
xterm starts with a 10x17 pixel window. The initial configure was
sending that size back (since > 0), so xterm rendered at 10x17.
Now use 640x480 default when the initial size is < 64px.
2026-05-25 10:33:18 +02:00
Window Maker d4d5240426 wl_backend: raise xwayland minimum size threshold to 64px
xterm's initial xw_surface size is 10x17 which passed the > 1 check.
Use >= 64 as the threshold for falling back to 640x480 default.
2026-05-25 10:09:49 +02:00
Window Maker 1733e3449c wl_backend: use map_request signal instead of associate for xwayland
The associate signal fires when a wlr_surface is linked to the
xwayland surface, but this can happen before or during new_surface
processing. map_request fires when the X11 client actually sends
MapRequest — at which point WM_CLASS is set and the client is ready
to be managed and shown.
2026-05-25 09:53:19 +02:00
Window Maker 72a592a402 wl_backend: handle already-associated xwayland surfaces
If the xwayland surface already has a wlr_surface when we register
listeners (associate event already fired), trigger the map handler
directly. This fixes xterm and other X11 clients that associate
their surface before the new_surface handler runs.
2026-05-25 09:48:42 +02:00
Window Maker 044a5b3486 wl_backend: use minimum 640x480 for xwayland surfaces with tiny initial size
xterm and other X11 clients create a small initial window then resize.
At map time, xw_surface->width/height may still be the initial tiny
value (e.g. 10x17). wManageWindow reads this and creates a tiny frame.
Use 640x480 as fallback when width/height <= 1.
2026-05-25 09:13:42 +02:00
Window Maker c243ac22ab wl_backend: don't skip XWayland surfaces without initial class/title
X11 clients (like xterm) create their window first, then set WM_CLASS
before mapping. The previous code skipped surfaces that had no class
AND no title at new_surface time, marking them override_redirect. This
prevented xterm and other well-behaved X11 apps from ever being managed.

Fix: log a warning but continue registering listeners. The surface will
be managed when it maps (associate signal) at which point WM_CLASS is
typically set.
2026-05-25 09:06:25 +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 b040b5c7fe wmaker-popup-v1: raise popup above parent and track parent moves
Two issues:
1. Popups rendered behind their parent toplevel because the scene node
   wasn't raised on apply. Add wlr_scene_node_raise_to_top so popups
   appear above the parent.
2. When the parent toplevel moved, popups stayed at their old screen
   position. In wl_window_move (compositor-side), after a regular
   toplevel's position changes, iterate all popups and re-apply those
   whose popup_parent is this toplevel so they track the parent.
2026-05-24 18:58:07 +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 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 777a5229eb wl_backend: fix shadowed sx/sy in wl_update_pointer_focus
The inner 'double sx, sy' declaration shadowed the outer one. When
view_surface_at succeeded inside the frame_buf check, the surface-local
coordinates were written to the inner variables and lost. The outer
sx/sy (used by wlr_seat_pointer_notify_enter/motion) remained
uninitialized, causing the client to receive garbage coordinates.

Also removed debug print from wclient_wl.c.
2026-05-24 16:14:10 +02:00
Window Maker 1d1f5bdc71 wl_backend: forward pointer events to client surface in client area
frame_buf_at() matches the entire window frame tree including the
client content area. This caused wl_update_pointer_focus to never
set seat pointer focus on the client surface, so Wayland clients
(like WPrefs) never received pointer enter/motion/button events.

Fix: when frame_buf_at returns a hit, also check view_surface_at.
If a client surface is found at the pointer position, use it instead
of treating the area as WM-internal decorations.
2026-05-24 16:04:25 +02:00
Window Maker 7a422330f6 defaults: use nanosecond mtime precision to detect rapid file writes
The user-facing root menu's 'Background → Images → Tiled' menu item runs
'wmsetbg -u -t image.jpg', which internally invokes wdwrite TWICE in
rapid succession:
  1. wdwrite WindowMaker SmoothWorkspaceBack NO
  2. wdwrite WindowMaker WorkspaceBack '(tpixmap, ...)'

Both writes complete within the same wall-clock second. The previous
mtime check used time_t (1-second precision):
  if (timestamp < stbuf.st_mtime)

After the first write, wmaker reads the file (only SmoothWorkspaceBack
changed; WorkspaceBack still old) and sets timestamp = current second.
The second write happens in the same second, so st_mtime equals
timestamp exactly — the check fails and the second write is silently
ignored. The user has to invoke the menu twice for the change to apply.

Fix: WDDomain.timestamp is now struct timespec (nanosecond precision).
New MTIME_NEWER() macro compares full timespec values. Two writes within
the same second now have distinct nanosecond components so both are
detected.

Also removed all debug logging.
2026-05-24 15:24:03 +02:00
Window Maker 861082f1ad wl_backend: react to IN_MOVED_TO, skip temp files
The atomic-rename pattern (mkstemp + rename) generates two relevant
events per save: IN_CLOSE_WRITE on the temp file (e.g.
WindowMaker.AjuyyW), then IN_MOVED_TO on the destination (WindowMaker).

Previously we reacted to IN_CLOSE_WRITE first and set oneShotFlag,
which then suppressed the IN_MOVED_TO. But at the time of
IN_CLOSE_WRITE, the rename hadn't happened yet — stat(WindowMaker)
returned the OLD mtime — so we'd skip and never re-check on the
subsequent IN_MOVED_TO either.

Fix: skip events on temp files (filenames containing a dot) and
react on IN_MOVED_TO for the actual destination filename. Direct
in-place writes (rare) are still handled via IN_CLOSE_WRITE for
non-temp filenames.

Also removed debug logging.
2026-05-24 15:03:46 +02:00
Window Maker c9a0fe8654 wl_backend: handle IN_MOVED_TO for atomic file replacement
WMWritePropListToFile uses an atomic write pattern: write to a temp
file, fsync, fclose, then rename to the destination. The inotify
directory watch sees:
  1. IN_CLOSE_WRITE on the temp file (WindowMaker.XXXXXX)
  2. IN_MOVED_TO on WindowMaker (the rename)

We were only handling IN_CLOSE_WRITE, but at that point the rename
hadn't happened yet, so stat(WindowMaker) returned the OLD mtime
and we skipped. The NEXT save's IN_CLOSE_WRITE would then catch
the PREVIOUS save's mtime change — hence the 'one click behind'
pattern users observed.

Now handles IN_MOVED_TO too, catching the actual atomic replacement.

Also removed debug logging.
2026-05-24 14:55:10 +02:00
Window Maker e97c76b582 wl_backend: use IN_CLOSE_WRITE instead of IN_MODIFY for inotify
IN_MODIFY fires during the write, before the file is fully written.
The Wayland event loop's 16ms dispatch catches this almost instantly,
reading stale/partial content. IN_CLOSE_WRITE fires after the writer
closes the file, guaranteeing the content is complete.

This fixes the 'one step behind' issue where background changes only
appeared on the second apply.
2026-05-24 14:45:45 +02:00
Window Maker 6ecdeca3fc wl_backend: fix background not updating on first apply
Two issues:
1. bg_image scene node wasn't re-enabled when switching from solid
   color back to an image background
2. No frame was scheduled after background change, so the compositor
   wouldn't repaint until the next natural frame tick

Now explicitly enables the bg_image node and calls
wlr_output_schedule_frame() after any background change.
2026-05-24 14:44:26 +02:00
Window Maker 94054aeec8 wl_backend: implement all background image fill modes
- tpixmap (Tiled): repeat image across screen
- spixmap (Scaled): stretch to exact screen dimensions
- cpixmap (Centered): place at center with background color fill
- mpixmap (Maximized): scale preserving aspect ratio, letterbox
- fpixmap (Filled): scale preserving aspect ratio, crop to fill

All modes now use RSmoothScaleImage for quality, respect the
background color (3rd texture argument) for uncovered areas, and
handle transparent images via RCombineImageWithColor.
2026-05-24 14:41:57 +02:00
Window Maker 07022840ac wl_backend: add X11 rgb.txt color name lookup
The named color table only had basic colors (black, white, red, etc.)
but Window Maker backgrounds use extended X11 color names like
deepskyblue4, tomato4, blue4. Now falls back to parsing
/usr/share/X11/rgb.txt for any unrecognized color name.

Fixes Sunset and other multi-color gradient backgrounds that use
X11 named colors.
2026-05-24 14:38:14 +02:00
Window Maker a62a05bbb7 wl_backend: fix background color parsing to support named colors
The color parser only handled #RRGGBB and rgb:XX/XX/XX formats, but
Window Maker backgrounds use X11 named colors (deepskyblue4, black,
tomato4, etc.). Now uses wGetColor() which resolves any color format
through the backend's color lookup.

Also: solid backgrounds now update the scene rect directly (faster,
no buffer allocation needed).
2026-05-24 14:35:50 +02:00
Window Maker 286e588110 wl_backend: add multi-color gradient support for desktop backgrounds
Added mvgradient/mhgradient/mdgradient handling using wrlib's
RRenderMultiGradient(). This covers presets like 'Sunset' which use
more than 2 color stops.

Also cleaned up color parsing (removed empty else branches) and
removed debug logging.
2026-05-24 14:30:57 +02:00
Window Maker 93587378d9 wl_backend: implement desktop background rendering
On X11, wmsetbg sets the root window pixmap. On Wayland there's no root
window, so the compositor must render the background directly.

Added wm_backend->set_desktop_background() vtable method:
- X11: NULL (wmsetbg continues to handle it)
- Wayland: parses the texture proplist description and renders:
  - solid colors
  - vertical/horizontal/diagonal gradients (via RRenderGradient)
  - pixmap/spixmap/cpixmap images (via RLoadImage + RScaleImage)

The rendered image is displayed as a wlr_scene_buffer in the
WL_LAYER_BACKGROUND layer, replacing the static solid-color rect.

setWorkspaceBack() in defaults.c now calls the backend method when
available, so background changes from WPrefs take effect immediately.
2026-05-24 14:23:09 +02:00
Window Maker aab89ab11a icon: repaint clip text after tile change
tileObserver was calling window_clear after update_icon_pixmap, which on
Wayland overwrote the painted icon with just the background tile (erasing
clip workspace number/name text). On X11 this worked because XClearWindow
triggers an Expose event that repaints everything.

Fix: remove the redundant window_clear (update_icon_pixmap already calls
wIconPaint which does window_clear + icon_paint internally), and
explicitly call wClipIconPaint for the clip icon to redraw the workspace
number and name overlay.
2026-05-24 14:18:39 +02:00
Window Maker 91c49c8631 wl_backend: fix window_clear to repaint from background pixmap
wl_window_clear was filling with transparent black, which made dock/clip
icons disappear after a theme change. On X11, XClearWindow repaints the
window with its background pixmap — the Wayland backend now does the same.

Added bg_pixmap field to frame_buf, set in window_set_background_pixmap,
and used in window_clear to composite the background back into the buffer.
2026-05-24 14:15:46 +02:00
Window Maker 0d795bc84a wl_backend: add inotify config-file watching to event loop
The Wayland event loop was missing inotify polling, so theme/style/
background changes from WPrefs (or manual edits to ~/GNUstep/Defaults/)
were never detected. The X11 backend had this since day one.

Now the Wayland event loop checks inotify on every iteration (non-blocking
select with zero timeout). When a config file modification is detected,
wDefaultsCheckDomains() is called, which re-reads the defaults database
and posts appearance-change notifications — triggering immediate visual
refresh of title bars, menus, icons, and backgrounds.
2026-05-24 14:13:14 +02:00
Window Maker 23f0bb320e wm core: shared src/ is now X11-free at the source level
Audit: every "X11 in src/" hit outside src/backend/x11/ and
src/backend/wayland/ is now either:
  - a comment mentioning X11 by name, or
  - a backend-neutral macro/type alias (WM_STATE_*, CW*, P*, etc.)
    whose numeric value happens to match the X11 protocol value.

Zero <X11/...> includes in shared headers/sources.
Zero X11 type uses (XColor/XContext/XEvent/XFontStruct/XGCValues/
XWindowAttributes/XSetWindowAttributes/XErrorEvent etc.).
Zero X11 symbol references in compiled shared object files
(verified: `nm src/*.o | awk '$1=="U"' | grep '^X[A-Z][a-z]'`
returns empty).

Real architectural fixes:

src/backend/backend_types.h — added the missing backend-neutral
constants/types so shared code never names an X11 macro:
  - WM_STATE_WITHDRAWN/NORMAL/ICONIC (ICCCM values 0/1/3).
  - WContextID (typedef int) — XContext analogue.
  - WMColorRGB (XColor-layout-compatible struct).
  - CWX..CWStackMode (ConfigureWindow value-mask bits).
  - ForgetGravity..StaticGravity (ICCCM win_gravity values).
  - PMinSize..PWinGravity (XSizeHints flags) as WNH_* aliases.
  - NoSymbol, NoEventMask, ColormapInstalled/Uninstalled.
  - BitmapSuccess..BitmapNoMemory.
All guarded by #ifndef so the X11 backend's <X11/X.h> include
overrides them with the real X11 values when present.

src/screen.h — w_visual is now `void *` (was `Visual *`).  Added
w_visual_id / w_visual_class so dialog.c's crash panel can read
visual info without dereferencing a Visual struct.  tech_draw_font
is also `void *` with companion tech_draw_font_ascent/descent ints
populated by the backend (X11 from XFontStruct->ascent, Wayland
from a hard-coded bitmap-font metric).

src/WindowMaker.h — XContext fields → WContextID, X11 includes
removed, Time → WMNativeTime.

src/dialog.c — wShowCrashingDialogPanel now uses WMOpenScreen +
WMScreenWidth/Height instead of DefaultScreen()/ScreenOfDisplay()/
WidthOfScreen() so it works in either backend.

Dead-code cleanup found while auditing:

src/event.c — handleUnmapNotify and handleEnterNotify both built
an uninitialized `XEvent ev` and ran it through x11_translate_event
on a peeked queue event.  The translation always read stack garbage
(the local was never populated).  Replaced with the correct flow:
  - unmap handler: skip wUnmanageWindow if a Destroy is already
    queued, the Destroy handler will run it next iteration.
  - enter handler: skip if a Leave for the same window is queued,
    no need to inspect mode/detail (was always reading garbage).
Also dropped the now-unused x11_compat.h / x11_event.h includes
from event.c, dock.c, window.c.

src/misc.c — eatExpose's `XEvent event, foo` were never used; the
function delegates to wm_backend->event_flush_type(WME_EXPOSE).

src/wsmap.c — workspace_map_destroy built an XEvent ClientMessage
but never sent it; the actual notify path is switchpanel_send_enter.
Pass the real root_win argument (was passing literal 0) and drop
the dead XEvent.

src/icon.c — set_dockapp_in_icon's `XWindowAttributes attr` and
cycleColor's `XGCValues gcv` were unused.

src/window.c — windowLanguageClick had a function-signature
mismatch with framewin.h's on_click_language (XEvent* vs WMEvent*).
The framewin code passes a WMEvent.  Fixed the parameter type and
its single use of event->xbutton.button.

src/moveres.c — already-disabled `#if 0` block in showPosition
that called XDrawLine / XSetForeground on the root window: removed.
Replaced tech_draw_font->ascent/descent dereferences with the new
flat int fields.

src/xutil.h — removed declarations of FormatXError (moved to X11
backend long ago) and Request/GetSelection (never implemented).

Mass renames in shared src/:
  IconicState/NormalState/WithdrawnState → WM_STATE_ICONIC/NORMAL/
                                            WITHDRAWN
  IconWindowHint/InputHint/StateHint/IconPixmapHint/IconMaskHint/
  IconPositionHint/WindowGroupHint/UrgencyHint
                                          → WMH_*_HINT
  XColor (in struct fields and locals)    → WMColorRGB
  XContext (in screen.context.{...})      → WContextID
  XPoint                                  → WMShortPoint
  XRectangle                              → WMShortRect
  XFontStruct *                           → void *

Backends translate at the boundary:
  src/backend/x11/x11_backend.c keeps its native XColor/XContext/
  XFontStruct internally and casts to the backend-neutral types
  at the public-API surface (memcpy works because WMColorRGB has
  XColor's exact layout).
  src/backend/wayland/wl_backend.c populates the same fields
  directly without any X11 indirection.

Verified:
  Default build: `Window Maker 0.96.0` runs.
  Shared src/ object files: `nm | grep '^X[A-Z][a-z]'` is empty.
  All shared X11 macro names (CW*, P*, ICCCM hint flags) are
  defined locally in backend_types.h, no X11 dep needed.

Out of scope (separate refactor): wmaker compositor binary itself
still links libX11 because src/backend/x11/* and the XWayland
integration in src/backend/wayland/wl_backend.c need real X11
types.  Both are below the vtable, so shared code is unaffected.
2026-05-24 12:52:31 +02:00
Window Maker a4eb10b2b5 wrlib: split X11 image-conversion path; --disable-x11-wraster
Phase θ — wrlib's X11 image-conversion path (RConvertImage,
RPutXImage, the X11 RCreateContext, libXpm-based RLoadXPM) is now
opt-out via --disable-x11-wraster, mirroring the WINGs phase η work.

Result: with --disable-x11-backend --disable-x11-wraster the WINGs
runtime libraries have ZERO libX11 transitive deps:

  $ ldd WINGs/.libs/libWINGs.so | grep X11
  (empty)
  $ ldd wrlib/.libs/libwraster.so | grep X11
  (empty)

That was the last libX11 transitive dep coming through WINGs — the
goal of the API surgery is now achieved at the library level.

configure.ac:
- New --enable-x11-wraster (default = same as --enable-x11-backend),
  AM_CONDITIONAL([USE_X11_WRASTER]), AC_DEFINE([USE_X11_WRASTER]).
  Forcing --disable-x11-wraster while --enable-x11-backend is rejected
  (X11 backend depends on the X11 conversion path).
- WRASTER_GFXLIBS_NO_XPM: GFXLIBS with -lXpm filtered (libXpm pulls
  libX11; load_xpm_normalized.c is the X11-free XPM loader used in
  the disabled-X11 build).
- WINGS_PANGO_LIBS_NO_XFT: PANGO_LIBS with -lpangoxft-* and -lXft
  filtered (PangoXft depends on libXft -> libX11; pangoft2 path is
  the X11-free Pango variant).

wrlib/Makefile.am:
- Split source list: shared / X11-only / Wayland-only.
- X11 sources (context.c, convert.c, convert_x11.c, xpixmap.c,
  xutil.c, load_xpm.c) only built when USE_X11_WRASTER.
- New context_wl.c (always built) — Wayland-mode RCreateContextWayland
  extracted from context.c.
- New wstubs_no_x11.c (only built when !USE_X11_WRASTER) — empty
  stubs for the public wraster API symbols normally exported from
  the X11 path (RCreateContext, RDestroyContext, RConvertImage,
  RConvertImageMask, RGetClosestXColor, RCreateImageFromDrawable,
  RCreateImageFromXImage, RCreateXImage, RGetXImage, RDestroyXImage,
  RPutXImage, RGetImageFromXPMData, RLoadXPM).
- @XLIBS@/@LIBXMU@ only added to libwraster_la_LIBADD when
  USE_X11_WRASTER; load_xpm.c only built when (USE_XPM and
  USE_X11_WRASTER).
- Added --filter USE_X11_WRASTER to the wraster.h generation rule
  so the @!USE_X11_WRASTER@ lines (forcing WRASTER_NO_XLIB) are
  retained when X11 is disabled.

wrlib/wraster.h.in:
- When USE_X11_WRASTER is undefined at config time, force
  WRASTER_NO_XLIB so consumers see only forward-decl'd opaque types
  (Display/Visual/XImage/etc.) and never <X11/Xlib.h>.
- Added True/False/None defines to the WRASTER_NO_XLIB block (raster.c
  uses them; previously got via Xlib.h).

wrlib/context.c — RCreateContextWayland moved out to context_wl.c.
wrlib/wstubs_no_x11.c — new (157 lines).
wrlib/context_wl.c — new (extracted Wayland branch).

WINGs/Makefile.am:
- libWINGs_la_LIBADD now uses WINGS_PANGO_LIBS, which is @PANGO_LIBS@
  when X11 backend enabled, @WINGS_PANGO_LIBS_NO_XFT@ otherwise.

WINGs/widgets.c — WMCreateScreen + WMCreateSimpleApplicationScreen
moved to widgets_x11.c (they use DefaultScreen() and RCreateContext()
which are X11-only).  Stubs added in WINGs/wstubs_no_x11.c.
WINGs shared rootWin assignment now reads context->drawable instead
of calling X11 RootWindow() macro directly.

WINGs/WINGs/wnative.h — added X11-numeric constants used by shared
WINGs widget code:
- Button1..Button5 (X.h pointer-button numbers)
- KeyPress..LASTEvent (X.h event-type constants)
- CWBackPixmap..CWCursor (X.h CreateWindow/ChangeWindowAttributes
  mask bits)
- WMShortRect (XRectangle layout-compatible struct)
All guarded by #ifndef so X11 headers can override on the X11 build.

WINGs/configuration.c — dropped unused <X11/Xlocale.h> include.
WINGs/wruler.c, wtabview.c, wtext.c — replaced raw XPoint/XRectangle
with WMShortPoint/WMShortRect; replaced lone XSetWindowAttributes
shim with direct view->attribs.cursor access.

src/WindowMaker.h — explicitly include <X11/Xlib.h>, <X11/Xutil.h>,
<X11/Xresource.h>, <X11/Xatom.h>.  The wmaker compositor uses these
extensively in its public headers; previously got them transitively
via WINGs.h.  After the WINGs surgery WINGs.h no longer pulls Xlib,
so the compositor must include them itself.

Verified:
  Default build (--enable-x11-backend, --enable-x11-wraster):
    Window Maker 0.96.0 boots, all features intact.
  Pure-Wayland build (--disable-x11-backend --disable-x11-wraster):
    libwraster.so / libWINGs.so build cleanly, no libX11 in ldd.
    (wmaker compositor binary still has X11 deps from src/* code;
     out of scope for this commit — that's a compositor-level
     refactor.)
2026-05-24 12:24:45 +02:00
Window Maker 9a02eb658e wings: phase ζ — formal W_PlatformBackend vtable for WMOpenScreen
Replaces the inline if-Wayland-else-X11 dispatch in WMOpenScreen with
a proper vtable.  Each platform-open backend now provides one
W_PlatformBackend instance:

  W_PlatformBackendX11       — widgets_x11.c
  W_PlatformBackendWLClient  — wclient_wl.c

WMOpenScreen walks an ordered registry of weak references to these
instances:

  static const W_PlatformBackend *const wm_platform_backends[] = {
      &W_PlatformBackendWLClient,
      &W_PlatformBackendX11,
      NULL
  };

It first asks each backend's .detect() whether it wants the supplied
display string, and calls .open() on the first to claim it.  If no
backend claims, it falls back to trying each .open() in order.

Weak references mean a build that excludes a backend (e.g. a future
--disable-x11-backend) link cleanly with the symbol resolving to
NULL — WMOpenScreen just skips it.

The detection logic moves out of shared widgets.c entirely:
  x11_detect: explicit non-wayland display string OR $DISPLAY set.
  wlclient_detect: 'wayland' prefix, WMAKER_USE_WAYLAND, or
                   $WAYLAND_DISPLAY set + $DISPLAY unset.

Also replaces WMCreateScreenWithRContext's getenv("WMAKER_USE_WAYLAND")
with an explicit W_SetNextScreenKind(WM_SCREEN_KIND_WAYLAND) flag the
platform backend sets before calling.  No env detection in shared code.

Updated wmaker compositor (src/backend/wayland/wl_backend.c) to call
W_SetNextScreenKind(WM_SCREEN_KIND_WAYLAND) before its own
WMCreateScreenWithRContext call.

Verified: wmaker still starts in compositor mode, version string OK,
build clean, vtable dispatch invokes the right backend.
2026-05-24 11:49:34 +02:00
Window Maker fb817a7b27 wings: revert accidental WNativeWindow rename in strings/comments
The phase-β sed pass renamed every C 'Window' identifier to
WNativeWindow.  That correctly updated typedef uses but also rewrote
the string 'Window Maker', UI labels like 'Window Manager', 'Window
Placement', 'Window Title', 'Window List', 'Window System session',
etc., plus a number of comments referring to 'Window' as English.

Fixed all of those.  The C type uses (WNativeWindow rootWin, etc.)
remain unchanged — only English-language occurrences are reverted.
Verified that wmaker now reports 'Window Maker 0.96.0' again and
build is clean across WINGs/, src/, WPrefs.app/.
2026-05-24 11:42:37 +02:00
Window Maker ef06f5ad8e wings: phases δ + ε — drop X11/Xlib.h from public WINGs headers
Major architectural milestone: WINGs Wayland-only files now have ZERO
transitive X11 header dependency at the preprocessor level (verified by
gcc -E grep: 0 mentions of X11/Xlib.h after preprocessing).

Changes:

  1. WUtil.h, WINGs.h: stop including <X11/Xlib.h>.  wnative.h provides
     forward declarations for Display, GC, XftDraw, XftFont; opaque
     pointer-only typedefs that don't conflict with Xlib.h's actual
     definitions when the X11 backend later includes it.

  2. WINGsP.h.in: stop including <X11/Xlib.h>, <X11/Xutil.h>.  Drop or
     re-route every X11-typed prototype:
       - W_HandleDNDClientMessage / W_DragSourceStateHandler /
         W_DragDestinationStoreEnterMsgInfo / etc.
         → moved to new private header WINGs/WINGsX11.h
       - W_HandleSelectionEvent → moved
       - W_LookupString / W_KeycodeToKeysym → moved
       - W_DndState typedef → moved (renamed to W_X11DndState)
       - XClientMessageEvent / XEvent / XKeyPressedEvent in prototypes
         → use void * in shared protos, cast in WINGsX11.h

  3. W_View struct: replaces XSetWindowAttributes by-value field with a
     small backend-neutral 'attribs' struct holding only the fields
     wmaker/WPrefs actually use (background_pixmap, background_pixel,
     border_pixel, colormap, cursor, override_redirect, save_under,
     event_mask, do_not_propagate_mask).  The X11 view backend
     translates to XSetWindowAttributes when calling XCreateWindow.
     XIC xic field replaced with void *xic.

  4. W_Color struct: replaces XColor by-value with a same-layout
     anonymous struct (pixel/red/green/blue/flags/pad).  X11 backend
     uses memcpy to populate from XColor.

  5. WINGs/WINGsX11.h (new): private header for X11 backend.  Includes
     <X11/Xlib.h>, declares X11-typed internal prototypes used by drag*,
     selection, IME, x11_backend, KeyboardShortcuts.  Required by every
     *_x11.c file plus a handful of cross-component callers
     (KeyboardShortcuts.c, MouseSettings.c, xmodifier.c, wmiv.c,
     x11_backend.c).

  6. wraster.h.in: gain a WRASTER_NO_XLIB compile-time switch.  When
     defined the header skips <X11/Xlib.h> and provides forward decls
     for Display/Visual/XImage/GC/XftFont/etc.  All Wayland-only WINGs
     files (wclient_wl.c, w*_wl.c) define WRASTER_NO_XLIB on line 1.

  7. wfont.c: adds W_ParseXLFDToFcPattern (a hand-written XLFD parser)
     replacing XftXlfdParse.  Removes <X11/Xft/Xft.h> include from the
     shared font dispatcher (and from wfontpanel.c too).

Verification:

  $ for f in WINGs/.libs/{*_wl.o,wclient_wl.o}; do
        nm $f | awk '$1=="U"{print $2}' | grep -cE '^X[A-Z][a-z]'
    done
  0 for every Wayland file

  $ for f in WINGs/.libs/*.o; do
        case $f in *_x11.o|*_wl.o|wclient_wl.o) continue;; esac
        c=$(nm $f | awk '$1=="U"{print $2}' | grep -cE '^X[A-Z][a-z]')
        [ $c -gt 0 ] && echo "$c $f"
    done
  (empty — no shared file leaks an X11 symbol either)

  $ gcc -E ... wclient_wl.c | grep -c X11/Xlib.h
  0 (no transitive Xlib include)

The library still pulls libX11 through the *_x11.c files when those are
linked in.  A future configure flag --disable-x11-backend will exclude
*_x11.c entirely; at that point libWINGs will be a pure-Wayland library
with zero libX11 dependency.
2026-05-24 11:37:18 +02:00
Window Maker b5c46870aa wings: phase γ (widgets.c) — split Xlib calls into widgets_x11.c
WINGs/widgets.c is now Xlib-call-free.  All Xlib code moved to a new
WINGs/widgets_x11.c:

  * W_X11PlatformInit (~178 lines): GC creation, modifier-mask probe,
    cut-buffer setup, cursor/atom/IM init.  Hooked from
    W_ViewBackendX11.platform_init exactly as before.
  * W_X11OpenDisplay: XOpenDisplay + WMCreateSimpleApplicationScreen.
    WMOpenScreen() in widgets.c now calls this for the X11 path.

Dead code removed: the static renderPixmap that nothing called and
that was the last X11-direct-draw path in widgets.c.

X11 includes dropped from widgets.c.  Display * typedef still resolves
through WINGsP.h transitively until phase ε removes that.

Build verified clean, wmaker still links and runs.
2026-05-24 10:55:30 +02:00
Window Maker 0f839a9c6d wings: phase β — rename X11 typedefs in shared/Wayland code
Sed pass restricted to files outside src/backend/x11 and outside
*_x11.c — i.e. all code that should not depend on X11 typedefs.

  Window     → WNativeWindow      (existing typedef in WMEvent.h)
  Atom       → WMNativeAtom
  Pixmap     → WMNativePixmap
  Cursor     → WMNativeCursor
  Drawable   → WMDrawable          (existing typedef)
  KeySym     → WMNativeKeysym
  Colormap   → WMNativeColormap

X11 backend files keep using the original Xlib type names because
they make Xlib API calls that take those types.  All typedefs are
ultimately 'unsigned long' so values pass through opaquely between
shared code and the backend.

Public WINGs API affected: WMSelectionCallback, WMSelectionProcs,
and a handful of widget signatures that took Atom/Window arguments
now take WMNativeAtom/WNativeWindow.  Source-compatible at the
binary level; consumers must rebuild.

Time and XID typedefs left for a follow-up — both are heavily used
in field/variable names that need careful auditing.  Display* and
GC stay in W_Screen for now; phase δ moves them into an X11-only
sub-struct.
2026-05-24 10:49:47 +02:00
Window Maker 1a49d6b857 wings: phase α.3 — rename X11 enum/macro values to WM_*
Mass rename of X11 enum values used in shared/Wayland code, with the
numeric values preserved so the wire layout of WMEvent fields and
the X11-backend translation tables remain unchanged.

  NotifyNormal/Grab/Ungrab/WhileGrabbed     → WM_NOTIFY_*
  NotifyAncestor/Virtual/Inferior/Nonlinear/
    NonlinearVirtual/Pointer/PointerRoot/
    DetailNone                              → WM_NOTIFY_*
  PropertyNewValue/Delete                   → WM_PROPERTY_*
  MappingModifier/Keyboard/Pointer          → WM_MAPPING_*
  VisibilityUnobscured/PartiallyObscured/
    FullyObscured                           → WM_VISIBILITY_*
  GrabModeAsync/Sync, GrabSuccess           → WM_GRAB_*
  RevertToParent/PointerRoot/None           → WM_REVERT_TO_*
  CurrentTime                               → WM_CURRENT_TIME
  None                                      → WM_NATIVE_NONE

The 'None' rename was done with conservative regex passes restricted
to clear C-context patterns (= None;, , None,, !=None, etc.) so that
'"None"' string literals in defaults and XPM 'c None' colour names
are preserved untouched.  Comments containing the English word 'None'
are also left alone.  X11 backend files that legitimately use the
X11 macros (Xlib calls returning None, etc.) are unchanged.

Build clean across WINGs, src, WPrefs.app.
2026-05-24 10:47:37 +02:00
Window Maker 82e3c3b65d wings: phase α.2 — rename X11 event masks to WM_EVMASK_*
Sed pass.  KeyPressMask, KeyReleaseMask, ButtonPressMask,
ButtonReleaseMask, EnterWindowMask, LeaveWindowMask,
PointerMotionMask, PointerMotionHintMask, Button{1..5}MotionMask,
ButtonMotionMask, KeymapStateMask, ExposureMask,
VisibilityChangeMask, StructureNotifyMask, ResizeRedirectMask,
SubstructureNotifyMask, SubstructureRedirectMask, FocusChangeMask,
PropertyChangeMask, ColormapChangeMask all renamed to the
corresponding WM_EVMASK_* names defined in wnative.h.

Numeric values still match the X11 layout, so wevent_x11.c's
matching tables and any code that masks bits keep working
unchanged.  Build clean across WINGs/, src/, WPrefs.app/.
2026-05-24 10:43:23 +02:00
Window Maker 5f65867974 wings: introduce wnative.h; phase α.1 (modifier/button masks)
WINGs API surgery, step 1.

New public header WINGs/WINGs/wnative.h declares WINGs-native types
and constants with no X11 header dependency:

  - WMNativePixmap/Atom/Cursor/Keysym/Time/XID/Colormap/Bool typedefs
    (all 'unsigned long' — compatible with the existing wire ABI).
  - WM_MOD_SHIFT/LOCK/CONTROL/MOD{1..5} modifier mask bits.
  - WM_BUTTON{1..5}_MASK pointer button mask bits.
  - WM_ANY_MODIFIER/WM_ANY_BUTTON sentinels.
  - WM_EVMASK_* event-mask bits (forthcoming patches).
  - WM_NOTIFY_* crossing modes/details.
  - WM_PROPERTY_* / WM_MAPPING_* / WM_VISIBILITY_* / WM_GRAB_* /
    WM_REVERT_TO_* / WM_CURRENT_TIME / WM_NATIVE_NONE.

Numeric values intentionally match X11 layout for binary
compatibility with WMEvent fields (which were defined that way
originally).  The architectural property that matters is that NONE
of the symbol *names* below are X11 macros — they belong to WINGs
and are defined without any X11 header.

WMEvent.h, WUtil.h, WINGs.h now #include <WINGs/wnative.h> so every
WINGs consumer picks up the new constants.  X11 headers are still
included from the public WINGs headers; phase ε will drop them.

Phase α.1 — first sed pass:

  ShiftMask     → WM_MOD_SHIFT
  ControlMask   → WM_MOD_CONTROL
  LockMask      → WM_MOD_LOCK
  Mod{1..5}Mask → WM_MOD_MOD{1..5}
  Button{1..5}Mask → WM_BUTTON{1..5}_MASK
  AnyModifier   → WM_ANY_MODIFIER
  AnyButton     → WM_ANY_BUTTON

Applied across WINGs/, src/, WPrefs.app/ via sed with word
boundaries.  Build verified clean; binary identical in size,
re-linked successfully.
2026-05-24 10:42:04 +02:00
Window Maker 852fb2adeb wayland: NULL fb->scene_buf/tree when wlroots auto-destroys cascade
ASan caught a SEGV in wl_list_remove inside wlr_scene_node_destroy
called from wl_view_destroy_backing_impl when closing a dock-icon
rename input dialog.

The flow:
  1. wInputDialog opens.  fake_leader fb_FAKE created with tree+scene_buf;
     panel content fb_PANEL created with scene_buf parented under
     fb_FAKE's tree; wManageInternalWindow wraps fake_leader in a
     WMaker frame fb_FRAME (also with tree).
  2. Dialog closes.  wUnmanageWindow runs wl_frame_destroy on fb_FRAME.
     wlr_scene_node_destroy on fb_FRAME->tree cascades — wlroots
     auto-destroys fb_FAKE->tree, fb_FAKE->scene_buf, AND
     fb_PANEL->scene_buf.
  3. WMDestroyInputPanel later calls destroyView →
     wl_view_destroy_backing_impl(panel_id), which tries
     wlr_scene_node_destroy on fb_PANEL->scene_buf — already destroyed
     and freed.  wl_list_remove on a zeroed link → SEGV.

Fix: register destroy listeners on every fb->scene_buf and fb->tree
when they're created.  When wlroots destroys the node (typically via
parent-tree cascade), the listener fires and NULLs the field.
wl_view_destroy_backing_impl and wl_frame_destroy already check for
NULL before destroying, so the second-destroy is now safely skipped.

Same pattern as the earlier fix for view->scene_node (commit 8cb56fc),
applied to all wl_frame_buf scene-graph fields.
2026-05-24 10:06:36 +02:00
Window Maker c55d42c169 wayland: guard wl_menu_paint_entry against unrealized menus
ASan caught a heap-buffer-overflow at wl_menu_paint_entry:8675 writing
4 bytes BEFORE a pixman-allocated 40-byte region.  The bottom-bevel
loop wrote dst[(y + h - 1) * stride + col] where y=0, h=0 (because
menu->entry_height was still 0 from before wMenuRealize), stride=1
(initial 1×10 buffer), col=0 → offset -1 → write -4 bytes from start
of the pixman bits buffer.

Trigger: updateOptionsMenu (right-click on titlebar) calls
wMenuSetEnabled which paints both the menu and its brother
unconditionally.  The brother had not been realized yet on first
right-click, so menu->entry_height was 0.

This was the corruption that caused the later 'double free' on
pixman_image_unref of unrelated frame buffers (heap was already
trashed by this OOB write).

Fix: bail early in wl_menu_paint_entry if entry_height is zero or
if fb is still at the 1×10 placeholder size — paint will fire again
correctly after wMenuRealize.
2026-05-24 10:01:18 +02:00
Window Maker d6e10203fa wayland: fix use-after-free of WDock during clip restore
ASan caught it: heap-use-after-free at wl_clip_icon_paint:8478 reading
workspace->clip->collapsed.  The flow is:

  wWorkspaceRestoreState()
    if (scr->workspaces[i]->clip)
        wDockDestroy(scr->workspaces[i]->clip);     // frees the WDock
    scr->workspaces[i]->clip = wDockRestoreState(); // creates new
        wDockCreate -> dockIconPaint -> wClipIconPaint
            -> wl_clip_icon_paint
                workspace->clip->collapsed   // ← reads freed pointer

scr->workspaces[i]->clip is dangling between the wDockDestroy and the
assignment from wDockRestoreState.  wl_clip_icon_paint runs inside
that gap (wDockCreate calls dockIconPaint via wClipIconPaint).

Fix: read collapsed state from aicon->dock instead of workspace->clip.
aicon->dock is the dock currently being constructed and is always
valid at this point.

This was masquerading as a 'double free' on right-click of titlebars
(the heap corruption from the early use-after-free corrupted later
allocations that pixman_image_unref then tripped over).  Removed
the diagnostic logs added while debugging.
2026-05-24 09:59:15 +02:00