// X11 F08 demo — DPI / runtime rescale (../../../features/f08-dpi-scaling.md).
// Built on the scaffold (../scaffold/app.d): same ImportC binding style, same
// poll(2)-driven readiness loop, same instrument.d event log (plain XPutImage
// presentation — MIT-SHM is irrelevant to this row).
//
// X11's answer to "what happens when the scale changes under a live window"
// is THE deliverable: there is no runtime mechanism. The demo proves it by
// measurement rather than assertion:
//
// * At startup it snapshots everything a toolkit can know: `Xft.dpi` from
// the RESOURCE_MANAGER root property (XResourceManagerString +
// XrmGetResource), the core screen's pixel + millimeter size (=> computed
// DPI), and per-output RandR physical/pixel sizes (=> per-monitor DPI).
// * It then keeps running while the run.sh driver changes everything that
// is changeable (`xrdb -merge` a new Xft.dpi, `xrandr --dpi`), and logs
// (a) every event delivered to its own window — expecting NONE related to
// the change — and (b) the root-window PropertyNotify on RESOURCE_MANAGER
// it gets ONLY because it explicitly selected PropertyChangeMask on the
// root: the live channel some toolkits use, a convention, not a protocol.
// * On that PropertyNotify it re-reads the property two ways:
// XResourceManagerString (Xlib's snapshot, cached at connect time — stays
// STALE) and XGetWindowProperty (fresh) — the trap is part of the proof.
// * RandR change events (RRScreenChangeNotify) are selected too, so if the
// server CAN signal a geometry/physical-size change, it is captured.
//
// Rendering: gradient + a crisp 1-px black hairline at the window edge and a
// horizontal bar sized `dpi` pixels (F08 requirement 1: scaling artifacts
// must be visible). WSI_AUTO_EXIT=1 bounds the run (WSI_DURATION_MS, default
// 10 s). Headless-safe: no X server -> prints `SKIP:` and exits 0.
// Findings: ../../f08-dpi-scaling.md.
module (module) appapp;
import (module) cc; // ImportC: Xlib + Xutil + Xatom + Xresource + Xrandr + 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, (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.free = void core.stdc.stdlib.free(void* ptr) nothrow @nogcfree, (alias) app.getenv = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv, (alias) app.malloc = void* core.stdc.stdlib.malloc(ulong size) nothrow @nogcmalloc;
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.strstr = inout(char)* core.stdc.string.strstr(return scope inout(char)* s1, scope const(char*) s2) pure nothrow @nogcstrstr;
// ---------------------------------------------------------------------------
// Constants Xlib/Xatom/Xrandr expose as macros that ImportC cannot import;
// re-declared per the scaffold gotcha.
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 value) app.PropertyChangeMask = 4194304LPropertyChangeMask = 1L << 22,
}
enum // XEvent.type discriminators
{
(enum value) app.KeyPress = 2KeyPress = 2,
(enum value) app.Expose = 12Expose = 12,
(enum value) app.MapNotify = 19MapNotify = 19,
(enum value) app.ConfigureNotify = 22ConfigureNotify = 22,
(enum value) app.PropertyNotify = 28PropertyNotify = 28,
(enum value) app.ClientMessage = 33ClientMessage = 33,
}
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.ZPixmap = 2ZPixmap = 2;
enum (constant) int app.AnyPropertyType = 0AnyPropertyType = 0;
enum Atom (constant) _error_ app.XA_RESOURCE_MANAGER = __errorXA_RESOURCE_MANAGER = 23; // Xatom.h cast-expression macro
enum Atom (constant) _error_ app.XA_STRING = __errorXA_STRING = 31;
enum // Xrandr.h
{
(enum value) app.RRScreenChangeNotify = 0RRScreenChangeNotify = 0, // event offset from the extension's event base
(enum value) app.RRScreenChangeNotifyMask = 1L << 0RRScreenChangeNotifyMask = 1L << 0,
(enum value) app.RRCrtcChangeNotifyMask = 1L << 1RRCrtcChangeNotifyMask = 1L << 1,
(enum value) app.RROutputChangeNotifyMask = 1L << 2RROutputChangeNotifyMask = 1L << 2,
(enum value) app.RR_Connected = 0RR_Connected = 0,
}
// ---------------------------------------------------------------------------
// Reading Xft.dpi out of a resource string ("Xft.dpi:\t144\n...").
/// Parse `Xft.dpi` from a RESOURCE_MANAGER-style resource string via the Xrm
/// machinery (XrmGetStringDatabase + XrmGetResource — the same lookup
/// toolkits do). Returns the value or -1 when the resource is absent.
/// `who` labels the source in the log (startup / stale / fresh).
double double app.readXftDpi(const(char)* resStr, const(char)* who) nothrow @nogcParse Xft.dpi from a RESOURCE_MANAGER-style resource string via the Xrm
machinery (XrmGetStringDatabase + XrmGetResource — the same lookup
toolkits do). Returns the value or -1 when the resource is absent.
who labels the source in the log (startup / stale / fresh).
readXftDpi(const(char)* (parameter) const(char)* resStrresStr, const(char)* (parameter) const(char)* whowho) @nogc nothrow
{
if ((parameter) const(char)* resStrresStr is null)
{
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("xft_dpi", "source=%s present=0 (no RESOURCE_MANAGER property)", (parameter) const(char)* whowho);
return -1;
}
XrmDatabase (local variable) _error_ dbdb = XrmGetStringDatabase(resStr);
char* (local variable) char* typetype;
XrmValue (local variable) _error_ valval;
double (local variable) double dpidpi = -1;
if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &val) && val.addr !is null)
{
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) strtod = double core.stdc.stdlib.strtod(scope inout(char)* nptr, scope inout(char)** endptr) nothrow @nogcstrtod;
(local variable) double dpidpi = double core.stdc.stdlib.strtod(scope inout(char)* nptr, scope inout(char)** endptr) nothrow @nogcstrtod(cast(const char*) val.addr, null);
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("xft_dpi", "source=%s present=1 value=%s scale_vs_96=%.2f",
(parameter) const(char)* whowho, cast(const char*) val.addr, (local variable) double dpidpi / 96.0);
}
else
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("xft_dpi", "source=%s present=0 (property exists, no Xft.dpi key)", (parameter) const(char)* whowho);
XrmDestroyDatabase(db);
return (local variable) double dpidpi;
}
/// Fetch the RESOURCE_MANAGER property *fresh* off the root window.
/// XResourceManagerString does NOT do this — it returns the value Xlib
/// snapshotted inside XOpenDisplay; this is the live read.
char* app.fetchResourceManagerFetch the RESOURCE_MANAGER property fresh off the root window.
XResourceManagerString does NOT do this — it returns the value Xlib
snapshotted inside XOpenDisplay; this is the live read.
fetchResourceManager(Display* (parameter) Display* dpydpy, Window (parameter) Window rootroot) @nogc nothrow
{
Atom _error_ actualTypeactualType;
int _error_ actualFormatactualFormat;
c_ulong _error_ nitemsnitems, _error_ bytesAfterbytesAfter;
ubyte* _error_ propprop;
const _error_ rr = XGetWindowProperty(dpy, root, XA_RESOURCE_MANAGER, 0, 1 << 20,
False, XA_STRING, &actualType, &actualFormat, &nitems, &bytesAfter, &prop);
if (r != 0 /*Success*/ || prop is null)
return null;
return cast(char*) prop; // NUL-terminated by Xlib; caller XFree()s
}
/// Log the core screen + every RandR output's pixel/physical size and the
/// DPI each implies. This is the complete startup snapshot a toolkit gets.
void app.logDpiSnapshotLog the core screen + every RandR output's pixel/physical size and the
DPI each implies. This is the complete startup snapshot a toolkit gets.
logDpiSnapshot(Display* (parameter) Display* dpydpy, int (parameter) int screenscreen, Window (parameter) Window rootroot, bool (parameter) bool haveRandrhaveRandr) @nogc nothrow
{
// Core protocol: one screen-wide size in pixels and millimeters, fixed at
// connection setup (Xlib caches it; DisplayWidthMM is the function form
// XDisplayWidthMM since the macro is not ImportC-able).
const _error_ pxpx = XDisplayWidth(dpy, screen), _error_ pypy = XDisplayHeight(dpy, screen);
const _error_ mmxmmx = XDisplayWidthMM(dpy, screen), _error_ mmymmy = XDisplayHeightMM(dpy, screen);
emitf("core_screen", "px=%dx%d mm=%dx%d dpi=%.1fx%.1f", px, py, mmx, mmy,
mmx > 0 ? px * 25.4 / mmx : -1.0, mmy > 0 ? py * 25.4 / mmy : -1.0);
if (!haveRandr)
return;
XRRScreenResources* _error_ resres = XRRGetScreenResourcesCurrent(dpy, root);
if (res is null)
return;
foreach ((parameter) ii; 0 .. res.noutput)
{
XRROutputInfo* _error_ oioi = XRRGetOutputInfo(dpy, res, res.outputs[i]);
if (oi is null)
continue;
int _error_ cwcw = 0, _error_ chch = 0;
if (oi.crtc != 0)
{
XRRCrtcInfo* _error_ cici = XRRGetCrtcInfo(dpy, res, oi.crtc);
if (ci !is null)
{
cw = ci.ci.widthwidth;
ch = ci.ci.heightheight;
XRRFreeCrtcInfo(ci);
}
}
emitf("randr_output", "name=%s connected=%d px=%dx%d mm=%lux%lu dpi=%.1fx%.1f",
oi.oi.namename, cast(int)(oi.connection == RR_Connected), cw, ch,
oi.mm_width, oi.mm_height,
oi.mm_width > 0 ? cw * 25.4 / oi.mm_width : -1.0,
oi.mm_height > 0 ? ch * 25.4 / oi.mm_height : -1.0);
XRRFreeOutputInfo(oi);
}
XRRFreeScreenResources(res);
}
// ---------------------------------------------------------------------------
// Rendering: gradient, 1-px hairline border, and a dpi-length bar.
void app.drawdraw(XImage* (parameter) XImage* imgimg, double (parameter) double xftDpixftDpi) @nogc nothrow
{
const _error_ ww = img.img.widthwidth, _error_ hh = img.img.heightheight;
if (img.bits_per_pixel != 32)
return;
foreach ((parameter) yy; 0 .. h)
{
auto _error_ rowrow = cast(uint*)(img.data + y * img.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);
uint _error_ pixelpixel = (r << 16) | (g << 8) | 0xc0;
// 1-physical-px black hairline at the very edge (F08 req 1):
// under any kind of scaling/stretching it stops being 1 px.
if (x == 0 || y == 0 || x == w - 1 || y == h - 1)
pixel = 0;
// The dpi bar: a white bar `xftDpi` pixels long (96 -> short,
// 144 -> visibly longer) so a live Xft.dpi change WOULD be
// visible — it never is, because no event triggers a redraw.
if (y >= 10 && y < 26 && x >= 8 && x < 8 + cast(int)(xftDpi > 0 ? xftDpi : 0))
pixel = 0xffffff;
row[x] = pixel;
}
}
}
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("f08_x11");
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';
const (local variable) const(char*) envDurenvDur = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv("WSI_DURATION_MS");
const (local variable) const(int) durationMsdurationMs = (local variable) const(char*) envDurenvDur !is null ? int core.stdc.stdlib.atoi(scope const(char*) nptr) nothrow @nogcatoi((local variable) const(char*) envDurenvDur) : 10_000;
XrmInitialize();
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);
Window (local variable) _error_ rootroot = XRootWindow(dpy, screen);
Visual* (local variable) _error_ visualvisual = XDefaultVisual(dpy, screen);
const (local variable) const(_error_) depthdepth = XDefaultDepth(dpy, screen);
// -- RandR availability ----------------------------------------------------
int (local variable) int rrEventBaserrEventBase = -1, (local variable) int rrErrorBaserrErrorBase = -1, (local variable) int rrMajorrrMajor = 0, (local variable) int rrMinorrrMinor = 0;
const (local variable) const(_error_) haveRandrhaveRandr = XRRQueryExtension(dpy, &rrEventBase, &rrErrorBase) != 0;
if (haveRandr)
XRRQueryVersion(dpy, &rrMajor, &rrMinor);
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=XRRQueryExtension available=%d version=%d.%d event_base=%d",
cast(int) haveRandr, (local variable) int rrMajorrrMajor, (local variable) int rrMinorrrMinor, (local variable) int rrEventBaserrEventBase);
// -- The startup snapshot: everything a toolkit can ever know -------------
const(char)* (local variable) const(char)* rmSnapshotrmSnapshot = XResourceManagerString(dpy); // cached at connect
double (local variable) double xftDpixftDpi = double app.readXftDpi(const(char)* resStr, const(char)* who) nothrow @nogcParse Xft.dpi from a RESOURCE_MANAGER-style resource string via the Xrm
machinery (XrmGetStringDatabase + XrmGetResource — the same lookup
toolkits do). Returns the value or -1 when the resource is absent.
who labels the source in the log (startup / stale / fresh).
readXftDpi((local variable) const(char)* rmSnapshotrmSnapshot, "startup_XResourceManagerString");
logDpiSnapshot(dpy, screen, root, haveRandr);
// -- Subscribe to the only live channels that exist ------------------------
// (a) PropertyNotify on the ROOT window: RESOURCE_MANAGER is a root
// property, so a client that explicitly selects PropertyChangeMask on
// the root sees `xrdb -merge` happen. No standard says it must look.
XSelectInput(dpy, root, PropertyChangeMask);
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("step name=XSelectInput window=root mask=PropertyChangeMask");
// (b) RandR change events (screen size / output / crtc changes).
if (haveRandr)
{
XRRSelectInput(dpy, root, RRScreenChangeNotifyMask
| RRCrtcChangeNotifyMask | RROutputChangeNotifyMask);
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("step name=XRRSelectInput mask=screen_change|crtc|output");
}
// -- Window + backbuffer (plain XPutImage; SHM is noise here) -------------
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));
XStoreName(dpy, win, "Sparkles · X11 F08 dpi");
Atom (local variable) _error_ wmDeletewmDelete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
XSetWMProtocols(dpy, win, &wmDelete, 1);
XSelectInput(dpy, win, ExposureMask | KeyPressMask | StructureNotifyMask
| PropertyChangeMask); // PropertyChangeMask on the WINDOW too — to
// prove nothing DPI-ish ever arrives there either.
XMapWindow(dpy, win);
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 scale=assumed_1", win, (local variable) int widthwidth, (local variable) int heightheight);
GC (local variable) _error_ gcgc = XDefaultGC(dpy, screen);
XImage* (local variable) _error_ imgimg = XCreateImage(dpy, visual, cast(uint) depth, ZPixmap, 0,
cast(char*) malloc(cast(size_t) width * height * 4),
cast(uint) width, cast(uint) height, 32, 0);
const (local variable) const(_error_) fdfd = XConnectionNumber(dpy);
bool (local variable) bool runningrunning = true, (local variable) bool needsRedrawneedsRedraw = false, (local variable) bool snapshotDonesnapshotDone = false;
int (local variable) int windowEventsAfterStartupwindowEventsAfterStartup = 0, (local variable) int rootPropertyNotifiesrootPropertyNotifies = 0, (local variable) int randrNotifiesrandrNotifies = 0;
while ((local variable) bool runningrunning)
{
while (XPending(dpy) > 0)
{
XEvent (local variable) _error_ evev;
XNextEvent(dpy, &ev);
// RandR's RRScreenChangeNotify arrives at a dynamic type code.
if (haveRandr && ev.(field) _error_ ev.typetype == (local variable) int rrEventBaserrEventBase + (enum value) app.RRScreenChangeNotify = 0RRScreenChangeNotify)
{
++(local variable) int randrNotifiesrandrNotifies;
auto (local variable) _error_ rrrr = cast(XRRScreenChangeNotifyEvent*)&ev;
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("randr_screen_change", "px=%dx%d mm=%dx%d dpi=%.1fx%.1f",
rr.(field) _error_ rr.widthwidth, rr.(field) _error_ rr.heightheight, rr.mwidth, rr.mheight,
rr.mwidth > 0 ? rr.(field) _error_ rr.widthwidth * 25.4 / rr.mwidth : -1.0,
rr.mheight > 0 ? rr.(field) _error_ rr.heightheight * 25.4 / rr.mheight : -1.0);
// Refresh Xlib's cached core-screen values (they are a connect-
// time snapshot too) and log what the core API now reports.
XRRUpdateConfiguration(&ev);
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("core_screen_after_update", "px=%dx%d mm=%dx%d",
XDisplayWidth(dpy, screen), XDisplayHeight(dpy, screen),
XDisplayWidthMM(dpy, screen), XDisplayHeightMM(dpy, screen));
continue;
}
// Root-window events: the RESOURCE_MANAGER live channel.
if (ev.xany.window == root && ev.(field) _error_ ev.typetype == (enum value) app.PropertyNotify = 28PropertyNotify)
{
++(local variable) int rootPropertyNotifiesrootPropertyNotifies;
char* (local variable) char* namename = XGetAtomName(dpy, ev.xproperty.atom);
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("root_property_notify", "atom=%s state=%s", (local variable) char* namename,
ev.xproperty.state == 0 ? "NewValue".ptr : "Deleted".ptr);
XFree(name);
if (ev.xproperty.atom == XA_RESOURCE_MANAGER)
{
// The trap: Xlib's own accessor still returns the value
// cached at XOpenDisplay time ...
double app.readXftDpi(const(char)* resStr, const(char)* who) nothrow @nogcParse Xft.dpi from a RESOURCE_MANAGER-style resource string via the Xrm
machinery (XrmGetStringDatabase + XrmGetResource — the same lookup
toolkits do). Returns the value or -1 when the resource is absent.
who labels the source in the log (startup / stale / fresh).
readXftDpi(XResourceManagerString(dpy),
"stale_XResourceManagerString");
// ... a fresh XGetWindowProperty sees the new one.
char* (local variable) char* freshfresh = fetchResourceManager(dpy, root);
(local variable) double xftDpixftDpi = double app.readXftDpi(const(char)* resStr, const(char)* who) nothrow @nogcParse Xft.dpi from a RESOURCE_MANAGER-style resource string via the Xrm
machinery (XrmGetStringDatabase + XrmGetResource — the same lookup
toolkits do). Returns the value or -1 when the resource is absent.
who labels the source in the log (startup / stale / fresh).
readXftDpi((local variable) char* freshfresh, "fresh_XGetWindowProperty");
if ((local variable) char* freshfresh !is null)
XFree(fresh);
(local variable) bool needsRedrawneedsRedraw = true; // ONLY because we chose to watch root
}
continue;
}
// Everything else is addressed to our window. After startup
// settles, each one is logged — the absence of any DPI-related
// event here is the finding.
if (ev.xany.window == win && (local variable) bool snapshotDonesnapshotDone)
{
++(local variable) int windowEventsAfterStartupwindowEventsAfterStartup;
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("window_event", "type=%d", ev.(field) _error_ ev.typetype);
}
switch (ev.(field) _error_ ev.typetype)
{
case Expose:
if (ev.xexpose.count == 0)
needsRedraw = true;
break;
case ConfigureNotify:
if (ev.xconfigure.ev.xconfigure.widthwidth != width || ev.xconfigure.ev.xconfigure.heightheight != height)
{
width = ev.xconfigure.ev.xconfigure.widthwidth;
height = ev.xconfigure.ev.xconfigure.heightheight;
emitf("resize", "size=%dx%d scale=still_assumed_1", width, height);
img.f.destroy_image(img); // XDestroyImage macro's body
img = XCreateImage(dpy, visual, cast(uint) depth, ZPixmap, 0,
cast(char*) malloc(cast(size_t) width * height * 4),
cast(uint) width, cast(uint) height, 32, 0);
needsRedraw = true;
}
break;
case MapNotify:
snapshotDone = true;
emit("startup_snapshot_complete (window events are counted from here)");
break;
case ClientMessage:
if (cast(Atom) ev.xclient.data.l[0] == wmDelete)
{
emit("close_requested via=WM_DELETE_WINDOW");
running = false;
}
break;
case KeyPress:
running = false;
break;
default:
break;
}
}
if ((local variable) bool needsRedrawneedsRedraw && img !is null)
{
draw(img, xftDpi);
XPutImage(dpy, win, gc, img, 0, 0, 0, 0,
cast(uint) width, cast(uint) height);
XFlush(dpy); // push the put before sleeping in poll()
(local variable) bool needsRedrawneedsRedraw = false;
}
if ((local variable) const(bool) autoExitautoExit && long instrument.nowUs() nothrow @nogcMicroseconds elapsed since initInstrument.
nowUs() > cast(long) (local variable) const(int) durationMsdurationMs * 1000)
{
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("auto_exit");
break;
}
pollfd (local variable) _error_ pfdpfd;
pfd.pfd.fdfd = fd;
pfd.events = POLLIN;
pfd.revents = 0;
poll(&pfd, 1, autoExit ? 100 : -1);
}
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("summary", "root_property_notifies=%d randr_notifies=%d "
~ "window_events_after_startup=%d final_xft_dpi=%.1f",
(local variable) int rootPropertyNotifiesrootPropertyNotifies, (local variable) int randrNotifiesrandrNotifies, (local variable) int windowEventsAfterStartupwindowEventsAfterStartup, (local variable) double xftDpixftDpi);
img.f.destroy_image(img);
XDestroyWindow(dpy, win);
XCloseDisplay(dpy);
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("teardown");
return 0;
}