Anchor menus on Wayland and load Motif Xft fonts - #73
Open
jserv wants to merge 5 commits into
Open
Conversation
jserv
added a commit
that referenced
this pull request
Jul 24, 2026
The new wayland job failed at SDL3 configure with "could not find X11 or Wayland development libraries": SDL3's Wayland video driver needs EGL, which the job did not install, so with no usable X11 set either SDL3 disabled both backends and aborted. Install libegl-dev and libgles-dev, and build SDL3 with SDL_X11=OFF since this path only exercises the Wayland driver: the incomplete system X11 set is irrelevant and turning X11 off keeps the configure honest. Also reflow scripts/validate-menu-rendering.sh and scripts/build-sdl3.sh through shfmt so the lint job's `shfmt -d` gate passes, and pick up the clang-format reflow of the popup unmap comment and the parent-unmap test.
jserv
added a commit
that referenced
this pull request
Jul 24, 2026
Address the PR #73 review: popup-parent-unmap.c printed PARENT_UNMAP_OK after the map/unmap/remap cycle regardless of what happened to the popup, so the gate passed on mere absence of an ASan crash. It now reads the popup's map_state after XUnmapWindow(main) and fails unless the parent-unmap sweep carried it to IsUnmapped, so the check validates the lifecycle behavior rather than only teardown cleanliness. While validating that, the teardown check turned out not to be re-runnable: its ASan rebuild instruments both build/src and the staged upstream libX11 objects under build/upstream/src, but restore_library wiped only build/src, so it relinked the leftover instrumented upstream objects and left libX11-compat.so carrying __asan_* references that break the ordinary test compiles on the next run. A new wipe_first_party_objs drops the upstream object files too (keeping their staged .c sources) before both the ASan build and the restore, so each rebuild is clean and the workspace is usable afterward. Confirmed on node1: two back-to-back runs both report 4/4 and the library is left uninstrumented.
A Wayland client cannot self-position a top-level, so override-redirect menu created as an ordinary window floats wherever the compositor drops it, detached from the widget that spawned it. When a mapped parent contains the popup's anchor, realize it as an SDL3 xdg_popup positioned relative to that parent surface. findPopupParentToplevel picks the innermost containing window (smallest area wins, so a cascade submenu anchors to its parent menu, not the shell); a zero-area transient is skipped so it cannot misanchor. Anchored popups are torn down before their parent's SDL surface through a single drainAnchoredPopups helper shared by the unmap, unrealize, and destroy paths. It unmaps each child through the full path (grab release, UnmapNotify, mapState flip) and forces the unrealize on any child left holding an sdlWindow, so the drain always converges and never leaves a popup stranded with a surface SDL would free out from under it. New popupParent field on WindowStruct, initialized in initWindowStruct; xc_CreatePopupWindow shim in sdl-compat.h (SDL3 only, no SDL2 equivalent).
Motif's FONT_IS_XFT render tables need a working Xft. The bundled
thentenaar/motif was built --without-xft, so those tables fell through
to an invalid rendition type and painted as a tiny crushed block. Build
it --with-xft against the in-tree Xft-over-SDL layer and add the entry
points that path links against: FcInit/FcFini, which Motif's Display.c
calls at display open and close, and XftDrawCreateBitmap, the XmRenderT
fallback that must at least resolve. Bump MOTIF_COMMIT to the matching
source revision.
The app differentials build the same --with-xft Motif, so keep them
green: install lib{fontconfig1,xft}-dev in CI, flip motif/mosaic/violawww
differential drivers to --with-xft with a pkg-config preflight,
recalibrate the demo comparison thresholds now that both sides render
through Xft, and widen the GIMP-Motif startup window-wait. GIMP's
heavier --with-xft cold start, captured concurrently with the system
side, now maps its toolbox past the old five-second bound on a two-core
runner; the wait is a max, so the single-process path is unaffected.
tests/menu-rendering covers the two behaviors from the detached-menu report. A raw override-redirect popup and a real Motif XmPulldownMenu must each emit a Wayland xdg_popup anchored to their parent rather than a detached second toplevel; a FONT_IS_XFT render table must measure a legible height; and tearing a live popup's parent down, by both destroy and unmap, must carry the popup through the full unmap path (its own UnmapNotify, IsUnmapped, a clean remap) with no leak or use-after-free under AddressSanitizer. scripts/validate-menu-rendering.sh drives them under weston headless, building a Wayland-enabled SDL3 via scripts/build-sdl3.sh (pinned source with published checksums). For the teardown check it rebuilds the library with the sanitizer and restores an uninstrumented one afterward; the restore wipes the staged upstream objects the sanitizer instrumented too, so the harness leaves a clean tree and is re-runnable. Wayland checks skip with a reason on an X11-only host; CI_REQUIRE=1 turns a skip into a failure for a required gate.
Add a wayland job: it builds a Wayland-enabled SDL3 and SDL3_ttf from source (with EGL/GLES dev libs so SDL3's Wayland video driver actually configures), builds the compat stack and the --with-xft Motif, and runs scripts/validate-menu-rendering.sh against weston headless with CI_REQUIRE=1. It is the only job that exercises compositor-dependent override-redirect xdg_popup path; the SDL2 jobs and the console-only sdl3 job cannot see it.
Contributor
Author
Override-redirect popups on the SDL3/Wayland backend had two defects. An xdg_popup's geometry is fixed by its positioner at creation, so SDL_SetWindowSize only grew the surface buffer while the compositor kept clipping a popup resized after mapping to its creation rectangle: a Motif menu bar switching the armed pulldown from a short menu to a taller one rendered the taller menu cropped. configureWindow now re-runs the positioner once, after the move and resize blocks have settled the geometry, through repositionAnchoredPopup, so the compositor grows the popup to the final size. A constraint-enforcing compositor slides or flips a popup that would fall off screen and reports the new position only through SDL's MOVED event, which onSdlEvent dropped. ws->x/y then kept the requested position, so pointer-to-root translation for a grabbed submenu was offset by the slide and the highlight drifted from the cursor. onSdlEvent now lets an anchored popup's MOVED through; convertEvent records the real origin (parent origin plus the parent-relative offset) and emits no ConfigureNotify, so the toolkit's own menu tracking is undisturbed.
Contributor
Author
Can you help validate commit ad85532 ? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Motif drop-down menus rendered as free-standing windows and FONT_IS_XFT render tables came out as tiny unreadable blocks on the SDL3 backend under a Wayland compositor.
Menus: an override-redirect popup is a top-level, and a Wayland client cannot self-position a top-level, so each menu shell floated wherever the compositor dropped it. Under the SDL3 backend these popups are now created with SDL_CreatePopupWindow anchored to the parent surface whose rectangle contains the menu anchor. findPopupParentToplevel picks the innermost containing realized window (cascade submenus anchor to their parent menu), and the popup is placed at a parent-relative offset. SDL owns the popup as a child window, so parent teardown unrealizes any anchored popup first to avoid a dangling SDL_Window double free. The SDL2 path is unchanged: popupParent stays None and the absolute-position top-level path is preserved.
Fonts: libXm was built --without-xft, so a FONT_IS_XFT rendition took the INVALID_TYPE branch and loaded no font. Build Motif --with-xft against the in-tree Xft, adding Fc{Init,Fini} and XftDrawCreateBitmap the Motif Xft path links. The differential reference builds flip to --with-xft as well, with matching libxft/libfontconfig deps, so both sides render the same font stack; measured baselines are unchanged.
Add a wayland CI job that builds a Wayland-enabled SDL3 from source and runs scripts/validate-menu-rendering.sh under weston headless: a real Motif XmPulldownMenu, a raw override-redirect popup, a FONT_IS_XFT rendition, and a parent-first teardown under AddressSanitizer.
Close #68
Summary by cubic
Fix Wayland menus by anchoring override‑redirect popups as parent‑anchored
SDL3xdg_popups and make Motif Xft fonts render at readable sizes. Also track compositor moves and re-run the popup positioner on resize so menus land and grow correctly.Bug Fixes
SDL_CreatePopupWindow;findPopupParentToplevelpicks the innermost mapped parent (skips zero‑area transients). SDL2 still uses absolute toplevels.MOVEDto record the real origin (no spuriousConfigureNotify), and re-run the xdg positioner after resize viarepositionAnchoredPopup; drop invalid always‑on‑top on xdg_popups.drainAnchoredPopupsunmaps/unrealizes anchored popups before parent unmap/destroy/unrealize; tests assert the popup’sUnmapNotifyand finalIsUnmapped; guardXGetWindowAttributesfailure.--with-xftand addFcInit/FcFiniandXftDrawCreateBitmapsoXmFONT_IS_XFTloads scalable fonts.New Features
SDL3/SDL3_ttfwithscripts/build-sdl3.shand runsscripts/validate-menu-rendering.shunder headlessweston.XmPulldownMenu, parent destroy/unmap teardown, popup resize growth, and compositor slide offsets; CI asserts the Wayland driver, runs under AddressSanitizer, and enforces required checks withCI_REQUIRE=1.Written for commit ad85532. Summary will update on new commits.