macXcapture: X11 wire protocol capture, replay, and decode for macOS
macXcapture is a wire-level X11 capture, replay, and decode tool for
macOS. It sits between any X client and any X server, records every
byte of the conversation, plays it back like a video, and decodes the
protocol bytes into human-readable form. Captures are saved as
.xtap files that you can replay against another server, diff
against each other, or just read.
It is part of the macXserver project but ships as its own app. You do not need macXserver to use it. Capture a session between any X client (xterm, dtpad, an old Motif app, your own debugger target) and any X server (XQuartz, a vintage Sun, a Linux box across the network) and macXcapture will record and decode the whole thing.
What it does
Three modes, three views. The whole loop fits in one app.
Three modes
Record, Open, Replay. Open the app and pick one. Each mode is self-contained: no project files, no setup state to lose. The decoded view (the hero image above) is what Open shows you — the full chrono dump with opcodes, atoms, and properties resolved inline.
Record
A short wizard stands up a one-shot proxy between any X client and any X server. Pick the listen port, point the client's DISPLAY here, set the forwarding target, name the file. Auto-capture works too — turn it on in the server and every session gets its own .xtap.
Replay
Pipe a saved .xtap into any live X server. Smoke-test your server's responses against a known-good session, or watch a recorded run render in real-time pacing or fast-pump mode. Keep the connection open after the last frame so windows stay mapped.
Useful on its own
I wrote macXcapture as a tool to build macXserver. When something rendered wrong on a Sun client, I needed to see the exact bytes that crossed the wire. The capture-first methodology became the whole development loop: record a real session, read X11R6 to find the era-correct behavior, write a Swift handler, replay the capture, diff against the captured replies.
That same loop is useful to anyone working with X11 at the protocol level, with or without macXserver:
- Debugging a misbehaving X client. "Run it once, capture, replay until you understand what's happening" beats "run it interactively and hope you can reproduce."
- Learning X11 from the wire up. The decoded chrono dump names every opcode, resolves every atom, and types every property. It's a readable transcript of the dialog between client and server.
- Reverse-engineering a vintage client whose source is gone or unbuildable. The wire is the spec when everything else is gone.
- Comparing two servers' behavior for the same client. Capture against server A, replay against server B, field-level diff the responses.
It runs on any modern Mac. It does not require macXserver to be installed. Point it at a vintage Sun running real X11R6, or at XQuartz, or at a Linux box over the network — it captures bytes, it doesn't care who's on either end.
What a decoded chrono dump looks like
A few lines from the start of an xclock session:
13.872µs [seq 0019] → InternAtom only-if-exists=0 name="WM_PROTOCOLS"
14.041µs [seq 0019] ← InternAtomReply atom=312 (WM_PROTOCOLS)
14.219µs [seq 0020] → ChangeProperty window=0x0040000a (xclock toplevel)
property=312 (WM_PROTOCOLS)
type=4 (ATOM) format=32 mode=Replace
data=[314 (WM_DELETE_WINDOW)]
14.387µs [seq 0021] → MapWindow window=0x0040000a (xclock toplevel)Each request lists the named opcode, the resolved atom values, the typed property contents, and the resource ID with a human-readable annotation drawn from the session-wide resource registry. Replies are matched to their requests by sequence number and shown with the ← glyph. Field-level diff between two captures compares by structure, not by text, so byte-equivalent captures with different timestamps register as identical.
Technical detail
The .xtap format is documented in the macXcapture feature checklist. Key pieces:
- Time-stamped frames with C2S and S2C bytes, length-prefixed.
- A session-wide resource registry annotates the chrono dump with object lineage and leak detection.
- Type-aware decode for ICCCM
WM_*,_MOTIF_*,_NET_*, RANDR, XKB, XInput v1, RENDER, MIT-SHM, BIG-REQUESTS, XC-MISC, XTEST, RECORD, SHAPE. - Narrative landmark detection: clients that emit
XLog-style markers get them surfaced as left-justified#callouts in the viewer with sidebar navigation (Cmd-] / Cmd-[). - Field-level semantic diff between two captures, with longest-common-subsequence alignment.
- CLI tool for headless scripted captures and dumps (
macxcapture --listen ... --forward ...).
The capture app and the macXserver server share a SwiftXCaptureUI library, so the viewer in the capture app and the debug viewer inside the server are pixel-identical. Same code, same rendering, same keyboard shortcuts.
Where it came from
macXcapture was extracted from macXserver, a modern rootless X11 server for macOS I wrote in 30 days using Claude Code. The whole server was built capture-first — record a session against a real Sun, then implement the opcodes one at a time, replaying the capture until the wire matched. The capture tool kept growing through that build, and the GUI app shipped about three weeks in when it was obvious the decoder needed a proper viewer.
The two ship together in the same repo. Build the whole thing with Xcode or just swift build -c release for the CLI. macXcapture works against any X server — you do not need macXserver to use it.