WMEButton and WMEMotion are separate structs in the WMEvent union with different field layouts — u.motion.x is at a different memory offset than u.button.x. The WME_BUTTON_PRESS handler in wslider.c was calling valueForMousePoint(sPtr, event->u.motion.x, event->u.motion.y) which reads uninitialized memory on Wayland (memset zero = 0). On X11 this happened to work because the X11 event translator writes to both union members. Fix: changed all event->u.motion.x/y references to event->u.button.x/y in the WME_BUTTON_PRESS case.
Slider value changes (from click, drag, or programmatic set) call paintSlider to draw the new knob position into the pixman backing buffer, but the backing was never committed to the scene graph on Wayland. Added W_RedisplayView(slider->view) after each paintSlider call in: WMSetSliderValue, WMSetSliderMinValue, WMSetSliderMaxValue, and handleActionEvents (WME_MOTION/WME_BUTTON_RELEASE). The expose handler path does NOT get W_RedisplayView to avoid recursion.
The blue position indicator (gview — geometry view with blue-gray background,
white coordinate text) was visible during window move, but never disappeared
when the move ended.
Root cause: W_ViewSceneNodeSetEnabledFn was declared in wview_wl.c and
called by wl_unmap() to disable the view's scene buffer node, but
wl_backend.c never wired it up — the pointer was NULL.
Also: add auto-create of frame_buf for WINGs toplevel views not explicitly
reparented (switch panel, menus, gview), sync position from WINGs view on
commit, auto-realize children in wl_map/wl_map_subviews, and guard scene
node enablement against unmapped views.
Three issues prevented WPrefs from being managed by wmaker in X11:
1. W_X11OpenDisplay: XOpenDisplay("") fails; treat empty string as NULL
so it falls back to DISPLAY env var.
2. wevent_x11.c/wevent.c: MapRequest and ConfigureRequest events must
bypass WINGs view lookup (both _view_target in x11_fetch and
W_GetViewForWindow in WMHandleEvent) to reach DispatchEvent via
extraEventHandler.
3. wview_x11.c: x11_create_root must use RootWindow() not
scr->rootWin (which may be an intermediate window created by
RCreateContext for non-default visuals). Otherwise toplevel
WINGs windows are created as children of the wrong parent and
their MapRequest never reaches the WM.
WPrefs.app/main.c defaults display_name to "" (empty string).
W_X11OpenDisplay passed this directly to XOpenDisplay, but
XOpenDisplay("") is not equivalent to XOpenDisplay(NULL) — the
former fails while the latter uses the DISPLAY environment variable.
Treat empty display string as NULL so XOpenDisplay falls back to
the environment variable.
Also remove debug traces from event.c and x11_backend.c.
SubstructureRedirect events (MapRequest, ConfigureRequest) are delivered
to the root window. Since the root window has a WINGs view registered
(the WMScreen root view), W_GetViewForXWindow resolves it and
WMHandleEvent dispatches to the view handlers instead of falling through
to extraEventHandler (DispatchEvent).
This caused WINGs-based apps like WPrefs to never have their windows
managed by the WM - the MapRequest was silently consumed.
Fix: skip the view lookup for MapRequest and ConfigureRequest events so
they always reach DispatchEvent via extraEventHandler.
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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)
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.
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.
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.
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)
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.
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).
wl_redisplay fills the view's background then paints widget content
via expose, but never re-composited child views. Children painted
earlier were erased by the parent's background fill. Now recursively
calls wl_redisplay on all mapped+realized children after the parent's
expose handler, so the full widget tree composites bottom-up.
WPrefs passes display_name="" by default. wl_display_connect("")
treats the empty string as a literal socket name and fails. Pass NULL
instead so it uses $WAYLAND_DISPLAY. Also handle the literal string
"wayland" (from detect matching) the same way.
wlclient_open passed NULL as the RContext, causing a segfault in
RConvertImageMask (called from makePixmap during screen init) when
WPrefs.app launched under Wayland.
Fix: call RCreateContextWayland() to create a valid 32-bit ARGB
context before passing it to WMCreateScreenWithRContext.
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.)
Phase η: completes the WINGs API surgery by adding the configure
option that actually excludes the X11 backend from the build.
./configure --enable-wayland-client --disable-x11-backend
→ libWINGs has zero X11 symbol dependencies in its own objects
configure.ac: --enable-x11-backend (default yes) controls the new
USE_X11_BACKEND m4 conditional and #define.
WINGs/Makefile.am: split source list into:
- backend-neutral / shared sources (always)
- if USE_X11_BACKEND: dragsource_x11.c / dragdestination_x11.c /
dragcommon_x11.c / selection_x11.c / wappresource_x11.c /
wballoon_x11.c / wcolor_x11.c / wcolorpanel_x11.c /
wevent_x11.c / wfont_x11.c / widgets_x11.c /
winputmethod_x11.c / wrender_x11.c / wview_x11.c
- else: wstubs_no_x11.c (new)
XLIBS / XFT_LIBS only added to libWINGs_la_LIBADD when X11 backend
is enabled.
WINGs/wstubs_no_x11.c: empty implementations of every public WINGs
API symbol that normally lives in *_x11.c — DnD setup, selection
handlers, IM context, balloons, color/font panels, application icon
hints, atom helpers. Each stub is a no-op (returns success-neutral
value); calling them in pure-Wayland mode is silent rather than an
error, so future Wayland equivalents can drop in transparently.
WINGs/widgets.c: WMCreateScreenWithRContext uses weak references
to the W_*BackendX11 instances so they can resolve to NULL when not
linked. If the caller asked for X11 mode but X11 backend is not
built, falls back to Wayland with a warning.
WINGs/WINGsP.h.in: backend extern decls (W_*BackendX11) marked weak
so links resolve to NULL when X11 backend is excluded.
Verification:
$ ./configure --disable-x11-backend && make
$ for f in WINGs/.libs/*.o; do nm $f | grep '^.*U X[A-Z][a-z]'; done
(empty)
libWINGs.so still pulls libX11 transitively via wrlib (wraster's X11
image-conversion path); that's a separate refactor. At the WINGs
source level the partition is now complete: shared / X11 / Wayland.
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.
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/.
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.
Last X11 symbol leak in shared WINGs code: wtextfield.c's selection
request handler called XInternAtom directly to look up TEXT,
COMPOUND_TEXT, and TARGETS atoms.
Replaced with W_NativeInternAtom(scr, name) — implemented in
widgets_x11.c as a thin XInternAtom wrapper. The Wayland-only
build (future) will provide a stub that returns WM_NATIVE_NONE,
making the requestHandler's downstream comparisons all fall through
to 'no match' cleanly.
Architectural verification: every WINGs shared object file (i.e.
not *_x11.o, not *_wl.o) now has ZERO undefined X11 symbols:
$ for f in WINGs/.libs/*.o; do
case $f in *_x11.o|*_wl.o) continue;; esac
nm $f | awk '$1=="U" {print $2}' | grep -c '^X[A-Z][a-z]'
done | sort -u
0
Combined with the *_wl.c files already verified to have zero X11
deps, the WINGs library now cleanly partitions: shared code is
backend-neutral, X11 backend code lives in *_x11.c, Wayland backend
code lives in *_wl.c. A --disable-x11-backend configure option can
now be added that drops *_x11.c from the build for pure-Wayland
WINGs apps.
The only Xft API used in shared font code was XftXlfdParse — used
to convert legacy X11 XLFD font names to fontconfig FcPatterns so
old WINGs config files keep working.
Replaced with a hand-written parser W_ParseXLFDToFcPattern (defined
in wfont.c, declared in WINGsP.h) that walks the 14 dash-separated
XLFD fields and pulls out family / weight / slant / pixelsize.
This is enough for every XLFD that wmaker / WPrefs has ever used
in practice (~14 different names across all defaults).
Both wfont.c and wfontpanel.c now use W_ParseXLFDToFcPattern, and
neither file includes <X11/Xft/Xft.h> any more. libXft is now only
pulled in by the X11 font backend (wfont_x11.c), as it should be.
Build clean. Wayland-only files still have zero X11 symbol
dependencies (verified via nm).
The color picker dialog uses XGetImage / XGrabPointer / XShape /
XPutImage / XCopyArea to implement the magnify-glass screen picker.
Wayland intentionally does not allow reading pixels from other
windows (security), so the screen-pick feature has no Wayland
equivalent. The whole file is therefore X11-only.
A future commit will provide a stub that returns a wwarning when
WMShowColorPanel is called in Wayland mode.
Mechanical rename to make architectural intent visible:
dragsource.c → dragsource_x11.c
dragdestination.c → dragdestination_x11.c
selection.c → selection_x11.c
winputmethod.c → winputmethod_x11.c
wballoon.c → wballoon_x11.c
wappresource.c → wappresource_x11.c
Each is essentially a wrapper around X11 features (XDND, X11
selection, XIM, XShape, X11 WM hints) with no Wayland equivalent
yet. These files keep providing their public WINGs API symbols
unchanged, so build+link is identical at this point.
A future commit will add Wayland counterparts (or no-op stubs) and
wire a configure option that conditionally excludes *_x11.c when
the build is Wayland-client-only. Doing this now would force a
choice between linkable but Wayland-broken (no stubs) or partial
double-implementation (some functions stubbed); the right answer
needs vtable dispatchers in shared *.c files for each, which is
the next round of surgery.
dragcommon.c now contains only backend-neutral helpers
(WMCreateDragOperationArray/Item, accessors, W_FreeViewXdndPart).
dragcommon_x11.c is new and owns:
- W_OperationToAction/W_ActionToOperation atom translation
- windowExists / handleNoWindowXError probe
- W_SendDnDClientMessage XSendEvent path
- W_HandleDNDClientMessage XDND wire dispatch
- handleLeaveMessage
These touch scr->xdnd*Atom fields and call XSendEvent / XGetAtomName /
XSetErrorHandler etc. — strictly X11. Wayland DnD will be added later
through wl_data_device.
dragsource.c and dragdestination.c still contain Xlib calls; those
splits come next.
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.
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.
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.
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/.
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.
Task 5.
wclient_wl.c subscribes to wl_seat.capabilities. On POINTER capability
it binds wl_pointer with a listener that translates enter/leave/motion/
button events into WMEvent (WME_ENTER, WME_LEAVE, WME_MOTION,
WME_BUTTON_PRESS/RELEASE). Linux evdev button codes (BTN_LEFT, etc.)
map to X-style numbers (1, 2, 3, 8, 9). Pointer focus is tracked per
wl_surface and resolved to a top-level W_View via wlc_top_for_surface.
On KEYBOARD capability it binds wl_keyboard. The keymap event mmaps
the fd, compiles via xkb_keymap_new_from_string, and stashes the result
in WLClientCtx (xkb_keymap + xkb_state). Subsequent key events use
xkb_state_key_get_one_sym for the keysym and xkb_state_key_get_utf8 for
text — these populate WMEKey.{keycode, keysym, utf8, utf8_len}.
Modifiers update xkb_state and a cached X-style mod mask
(ShiftMask, ControlMask, Mod1Mask, Mod4Mask) used in WMEKey.state and
WMEButton.state.
Focus enter/leave on the keyboard side dispatches WME_FOCUS_IN /
WME_FOCUS_OUT so WINGs widgets get focus tracking.
Events flow via W_WL_EnqueueEvent, the same ring buffer wevent_wl.c
already drains for the compositor side — no separate event-pump
required. Axis (scroll) events are stubbed out for now and will be
synthesized as button 4/5 in a follow-up.
Tasks 3 + 4.
wclient_wl.c grows a memfd+mmap-backed wl_shm buffer allocator that
wraps the mapped region as a pixman_image_t. Painting into the pixman
image writes directly into the buffer the compositor will read on
commit, so no copy is needed. wl_buffer.release marks the buffer as
released for future reuse (currently we just realloc on resize, so the
flag is informational).
Each top-level W_View now acquires a WLClientToplevel with a wl_surface,
xdg_surface, and xdg_toplevel. The first xdg_surface.configure event
acks and attaches the buffer; subsequent configures with non-zero
dimensions realloc the backing. xdg_toplevel.close sets a want_close
flag (closure event synthesis is in the next pass).
wview_wl.c now calls three function-pointer hooks for top-level views:
W_WLClientToplevelRealizeFn(view, w, h) -> pixman_image_t *
W_WLClientToplevelResizeFn(view, w, h)
W_WLClientToplevelDestroyFn(view)
When the hooks are NULL (compositor-internal mode or X11), wview_wl.c
falls back to plain pixman_image_create_bits — preserving every
existing behaviour. When wclient_wl.c installs them, the same paints
end up on a wl_shm buffer attached to a real Wayland surface.
W_ViewCommitBackingFn and W_ViewDestroyBackingFn are also installed by
W_WLClientInit so wview_wl.c's existing redisplay/destroy plumbing
drives wl_surface_attach / damage / commit / surface destruction.
No input handling yet; that's task 5.
The previous commit accidentally swept in a personal scratch file (+cmd)
and the auto-generated xdg-shell-protocol.c. Both are now ignored.
Generated files come from $(WAYLAND_SCANNER) at build time.
Task 1 + 2 of the WINGs Wayland-client backend.
Adds optional --enable-wayland-client (default auto) which depends on
wayland-client, xkbcommon, wayland-protocols, and wayland-scanner.
configure.ac probes for these and exposes WAYLAND_CLIENT_CFLAGS/LIBS
plus the protocols data dir and scanner path.
WINGs/Makefile.am wires a wayland-scanner rule that generates
xdg-shell-{protocol,client-protocol}.{c,h} at build time and links
libWINGs against wayland-client + xkbcommon when enabled.
WINGs/wclient_wl.c is the new file. It self-stubs when the feature
is disabled. When enabled it provides:
W_WLClientInit(scrPtr, display)
Connects via wl_display_connect, registers a wl_registry listener,
binds wl_compositor/wl_shm/wl_seat/xdg_wm_base, creates an xkb_context,
and hooks the libwayland event pump into the existing
W_EventBackendWL via W_WL_SetLoopDispatch(). This lets the existing
ring-buffer event mechanism keep working unchanged: the ring is fed
by client-side wl_pointer/wl_keyboard listeners (next task) and
drained by WMNextEvent / WMMaskEvent / waitForEvent.
W_WLClientShutdown(scrPtr)
Tears down all bound globals and disconnects the wl_display.
No surfaces / buffers / input dispatch yet — those come in tasks 3-5.
Build verified clean, wmaker still links and runs as before.
The Window Inspector's 'select window' feature grabbed the pointer on
the root window then read XEvent.xbutton.subwindow to learn which
window was clicked. This was the last access to _x11_raw in shared
src/ code.
Add a subwindow field to WMEButton. The X11 backend's WMEvent
translation populates it from xbutton.subwindow. The Wayland backend
leaves it 0 (Wayland has no equivalent grab semantic — the click
reports the actual frame as event.window directly). winspector.c
now uses event.u.button.subwindow with fallback to
event.u.button.window, working on both backends.
Net: src/ has zero _x11_raw escapes from shared code. Remaining
references are inside the X11 backend (legitimately consuming the
field set by its own event translator).
Implement wl_drag_begin as a simple internal drag-and-drop state machine
for WINGs-to-WINGs drag operations (color wells, text widgets).
On button press, waits for motion to confirm drag intent, then enters
a drag loop tracking the cursor. On release, finds the view under the
cursor and if it has drag destination procs + matching types, fetches
data from the source and calls performDragOperation on the destination.
This handles same-process DnD without needing wl_data_device protocol.
The optimization that skipped pango_layout_set_text when the text
matched the previous layout content used pango_layout_get_text()
which returns an internal pointer. This pointer comparison with
arbitrary input text could read freed memory or trigger heap
corruption in Pango internals when the layout state was stale.
Always call pango_layout_set_text unconditionally. The cost is
negligible (Pango internally short-circuits if text is identical).