// X11 windowing-demo scaffold — the evolved form of ../../example/app.d (the
// irreducible Xlib window). On top of the minimal sequence it implements the
// full demo contract every feature demo (F01..F17) in this tree follows:
//
// * WM_PROTOCOLS / WM_DELETE_WINDOW close handshake (graceful close)
// * software gradient presented through MIT-SHM (XShmCreateImage /
// XShmPutImage with a requested XShmCompletionEvent); when the extension
// is absent (or attach fails) it logs `step name=shm_fallback` and uses
// plain XPutImage + XSync instead
// * readiness event loop: poll(2) on the connection fd
// (ConnectionNumber(dpy), reached via its function form XConnectionNumber
// since the macro is not ImportC-able), draining with XPending/XNextEvent
// after readiness — never blocking inside XNextEvent
// * redraw on Expose; image + SHM segment reallocation on ConfigureNotify
// size changes; clean teardown (XShmDetach -> destroy_image -> shmdt)
// * instrumentation per the F01 spec (../../../features/f01-first-pixel.md)
// via instrument.d: init_start, step name=<api>, window_created,
// first_configure, first_pixel_presented, resize, frame_callback,
// close_requested
// * WSI_AUTO_EXIT=1 -> bounded run (~120 redraws or ~2 s) including a
// 10-step programmatic XResizeWindow storm (F02 spec), then exit 0;
// otherwise runs until the WM delivers WM_DELETE_WINDOW
//
// Headless-safe: if no X server is reachable, prints `SKIP:` to stdout and
// exits 0. See ../../index.md (the X11 OS-API survey) and ../../scaffold.md
// (the findings this program produced).
module (module) appapp;
import (module) cc; // ImportC: Xlib + Xutil + Xatom + XShm + sys/ipc + sys/shm + poll
import (module) instrumentinstrument;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.configD compatible types that correspond to various basic types in associated
C and C++ compilers.
Source
core/stdc/config.d
config : c_long, c_ulong;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.stdioD header file for C99 <stdio.h>
pubs.opengroup.org/onlinepubs/009695399/basedefs/stdio.h.html, stdio.h
Source
core/stdc/stdio.d
stdio : (alias) app.printf = int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.stdlibD header file for C99.
pubs.opengroup.org/onlinepubs/009695399/basedefs/stdlib.h.html, stdlib.h
Source
core/stdc/stdlib.d
stdlib : (alias) app.getenv = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv;
// ---------------------------------------------------------------------------
// Constants Xlib/glibc expose as macros that ImportC does not reliably import
// (expression macros like `(1L<<15)` and cast-expressions like IPC_PRIVATE).
// Re-declared per the ImportC guide; module-local declarations shadow any
// same-named enums the shim may have imported.
enum : c_long
{
(enum value) app.KeyPressMask = 1LKeyPressMask = 1L << 0,
(enum value) app.ExposureMask = 32768LExposureMask = 1L << 15,
(enum value) app.StructureNotifyMask = 131072LStructureNotifyMask = 1L << 17,
}
enum // XEvent.type discriminators
{
(enum value) app.KeyPress = 2KeyPress = 2,
(enum value) app.MapNotify = 19MapNotify = 19,
(enum value) app.ConfigureNotify = 22ConfigureNotify = 22,
(enum value) app.Expose = 12Expose = 12,
(enum value) app.ClientMessage = 33ClientMessage = 33,
}
enum (constant) int app.ZPixmap = 2ZPixmap = 2; // XImage format
enum (constant) int app.ShmCompletion = 0ShmCompletion = 0; // event offset inside MIT-SHM's allocated event range
enum (constant) int app.False = 0False = 0;
enum (constant) int app.True = 1True = 1;
enum (constant) int app.POLLIN = 1POLLIN = 0x001;
enum (constant) int app.IPC_PRIVATE = 0IPC_PRIVATE = 0;
enum (constant) int app.IPC_CREAT = 512IPC_CREAT = 0x200; // 01000 octal
enum (constant) int app.IPC_RMID = 0IPC_RMID = 0;
// ---------------------------------------------------------------------------
// The software framebuffer: an XImage either backed by a SysV shared-memory
// segment the X server attaches (MIT-SHM, zero protocol-stream copies) or by
// a malloc'd buffer pushed through the wire with XPutImage.
struct (struct) app.BackbufferBackbuffer
{
XImage* (field) _error_ app.Backbuffer.ximgximg;
XShmSegmentInfo (field) _error_ app.Backbuffer.shminfoshminfo;
bool (field) bool app.Backbuffer.usingShmusingShm;
int (field) int app.Backbuffer.widthwidth, (field) int app.Backbuffer.heightheight;
}
/// (Re)allocate the backbuffer at `w`x`h`. Tries MIT-SHM when `wantShm`;
/// falls back to a plain client-side XImage (and reports it) on any failure.
/// `logSteps` makes the first allocation emit one `step` line per API call.
(struct) app.BackbufferBackbuffer app.createBackbuffer(Re)allocate the backbuffer at wxh. Tries MIT-SHM when wantShm;
falls back to a plain client-side XImage (and reports it) on any failure.
logSteps makes the first allocation emit one step line per API call.
createBackbuffer(Display* (parameter) Display* dpydpy, Visual* (parameter) Visual* visualvisual, int (parameter) int depthdepth,
int (parameter) int ww, int (parameter) int hh, bool wantShm, bool logSteps) @nogc nothrow
{
(unresolved type) BackbufferBackbuffer _error_ bb;
b.b.widthwidth = w;
b.b.heightheight = h;
if (wantShm)
{
b.b.ximgximg = XShmCreateImage(dpy, visual, cast(uint) depth, ZPixmap,
null, &b.b.shminfoshminfo, cast(uint) w, cast(uint) h);
if (logSteps && b.b.ximgximg !is null)
emitf("step", "name=XShmCreateImage size=%dx%d bpl=%d bpp=%d",
w, h, b.b.ximgximg.bytes_per_line, b.b.ximgximg.bits_per_pixel);
if (b.b.ximgximg !is null)
{
const _error_ nbytesnbytes = cast(size_t)(b.b.ximgximg.bytes_per_line * b.b.ximgximg.b.ximg.heightheight);
b.b.shminfoshminfo.shmid = shmget(IPC_PRIVATE, nbytes, IPC_CREAT | 0x180 /* 0600 */ );
if (logSteps)
emitf("step", "name=shmget shmid=%d bytes=%zu", b.b.shminfoshminfo.shmid, nbytes);
if (b.b.shminfoshminfo.shmid >= 0)
{
b.b.shminfoshminfo.shmaddr = cast(char*) shmat(b.b.shminfoshminfo.shmid, null, 0);
if (logSteps)
emitf("step", "name=shmat ok=%d", cast(int)(b.b.shminfoshminfo.shmaddr !is cast(char*)-1));
if (b.b.shminfoshminfo.shmaddr !is cast(char*)-1)
{
b.b.ximgximg.b.ximg.datadata = b.b.shminfoshminfo.shmaddr;
b.b.shminfoshminfo.readOnly = False;
XShmAttach(dpy, &b.b.shminfoshminfo); // async request: server maps the segment
XSync(dpy, False); // round-trip so the server has attached ...
shmctl(b.b.shminfoshminfo.shmid, IPC_RMID, null); // ... before we mark-for-delete
if (logSteps)
{
emit("step name=XShmAttach");
emit("step name=XSync");
emit("step name=shmctl_IPC_RMID");
}
b.b.usingShmusingShm = true;
return b;
}
shmctl(b.b.shminfoshminfo.shmid, IPC_RMID, null);
}
b.b.ximgximg.f.destroy_image(b.b.ximgximg); // XDestroyImage is a macro; call its body
b.b.ximgximg = null;
}
emitf("step", "name=shm_fallback reason=%s",
b.b.ximgximg is null ? "alloc_failed".ptr : "attach_failed".ptr);
}
// Plain path: client-side buffer, pixels travel through the X byte stream.
import core.stdc.stdlib : malloc;
auto _error_ datadata = cast(char*) malloc(cast(size_t) w * h * 4);
b.b.ximgximg = XCreateImage(dpy, visual, cast(uint) depth, ZPixmap, 0, data,
cast(uint) w, cast(uint) h, 32, 0);
if (logSteps)
emitf("step", "name=XCreateImage size=%dx%d bpl=%d bpp=%d",
w, h, b.b.ximgximg.bytes_per_line, b.b.ximgximg.bits_per_pixel);
b.b.usingShmusingShm = false;
return b;
}
/// Tear down in the order the MIT-SHM spec prescribes:
/// XShmDetach -> XDestroyImage -> shmdt (the segment id was already
/// IPC_RMID-marked at creation, so the kernel frees it once both detach).
void app.destroyBackbufferTear down in the order the MIT-SHM spec prescribes:
XShmDetach -> XDestroyImage -> shmdt (the segment id was already
IPC_RMID-marked at creation, so the kernel frees it once both detach).
destroyBackbuffer(Display* (parameter) Display* dpydpy, ref (struct) app.BackbufferBackbuffer (parameter) Backbuffer bb) @nogc nothrow
{
if (b.b.ximgximg is null)
return;
if (b.b.usingShmusingShm)
{
XSync(dpy, False); // let any in-flight XShmPutImage finish reading
XShmDetach(dpy, &b.b.shminfoshminfo);
}
b.b.ximgximg.f.destroy_image(b.b.ximgximg); // shm variant frees only the struct; plain variant frees data too
if (b.b.usingShmusingShm)
shmdt(b.b.shminfoshminfo.shmaddr);
b.b.ximgximg = null;
}
/// Corner-anchored diagonal gradient so stretching / stale buffers during the
/// resize storm are visible (F02 requirement 1). `phase` animates the blue
/// channel so successive frames are distinguishable.
void app.drawGradientCorner-anchored diagonal gradient so stretching / stale buffers during the
resize storm are visible (F02 requirement 1). phase animates the blue
channel so successive frames are distinguishable.
drawGradient(XImage* (parameter) XImage* ximgximg, int (parameter) int phasephase) @nogc nothrow
{
const _error_ ww = ximg.ximg.widthwidth, _error_ hh = ximg.ximg.heightheight;
if (ximg.bits_per_pixel == 32)
{
foreach ((parameter) yy; 0 .. h)
{
auto _error_ rowrow = cast(uint*)(ximg.ximg.datadata + y * ximg.bytes_per_line);
const _error_ gg = cast(uint)(h > 1 ? (y * 255) / (h - 1) : 0);
foreach ((parameter) xx; 0 .. w)
{
const _error_ rr = cast(uint)(w > 1 ? (x * 255) / (w - 1) : 0);
const _error_ blbl = cast(uint)((x + y + phase) & 0xff);
row[x] = (r << 16) | (g << 8) | bl;
}
}
}
else // unexpected visual: visible flat fill rather than garbage
{
import core.stdc.string : memset;
memset(ximg.ximg.datadata, 0x55, cast(size_t)(ximg.bytes_per_line * h));
}
}
int int D main()main()
{
void instrument.initInstrument(const(char)* demoName) nothrow @nogcNames the demo, starts the monotonic clock, and emits init_start.
Call this first, before any platform API call.
initInstrument("x11_scaffold");
const (local variable) const(char*) envAutoenvAuto = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv("WSI_AUTO_EXIT");
const (local variable) const(bool) autoExitautoExit = (local variable) const(char*) envAutoenvAuto !is null && (local variable) const(char*) envAutoenvAuto[0] == '1';
// -- Connect ------------------------------------------------------------
Display* (local variable) _error_ dpydpy = XOpenDisplay(null);
if (dpy is null)
{
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("SKIP: no X11 display (XOpenDisplay returned null)\n");
return 0;
}
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("step", "name=XOpenDisplay fd=%d", XConnectionNumber(dpy));
const (local variable) const(_error_) screenscreen = XDefaultScreen(dpy);
const (local variable) const(_error_) rootroot = XRootWindow(dpy, screen);
Visual* (local variable) _error_ visualvisual = XDefaultVisual(dpy, screen);
const (local variable) const(_error_) depthdepth = XDefaultDepth(dpy, screen);
// -- MIT-SHM availability (this query is a round-trip) -------------------
// WSI_NO_SHM=1 forces the plain-XPutImage path (what a TCP/remote display
// would get: MIT-SHM only works when client and server share a kernel).
bool (local variable) bool haveShmhaveShm = XShmQueryExtension(dpy) != 0;
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("step", "name=XShmQueryExtension available=%d", cast(int) (local variable) bool haveShmhaveShm);
int (local variable) int shmCompletionTypeshmCompletionType = -1;
if ((local variable) bool haveShmhaveShm && char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv("WSI_NO_SHM") !is null)
{
(local variable) bool haveShmhaveShm = false;
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("step name=shm_fallback reason=WSI_NO_SHM");
}
else if ((local variable) bool haveShmhaveShm)
{
(local variable) int shmCompletionTypeshmCompletionType = XShmGetEventBase(dpy) + ShmCompletion;
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("step", "name=XShmGetEventBase completion_event=%d", (local variable) int shmCompletionTypeshmCompletionType);
}
else
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("step name=shm_fallback reason=extension_absent");
// -- Window + WM close handshake -----------------------------------------
int (local variable) int widthwidth = 480, (local variable) int heightheight = 320;
Window (local variable) _error_ winwin = XCreateSimpleWindow(dpy, root, 0, 0, width, height, 1,
XBlackPixel(dpy, screen), XWhitePixel(dpy, screen));
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("step", "name=XCreateSimpleWindow xid=0x%lx", win);
XStoreName(dpy, win, "Sparkles · X11 scaffold");
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("step name=XStoreName");
Atom (local variable) _error_ wmDeletewmDelete = XInternAtom(dpy, "WM_DELETE_WINDOW", False); // round-trip
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("step", "name=XInternAtom atom=%lu", wmDelete);
XSetWMProtocols(dpy, win, &wmDelete, 1);
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("step name=XSetWMProtocols");
XSelectInput(dpy, win, ExposureMask | KeyPressMask | StructureNotifyMask);
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("step name=XSelectInput");
XMapWindow(dpy, win);
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("step name=XMapWindow");
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("window_created", "xid=0x%lx size=%dx%d", win, (local variable) int widthwidth, (local variable) int heightheight);
GC (local variable) _error_ gcgc = XDefaultGC(dpy, screen);
// -- Backbuffer -----------------------------------------------------------
auto (local variable) _error_ bufbuf = createBackbuffer(dpy, visual, depth, width, height, haveShm, true);
scope (exit)
{
destroyBackbuffer(dpy, buf);
XDestroyWindow(dpy, win);
XCloseDisplay(dpy);
}
// -- Event loop: poll(2) the connection fd, drain with XPending -----------
// The resize storm the F02 spec asks for: 10 XResizeWindow calls of
// varying sizes, one every ~10 frames, so event-loop turns interleave.
static immutable int[2][10] (immutable global) immutable(int[2][10]) app.main.stormstorm = [
[640, 400], [320, 240], [800, 520], [400, 300], [720, 480],
[360, 260], [900, 560], [480, 300], [560, 380], [640, 420],
];
const (local variable) const(_error_) fdfd = XConnectionNumber(dpy);
bool (local variable) bool runningrunning = true, (local variable) bool needsRedrawneedsRedraw = false, (local variable) bool awaitingCompletionawaitingCompletion = false;
bool (local variable) bool sawFirstConfiguresawFirstConfigure = false, (local variable) bool sawFirstPixelsawFirstPixel = false;
int (local variable) int framesframes = 0, (local variable) int phasephase = 0, (local variable) int resizesIssuedresizesIssued = 0;
while ((local variable) bool runningrunning)
{
// Drain everything already queued / readable. XPending flushes the
// output buffer as a side effect, so buffered requests always reach
// the server before we sleep in poll().
while (XPending(dpy) > 0)
{
XEvent (local variable) _error_ evev;
XNextEvent(dpy, &ev); // does not block: an event is pending
switch (ev.type)
{
case MapNotify:
if (!sawFirstConfigure)
{
sawFirstConfigure = true;
emitf("first_configure", "kind=MapNotify size=%dx%d", width, height);
}
break;
case ConfigureNotify:
const _error_ cwcw = ev.xconfigure.ev.xconfigure.widthwidth, _error_ chch = ev.xconfigure.ev.xconfigure.heightheight;
if (!sawFirstConfigure)
{
sawFirstConfigure = true;
emitf("first_configure", "kind=ConfigureNotify size=%dx%d", cw, ch);
}
if (cw != width || ch != height)
{
width = cw;
height = ch;
emitf("resize", "size=%dx%d scale=1", width, height);
destroyBackbuffer(dpy, buf);
buf = createBackbuffer(dpy, visual, depth, width, height, haveShm, false);
emitf("buffer_realloc", "size=%dx%d shm=%d bytes=%d", width, height,
cast(int) buf.buf.usingShmusingShm, buf.buf.ximgximg.bytes_per_line * buf.buf.ximgximg.buf.ximg.heightheight);
needsRedraw = true;
}
break;
case Expose:
emitf("expose", "count=%d area=%dx%d+%d+%d", ev.xexpose.count,
ev.xexpose.ev.xexpose.widthwidth, ev.xexpose.ev.xexpose.heightheight, ev.xexpose.ev.xexpose.xx, ev.xexpose.ev.xexpose.yy);
if (ev.xexpose.count == 0) // last Expose of the batch
needsRedraw = true;
break;
case ClientMessage:
if (cast(Atom) ev.xclient.ev.xclient.datadata.l[0] == wmDelete)
{
emit("close_requested via=WM_DELETE_WINDOW");
running = false;
}
break;
case KeyPress:
emit("close_requested via=KeyPress");
running = false;
break;
default:
if (buf.buf.usingShmusingShm && ev.type == shmCompletionType)
{
awaitingCompletion = false;
if (!sawFirstPixel)
{
sawFirstPixel = true;
const _error_ shsh = cast(const XShmCompletionEvent*)&ev;
emitf("first_pixel_presented",
"method=XShmCompletionEvent drawable=0x%lx", sh.drawable);
}
}
break;
}
}
// Auto-exit drive: animate continuously and feed the resize storm.
if ((local variable) const(bool) autoExitautoExit && (local variable) bool sawFirstPixelsawFirstPixel && !(local variable) bool awaitingCompletionawaitingCompletion)
{
if ((local variable) int resizesIssuedresizesIssued < (immutable global) immutable(int[2][10]) app.main.stormstorm.(constant) ulong immutable(int[2][10]).length = 10LUlength && (local variable) int framesframes >= ((local variable) int resizesIssuedresizesIssued + 1) * 10)
{
const (local variable) immutable(int[2]) ss = (immutable global) immutable(int[2][10]) app.main.stormstorm[(local variable) int resizesIssuedresizesIssued];
XResizeWindow(dpy, win, cast(uint) s[0], cast(uint) s[1]);
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("step", "name=XResizeWindow n=%d size=%dx%d",
(local variable) int resizesIssuedresizesIssued + 1, (local variable) immutable(int[2]) ss[0], (local variable) immutable(int[2]) ss[1]);
++(local variable) int resizesIssuedresizesIssued;
}
++(local variable) int phasephase;
(local variable) bool needsRedrawneedsRedraw = true;
}
// Present a frame. With MIT-SHM the segment must not be rewritten
// until the server is done reading it, so we request a completion
// event (send_event=True) and gate the next draw on it.
if ((local variable) bool needsRedrawneedsRedraw && !(local variable) bool awaitingCompletionawaitingCompletion && buf.(field) _error_ buf.ximgximg !is null)
{
drawGradient(buf.buf.ximgximg, phase);
if (buf.(field) _error_ buf.usingShmusingShm)
{
XShmPutImage(dpy, win, gc, buf.buf.ximgximg, 0, 0, 0, 0,
cast(uint) buf.buf.widthwidth, cast(uint) buf.buf.heightheight, True);
XFlush(dpy); // push the put before sleeping in poll()
(local variable) bool awaitingCompletionawaitingCompletion = true;
}
else
{
XPutImage(dpy, win, gc, buf.buf.ximgximg, 0, 0, 0, 0,
cast(uint) buf.buf.widthwidth, cast(uint) buf.buf.heightheight);
XSync(dpy, False); // no completion contract: round-trip instead
if (!(local variable) bool sawFirstPixelsawFirstPixel)
{
(local variable) bool sawFirstPixelsawFirstPixel = true;
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("first_pixel_presented method=XPutImage_XSync");
}
}
++(local variable) int framesframes;
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("frame_callback", "t=%lld frame=%d size=%dx%d",
long instrument.nowUs() nothrow @nogcMicroseconds elapsed since initInstrument.
nowUs(), (local variable) int framesframes, buf.(field) _error_ buf.widthwidth, buf.(field) _error_ buf.heightheight);
(local variable) bool needsRedrawneedsRedraw = false;
}
if ((local variable) const(bool) autoExitautoExit && ((local variable) int framesframes >= 120 || long instrument.nowUs() nothrow @nogcMicroseconds elapsed since initInstrument.
nowUs() > 2_000_000))
{
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("auto_exit", "frames=%d resizes=%d", (local variable) int framesframes, (local variable) int resizesIssuedresizesIssued);
break;
}
// Readiness wait on the X connection fd — the loop a real app would
// multiplex with its own timers/sockets. Auto mode ticks at ~5 ms so
// animation frames keep flowing even when the queue is silent.
pollfd (local variable) _error_ pfdpfd;
pfd.pfd.fdfd = fd;
pfd.events = POLLIN;
pfd.revents = 0;
poll(&pfd, 1, autoExit ? 5 : -1);
}
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("teardown");
return 0;
}