Conversation
11529b5 to
69ae575
Compare
Contributor
Author
7ead0a6 to
74bbb8f
Compare
This adds lib{Xmu,Xpm,Xext}-compat shared libraries for the active Motif
fork[1] alongside the existing libXt-compat, and rework the Xrm resource
cascade, event layer, and font surface so its widgets function.
Xrm: matchEntry returns a per-component specificity vector compared with
memcmp, replacing the flat-sum scoring that violated Xt precedence;
XrmEnumerateDatabase honors loose-bound entries so *background-style
resources surface under any prefix; parseLine and entryPatternToString
decode and re-encode \n, \t, \r, \\, and octal escapes so
XtParseTranslationTable consumers see the byte values their grammars
expect.
Events and input: convertModifierState maps KMOD_ALT to Mod1Mask and
KMOD_NUM to Mod2Mask; XK_Alt_R joins XK_Alt_L on Mod1Mask in
XkbKeysymToModifiers and the XGetModifierMapping table. XSetInputFocus
drives keyboardFocus instead of warning; XGrabKey / XUngrabKey track
passive grabs; XGrabKeyboard / XUngrabKeyboard track a modal grab; the
event layer routes key events through grabs before falling back to
focus. The implicit setKeyboardFocus calls in XSelectInput and
XMapWindow are removed so XSetInputFocus is the sole focus authority.
postEvent ClientMessage uses calloc, sets send_event = True, and carries
a timestamp in data.l[1] for ICCCM compliance. getEventQueueLength lifts
its peek buffer from 25 to 256 so expose and configure bursts during
Motif geometry negotiation are not silently truncated.
Bounds and threading: parsePositiveInt guards INT_MAX before each XLFD
digit multiply; decodeString bounds-checks every \\x, \\u, and
trailing-backslash payload; the text-render cache wraps lookup, insert,
eviction, and the dependent SDL_RenderCopy under one SDL_mutex so
concurrent evictions cannot free a texture mid-draw.
XSetRGBColormaps and XGetRGBColormaps replace WARN_UNIMPLEMENTED stubs
with the ICCCM 6.4 ten-CARD32 wire format (visualid, killid, colormap,
red_max, red_mult, green_max, green_mult, blue_max, blue_mult, base_pixel).
XmuLookupStandardColormap uses them to synthesize a 24bpp RGB cube on the
root window when the requested visual is not already installed.
destroyWindow loses its vestigial WARN_UNIMPLEMENTED that was firing on
every Motif demo teardown.
The shape extension was previously a probe-only stub; this lands a
per-pixel snapshot+composite at every destination-side draw entry point
so XCopyArea, XCopyPlane, XDrawRectangle/XFillRectangles, XDrawLine(s),
XDrawSegments, XDrawPoint(s), XDrawArc(s)/XFillArc(s), XFillPolygon,
XClearArea, XPutImage, and the text renderText path all honor the
window's installed shape masks.
Shape semantics: split the single shapeMask field into shapeBoundingMask
and shapeClipMask with independent offsets, matching X's two slots. The
composite intersects them: a pixel is admitted iff every installed mask
admits it (pixelInsideShape / ShapeMaskView). XShapeQueryExtents reports
each kind separately. XShapeCombineMask's black-pixel visual indicator
paints only for ShapeBounding (clip masks don't define the window
outline). New tests cover the single-mask, intersection, and combine
semantics.
Shape integration: ShapeGuard (begin/end pair) consolidates the
snapshot+composite wrap across the draw primitives so each entry point
gets one declaration and one end call that handles all early returns.
applyShapeMaskOverDrawnRect returns Bool so callers can suppress
presentDrawableIfVisible / repaintMappedChildrenInRect when the SDL
readback or texture upload fails, keeping mask-violating output off the
visible surface. captureShapeMaskBaseline pre-clips the request to the
window bounds so pathological coordinates can't allocate huge readback
surfaces.
Overflow hardening: every bbox-arithmetic site now runs in int64 with
saturating clampToInt. CoordModePrevious accumulators in XFillPolygon
and XDrawLines clamp at INT_MIN/INT_MAX before storing into SDL_Point;
unionRect, lineDamageRect, polylineDamageRect, arcDamageRect, and the
XDrawRectangle damage+corner math all use int64 with DAMAGE_PAD_CAP on
stroke padding. XCopyPlane gained a missing BadGC check on gc and a
BadMatch return when a shape mask is installed but the destination
readback failed (avoids mixing real prior pixels with synthetic GC
background in masked-out positions). Wide-line fallback now LOGs the
silent 1-pixel downgrade when strokeLineOnRenderer / rasterStrokePath
fails so a regression in the path rasterizer isn't invisible.
Supporting infrastructure: libXinerama compat shim covering the
Xt/Motif probe surface (XineramaQueryExtension/Version/IsActive,
QueryScreens enumerates all dpy->screens with SHRT_MAX clamp).
libSDL2-x11compat / libSDL2_ttf-x11compat dlopen wrapper libraries so
the Motif demo link line doesn't double-bind real SDL2. Mapping-list
mutex with eager init from initScreenWindow on the single-threaded
XOpenDisplay path and NULL-tolerant lock/unlock wrappers so a
SDL_CreateMutex failure degrades to unsynchronized rather than crashes.
Motif build wiring propagates LIBS / iconv / runtime LD_LIBRARY_PATH
through every sub-make (config, lib/Xm, lib/Mrm, tools/wml, clients/uil,
demos).
Scripts and build artifacts: capture-motif-demo-screenshots.sh honors
MOTIF_DEMO_SCREENSHOT_RESULT_FILE env override post log_dir resolve;
compare-motif-reference.py groups by status-row comparison;
sync-upstream-headers.py switched from x.org/individual tarballs to
gitlab.freedesktop.org archives with corresponding sha256s; the new
motif-demos-check, motif-demos-screenshots, and motif-differential-tests
make targets wire up the demo validation pipeline.
ASan refuses dlopen() with RTLD_DEEPBIND because deep binding bypasses
its symbol interception; the CI sanitize job aborts at the first test
binary launch with:
You are trying to dlopen ... with RTLD_DEEPBIND flag which is
incompatible with sanitizer runtime
Detect __SANITIZE_ADDRESS__ / __SANITIZE_THREAD__ at compile time
(plus __has_feature() for Clang's address/memory/thread sanitizers)
and force RTLD_DEEPBIND to 0 in that case. RTLD_LOCAL was already on
both dlopen() sites, so the only thing lost is the belt-and-suspenders
protection against the wrapper resolving symbols back into itself.
Promote two unresolved review findings to real sync
The two annotations from d27706e where the prior pass left only a
comment got pushed back as still-unresolved by the PR reviewer. Land
actual synchronization for both.
src/error.c: guard the per-Display side table with an SDL_mutex,
lazily allocated on first use (NULL-tolerant lock/unlock if
SDL_CreateMutex fails). Read getLastRequestCode's byte under the lock
rather than returning the entry pointer — releaseLastRequestCode can
otherwise free the node between our unlock and the caller's deref.
src/wrapper/sdl-wrapper.c, src/wrapper/sdl-ttf-wrapper.c: load and
store the shared dlopen handle plus every per-wrapper realFunc cache
via __atomic_load_n / __atomic_store_n with ACQUIRE/RELEASE ordering.
The races were always benign (dlopen ref-counts and dlsym is
idempotent so the value written is always the same) but C's memory
model still classified the unsynchronized accesses as UB; the atomics
let the compiler reason about them as ordinary loads/stores rather
than fold the cache check into a single read.
include/X11/Xmu/Misc.h: MAXDIMENSION was computed as ((1 << 31) - 1)
which shifts into the sign bit of a signed int, which C99 6.5.7p4
leaves undefined. Replace with the equivalent hex literal 0x7FFFFFFF.
This header gets pulled in by Xmu consumers; UBSan -fsanitize=shift
would flag it at every translation unit that includes it.
mk/library.mk: the Darwin link line set the dylib install_name to
@rpath/<self> but did not add an LC_RPATH entry. A consumer linked
against libX11-compat that loads sibling compat dylibs (libXt-compat,
libXpm-compat, etc.) via the same @rpath relies on the loader having
some @rpath registered. Add -Wl,-rpath,@loader_path so the dylib
resolves its siblings relative to its own directory without forcing
the consumer to bake an absolute rpath in.
Verified via otool -l on the rebuilt libX11-compat.so:
cmd LC_RPATH
path @loader_path
This rewrites the workflow to fan out into five parallel top-level jobs
and add long-lived caches so CI doesn't redo deterministic work.
Topology. Previously lint / build / sanitize ran in parallel and the
build job did a release pass plus a make clean + DEBUG_LIBX11_COMPAT
rebuild serially. Split debug into its own debug-build job so the
release and debug paths run concurrently. Add a motif job that builds
the thentenaar/motif libXm and libMrm against the compat stack, builds
every Motif demo, and runs scripts/validate-motif-demos.sh under
SDL_VIDEODRIVER=dummy. This is the local half of the motif-differential
make target; the remote ssh comparison half needs a separate physical
host and isn't reproducible in a stock GitHub runner.
Caching. Three layers, all keyed so cache content can never substitute
for differently-flagged content from another job:
* upstream-src (shared across build / debug-build / sanitize / motif)
caches the deterministic outputs of sync-upstream-headers.py: the
tarball download cache, extracted X11 headers, and extracted
upstream .c source slices. Excludes build/upstream/**/*.o and *.d
so CFLAGS-sensitive objects from $(OUT)/upstream/src/%.o rules
don't cross-contaminate release vs debug vs sanitizer builds.
Also excludes build/upstream/motif-src which has its own cache.
* motif-src (motif job only) caches the Motif git clone and the
autoreconf output. Exact-match key on mk/motif.mk + patches with
no restore-keys fallback: the .source-stamp / .autogen-stamp files
inside motif-src don't depend on those inputs in the makefile, so
a stale prefix restore would let make skip a fresh clone+patch+
autoreconf after inputs changed.
* ccache per-job (build / debug-build / sanitize / motif) with
distinct keys so the sanitizer-instrumented objects, debug-flag
objects, and release objects each get their own pool. CC=ccache
clang is exported via $GITHUB_ENV; mk/toolchain.mk's
"ifeq ($(origin CC),default)" check correctly treats the
env-supplied value as authoritative.
Concurrency. cancel-in-progress fires only when github.ref is not
main, so PR pushes still cancel stale runs but main commits get a
complete CI record and cache-save pass.
Motif autotools. Ubuntu's bison package ships /usr/bin/bison but no
/usr/bin/yacc. mk/motif.mk defaults MOTIF_YACC to yacc on Linux;
set MOTIF_YACC=bison -y at job env so the Mrm parser generation
works without adding byacc as a build dep. The configure script
bakes the resolved YACC value into the generated Makefile so the
recursive submakes pick it up.
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.

This adds lib{Xmu,Xpm,Xext}-compat shared libraries for the active Motif fork[1] alongside the existing libXt-compat, and rework the Xrm resource cascade, event layer, and font surface so its widgets function.
Xrm: matchEntry returns a per-component specificity vector compared with memcmp, replacing the flat-sum scoring that violated Xt precedence; XrmEnumerateDatabase honors loose-bound entries so *background-style resources surface under any prefix; parseLine and entryPatternToString decode and re-encode \n, \t, \r, \, and octal escapes so XtParseTranslationTable consumers see the byte values their grammars expect.
Events and input: convertModifierState maps KMOD_ALT to Mod1Mask and KMOD_NUM to Mod2Mask; XK_Alt_R joins XK_Alt_L on Mod1Mask in XkbKeysymToModifiers and the XGetModifierMapping table. XSetInputFocus drives keyboardFocus instead of warning; XGrabKey / XUngrabKey track passive grabs; XGrabKeyboard / XUngrabKeyboard track a modal grab; the event layer routes key events through grabs before falling back to focus. The implicit setKeyboardFocus calls in XSelectInput and XMapWindow are removed so XSetInputFocus is the sole focus authority. postEvent ClientMessage uses calloc, sets send_event = True, and carries a timestamp in data.l[1] for ICCCM compliance. getEventQueueLength lifts its peek buffer from 25 to 256 so expose and configure bursts during Motif geometry negotiation are not silently truncated.
Bounds and threading: parsePositiveInt guards INT_MAX before each XLFD digit multiply; decodeString bounds-checks every \x, \u, and trailing-backslash payload; the text-render cache wraps lookup, insert, eviction, and the dependent SDL_RenderCopy under one SDL_mutex so concurrent evictions cannot free a texture mid-draw.
XSetRGBColormaps and XGetRGBColormaps replace WARN_UNIMPLEMENTED stubs with the ICCCM 6.4 ten-CARD32 wire format (visualid, killid, colormap, red_max, red_mult, green_max, green_mult, blue_max, blue_mult, base_pixel). XmuLookupStandardColormap uses them to synthesize a 24bpp RGB cube on the root window when the requested visual is not already installed. destroyWindow loses its vestigial WARN_UNIMPLEMENTED that was firing on every Motif demo teardown.
Build glue: mk/libxpm.mk pins libXpm-3.5.19; mk/xcompat-libs.mk links the new compat libraries against libXt-compat and libX11-compat; mk/pkgconfig.mk emits x11/xt/xpm/xmu/xext .pc files so motif's autotools configure resolves the compat stack instead of host X11; mk/motif.mk clones the pinned motif fork, runs autoreconf and configure quietly (V=1 to surface details, per-stage log files redirected on failure), and stages lib{Xm,Mrm}.so plus the upstream demos. A new shared_lib_rpath_ldflags macro in mk/common.mk consolidates the Linux$ORIGIN / Darwin @rpath+@loader_path conditionals; mk/deps.mk aggregates per-fragment dep files via $ (ALL_DEPS).
Tests: XSet/GetRGBColormaps round-trip exercising all ten wire fields, XkbKeysymToModifiers including XK_Alt_R on Mod1Mask, libXt resource cascade enumeration with mixed tight and loose bindings, Xmu converter address pinning so a dropped converter fails at link, plus lib{xpm,xt,xmu} and motif link-and-resource binaries. make check reports 31 ok markers in release and debug (CFLAGS_EXTRA=-DDEBUG_LIBX11_COMPAT) builds.
[1] https://github.com/thentenaar/motif
Summary by cubic
Adds Motif support to the compat stack with new
Xmu/Xpm/Xext/Xineramashims, pkg-config metadata, and amake motifbuild target. Implements full Shape masks with per‑pixel compositing and upgrades Xrm/XIM/events so Motif widgets link and render correctly.New Features
libXmu-compat,libXpm-compat,libXext-compat,libXinerama-compat; pkg-config forx11,xt,xpm,xmu,xext,xinerama;make motifbuilds the pinned fork and stageslibXm,libMrm, demos, and helper scripts (validate, profile, diff, screenshots).XShape*combine/query and per‑pixel compositing across all draw paths (copy, fills, lines, text, clears,XPutImage); tests cover single/intersection/combine semantics.XGrabKeyand modalXGrabKeyboard,XSetInputFocusas sole focus authority, OSF keysyms, larger queue, deferred/coalesced presents and child expose propagation; per‑Display last request code with mutex‑guarded tracking for accurateXErrorEvent.\n,\t,\r,\\, octal); UTF‑8/XIM/XOM (Xutf8*,XSetOCValues/XGetOCValues,XOMOfOC) and text‑extent APIs; GC font ref‑counting and cache invalidation; bounds checks in XLFD/string decoders.XSet/GetRGBColormaps;XmuLookupStandardColormapsynthesizes a 24bpp RGB cube;XReadBitmapFileData; link tests forxmu,xpm,xinerama; Motif link/resource and visible rendering tests; Motif examples (hello,simpleapp,togglebox).$ORIGIN) and macOS (install_name@rpath/<self>+@loader_path); SDL wrapper libslibSDL2-x11compatandlibSDL2_ttf-x11compatwith sanitizer‑safe loading (auto‑disableRTLD_DEEPBIND) and atomic dlsym caching; upstream headers synced tolibX111.8.13; stubX11/SM/SM.h.SDL_VIDEODRIVER=dummy(Linux setsMOTIF_YACC="bison -y").Migration
PKG_CONFIG_PATH=$PWD/build/pkgconfigso autotools resolvex11,xt,xpm,xmu,xext,xineramato the compat libs.make motifto build Motif against the compat stack; demos are staged and can be validated/profiled with the provided scripts.LD_LIBRARY_PATHneeded when linking via the provided.pcfiles; rpath is set for Linux/macOS as above.Written for commit 4886eae. Summary will update on new commits.