Conversation
XCB clients compile against exact structure layouts, enum values and inline accessors, so the public headers cannot be hand-written approximations without silently breaking callers. They also cannot come from libxcb's release tarball: xproto.h is generated from xcb-proto's XML during that build, so only a binary package carries the generated result. Extend scripts/sync-upstream-headers.py to stage them into build/upstream/include/xcb/ alongside the Xorg headers, from a pinned libxcb1-dev 1.15-1ubuntu2 whose archive digest and per-header digests are both verified. Tracking 13k lines of generated upstream code instead would put it through this repository's formatting and whitespace gates, which upstream style does not satisfy, and would carry it in every clone forever. A .deb is an ar archive, so the script reads its data.tar member directly rather than shelling out to dpkg-deb, which exists only on Debian derivatives and would make the fetch fail on the macOS development host.
jserv
force-pushed
the
xcb
branch
3 times, most recently
from
August 31, 2026 15:06
a2b8da6 to
7209bde
Compare
An XCB client needs a connection handle, the server setup record, XID
allocation and an event queue before a single request can be expressed,
and none of those parts can be exercised without the others. An
unchecked request reports its protocol error through the event queue,
and the queue only changes hands through XSetEventQueueOwner, so
splitting this into three commits would produce two that cannot be
tested.
The connection is a thin handle over an existing Display rather than a
transport, so XGetXCBConnection returns the connection already bound to
that display and both APIs observe one event source. Ownership follows
libX11-xcb: after XSetEventQueueOwner(XCBOwnsEventQueue), the Xlib
dequeue paths stop consuming. Hooks let the connection observe display
close and cancelled event waits without src/ ever depending on the XCB
layer, which keeps the core library free of a reverse dependency on a
sibling shim.
xcb_get_file_descriptor returns -1 because there is no socket to hand
out. Clients that poll a connection FD have no equivalent here and must
use the wait and poll entry points instead.
XIDs move from a growing array to a chunked table under a mutex.
xcb_generate_id is callable from any thread, and the old array handed
out interior pointers that a concurrent realloc could invalidate. The
chunked form keeps allocated slots pinned and lets a client reserve an
id it chose itself, which the protocol expects: a client derives ids as
base | (n & mask), so the advertised base and mask must not share a bit
and the whole range has to fit the protocol's 29 bits. Both now come
from one definition that the table and the setup record share.
Installing xcb.pc and x11-xcb.pc drags x11.pc along, because x11-xcb.pc
says "Requires: x11 xcb" and pkg-config would otherwise resolve x11
against the host's real libX11 and link it beside the shim. The
generated files now spell their Libs and Cflags with the ${libdir} and
${includedir} variables instead of absolute build paths, so install can
relocate them by rewriting the variable lines; pkg-config expands them,
so an in-tree consumer reads exactly the flags it read before.
The layer is opt-in through XCB=1. No in-tree workload needs it yet, and
a default build should not grow two libraries, two pkg-config files and
an installed include/xcb that nothing asks for. The CI jobs pass XCB=1,
so the tests and the ABI gate still run on every push.
No core request is advertised yet, so a client linking against an
unimplemented opcode fails at link time instead of receiving a cookie
that silently never completes.
Translate the window, property and selection opcodes (1-4, 7-8, 10, 12, 14-24) onto the existing Xlib implementation, so an XCB client can create and configure windows, read geometry and the window tree, and exchange properties and selections. Each request validates its arguments before touching shared state, because the protocol answer for a bad request is a specific error code, not whatever the Xlib path happens to do with an out-of-range value. A checked request stores its error for xcb_request_check; an unchecked one queues the error for the event loop, which is where an XCB client expects to find it. A request that reserved the client's chosen id and then failed validation gives the id back, so a client can retry with it. Three gaps in the shared code had to be closed rather than worked around in the shim, so Xlib callers get the same fixes: XCreateWindow could not be handed a caller-chosen resource id, which is how every XCB client names its windows; restack honored only Above and Below, leaving TopIf, BottomIf and Opposite silently rejected; and XGetWindowAttributes left border width, class, gravities and screen unset while configure ignored CWBorderWidth. Drawing, pixmap and graphics-context requests are still deferred and remain absent from the export manifest.
Translate the remaining core rendering opcodes (53-57, 60-68, 72-73, 76-77) so an XCB client can allocate pixmaps and graphics contexts, draw points, lines, segments, rectangles, arcs and text, copy between drawables, and move images in and out. Drawing is where a wrong argument corrupts output instead of failing loudly, so value masks, coordinate modes and image geometry are validated up front and answered with the protocol error the client expects. Depth compatibility is checked against the drawable rather than assumed: a GC or copy spanning mismatched depths is BadMatch, not a silently wrong blit. GetImage rejects a rectangle that leaves the drawable instead of returning a reply padded with invented pixels, and CopyPlane rejects a plane the source depth does not contain. Graphics contexts need a display, owner and depth that the shared XID table does not record, so the shim keeps that state alongside the id and releases it when the owning connection disconnects. This leaves the GC lookup linear and the state duplicated; folding it into the shared table means changing what src/gc.c stores per XID, which is a larger change than this translation layer should carry. XCreatePixmap and XCreateGC gain caller-chosen id variants, matching the window change in the previous commit, because an XCB client names every resource itself.
The XCB tests drive the shim through assertions, which proves the return values but never renders a frame. These two programs are XCB clients with no Xlib calls at all, so they exercise the paths a real client takes: connection setup, a mapped window, a graphics context, image upload and the primitive calls, ending on the event loop. They build with the other clients under make examples rather than a target of their own, since XCB=1 already decides whether they exist and a second entry point only gives the same set two names. Both accept --smoke for a headless run under SDL's dummy video driver, so make check-xcb-showcases keeps them building and running rather than rotting as documentation. Smoke mode drains the event queue before exiting, because the drawing requests are unchecked and a run that failed every one of them would otherwise still exit successfully.
Three WebKitGTK paint jobs failed once each on a single pull request and passed on re-run with no code change: two with "driver request timeout" and one with a blank first frame. The harness had no tolerance for a loaded runner. Every driver request carried a hardcoded five second budget, and a run that never got a frame failed outright. Give the per-request budget its own knob at fifteen seconds, and retry a failed attempt once. Only a transient outcome is retried: losing the driver, or never painting at all, says nothing about the build. A run that painted and then failed an input, layout or visual assertion is a real result and is reported on the first attempt, so this cannot mask a regression. The four app recipes ran the same node smoke plus paint check with slightly different wrappers, and had drifted: xnedit skipped the paint check silently when node was missing rather than honoring WASM_PAINT_REQUIRED, so a runner without node passed it while the other three failed. One macro now covers all four, and xnedit gains the gate.
|
Wonderful work, thanks a lot!! |
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.
Adds an XCB compatibility layer over the in-process Xlib shim, so an XCB
client runs against libx11-compat with no X server present.
The layer is opt-in.
XCB=1buildslibxcb-compat.so, thelibX11-xcb-compat.sobridge, their tests, pkg-config files and exampleclients; a default build produces none of them. The CI jobs pass
XCB=1,so the tests and the 143-symbol ABI gate run on every push.
Shared code
Three gaps were closed in
src/rather than worked around in the shim,so Xlib callers get the same fixes:
XCreateWindowcould not be handed acaller-chosen resource id, which is how every XCB client names its
windows; restack honored only Above and Below, silently rejecting TopIf,
BottomIf and Opposite; and
XGetWindowAttributesleft border width,class, gravities and screen unset while configure ignored CWBorderWidth.
The XID table moved from a growing array to a chunked table under a
mutex.
xcb_generate_idis callable from any thread, and the old arrayhanded out interior pointers a concurrent realloc could invalidate. The
advertised base and mask now come from one definition shared by the table
and the setup record, so a client deriving ids as
base | (n & mask)cannot collide with the allocator.
Known gaps
check-xcb-referencecompiles its probe against system XCB and diffsit against a file that same binary produced, so it asserts nothing
about this shim yet.
BadValue rather than guessing at a conversion.
shim owns, because the shared XID table records none of them. The
lookup is linear under one mutex.
XCB=0is a packaging gate, not a compile-out: the core library stillcarries the caller-chosen-id paths and the hook setters.
Close #81