// F09 output enumeration & hotplug demo — what a Wayland window can know
// about the displays it lives on, on top of the xdg-shell scaffold
// (../scaffold/app.d; findings: ../../scaffold.md, ../../f09-outputs.md).
//
// - Binds EVERY wl_output global at v4 (geometry/mode/scale + the v4
// name/description events), logging one `output id=… geom=… scale=…
// refresh=…` line per atomic `wl_output.done` batch.
// - Binds zxdg_output_manager_v1 when offered and attaches a
// zxdg_output_v1 to each output: the *logical* position/size (the
// compositor-space rectangle after scaling/transform) vs wl_output's
// physical mode — the logical-vs-physical split is the deliverable.
// - Tracks surface↔output occupancy via wl_surface.enter/leave (Wayland is
// the only platform that tells the surface directly) and logs
// `surface_output enter|leave id=…` plus a running occupancy count.
// - Handles hotplug: wl_registry.global → `output_added`,
// wl_registry.global_remove → `output_removed` + zxdg_output_v1.destroy +
// wl_output.release (v3+) — including the window's *current* output
// vanishing mid-run (./run.sh choreographs that with swaymsg).
//
// Env knobs: WSI_AUTO_EXIT=1 + WSI_RUN_MS=<ms> bound the run. Headless-safe:
// no compositor → SKIP, exit 0.
module (module) appapp;
import c; // ImportC: wayland-client + xdg-shell/xdg-output glue
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, (alias) app.snprintf = int core.stdc.stdio.snprintf(scope char* s, ulong n, scope const(char*) format, scope const ...) nothrow @nogcsnprintf;
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.atoi = int core.stdc.stdlib.atoi(scope const(char*) nptr) nothrow @nogcatoi, (alias) app.getenv = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv;
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;
enum int (constant) int app.defaultWidth = 640defaultWidth = 640;
enum int (constant) int app.defaultHeight = 480defaultHeight = 480;
enum int (constant) int app.maxOutputs = 8maxOutputs = 8;
// -------------------------------------------------------------------- state
/// Everything one wl_output global tells us, physical (wl_output) and
/// logical (zxdg_output_v1) halves side by side. Events accumulate into the
/// slot; `wl_output.done` makes the batch atomic and triggers the log line.
struct (struct) app.OutputEverything one wl_output global tells us, physical (wl_output) and
logical (zxdg_output_v1) halves side by side. Events accumulate into the
slot; wl_output.done makes the batch atomic and triggers the log line.
Output
{
bool (field) bool app.Output.usedused;
uint (field) uint app.Output.globalNameglobalName; // the registry name — the hotplug identity
wl_output* (field) _error_ app.Output.outputoutput;
zxdg_output_v1* (field) _error_ app.Output.xdgOutputxdgOutput;
// wl_output (physical) state
int (field) int app.Output.xx, (field) int app.Output.yy; // geometry position, "within the global compositor space"
int (field) int app.Output.physMmWphysMmW, (field) int app.Output.physMmHphysMmH; // physical size in millimetres
int (field) int app.Output.modeWmodeW, (field) int app.Output.modeHmodeH, (field) int app.Output.refreshMHzrefreshMHz; // current mode, *buffer* pixels
int (field) int app.Output.scalescale = 1;
char[64] (field) char[64] app.Output.namename = '\0'; // v4 wl_output.name (e.g. HEADLESS-1)
char[128] (field) char[128] app.Output.descriptiondescription = '\0';
// zxdg_output_v1 (logical) state
int (field) int app.Output.logXlogX, (field) int app.Output.logYlogY, (field) int app.Output.logWlogW, (field) int app.Output.logHlogH;
bool (field) bool app.Output.haveLogicalhaveLogical;
bool (field) bool app.Output.announcedannounced; // first done already logged as part of output_added
bool (field) bool app.Output.occupiedoccupied; // surface currently on this output (enter/leave)
}
struct (struct) app.BufferBuffer
{
wl_buffer* (field) _error_ app.Buffer.handlehandle;
uint* (field) uint* app.Buffer.pixelspixels;
(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;
xdg_wm_base* _error_ app.g_wmBaseg_wmBase;
zxdg_output_manager_v1* _error_ app.g_xdgOutMgrg_xdgOutMgr;
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;
(struct) app.OutputEverything one wl_output global tells us, physical (wl_output) and
logical (zxdg_output_v1) halves side by side. Events accumulate into the
slot; wl_output.done makes the batch atomic and triggers the log line.
Output[(constant) int app.maxOutputs = 8maxOutputs] _error_ app.g_outputsg_outputs;
(struct) app.BufferBuffer[2] _error_ app.g_buffersg_buffers;
int (__gshared global) int app.g_widthg_width = defaultWidth, (__gshared global) int app.g_heightg_height = defaultHeight;
int (__gshared global) int app.g_pendingWidthg_pendingWidth, (__gshared global) int app.g_pendingHeightg_pendingHeight;
bool (__gshared global) bool app.g_configuredg_configured, (__gshared global) bool app.g_runningg_running = true, (__gshared global) bool app.g_autoExitg_autoExit, (__gshared global) bool app.g_initialEnumDoneg_initialEnumDone;
long (__gshared global) long app.g_runUsCapg_runUsCap = 2_500_000;
int (__gshared global) int app.g_framesg_frames, (__gshared global) int app.g_commitsg_commits;
}
(struct) app.OutputEverything one wl_output global tells us, physical (wl_output) and
logical (zxdg_output_v1) halves side by side. Events accumulate into the
slot; wl_output.done makes the batch atomic and triggers the log line.
Output* app.slotByOutputslotByOutput(wl_output* o) nothrow @nogc
{
foreach (ref (parameter) ss; g_outputs)
if (s.s.usedused && s.s.outputoutput is o)
return &s;
return null;
}
int int app.occupancyCount() nothrow @nogcoccupancyCount() nothrow @nogc
{
int (local variable) int nn;
foreach (ref (parameter) ss; g_outputs)
n += s.s.usedused && s.s.occupiedoccupied;
return (local variable) int nn;
}
/// The per-enumeration line the F09 spec asks for, plus the logical rectangle
/// when xdg-output supplied one — the physical/logical split on one screen.
void app.logOutputThe per-enumeration line the F09 spec asks for, plus the logical rectangle
when xdg-output supplied one — the physical/logical split on one screen.
logOutput(ref (struct) app.OutputEverything one wl_output global tells us, physical (wl_output) and
logical (zxdg_output_v1) halves side by side. Events accumulate into the
slot; wl_output.done makes the batch atomic and triggers the log line.
Output (parameter) Output ss) nothrow @nogc
{
instrEvent("output", "id=%u name=%s geom=%dx%d+%d+%d scale=%d refresh_mhz=%d physical_mm=%dx%d",
s.s.globalNameglobalName, s.s.namename.ptr, s.s.modeWmodeW, s.s.modeHmodeH, s.s.xx, s.s.yy, s.s.scalescale,
s.s.refreshMHzrefreshMHz, s.s.physMmWphysMmW, s.s.physMmHphysMmH);
if (s.s.haveLogicalhaveLogical)
instrEvent("output_logical", "id=%u pos=%d,%d size=%dx%d (zxdg_output_v1)",
s.s.globalNameglobalName, s.s.logXlogX, s.s.logYlogY, s.s.logWlogW, s.s.logHlogH);
}
// --------------------------------------------------- wl_output / xdg_output
extern (C) void app.onOutputGeometryonOutputGeometry(void* data, wl_output* o, int (parameter) int xx, int (parameter) int yy, int physW,
int physH, int subpixel, const(char)* make, const(char)* model, int transform) nothrow @nogc
{
auto _error_ ss = cast((unresolved type) OutputOutput*) data;
s.s.xx = x;
s.s.yy = y;
s.s.physMmWphysMmW = physW;
s.s.physMmHphysMmH = physH;
}
extern (C) void app.onOutputModeonOutputMode(void* data, wl_output* o, uint flags, int (parameter) int ww, int (parameter) int hh, int refresh) nothrow @nogc
{
auto _error_ ss = cast((unresolved type) OutputOutput*) data;
if (flags & WL_OUTPUT_MODE_CURRENT)
{
s.s.modeWmodeW = w;
s.s.modeHmodeH = h;
s.s.refreshMHzrefreshMHz = refresh;
}
}
extern (C) void app.onOutputScaleonOutputScale(void* data, wl_output* o, int factor) nothrow @nogc
{
(cast((unresolved type) OutputOutput*) data).(cast(Output*)data).scalescale = factor;
}
extern (C) void app.onOutputNameonOutputName(void* data, wl_output* o, const(char)* (parameter) const(char)* namename) nothrow @nogc
{
auto _error_ ss = cast((unresolved type) OutputOutput*) data;
snprintf(s.s.namename.ptr, s.s.namename.length, "%s", name);
}
extern (C) void app.onOutputDescriptiononOutputDescription(void* data, wl_output* o, const(char)* desc) nothrow @nogc
{
auto _error_ ss = cast((unresolved type) OutputOutput*) data;
snprintf(s.s.descriptiondescription.ptr, s.s.descriptiondescription.length, "%s", desc);
}
extern (C) void app.onOutputDoneonOutputDone(void* data, wl_output* o) nothrow @nogc
{
auto _error_ ss = cast((unresolved type) OutputOutput*) data;
if (!s.s.announcedannounced)
{
s.s.announcedannounced = true;
// Initial enumeration vs runtime hotplug, distinguished by whether the
// first registry roundtrip has completed yet.
instrEvent(g_initialEnumDone ? "output_added" : "output_enumerated",
"id=%u name=%s", s.s.globalNameglobalName, s.s.namename.ptr);
}
logOutput(*s);
}
extern (C) void app.onXdgOutLogicalPosonXdgOutLogicalPos(void* data, zxdg_output_v1* xo, int (parameter) int xx, int (parameter) int yy) nothrow @nogc
{
auto _error_ ss = cast((unresolved type) OutputOutput*) data;
s.s.logXlogX = x;
s.s.logYlogY = y;
s.s.haveLogicalhaveLogical = true;
}
extern (C) void app.onXdgOutLogicalSizeonXdgOutLogicalSize(void* data, zxdg_output_v1* xo, int (parameter) int ww, int (parameter) int hh) nothrow @nogc
{
auto _error_ ss = cast((unresolved type) OutputOutput*) data;
s.s.logWlogW = w;
s.s.logHlogH = h;
s.s.haveLogicalhaveLogical = true;
}
extern (C) void app.onXdgOutDoneonXdgOutDone(void* data, zxdg_output_v1* xo) nothrow @nogc
{
// Only sent when bound below v3 (weston offers v2); at v3+ the protocol
// deprecates it and latches logical updates to wl_output.done instead.
auto _error_ ss = cast((unresolved type) OutputOutput*) data;
instrEvent("output_logical", "id=%u pos=%d,%d size=%dx%d (zxdg_output_v1 v2 done)",
s.s.globalNameglobalName, s.s.logXlogX, s.s.logYlogY, s.s.logWlogW, s.s.logHlogH);
}
extern (C) void app.onXdgOutNameonXdgOutName(void* data, zxdg_output_v1* xo, const(char)* (parameter) const(char)* namename) nothrow @nogc
{
}
extern (C) void app.onXdgOutDescriptiononXdgOutDescription(void* data, zxdg_output_v1* xo, const(char)* d) nothrow @nogc
{
}
__gshared wl_output_listener _error_ app.g_outputListenerg_outputListener = {
&onOutputGeometry, &onOutputMode, &onOutputDone, &onOutputScale,
&onOutputName, &onOutputDescription
};
__gshared zxdg_output_v1_listener _error_ app.g_xdgOutputListenerg_xdgOutputListener = {
&onXdgOutLogicalPos, &onXdgOutLogicalSize, &onXdgOutDone,
&onXdgOutName, &onXdgOutDescription
};
void app.attachXdgOutputattachXdgOutput(ref (struct) app.OutputEverything one wl_output global tells us, physical (wl_output) and
logical (zxdg_output_v1) halves side by side. Events accumulate into the
slot; wl_output.done makes the batch atomic and triggers the log line.
Output (parameter) Output ss) nothrow @nogc
{
if (g_xdgOutMgr is null || s.s.xdgOutputxdgOutput !is null)
return;
s.s.xdgOutputxdgOutput = wsi_xdg_output_manager_get_xdg_output(g_xdgOutMgr, s.s.outputoutput);
wsi_xdg_output_add_listener(s.s.xdgOutputxdgOutput, &g_xdgOutputListener, &s);
}
// ------------------------------------------------------- surface enter/leave
extern (C) void app.onSurfaceEnteronSurfaceEnter(void* data, wl_surface* surf, wl_output* o) nothrow @nogc
{
auto _error_ ss = slotByOutput(o);
if (s !is null)
{
s.s.occupiedoccupied = true;
instrEvent("surface_output", "enter id=%u name=%s", s.s.globalNameglobalName, s.s.namename.ptr);
}
else // an output this client never bound (or already released)
instrEvent("surface_output", "enter id=? (unknown wl_output proxy)");
instrEvent("occupancy", "count=%d", occupancyCount());
}
extern (C) void app.onSurfaceLeaveonSurfaceLeave(void* data, wl_surface* surf, wl_output* o) nothrow @nogc
{
auto _error_ ss = slotByOutput(o);
if (s !is null)
{
s.s.occupiedoccupied = false;
instrEvent("surface_output", "leave id=%u name=%s", s.s.globalNameglobalName, s.s.namename.ptr);
}
else
instrEvent("surface_output", "leave id=? (unknown wl_output proxy)");
instrEvent("occupancy", "count=%d", occupancyCount());
}
extern (C) void app.onPreferredBufferScaleonPreferredBufferScale(void* data, wl_surface* (parameter) wl_surface* ss, int factor) nothrow @nogc
{
}
extern (C) void app.onPreferredBufferTransformonPreferredBufferTransform(void* data, wl_surface* (parameter) wl_surface* ss, uint t) nothrow @nogc
{
}
// ------------------------------------------------------------------ registry
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, 6));
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, zxdg_output_manager_v1_interface.zxdg_output_manager_v1_interface.namename) == 0)
{
g_xdgOutMgr = cast(zxdg_output_manager_v1*) wsi_registry_bind(reg, name,
&zxdg_output_manager_v1_interface, capped(ver, 3));
instrEvent("xdg_output_manager", "version=%u (logical geometry available)", ver);
}
else if (strcmp(iface, wl_output_interface.wl_output_interface.namename) == 0)
{
foreach (ref (parameter) ss; g_outputs)
if (!s.s.usedused)
{
s = Output.init;
s.s.usedused = true;
s.s.globalNameglobalName = name;
// v4 = name/description events; release (the destroy request)
// needs only v3.
s.s.outputoutput = cast(wl_output*) wsi_registry_bind(reg, name,
&wl_output_interface, capped(ver, 4));
wsi_output_add_listener(s.s.outputoutput, &g_outputListener, &s);
attachXdgOutput(s);
return;
}
instrEvent("output_ignored", "id=%u (more than %d outputs)", name, maxOutputs);
}
}
extern (C) void app.onGlobalRemoveonGlobalRemove(void* data, wl_registry* reg, uint (parameter) uint namename) nothrow @nogc
{
foreach (ref (parameter) ss; g_outputs)
if (s.s.usedused && s.s.globalNameglobalName == name)
{
instrEvent("output_removed", "id=%u name=%s occupied=%d",
name, s.s.namename.ptr, s.s.occupiedoccupied ? 1 : 0);
// The destroyed-global contract: the server already destroyed the
// global; the client must drop its proxies. zxdg_output_v1 first
// (child), then wl_output.release (v3+).
if (s.s.xdgOutputxdgOutput !is null)
wsi_xdg_output_destroy(s.s.xdgOutputxdgOutput);
wsi_output_release(s.s.outputoutput);
s = Output.init;
instrEvent("occupancy", "count=%d", occupancyCount());
return;
}
}
// ------------------------------------------------ window plumbing (scaffold)
bool app.ensureBufferensureBuffer(ref (struct) app.BufferBuffer (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-f09", MFD_CLOEXEC);
if (fd < 0 || ftruncate(fd, cast(long) size) != 0)
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;
}
wl_shm_pool* _error_ poolpool = wsi_shm_create_pool(g_shm, fd, cast(int) size);
b.b.handlehandle = wsi_shm_pool_create_buffer(pool, 0, w, h, stride, WL_SHM_FORMAT_ARGB8888);
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;
return true;
}
void void app.render() nothrow @nogcrender() nothrow @nogc
{
(struct) app.BufferBuffer* (local variable) _error_ bufbuf = null;
foreach (ref (parameter) bb; g_buffers)
if (!b.b.busybusy)
{
buf = &b;
break;
}
if (buf is null)
return; // recovered by the wl_buffer.release handler
if (!ensureBuffer(*buf, g_width, g_height))
return;
foreach ((parameter) yy; 0 .. buf.(field) _error_ buf.heightheight) // simple gradient; pixels are not the point here
foreach ((parameter) xx; 0 .. buf.buf.widthwidth)
buf.buf.pixelspixels[cast((unresolved type) size_tsize_t) y * buf.buf.widthwidth + x] =
0xff000000 | ((x * 255 / buf.buf.widthwidth) << 16) | ((y * 255 / buf.buf.heightheight) << 8);
wsi_surface_attach(g_surface, buf.buf.handlehandle, 0, 0);
wsi_surface_damage_buffer(g_surface, 0, 0, buf.buf.widthwidth, buf.buf.heightheight);
if (g_frameCb is null)
{
g_frameCb = wsi_surface_frame(g_surface);
wsi_callback_add_listener(g_frameCb, &g_frameListener, null);
}
wsi_surface_commit(g_surface);
buf.buf.busybusy = true;
(__gshared global) int app.g_commitsg_commits++;
}
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);
}
extern (C) void app.onToplevelConfigureonToplevelConfigure(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);
}
extern (C) void app.onXdgSurfaceConfigureonXdgSurfaceConfigure(void* data, xdg_surface* (parameter) xdg_surface* ss, 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);
immutable _error_ resizedresized = w != g_width || h != g_height;
g_width = w;
g_height = h;
if (!g_configured)
{
g_configured = true;
instrFirstConfigure();
render();
}
else if (resized)
{
instrResize(w, h, 1);
render();
}
}
extern (C) void app.onToplevelCloseonToplevelClose(void* data, xdg_toplevel* t) nothrow @nogc
{
instrCloseRequested();
g_running = false;
}
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
{
}
extern (C) void app.onBufferReleaseonBufferRelease(void* data, wl_buffer* (parameter) wl_buffer* bb) nothrow @nogc
{
(cast((unresolved type) BufferBuffer*) data).(cast(Buffer*)data).busybusy = false;
if (g_running && g_configured && g_frameCb is null)
render();
}
extern (C) void app.onFrameDoneonFrameDone(void* data, wl_callback* cb, uint timeMs) nothrow @nogc
{
wsi_callback_destroy(cb);
g_frameCb = null;
g_frames++;
if (g_frames == 1)
instrFirstPixelPresented();
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_surface_listener _error_ app.g_surfaceListenerg_surfaceListener = {
&onSurfaceEnter, &onSurfaceLeave, &onPreferredBufferScale, &onPreferredBufferTransform
};
__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};
// --------------------------------------------------------------------- main
int int D main()main()
{
instrInit("f09_wayland");
const (local variable) const(char*) autoEnvautoEnv = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv("WSI_AUTO_EXIT");
(__gshared global) bool app.g_autoExitg_autoExit = (local variable) const(char*) autoEnvautoEnv !is null && *(local variable) const(char*) autoEnvautoEnv == '1';
const (local variable) const(char*) runMsrunMs = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv("WSI_RUN_MS");
if ((local variable) const(char*) runMsrunMs !is null)
(__gshared global) long app.g_runUsCapg_runUsCap = int core.stdc.stdlib.atoi(scope const(char*) nptr) nothrow @nogcatoi((local variable) const(char*) runMsrunMs) * 1000L;
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;
}
g_registry = wsi_display_get_registry(g_display);
wsi_registry_add_listener(g_registry, &g_registryListener, null);
// Roundtrip 1: globals (outputs bound, xdg_outputs attached where the
// manager arrived before the outputs). Roundtrip 2: the per-output event
// batches — enumeration needs a connection but NO window.
wl_display_roundtrip(g_display);
foreach (ref (parameter) ss; g_outputs) // manager may have arrived after the outputs
if (s.s.usedused)
attachXdgOutput(s);
wl_display_roundtrip(g_display);
(__gshared global) bool app.g_initialEnumDoneg_initialEnumDone = true;
{
int (local variable) int nn;
foreach (ref (parameter) ss; g_outputs)
n += s.s.usedused;
instrEvent("enumeration_done", "outputs=%d xdg_output_manager=%d",
n, g_xdgOutMgr !is null ? 1 : 0);
}
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\n");
wl_display_disconnect(g_display);
return 0;
}
wsi_wm_base_add_listener(g_wmBase, &g_wmBaseListener, null);
g_surface = wsi_compositor_create_surface(g_compositor);
wsi_surface_add_listener(g_surface, &g_surfaceListener, null);
g_xdgSurface = wsi_wm_base_get_xdg_surface(g_wmBase, g_surface);
wsi_xdg_surface_add_listener(g_xdgSurface, &g_xdgSurfaceListener, null);
g_toplevel = wsi_xdg_surface_get_toplevel(g_xdgSurface);
wsi_toplevel_add_listener(g_toplevel, &g_toplevelListener, null);
wsi_toplevel_set_title(g_toplevel, "wsi-f09-outputs");
wsi_toplevel_set_app_id(g_toplevel, "wsi-f09-outputs");
instrWindowCreated();
wsi_surface_commit(g_surface); // mandatory no-buffer first commit
// Poll-based pump (the F04 lesson) so the wall-clock auto-exit stays live
// even when the compositor throttles frame callbacks.
immutable (local variable) immutable(_error_) dispFddispFd = wl_display_get_fd(g_display);
while ((__gshared global) bool app.g_runningg_running)
{
while (wl_display_prepare_read(g_display) != 0)
wl_display_dispatch_pending(g_display);
wl_display_flush(g_display);
pollfd (local variable) _error_ pfdpfd = {dispFd, POLLIN, 0};
if (poll(&pfd, 1, 100) > 0)
wl_display_read_events(g_display);
else
wl_display_cancel_read(g_display);
if (wl_display_dispatch_pending(g_display) == -1)
break;
if ((__gshared global) bool app.g_autoExitg_autoExit && instrNowUs() > (__gshared global) long app.g_runUsCapg_runUsCap)
(__gshared global) bool app.g_runningg_running = false;
}
// Teardown: children before parents.
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);
}
if (g_frameCb !is null)
wsi_callback_destroy(g_frameCb);
wsi_toplevel_destroy(g_toplevel);
wsi_xdg_surface_destroy(g_xdgSurface);
wsi_surface_destroy(g_surface);
wsi_wm_base_destroy(g_wmBase);
foreach (ref (parameter) ss; g_outputs)
if (s.s.usedused)
{
if (s.s.xdgOutputxdgOutput !is null)
wsi_xdg_output_destroy(s.s.xdgOutputxdgOutput);
wsi_output_release(s.s.outputoutput);
}
if (g_xdgOutMgr !is null)
wsi_xdg_output_manager_destroy(g_xdgOutMgr);
wl_proxy_destroy(cast(wl_proxy*) g_shm);
wl_proxy_destroy(cast(wl_proxy*) g_compositor);
wl_proxy_destroy(cast(wl_proxy*) g_registry);
wl_display_disconnect(g_display);
int (local variable) int finalOutputsfinalOutputs;
foreach (ref (parameter) ss; g_outputs)
finalOutputs += s.s.usedused;
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("ok: frames=%d commits=%d outputs_final=%d\n",
(__gshared global) int app.g_framesg_frames, (__gshared global) int app.g_commitsg_commits, (local variable) int finalOutputsfinalOutputs);
return 0;
}