// F01 — first pixel & init cost (Wayland). Implements
// ../../../features/f01-first-pixel.md on top of the proven xdg-shell
// scaffold (../scaffold/app.d, findings in ../../scaffold.md):
//
// wl_display → wl_registry → bind {wl_compositor, wl_shm, xdg_wm_base}
// → wl_surface → xdg_surface → xdg_toplevel → initial *no-buffer* commit
// → xdg_surface.configure → ack_configure → wl_shm ARGB gradient
// → wl_surface.attach/damage_buffer/commit → first wl_surface.frame
// callback = first pixel CONFIRMED.
//
// What F01 adds over the scaffold:
// - one `step name=<api-call>` instrumentation event per init API call,
// with `rt=1` on the two calls that block on the compositor
// (wl_display_roundtrip, and the wl_display_dispatch that waits for the
// first configure after the no-buffer commit);
// - a `concept n=<i> name=<object-type>` event at the first touch of each
// distinct protocol object type, and a `summary concepts=N loc=N` line;
// - LOC is computed at compile time from this very file (string import),
// so the number can never go stale; instrument.d and the c.c shim are
// excluded per the spec;
// - the demo presents exactly one confirmed frame, holds a few frame
// callbacks to prove the loop is live, prints the F01 checklist to
// stdout, and exits 0 (Tier-A runnable; no resize storm — that is F02).
//
// "Presented" means what Wayland can actually confirm: the first `done`
// event on a wl_surface.frame callback requested with the first post-ack
// buffer commit. Headless-safe: no compositor → SKIP, exit 0.
module (module) appapp;
import c; // ImportC: <wayland-client.h> + xdg-shell glue + wsi_* wrappers
import instrument;
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.stringD header file for C99.
pubs.opengroup.org/onlinepubs/009695399/basedefs/string.h.html, string.h
Source
core/stdc/string.d
string : (alias) app.strcmp = int core.stdc.string.strcmp(scope const(char*) s1, scope const(char*) s2) pure nothrow @nogcstrcmp;
// ----------------------------------------------------------------- tunables
enum int (constant) int app.defaultWidth = 640defaultWidth = 640; // used while the compositor lets us pick (0x0 configure)
enum int (constant) int app.defaultHeight = 480defaultHeight = 480;
enum int (constant) int app.holdFrames = 10holdFrames = 10; // frame callbacks to observe after first_pixel_presented
enum long (constant) long app.hardStopUs = 5000000LhardStopUs = 5_000_000; // wall-clock backstop if the frame clock stalls
// ----------------------------------------------- compile-time LOC (F01 §5)
private struct (struct) app.LocLoc
{
int (field) int app.Loc.totaltotal;
int (field) int app.Loc.codecode; // non-blank, non-`//` lines
}
private Loc Loc countLoc()(string src)countLoc()((unresolved type) stringstring (parameter) string srcsrc)
{
(unresolved type) LocLoc _error_ rr;
(unresolved type) size_tsize_t _error_ ii = 0;
while (i < src.length)
{
(unresolved type) size_tsize_t _error_ jj = i;
while (j < src.length && src[j] != '\n')
++j;
auto _error_ lineline = src[i .. j];
(unresolved type) size_tsize_t _error_ kk = 0;
while (k < line.length && (line[k] == ' ' || line[k] == '\t'))
++k;
line = line[k .. $];
r.r.totaltotal++;
if (line.length > 0 && !(line.length >= 2 && line[0] == '/' && line[1] == '/'))
r.r.codecode++;
i = j + 1;
}
return r;
}
/// LOC of this demo, excluding `instrument.d` and the `c.c` shim (the spec's
/// definition). CTFE over the string import keeps it accurate by construction.
enum (struct) app.LocLoc (constant) _error_ app.demoLoc = __error__LOC of this demo, excluding instrument.d and the c.c shim (the spec's
definition). CTFE over the string import keeps it accurate by construction.
demoLoc = (template function) app.countLoc()(string src)countLoc(import("app.d"));
// -------------------------------------------------------------------- state
/// One wl_shm-backed ARGB8888 buffer. `busy` is owned by the compositor
/// between wl_surface.commit and the wl_buffer.release event.
struct (struct) app.BufferOne wl_shm-backed ARGB8888 buffer. busy is owned by the compositor
between wl_surface.commit and the wl_buffer.release event.
Buffer
{
wl_buffer* (field) _error_ app.Buffer.handlehandle;
uint* (field) uint* app.Buffer.pixelspixels; // mmap'ed, shared with the compositor
(alias) object.size_t = ulongsize_t (field) ulong app.Buffer.byteSizebyteSize;
int (field) int app.Buffer.widthwidth, (field) int app.Buffer.heightheight;
bool (field) bool app.Buffer.busybusy;
}
__gshared
{
wl_display* _error_ app.g_displayg_display;
wl_registry* _error_ app.g_registryg_registry;
wl_compositor* _error_ app.g_compositorg_compositor;
wl_shm* _error_ app.g_shmg_shm;
wl_seat* _error_ app.g_seatg_seat;
xdg_wm_base* _error_ app.g_wmBaseg_wmBase;
wl_surface* _error_ app.g_surfaceg_surface;
xdg_surface* _error_ app.g_xdgSurfaceg_xdgSurface;
xdg_toplevel* _error_ app.g_toplevelg_toplevel;
wl_callback* _error_ app.g_frameCbg_frameCb; // at most one outstanding frame callback
(struct) app.BufferOne wl_shm-backed ARGB8888 buffer. busy is owned by the compositor
between wl_surface.commit and the wl_buffer.release event.
Buffer[2] _error_ app.g_buffersg_buffers; // double buffering: paint one while the other is on screen
int (__gshared global) int app.g_widthg_width = defaultWidth; // last *acked* size — buffers must match it
int (__gshared global) int app.g_heightg_height = defaultHeight;
int (__gshared global) int app.g_pendingWidthg_pendingWidth; // from xdg_toplevel.configure (0 = client's choice)
int (__gshared global) int app.g_pendingHeightg_pendingHeight;
bool (__gshared global) bool app.g_configuredg_configured; // first configure acked (may attach buffers from now on)
bool (__gshared global) bool app.g_presentedg_presented; // first frame callback after the first buffer commit
bool (__gshared global) bool app.g_runningg_running = true;
int (__gshared global) int app.g_framesg_frames; // frame callbacks received
int (__gshared global) int app.g_commitsg_commits; // buffer commits sent
// F01 counters, all frozen at first_pixel_presented:
int (__gshared global) int app.g_conceptsg_concepts; // distinct protocol object types touched
int (__gshared global) int app.g_initStepsg_initSteps; // `step` events emitted
int (__gshared global) int app.g_roundTripsg_roundTrips; // steps that blocked on the compositor (rt=1)
long (__gshared global) long app.g_firstPixelUsg_firstPixelUs = -1; // init_start → first_pixel_presented
}
// ------------------------------------------------------ F01 instrumentation
// The counters freeze at first_pixel_presented: later events still log, but
// only pre-presentation work counts as "init" (F01 measures init cost only).
/// First touch of a distinct protocol object type (F01 §4 "concepts").
void void app.logConcept(const(char)* name) nothrow @nogcFirst touch of a distinct protocol object type (F01 §4 "concepts").
logConcept(const(char)* (parameter) const(char)* namename) nothrow @nogc
{
if ((__gshared global) bool app.g_presentedg_presented)
return;
(__gshared global) int app.g_conceptsg_concepts++;
instrEvent("concept", "n=%d name=%s", g_concepts, name);
}
/// An init API call that completed locally (no compositor wait).
void void app.step(const(char)* api) nothrow @nogcAn init API call that completed locally (no compositor wait).
step(const(char)* (parameter) const(char)* apiapi) nothrow @nogc
{
if (!(__gshared global) bool app.g_presentedg_presented)
(__gshared global) int app.g_initStepsg_initSteps++;
instrStep(api);
}
/// An init API call that *blocked* on the compositor (F01: round-trip).
void void app.stepRoundTrip(const(char)* api) nothrow @nogcAn init API call that blocked on the compositor (F01: round-trip).
stepRoundTrip(const(char)* (parameter) const(char)* apiapi) nothrow @nogc
{
if (!(__gshared global) bool app.g_presentedg_presented)
{
(__gshared global) int app.g_initStepsg_initSteps++;
(__gshared global) int app.g_roundTripsg_roundTrips++;
}
instrEvent("step", "name=%s rt=1", api);
}
// ---------------------------------------------------------- shm buffer pool
/// (Re)allocate `b` so it matches `w`×`h`. Only ever called on non-busy
/// buffers, so destroying a stale-sized wl_buffer here is race-free.
bool app.ensureBuffer(Re)allocate b so it matches w×h. Only ever called on non-busy
buffers, so destroying a stale-sized wl_buffer here is race-free.
ensureBuffer(ref (struct) app.BufferOne wl_shm-backed ARGB8888 buffer. busy is owned by the compositor
between wl_surface.commit and the wl_buffer.release event.
Buffer (parameter) Buffer bb, int (parameter) int ww, int (parameter) int hh) nothrow @nogc
{
if (b.b.handlehandle !is null && (b.b.widthwidth != w || b.b.heightheight != h))
{
wsi_buffer_destroy(b.b.handlehandle);
munmap(b.b.pixelspixels, b.b.byteSizebyteSize);
b = Buffer.init;
}
if (b.b.handlehandle !is null)
return true;
immutable _error_ stridestride = w * 4;
immutable _error_ sizesize = cast((unresolved type) size_tsize_t)(stride) * h;
immutable _error_ fdfd = memfd_create("wsi-f01", MFD_CLOEXEC);
if (fd < 0)
return false;
if (ftruncate(fd, cast(long) size) != 0)
{
close(fd);
return false;
}
void* _error_ memmem = mmap(null, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mem is cast(void*)-1)
{
close(fd);
return false;
}
step("memfd_create+mmap");
// The pool can be destroyed right after create_buffer: the buffer keeps
// the backing memory alive, and the compositor has dup'ed the fd.
wl_shm_pool* _error_ poolpool = wsi_shm_create_pool(g_shm, fd, cast(int) size);
if (g_commits == 0)
logConcept("wl_shm_pool");
step("wl_shm_create_pool");
b.b.handlehandle = wsi_shm_pool_create_buffer(pool, 0, w, h, stride, WL_SHM_FORMAT_ARGB8888);
if (g_commits == 0)
logConcept("wl_buffer");
step("wl_shm_pool_create_buffer");
wsi_shm_pool_destroy(pool);
close(fd);
wsi_buffer_add_listener(b.b.handlehandle, &g_bufferListener, &b);
b.b.pixelspixels = cast(uint*) mem;
b.b.byteSizebyteSize = size;
b.b.widthwidth = w;
b.b.heightheight = h;
b.b.busybusy = false;
instrEvent("buffer_alloc", "size=%dx%d bytes=%zu", w, h, size);
return true;
}
/// Corner-anchored diagonal gradient with a frame-driven blue channel so each
/// redraw is observable in a screen capture.
void app.paintCorner-anchored diagonal gradient with a frame-driven blue channel so each
redraw is observable in a screen capture.
paint(ref (struct) app.BufferOne wl_shm-backed ARGB8888 buffer. busy is owned by the compositor
between wl_surface.commit and the wl_buffer.release event.
Buffer (parameter) Buffer bb, int frame) nothrow @nogc
{
immutable _error_ maxXmaxX = b.b.widthwidth > 1 ? b.b.widthwidth - 1 : 1;
immutable _error_ maxYmaxY = b.b.heightheight > 1 ? b.b.heightheight - 1 : 1;
immutable uint _error_ blueblue = (frame * 2) & 0xff;
foreach ((parameter) yy; 0 .. b.b.heightheight)
{
uint* _error_ rowrow = b.b.pixelspixels + cast((unresolved type) size_tsize_t) y * b.b.widthwidth;
immutable uint _error_ gg = cast(uint)(y * 255 / maxY) << 8;
foreach ((parameter) xx; 0 .. b.b.widthwidth)
row[x] = 0xff00_0000 | (cast(uint)(x * 255 / maxX) << 16) | g | blue;
}
}
/// Paint into a free buffer and commit it, requesting the next frame callback.
void void app.render() nothrow @nogcPaint into a free buffer and commit it, requesting the next frame callback.
render() nothrow @nogc
{
(struct) app.BufferOne wl_shm-backed ARGB8888 buffer. busy is owned by the compositor
between wl_surface.commit and the wl_buffer.release event.
Buffer* (local variable) _error_ bufbuf = null;
foreach (ref (parameter) bb; g_buffers)
if (!b.b.busybusy)
{
buf = &b;
break;
}
if (buf is null)
{
// Both buffers held by the compositor: drop this frame. The
// wl_buffer.release handler re-renders as soon as one comes back.
instrEvent("frame_skipped", "reason=all_buffers_busy");
return;
}
if (!ensureBuffer(*buf, g_width, g_height))
{
(__gshared global) bool app.g_runningg_running = false;
return;
}
paint(*buf, g_frames);
assert(buf.(field) _error_ buf.widthwidth == (__gshared global) int app.g_widthg_width && buf.(field) _error_ buf.heightheight == (__gshared global) int app.g_heightg_height,
"committed buffer size does not match the acked configure size");
immutable (local variable) immutable(bool) firstCommitfirstCommit = (__gshared global) int app.g_commitsg_commits == 0;
wsi_surface_attach(g_surface, buf.buf.handlehandle, 0, 0);
if ((local variable) immutable(bool) firstCommitfirstCommit)
void app.step(const(char)* api) nothrow @nogcAn init API call that completed locally (no compositor wait).
step("wl_surface_attach");
wsi_surface_damage_buffer(g_surface, 0, 0, buf.buf.widthwidth, buf.buf.heightheight);
if ((local variable) immutable(bool) firstCommitfirstCommit)
void app.step(const(char)* api) nothrow @nogcAn init API call that completed locally (no compositor wait).
step("wl_surface_damage_buffer");
if (g_frameCb is null) // keep exactly one frame callback in flight
{
g_frameCb = wsi_surface_frame(g_surface);
if ((local variable) immutable(bool) firstCommitfirstCommit)
void app.step(const(char)* api) nothrow @nogcAn init API call that completed locally (no compositor wait).
step("wl_surface_frame");
wsi_callback_add_listener(g_frameCb, &g_frameListener, null);
}
wsi_surface_commit(g_surface);
if ((local variable) immutable(bool) firstCommitfirstCommit)
void app.step(const(char)* api) nothrow @nogcAn init API call that completed locally (no compositor wait).
step("wl_surface_commit(buffer)");
buf.buf.busybusy = true;
(__gshared global) int app.g_commitsg_commits++;
if ((local variable) immutable(bool) firstCommitfirstCommit)
instrEvent("first_commit", "size=%dx%d", buf.buf.widthwidth, buf.buf.heightheight);
}
// ---------------------------------------------------------------- listeners
// All callbacks are `extern (C) nothrow @nogc` to match the listener
// function-pointer types the ImportC `#pragma attribute` stamped in c.c.
extern (C) void app.onGlobalonGlobal(void* data, wl_registry* reg, uint (parameter) uint namename,
const(char)* iface, uint ver) nothrow @nogc
{
static uint uint capped(uint advertised, uint want) nothrow @nogccapped(uint (parameter) uint advertisedadvertised, uint (parameter) uint wantwant) nothrow @nogc
{
return advertised < want ? advertised : want;
}
if (strcmp(iface, wl_compositor_interface.wl_compositor_interface.namename) == 0)
g_compositor = cast(wl_compositor*) wsi_registry_bind(reg, name,
&wl_compositor_interface, capped(ver, 4)); // v4: wl_surface.damage_buffer
else if (strcmp(iface, wl_shm_interface.wl_shm_interface.namename) == 0)
g_shm = cast(wl_shm*) wsi_registry_bind(reg, name, &wl_shm_interface, 1);
else if (strcmp(iface, xdg_wm_base_interface.xdg_wm_base_interface.namename) == 0)
g_wmBase = cast(xdg_wm_base*) wsi_registry_bind(reg, name, &xdg_wm_base_interface, 1);
else if (strcmp(iface, wl_seat_interface.wl_seat_interface.namename) == 0)
g_seat = cast(wl_seat*) wsi_registry_bind(reg, name, &wl_seat_interface,
capped(ver, 2)); // v2: name event (headless weston has no seat at all)
else
return;
logConcept(iface);
instrEvent("step", "name=wl_registry_bind iface=%s version=%u", iface, ver);
g_initSteps++;
}
extern (C) void app.onGlobalRemoveonGlobalRemove(void* data, wl_registry* reg, uint (parameter) uint namename) nothrow @nogc
{
}
extern (C) void app.onWmBasePingonWmBasePing(void* data, xdg_wm_base* (parameter) xdg_wm_base* bb, uint serial) nothrow @nogc
{
wsi_wm_base_pong(b, serial); // liveness check — answer or be killed
instrEvent("ping_pong", "serial=%u", serial);
}
extern (C) void app.onSeatCapabilitiesonSeatCapabilities(void* data, wl_seat* s, uint caps) nothrow @nogc
{
instrEvent("seat_capabilities", "caps=0x%x", caps);
}
extern (C) void app.onSeatNameonSeatName(void* data, wl_seat* s, const(char)* (parameter) const(char)* namename) nothrow @nogc
{
instrEvent("seat_name", "name=%s", name);
}
/// xdg_toplevel.configure: the compositor *suggests* a size (0×0 = "you
/// pick"). Only latched here; it takes effect at xdg_surface.configure.
extern (C) void app.onToplevelConfigurexdg_toplevel.configure: the compositor suggests a size (0×0 = "you
pick"). Only latched here; it takes effect at xdg_surface.configure.
onToplevelConfigure(void* data, xdg_toplevel* t, int (parameter) int ww, int (parameter) int hh,
wl_array* states) nothrow @nogc
{
g_pendingWidth = w;
g_pendingHeight = h;
instrEvent("xdg_toplevel_configure", "size=%dx%d", w, h);
}
/// xdg_surface.configure: the atomic "apply everything" event. ack_configure
/// (with the event's serial) goes out *before* the buffer commit.
extern (C) void app.onXdgSurfaceConfigurexdg_surface.configure: the atomic "apply everything" event. ack_configure
(with the event's serial) goes out before the buffer commit.
onXdgSurfaceConfigure(void* data, xdg_surface* s, uint serial) nothrow @nogc
{
immutable _error_ ww = g_pendingWidth > 0 ? g_pendingWidth : defaultWidth;
immutable _error_ hh = g_pendingHeight > 0 ? g_pendingHeight : defaultHeight;
instrEvent("configure", "serial=%u size=%dx%d", serial, w, h);
wsi_xdg_surface_ack_configure(s, serial);
step("xdg_surface_ack_configure");
immutable _error_ resizedresized = w != g_width || h != g_height;
g_width = w;
g_height = h;
if (!g_configured)
{
g_configured = true;
instrFirstConfigure();
render(); // the first buffer commit — the window becomes visible
}
else if (resized)
{
instrResize(w, h, 1);
render(); // commit a matching buffer right away, don't wait a frame
}
}
extern (C) void app.onToplevelCloseonToplevelClose(void* data, xdg_toplevel* t) nothrow @nogc
{
instrCloseRequested();
g_running = false;
}
// Bound at xdg_wm_base v1, so these v4/v5 events never arrive; the listener
// struct generated from wayland-protocols 1.47 still has the slots to fill.
extern (C) void app.onToplevelConfigureBoundsonToplevelConfigureBounds(void* data, xdg_toplevel* t, int (parameter) int ww, int (parameter) int hh) nothrow @nogc
{
}
extern (C) void app.onToplevelWmCapabilitiesonToplevelWmCapabilities(void* data, xdg_toplevel* t, wl_array* caps) nothrow @nogc
{
}
/// wl_buffer.release: the compositor no longer reads the buffer; reuse it.
extern (C) void app.onBufferReleasewl_buffer.release: the compositor no longer reads the buffer; reuse it.
onBufferRelease(void* data, wl_buffer* (parameter) wl_buffer* bb) nothrow @nogc
{
auto _error_ bufbuf = cast((unresolved type) BufferBuffer*) data;
buf.buf.busybusy = false;
if (g_running && g_configured && g_frameCb is null)
render();
}
/// wl_surface.frame `done`: the compositor presented the surface — this is
/// the only confirmation Wayland gives that the pixels reached the screen.
extern (C) void app.onFrameDonewl_surface.frame done: the compositor presented the surface — this is
the only confirmation Wayland gives that the pixels reached the screen.
onFrameDone(void* data, wl_callback* cb, uint timeMs) nothrow @nogc
{
wsi_callback_destroy(cb); // one-shot object
g_frameCb = null;
g_frames++;
instrFrameCallback(timeMs);
if (!g_presented)
{
g_presented = true;
g_firstPixelUs = instrNowUs();
// First pixel confirmed: a post-ack buffer commit has been presented.
instrFirstPixelPresented();
instrEvent("summary",
"concepts=%d loc=%d loc_code=%d steps=%d roundtrips=%d first_pixel_us=%lld",
g_concepts, demoLoc.demoLoc.totaltotal, demoLoc.demoLoc.codecode, g_initSteps, g_roundTrips, g_firstPixelUs);
}
// Hold a few more frames to prove the redraw loop is live, then exit
// (F01 §1: exit cleanly shortly after presentation is confirmed).
if (g_frames >= 1 + holdFrames)
{
g_running = false;
return;
}
render();
}
__gshared wl_registry_listener _error_ app.g_registryListenerg_registryListener = {&onGlobal, &onGlobalRemove};
__gshared xdg_wm_base_listener _error_ app.g_wmBaseListenerg_wmBaseListener = {&onWmBasePing};
__gshared wl_seat_listener _error_ app.g_seatListenerg_seatListener = {&onSeatCapabilities, &onSeatName};
__gshared xdg_surface_listener _error_ app.g_xdgSurfaceListenerg_xdgSurfaceListener = {&onXdgSurfaceConfigure};
__gshared xdg_toplevel_listener _error_ app.g_toplevelListenerg_toplevelListener = {
&onToplevelConfigure, &onToplevelClose,
&onToplevelConfigureBounds, &onToplevelWmCapabilities
};
__gshared wl_buffer_listener _error_ app.g_bufferListenerg_bufferListener = {&onBufferRelease};
__gshared wl_callback_listener _error_ app.g_frameListenerg_frameListener = {&onFrameDone};
// ----------------------------------------------------------------- teardown
void void app.teardown() nothrow @nogcteardown() nothrow @nogc
{
instrEvent("teardown_start");
foreach (ref (parameter) bb; g_buffers)
if (b.b.handlehandle !is null)
{
wsi_buffer_destroy(b.b.handlehandle);
munmap(b.b.pixelspixels, b.b.byteSizebyteSize);
b = Buffer.init;
}
if (g_frameCb !is null)
wsi_callback_destroy(g_frameCb);
if (g_toplevel !is null)
wsi_toplevel_destroy(g_toplevel);
if (g_xdgSurface !is null)
wsi_xdg_surface_destroy(g_xdgSurface);
if (g_surface !is null)
wsi_surface_destroy(g_surface);
if (g_wmBase !is null)
wsi_wm_base_destroy(g_wmBase);
// The remaining globals have no destructor request at the bound versions;
// destroying the client-side proxy is the correct cleanup.
if (g_seat !is null)
wl_proxy_destroy(cast(wl_proxy*) g_seat);
if (g_shm !is null)
wl_proxy_destroy(cast(wl_proxy*) g_shm);
if (g_compositor !is null)
wl_proxy_destroy(cast(wl_proxy*) g_compositor);
if (g_registry !is null)
wl_proxy_destroy(cast(wl_proxy*) g_registry);
wl_display_disconnect(g_display);
instrEvent("teardown_done");
}
// --------------------------------------------------------------------- main
int int D main()main()
{
instrInit("f01-wayland");
// 1. Connect (SKIP cleanly on headless hosts without a compositor).
g_display = wl_display_connect(null);
if (g_display is null)
{
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("SKIP: no Wayland compositor (wl_display_connect returned null)\n");
return 0;
}
void app.logConcept(const(char)* name) nothrow @nogcFirst touch of a distinct protocol object type (F01 §4 "concepts").
logConcept("wl_display");
void app.step(const(char)* api) nothrow @nogcAn init API call that completed locally (no compositor wait).
step("wl_display_connect");
// 2. Registry: discover and bind the globals a window needs. The
// roundtrip is the first of the two compositor waits (rt=1) — it
// internally creates a wl_callback via wl_display.sync.
g_registry = wsi_display_get_registry(g_display);
void app.logConcept(const(char)* name) nothrow @nogcFirst touch of a distinct protocol object type (F01 §4 "concepts").
logConcept("wl_registry");
void app.step(const(char)* api) nothrow @nogcAn init API call that completed locally (no compositor wait).
step("wl_display_get_registry");
wsi_registry_add_listener(g_registry, &g_registryListener, null);
void app.logConcept(const(char)* name) nothrow @nogcFirst touch of a distinct protocol object type (F01 §4 "concepts").
logConcept("wl_callback"); // first touch: wl_display_roundtrip's internal sync
wl_display_roundtrip(g_display); // blocks; onGlobal binds during this
void app.stepRoundTrip(const(char)* api) nothrow @nogcAn init API call that blocked on the compositor (F01: round-trip).
stepRoundTrip("wl_display_roundtrip");
if (g_compositor is null || g_shm is null || g_wmBase is null)
{
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("SKIP: compositor lacks a required global (wl_compositor/wl_shm/xdg_wm_base)\n");
void app.teardown() nothrow @nogcteardown();
return 0;
}
wsi_wm_base_add_listener(g_wmBase, &g_wmBaseListener, null);
if (g_seat !is null)
wsi_seat_add_listener(g_seat, &g_seatListener, null);
// 3. Build the window object tree: wl_surface → xdg_surface → xdg_toplevel.
g_surface = wsi_compositor_create_surface(g_compositor);
void app.logConcept(const(char)* name) nothrow @nogcFirst touch of a distinct protocol object type (F01 §4 "concepts").
logConcept("wl_surface");
void app.step(const(char)* api) nothrow @nogcAn init API call that completed locally (no compositor wait).
step("wl_compositor_create_surface");
g_xdgSurface = wsi_wm_base_get_xdg_surface(g_wmBase, g_surface);
void app.logConcept(const(char)* name) nothrow @nogcFirst touch of a distinct protocol object type (F01 §4 "concepts").
logConcept("xdg_surface");
void app.step(const(char)* api) nothrow @nogcAn init API call that completed locally (no compositor wait).
step("xdg_wm_base_get_xdg_surface");
wsi_xdg_surface_add_listener(g_xdgSurface, &g_xdgSurfaceListener, null);
g_toplevel = wsi_xdg_surface_get_toplevel(g_xdgSurface);
void app.logConcept(const(char)* name) nothrow @nogcFirst touch of a distinct protocol object type (F01 §4 "concepts").
logConcept("xdg_toplevel");
void app.step(const(char)* api) nothrow @nogcAn init API call that completed locally (no compositor wait).
step("xdg_surface_get_toplevel");
wsi_toplevel_add_listener(g_toplevel, &g_toplevelListener, null);
wsi_toplevel_set_title(g_toplevel, "wsi-f01-first-pixel");
void app.step(const(char)* api) nothrow @nogcAn init API call that completed locally (no compositor wait).
step("xdg_toplevel_set_title");
wsi_toplevel_set_app_id(g_toplevel, "wsi-f01-first-pixel");
void app.step(const(char)* api) nothrow @nogcAn init API call that completed locally (no compositor wait).
step("xdg_toplevel_set_app_id");
instrWindowCreated();
// 4. The mandatory initial commit *without* a buffer (attaching one before
// the first configure is a protocol error). The compositor answers with
// the first xdg_surface.configure; the first pixel happens in its handler.
wsi_surface_commit(g_surface);
void app.step(const(char)* api) nothrow @nogcAn init API call that completed locally (no compositor wait).
step("wl_surface_commit(no-buffer)");
// 5. Block until the first configure arrives — the second compositor wait
// (rt=1). The configure handler acks and commits the first buffer, so
// by the time this step line is emitted the first frame is in flight.
while ((__gshared global) bool app.g_runningg_running && !(__gshared global) bool app.g_configuredg_configured && wl_display_dispatch(g_display) != -1)
{
}
void app.stepRoundTrip(const(char)* api) nothrow @nogcAn init API call that blocked on the compositor (F01: round-trip).
stepRoundTrip("wl_display_dispatch(first_configure)");
// 6. Event loop until the post-presentation hold expires.
while ((__gshared global) bool app.g_runningg_running && wl_display_dispatch(g_display) != -1)
{
if (instrNowUs() > hardStopUs)
(__gshared global) bool app.g_runningg_running = false; // backstop if the frame clock ever stalls
}
// 7. Clean teardown: buffers → frame callback → role objects → surface →
// globals → registry → disconnect (children before parents).
void app.teardown() nothrow @nogcteardown();
// F01 checklist (spec: features/f01-first-pixel.md § Findings to record).
immutable (local variable) immutable(char) presentedpresented = (__gshared global) bool app.g_presentedg_presented ? 'x' : ' ';
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("F01 checklist (wayland):\n");
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf(" [%c] one frame presented, confirmed by wl_surface.frame callback\n", (local variable) immutable(char) presentedpresented);
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf(" [x] init steps logged: %d (round-trips rt=1: %d)\n", (__gshared global) int app.g_initStepsg_initSteps, (__gshared global) int app.g_roundTripsg_roundTrips);
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf(" [x] concepts to first pixel: %d\n", (__gshared global) int app.g_conceptsg_concepts);
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf(" [x] loc: %d total, %d code (instrument.d and c.c excluded)\n",
demoLoc.(field) _error_ demoLoc.totaltotal, demoLoc.(field) _error_ demoLoc.codecode);
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf(" [%c] init_start -> first_pixel_presented: %lld us\n", (local variable) immutable(char) presentedpresented, (__gshared global) long app.g_firstPixelUsg_firstPixelUs);
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf(" [x] held %d extra frames, clean teardown, exit 0\n", (__gshared global) int app.g_framesg_frames - 1);
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("ok: presented %d frames in %d commits, final size %dx%d\n",
(__gshared global) int app.g_framesg_frames, (__gshared global) int app.g_commitsg_commits, (__gshared global) int app.g_widthg_width, (__gshared global) int app.g_heightg_height);
return (__gshared global) bool app.g_presentedg_presented ? 0 : 1;
}