// F08 DPI / runtime-rescale demo — both Wayland scale paths in one binary,
// runtime-selected by the advertised globals, on top of the xdg-shell scaffold
// (../scaffold/app.d; findings: ../../scaffold.md, ../../f08-dpi-scaling.md).
//
// (a) fractional path — wp_fractional_scale_v1.preferred_scale (units of
// 1/120) + wp_viewport.set_destination(logical size), buffer allocated
// at physical size, wl_surface buffer scale left at 1. Buffer-size math
// follows the fractional-scale-v1 XML: "the size is rounded halfway
// away from zero" (round half up for positive sizes) — asserted at
// compile time AND on every commit.
// (b) integer path — wl_surface.set_buffer_scale driven by
// wl_surface.preferred_buffer_scale (wl_compositor v6+), falling back
// to wl_output.scale via wl_surface.enter for older compositors.
//
// Every change logs `scale_changed scale120=N scale=X.XXX path=…` plus
// `commit_info logical=WxH buffer=WxH …`, and the *first* commit logs the
// scale the surface believed before any compositor event arrived — the
// "created at the wrong scale, then rescaled" evidence. The frame is a
// checkerboard with 1-physical-px hairlines on all four buffer edges, so a
// scaling/filtering artifact is immediately visible in a screenshot.
//
// Env knobs: WSI_FORCE_PATH=fractional|buffer_scale overrides the selection;
// WSI_AUTO_EXIT=1 + WSI_RUN_MS=<ms> bound the run (./run.sh drives live
// `swaymsg output … scale` changes against it). Headless-safe: no compositor
// → SKIP, exit 0.
module (module) appapp;
import c; // ImportC: wayland-client + xdg-shell/viewporter/fractional-scale 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;
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;
// ----------------------------------------------------------------- tunables
enum int (constant) int app.defaultWidth = 640defaultWidth = 640;
enum int (constant) int app.defaultHeight = 480defaultHeight = 480;
enum int (constant) int app.checkerPx = 16checkerPx = 16; // checkerboard cell, *physical* pixels
/// fractional-scale-v1 buffer math: logical size × (scale120/120), "rounded
/// halfway away from zero" (the protocol XML's words; sizes are positive, so
/// this is round half up).
int int app.physSize(int logical, uint scale120) pure nothrow @nogc @safefractional-scale-v1 buffer math: logical size × (scale120/120), "rounded
halfway away from zero" (the protocol XML's words; sizes are positive, so
this is round half up).
physSize(int (parameter) int logicallogical, uint (parameter) uint scale120scale120) pure nothrow @nogc @safe
{
return cast(int)((cast(long) (parameter) int logicallogical * (parameter) uint scale120scale120 + 60) / 120);
}
static assert(int app.physSize(int logical, uint scale120) pure nothrow @nogc @safefractional-scale-v1 buffer math: logical size × (scale120/120), "rounded
halfway away from zero" (the protocol XML's words; sizes are positive, so
this is round half up).
physSize(640, 120) == 640); // 1.0 → identity
static assert(int app.physSize(int logical, uint scale120) pure nothrow @nogc @safefractional-scale-v1 buffer math: logical size × (scale120/120), "rounded
halfway away from zero" (the protocol XML's words; sizes are positive, so
this is round half up).
physSize(640, 180) == 960); // 1.5 → exact
static assert(int app.physSize(int logical, uint scale120) pure nothrow @nogc @safefractional-scale-v1 buffer math: logical size × (scale120/120), "rounded
halfway away from zero" (the protocol XML's words; sizes are positive, so
this is round half up).
physSize(101, 180) == 152); // 151.5 rounds *up* (away from zero)
static assert(int app.physSize(int logical, uint scale120) pure nothrow @nogc @safefractional-scale-v1 buffer math: logical size × (scale120/120), "rounded
halfway away from zero" (the protocol XML's words; sizes are positive, so
this is round half up).
physSize(33, 150) == 41); // 41.25 rounds down
static assert(int app.physSize(int logical, uint scale120) pure nothrow @nogc @safefractional-scale-v1 buffer math: logical size × (scale120/120), "rounded
halfway away from zero" (the protocol XML's words; sizes are positive, so
this is round half up).
physSize(640, 150) == 800); // 1.25 → exact
// -------------------------------------------------------------------- state
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;
}
enum (enum) app.PathPath
{
(enum value) app.Path.bufferScale = 0bufferScale, // wl_surface.set_buffer_scale (integer)
(enum value) app.Path.fractional = 1fractional, // wp_fractional_scale_v1 + wp_viewport
}
__gshared
{
wl_display* _error_ app.g_displayg_display;
wl_registry* _error_ app.g_registryg_registry;
wl_compositor* _error_ app.g_compositorg_compositor;
uint (__gshared global) uint app.g_compositorVersiong_compositorVersion;
wl_shm* _error_ app.g_shmg_shm;
wl_output* _error_ app.g_outputg_output; // first advertised output (enough for headless runs)
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;
wp_viewporter* _error_ app.g_viewporterg_viewporter;
wp_viewport* _error_ app.g_viewportg_viewport;
wp_fractional_scale_manager_v1* _error_ app.g_fracManagerg_fracManager;
wp_fractional_scale_v1* _error_ app.g_fracScaleg_fracScale;
(struct) app.BufferBuffer[2] _error_ app.g_buffersg_buffers;
(enum) app.PathPath (__gshared global) app.Path app.g_pathg_path;
int (__gshared global) int app.g_logicalWg_logicalW = defaultWidth, (__gshared global) int app.g_logicalHg_logicalH = defaultHeight; // last acked
int (__gshared global) int app.g_pendingWidthg_pendingWidth, (__gshared global) int app.g_pendingHeightg_pendingHeight;
uint (__gshared global) uint app.g_scale120g_scale120 = 120; // current scale in 1/120ths (both paths normalize here)
int (__gshared global) int app.g_outputScaleg_outputScale = 1; // last wl_output.scale (the v5-and-older fallback)
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_firstCommitDoneg_firstCommitDone;
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;
}
const(char)* const(char)* app.pathName() nothrow @nogcpathName() nothrow @nogc
{
return (__gshared global) app.Path app.g_pathg_path == (enum) app.PathPath.(enum value) app.Path.fractional = 1fractional ? "fractional".(constant) immutable(char)* "fractional".ptr = "fractional"ptr : "buffer_scale".(constant) immutable(char)* "buffer_scale".ptr = "buffer_scale"ptr;
}
/// Log `scale_changed` in the canonical format (scale as a decimal, plus the
/// raw 120ths so fractional values stay exact).
void void app.logScaleChanged() nothrow @nogcLog scale_changed in the canonical format (scale as a decimal, plus the
raw 120ths so fractional values stay exact).
logScaleChanged() nothrow @nogc
{
instrEvent("scale_changed", "scale120=%u scale=%u.%03u path=%s",
g_scale120, g_scale120 / 120, (g_scale120 % 120) * 1000 / 120, pathName());
}
void void app.bufferDims(out int w, out int h) nothrow @nogcbufferDims(out int (parameter) int ww, out int (parameter) int hh) nothrow @nogc
{
if ((__gshared global) app.Path app.g_pathg_path == (enum) app.PathPath.(enum value) app.Path.fractional = 1fractional)
{
(parameter) int ww = int app.physSize(int logical, uint scale120) pure nothrow @nogc @safefractional-scale-v1 buffer math: logical size × (scale120/120), "rounded
halfway away from zero" (the protocol XML's words; sizes are positive, so
this is round half up).
physSize((__gshared global) int app.g_logicalWg_logicalW, (__gshared global) uint app.g_scale120g_scale120);
(parameter) int hh = int app.physSize(int logical, uint scale120) pure nothrow @nogc @safefractional-scale-v1 buffer math: logical size × (scale120/120), "rounded
halfway away from zero" (the protocol XML's words; sizes are positive, so
this is round half up).
physSize((__gshared global) int app.g_logicalHg_logicalH, (__gshared global) uint app.g_scale120g_scale120);
}
else
{
immutable (local variable) immutable(int) ss = cast(int) (__gshared global) uint app.g_scale120g_scale120 / 120;
(parameter) int ww = (__gshared global) int app.g_logicalWg_logicalW * (local variable) immutable(int) ss;
(parameter) int hh = (__gshared global) int app.g_logicalHg_logicalH * (local variable) immutable(int) ss;
}
}
// ------------------------------------------------------------ shm + drawing
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-f08", 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;
instrEvent("buffer_alloc", "size=%dx%d bytes=%zu", w, h, size);
return true;
}
/// Checkerboard at physical resolution + 1-physical-px hairlines on all four
/// buffer edges. If the compositor resamples (wrong buffer size for the
/// scale), the hairlines blur or vanish — the artifact detector.
void app.paintCheckerboard at physical resolution + 1-physical-px hairlines on all four
buffer edges. If the compositor resamples (wrong buffer size for the
scale), the hairlines blur or vanish — the artifact detector.
paint(ref (struct) app.BufferBuffer (parameter) Buffer bb) nothrow @nogc
{
foreach ((parameter) yy; 0 .. b.b.heightheight)
{
uint* _error_ rowrow = b.b.pixelspixels + cast((unresolved type) size_tsize_t) y * b.b.widthwidth;
foreach ((parameter) xx; 0 .. b.b.widthwidth)
row[x] = ((x / checkerPx + y / checkerPx) & 1) ? 0xff707070 : 0xff383838;
}
foreach ((parameter) xx; 0 .. b.b.widthwidth) // top/bottom hairlines, exactly 1 physical px
{
b.b.pixelspixels[x] = 0xffffffff;
b.b.pixelspixels[cast((unresolved type) size_tsize_t)(b.b.heightheight - 1) * b.b.widthwidth + x] = 0xffff2020;
}
foreach ((parameter) yy; 0 .. b.b.heightheight) // left/right hairlines
{
b.b.pixelspixels[cast((unresolved type) size_tsize_t) y * b.b.widthwidth] = 0xff20ff20;
b.b.pixelspixels[cast((unresolved type) size_tsize_t) y * b.b.widthwidth + b.b.widthwidth - 1] = 0xff40a0ff;
}
}
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
int (local variable) int bwbw, (local variable) int bhbh;
void app.bufferDims(out int w, out int h) nothrow @nogcbufferDims((local variable) int bwbw, (local variable) int bhbh);
if (!ensureBuffer(*buf, bw, bh))
return;
paint(*buf);
// The committed buffer must match the spec math for the current scale.
assert(buf.(field) _error_ buf.widthwidth == (local variable) int bwbw && buf.(field) _error_ buf.heightheight == (local variable) int bhbh,
"committed buffer size does not match the scale math");
if ((__gshared global) app.Path app.g_pathg_path == (enum) app.PathPath.(enum value) app.Path.fractional = 1fractional)
{
// Buffer at physical size, destination at logical size, buffer scale 1.
wsi_viewport_set_destination(g_viewport, g_logicalW, g_logicalH);
}
else
wsi_surface_set_buffer_scale(g_surface, cast(int) g_scale120 / 120);
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++;
if (!(__gshared global) bool app.g_firstCommitDoneg_firstCommitDone)
{
(__gshared global) bool app.g_firstCommitDoneg_firstCommitDone = true;
// The created-at-wrong-scale evidence: this is the scale the surface
// believed at its very first commit, before/after compositor events.
instrEvent("first_commit", "logical=%dx%d buffer=%dx%d scale120=%u path=%s",
g_logicalW, g_logicalH, buf.buf.widthwidth, buf.buf.heightheight, g_scale120, pathName());
}
}
/// Re-render right away on any scale/size change and log the new geometry.
void void app.applyChange(const(char)* why) nothrow @nogcRe-render right away on any scale/size change and log the new geometry.
applyChange(const(char)* (parameter) const(char)* whywhy) nothrow @nogc
{
int (local variable) int bwbw, (local variable) int bhbh;
void app.bufferDims(out int w, out int h) nothrow @nogcbufferDims((local variable) int bwbw, (local variable) int bhbh);
instrEvent("commit_info", "reason=%s logical=%dx%d buffer=%dx%d scale120=%u path=%s",
why, g_logicalW, g_logicalH, bw, bh, g_scale120, pathName());
if ((__gshared global) bool app.g_configuredg_configured)
void app.render() nothrow @nogcrender();
}
// ---------------------------------------------------------------- listeners
extern (C) void app.onFracPreferredScaleonFracPreferredScale(void* data, wp_fractional_scale_v1* f, uint scale) nothrow @nogc
{
instrEvent("preferred_scale", "scale120=%u (wp_fractional_scale_v1)", scale);
if (g_path == Path.Path.fractionalfractional && scale != g_scale120)
{
g_scale120 = scale;
logScaleChanged();
applyChange("preferred_scale");
}
}
extern (C) void app.onSurfaceEnteronSurfaceEnter(void* data, wl_surface* (parameter) wl_surface* ss, wl_output* o) nothrow @nogc
{
instrEvent("surface_enter", "output_scale=%d", g_outputScale);
// pre-v6 integer fallback: adopt the entered output's scale.
if (g_path == Path.Path.bufferScalebufferScale && g_compositorVersion < 6
&& g_outputScale * 120 != g_scale120)
{
g_scale120 = g_outputScale * 120;
logScaleChanged();
applyChange("wl_output.scale");
}
}
extern (C) void app.onSurfaceLeaveonSurfaceLeave(void* data, wl_surface* (parameter) wl_surface* ss, wl_output* o) nothrow @nogc
{
instrEvent("surface_leave");
}
extern (C) void app.onPreferredBufferScaleonPreferredBufferScale(void* data, wl_surface* (parameter) wl_surface* ss, int factor) nothrow @nogc
{
instrEvent("preferred_buffer_scale", "factor=%d (wl_surface v6)", factor);
if (g_path == Path.Path.bufferScalebufferScale && cast(uint)(factor * 120) != g_scale120)
{
g_scale120 = factor * 120;
logScaleChanged();
applyChange("preferred_buffer_scale");
}
}
extern (C) void app.onPreferredBufferTransformonPreferredBufferTransform(void* data, wl_surface* (parameter) wl_surface* ss, uint transform) nothrow @nogc
{
}
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
{
instrEvent("output_geometry", "physical_mm=%dx%d", physW, 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
{
instrEvent("output_mode", "size=%dx%d refresh_mhz=%d", w, h, refresh);
}
extern (C) void app.onOutputDoneonOutputDone(void* data, wl_output* o) nothrow @nogc
{
}
extern (C) void app.onOutputScaleonOutputScale(void* data, wl_output* o, int factor) nothrow @nogc
{
instrEvent("output_scale", "scale=%d (wl_output v2)", factor);
g_outputScale = factor;
}
extern (C) void app.onOutputNameonOutputName(void* data, wl_output* o, const(char)* name) nothrow @nogc
{
instrEvent("output_name", "name=%s", name);
}
extern (C) void app.onOutputDescriptiononOutputDescription(void* data, wl_output* o, const(char)* desc) nothrow @nogc
{
}
extern (C) void app.onGlobalonGlobal(void* data, wl_registry* reg, uint name,
const(char)* iface, uint ver) nothrow @nogc
{
instrEvent("global", "iface=%s version=%u", iface, ver); // the registry dump
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.name) == 0)
{
g_compositorVersion = capped(ver, 6); // v6: wl_surface.preferred_buffer_scale
g_compositor = cast(wl_compositor*) wsi_registry_bind(reg, name,
&wl_compositor_interface, g_compositorVersion);
}
else if (strcmp(iface, wl_shm_interface.name) == 0)
g_shm = cast(wl_shm*) wsi_registry_bind(reg, name, &wl_shm_interface, 1);
else if (strcmp(iface, xdg_wm_base_interface.name) == 0)
g_wmBase = cast(xdg_wm_base*) wsi_registry_bind(reg, name, &xdg_wm_base_interface, 1);
else if (strcmp(iface, wl_output_interface.name) == 0 && g_output is null)
g_output = cast(wl_output*) wsi_registry_bind(reg, name,
&wl_output_interface, capped(ver, 4)); // v2: scale, v4: name
else if (strcmp(iface, wp_viewporter_interface.name) == 0)
g_viewporter = cast(wp_viewporter*) wsi_registry_bind(reg, name,
&wp_viewporter_interface, 1);
else if (strcmp(iface, wp_fractional_scale_manager_v1_interface.name) == 0)
g_fracManager = cast(wp_fractional_scale_manager_v1*) wsi_registry_bind(reg, name,
&wp_fractional_scale_manager_v1_interface, 1);
}
extern (C) void app.onGlobalRemoveonGlobalRemove(void* data, wl_registry* reg, uint name) 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);
}
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 (logical)", 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_logicalW || h != g_logicalH;
g_logicalW = w;
g_logicalH = h;
if (!g_configured)
{
g_configured = true;
instrFirstConfigure();
render();
}
else if (resized)
{
instrEvent("resize", "size=%dx%d scale120=%u", w, h, g_scale120);
applyChange("configure");
}
}
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;
// sway/wlroots holds shm buffers until the *next* commit replaces them, so
// both buffers can be busy when a frame callback fires; recover here.
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 wl_output_listener _error_ app.g_outputListenerg_outputListener = {
&onOutputGeometry, &onOutputMode, &onOutputDone, &onOutputScale,
&onOutputName, &onOutputDescription
};
__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("f08_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);
wl_display_roundtrip(g_display);
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;
}
// Path selection: fractional when both fractional-scale-v1 AND viewporter
// are advertised (the former is useless without the latter), else integer.
const (local variable) const(char*) forceforce = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv("WSI_FORCE_PATH");
(__gshared global) app.Path app.g_pathg_path = g_fracManager !is null && g_viewporter !is null ? (enum) app.PathPath.(enum value) app.Path.fractional = 1fractional : (enum) app.PathPath.(enum value) app.Path.bufferScale = 0bufferScale;
if ((local variable) const(char*) forceforce !is null && int core.stdc.string.strcmp(scope const(char*) s1, scope const(char*) s2) pure nothrow @nogcstrcmp((local variable) const(char*) forceforce, "buffer_scale") == 0)
(__gshared global) app.Path app.g_pathg_path = (enum) app.PathPath.(enum value) app.Path.bufferScale = 0bufferScale;
if ((local variable) const(char*) forceforce !is null && int core.stdc.string.strcmp(scope const(char*) s1, scope const(char*) s2) pure nothrow @nogcstrcmp((local variable) const(char*) forceforce, "fractional") == 0 && (__gshared global) app.Path app.g_pathg_path != (enum) app.PathPath.(enum value) app.Path.fractional = 1fractional)
{
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("SKIP: fractional path forced but wp_fractional_scale_v1/wp_viewporter missing\n");
wl_display_disconnect(g_display);
return 0;
}
instrEvent("path_selected", "path=%s fractional_scale_v1=%d viewporter=%d compositor_version=%u",
pathName(), g_fracManager !is null ? 1 : 0, g_viewporter !is null ? 1 : 0,
g_compositorVersion);
wsi_wm_base_add_listener(g_wmBase, &g_wmBaseListener, null);
if (g_output !is null)
wsi_output_add_listener(g_output, &g_outputListener, null);
g_surface = wsi_compositor_create_surface(g_compositor);
wsi_surface_add_listener(g_surface, &g_surfaceListener, null);
if ((__gshared global) app.Path app.g_pathg_path == (enum) app.PathPath.(enum value) app.Path.fractional = 1fractional)
{
g_fracScale = wsi_fractional_scale_manager_get(g_fracManager, g_surface);
__gshared wp_fractional_scale_v1_listener _error_ app.main.fracListenerfracListener = {&onFracPreferredScale};
wsi_fractional_scale_add_listener(g_fracScale, &fracListener, null);
g_viewport = wsi_viewporter_get_viewport(g_viewporter, g_surface);
}
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-f08-dpi-scaling");
wsi_toplevel_set_app_id(g_toplevel, "wsi-f08-dpi-scaling");
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.
if (g_fracScale !is null)
wsi_fractional_scale_destroy(g_fracScale);
if (g_fracManager !is null)
wsi_fractional_scale_manager_destroy(g_fracManager);
if (g_viewport !is null)
wsi_viewport_destroy(g_viewport);
if (g_viewporter !is null)
wsi_viewporter_destroy(g_viewporter);
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);
if (g_output !is null)
wl_proxy_destroy(cast(wl_proxy*) g_output);
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 bwbw, (local variable) int bhbh;
void app.bufferDims(out int w, out int h) nothrow @nogcbufferDims((local variable) int bwbw, (local variable) int bhbh);
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("ok: frames=%d commits=%d path=%s final logical=%dx%d buffer=%dx%d scale120=%u\n",
(__gshared global) int app.g_framesg_frames, (__gshared global) int app.g_commitsg_commits, const(char)* app.pathName() nothrow @nogcpathName(), (__gshared global) int app.g_logicalWg_logicalW, (__gshared global) int app.g_logicalHg_logicalH, (local variable) int bwbw, (local variable) int bhbh, (__gshared global) uint app.g_scale120g_scale120);
return 0;
}