// X11 F12 demo — cursors (../../../features/f12-cursors.md). Built on the
// scaffold (../scaffold/app.d): same ImportC binding style, same poll(2)
// readiness loop, same instrument.d event log. Rendering is grid lines via
// the default GC — the pixels that matter here are the CURSOR's, and those
// are composited by the SERVER (contrast Wayland, where the client renders
// its cursor into a wl_surface and runs its own animation frame timer).
//
// A 3x3 hover-zone grid: the eight edge/corner zones carry the eight resize
// cursors; the centre zone cycles default -> text -> pointer -> a custom ARGB
// bullseye -> an animated 'watch' on each re-entry. Every switch is one
// `XDefineCursor` + `XFlush`, logged as `cursor_set`. Four mechanisms are
// exercised and distinguished in the log:
//
// * font — `XCreateFontCursor(dpy, glyph)`: the core `cursor` font baked
// into every X server since X11R1; glyph ids from <X11/cursorfont.h>
// (macros — re-declared below per the scaffold ImportC gotcha).
// * theme — `XcursorLibraryLoadCursor(dpy, "nw-resize")`: libXcursor file
// lookup through XCURSOR_THEME/XCURSOR_PATH/XCURSOR_SIZE (+ the display's
// Xcursor.theme/.size resources). Startup logs the whole resolution:
// `XcursorGetTheme`, `XcursorGetDefaultSize`, the env vars, and for every
// shape BOTH load results — the run.sh driver varies the env and measures
// which knobs libXcursor honors.
// * custom — `XcursorImageCreate(24,24)` filled with raw premultiplied-ARGB
// (a bullseye, hotspot 12,12) -> `XcursorImageLoadCursor`. The legacy
// core alternative, `XCreatePixmapCursor`, takes a 1-bit source + mask
// and exactly two colors — libXcursor is the only road to full ARGB.
// * animated — `XcursorLibraryLoadImages("watch", ...)` -> N frames with
// per-frame delays -> `XcursorImagesLoadCursor`. One call, then the
// SERVER animates; the client never wakes up again.
//
// Zones are driven either by the built-in XWarpPointer storm (WSI_AUTO_EXIT=1,
// disable with WSI_NO_WARP=1) or externally via `xdotool mousemove` (run.sh).
// WSI_DURATION_MS bounds the run (default 8 s). Headless-safe: no X server ->
// prints `SKIP:` and exits 0. Findings: ../../f12-cursors.md.
module (module) appapp;
import (module) cc; // ImportC: Xlib + Xcursor + poll
import (module) instrumentinstrument;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.configD compatible types that correspond to various basic types in associated
C and C++ compilers.
Source
core/stdc/config.d
config : c_long, c_ulong;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.stdioD header file for C99 <stdio.h>
pubs.opengroup.org/onlinepubs/009695399/basedefs/stdio.h.html, stdio.h
Source
core/stdc/stdio.d
stdio : (alias) app.printf = int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.stdlibD header file for C99.
pubs.opengroup.org/onlinepubs/009695399/basedefs/stdlib.h.html, stdlib.h
Source
core/stdc/stdlib.d
stdlib : (alias) app.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;
// ---------------------------------------------------------------------------
// Constants Xlib exposes as macros that ImportC cannot import; re-declared
// per the scaffold gotcha (../../scaffold.md).
enum : c_long
{
(enum value) app.KeyPressMask = 1LKeyPressMask = 1L << 0,
(enum value) app.EnterWindowMask = 16LEnterWindowMask = 1L << 4,
(enum value) app.LeaveWindowMask = 32LLeaveWindowMask = 1L << 5,
(enum value) app.PointerMotionMask = 64LPointerMotionMask = 1L << 6,
(enum value) app.ExposureMask = 32768LExposureMask = 1L << 15,
(enum value) app.StructureNotifyMask = 131072LStructureNotifyMask = 1L << 17,
}
enum // XEvent.type discriminators
{
(enum value) app.KeyPress = 2KeyPress = 2,
(enum value) app.MotionNotify = 6MotionNotify = 6,
(enum value) app.EnterNotify = 7EnterNotify = 7,
(enum value) app.LeaveNotify = 8LeaveNotify = 8,
(enum value) app.Expose = 12Expose = 12,
(enum value) app.ConfigureNotify = 22ConfigureNotify = 22,
(enum value) app.ClientMessage = 33ClientMessage = 33,
}
enum (constant) int app.False = 0False = 0;
enum (constant) int app.True = 1True = 1;
enum (constant) int app.None = 0None = 0;
enum (constant) int app.POLLIN = 1POLLIN = 0x001;
// <X11/cursorfont.h> glyph ids (the header is #defines only). Each shape in
// the core `cursor` font; the full table is in the header / XCreateFontCursor(3).
enum : uint
{
(enum value) app.XC_bottom_left_corner = 12uXC_bottom_left_corner = 12,
(enum value) app.XC_bottom_right_corner = 14uXC_bottom_right_corner = 14,
(enum value) app.XC_bottom_side = 16uXC_bottom_side = 16,
(enum value) app.XC_hand2 = 60uXC_hand2 = 60,
(enum value) app.XC_left_ptr = 68uXC_left_ptr = 68,
(enum value) app.XC_left_side = 70uXC_left_side = 70,
(enum value) app.XC_right_side = 96uXC_right_side = 96,
(enum value) app.XC_top_left_corner = 134uXC_top_left_corner = 134,
(enum value) app.XC_top_right_corner = 136uXC_top_right_corner = 136,
(enum value) app.XC_top_side = 138uXC_top_side = 138,
(enum value) app.XC_watch = 150uXC_watch = 150,
(enum value) app.XC_xterm = 152uXC_xterm = 152,
}
// ---------------------------------------------------------------------------
// The shape vocabulary: one row per shape, carrying its cursor-spec themed
// name (the names cursor-shape-v1 / CSS standardized) AND its core-font glyph.
struct (struct) app.ShapeShape
{
const(char)* (field) const(char)* app.Shape.namename; // themed (cursor-spec/CSS) name for XcursorLibraryLoadCursor
uint (field) uint app.Shape.glyphglyph; // core cursor-font id for XCreateFontCursor
Cursor (field) _error_ app.Shape.fontfont; // loaded font cursor
Cursor (field) _error_ app.Shape.themetheme; // loaded themed cursor (0 = lookup failed)
}
// Index 0..8 = grid zones (centre zone 4 is handled dynamically); 9..11 = the
// centre-cycle shapes (default arrow, text I-beam, hand/link).
__gshared (struct) app.ShapeShape[12] _error_ app.g_shapesg_shapes = [
Shape("nw-resize", XC_top_left_corner),
Shape("n-resize", XC_top_side),
Shape("ne-resize", XC_top_right_corner),
Shape("w-resize", XC_left_side),
Shape("default", XC_left_ptr), // placeholder for the centre zone
Shape("e-resize", XC_right_side),
Shape("sw-resize", XC_bottom_left_corner),
Shape("s-resize", XC_bottom_side),
Shape("se-resize", XC_bottom_right_corner),
Shape("default", XC_left_ptr),
Shape("text", XC_xterm),
Shape("pointer", XC_hand2),
];
/// Load one shape through BOTH mechanisms and log both results: theme=0x0
/// means the libXcursor lookup failed end-to-end (no theme file found AND no
/// core fallback in libXcursor's name->glyph table for this name).
void app.loadShapeLoad one shape through BOTH mechanisms and log both results: theme=0x0
means the libXcursor lookup failed end-to-end (no theme file found AND no
core fallback in libXcursor's name->glyph table for this name).
loadShape(Display* (parameter) Display* dpydpy, ref (struct) app.ShapeShape (parameter) Shape ss) @nogc nothrow
{
s.s.fontfont = XCreateFontCursor(dpy, s.s.glyphglyph);
s.s.themetheme = XcursorLibraryLoadCursor(dpy, s.s.namename);
emitf("cursor_load", "name=%s font_glyph=%d font=0x%lx theme=0x%lx",
s.s.namename, s.s.glyphglyph, s.s.fontfont, s.s.themetheme);
}
/// Pick a shape's cursor, preferring the themed one; returns the path label.
const(char)* app.pickPick a shape's cursor, preferring the themed one; returns the path label.
pick(const ref (struct) app.ShapeShape (parameter) Shape ss, out Cursor (parameter) Cursor curcur) @nogc nothrow
{
if (s.s.themetheme != 0)
{
cur = s.s.themetheme;
return "theme";
}
cur = s.s.fontfont;
return "font";
}
/// Log what the theme actually resolves a name to at the file level: frame
/// count, nominal size vs actual pixel size, per-frame delay. This is also
/// the F12 "log chosen size" requirement — the size XcursorGetDefaultSize
/// picked is what the library loads.
void void app.logShapeImages(const(char)* name, const(char)* theme, int size) nothrow @nogcLog what the theme actually resolves a name to at the file level: frame
count, nominal size vs actual pixel size, per-frame delay. This is also
the F12 "log chosen size" requirement — the size XcursorGetDefaultSize
picked is what the library loads.
logShapeImages(const(char)* (parameter) const(char)* namename, const(char)* (parameter) const(char)* themetheme, int (parameter) int sizesize) @nogc nothrow
{
XcursorImages* (local variable) _error_ imgsimgs = XcursorLibraryLoadImages(name, theme, size);
if (imgs is null || imgs.nimage == 0)
{
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("cursor_images", "name=%s theme=%s size=%d frames=0 (lookup failed)",
(parameter) const(char)* namename, (parameter) const(char)* themetheme is null ? "(null)".(constant) immutable(char)* "(null)".ptr = "(null)"ptr : (parameter) const(char)* themetheme, (parameter) int sizesize);
if (imgs !is null)
XcursorImagesDestroy(imgs);
return;
}
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("cursor_images", "name=%s theme=%s size_requested=%d frames=%d "
~ "frame0=%ux%u nominal=%u hot=%u,%u delay_ms=%u",
(parameter) const(char)* namename, (parameter) const(char)* themetheme is null ? "(null)".(constant) immutable(char)* "(null)".ptr = "(null)"ptr : (parameter) const(char)* themetheme, (parameter) int sizesize, imgs.nimage,
imgs.images[0].(field) _error_ (__error)[0].widthwidth, imgs.images[0].(field) _error_ (__error)[0].heightheight, imgs.images[0].(field) _error_ (__error)[0].sizesize,
imgs.images[0].xhot, imgs.images[0].yhot, imgs.images[0].delay);
XcursorImagesDestroy(imgs);
}
/// The custom cursor: a 24x24 premultiplied-ARGB bullseye, hotspot dead
/// centre (12,12) — a hotspot the arrow's (0,0) habit would get wrong.
Cursor app.makeBullseyeThe custom cursor: a 24x24 premultiplied-ARGB bullseye, hotspot dead
centre (12,12) — a hotspot the arrow's (0,0) habit would get wrong.
makeBullseye(Display* (parameter) Display* dpydpy) @nogc nothrow
{
XcursorImage* _error_ imgimg = XcursorImageCreate(24, 24);
if (img is null)
return 0;
img.xhot = 12;
img.yhot = 12;
foreach ((parameter) yy; 0 .. 24)
foreach ((parameter) xx; 0 .. 24)
{
const _error_ dxdx = x - 11.5, _error_ dydy = y - 11.5;
const _error_ r2r2 = dx * dx + dy * dy;
uint _error_ pxpx = 0x00000000; // transparent outside
if (r2 <= 2.5 * 2.5)
px = 0xff000000; // black centre dot
else if (r2 <= 5.5 * 5.5)
px = 0xffffffff; // white ring
else if (r2 <= 8.5 * 8.5)
px = 0xffcc2222; // red ring
else if (r2 <= 11.5 * 11.5)
px = 0xffffffff; // white rim
img.pixels[y * 24 + x] = px; // premultiplied ARGB (alpha 0xff/0)
}
Cursor _error_ curcur = XcursorImageLoadCursor(dpy, img);
XcursorImageDestroy(img);
emitf("cursor_custom", "api=XcursorImageLoadCursor size=24x24 hotspot=12,12 cursor=0x%lx "
~ "(legacy XCreatePixmapCursor: 1-bit source+mask, exactly 2 colors)", cur);
return cur;
}
// ---------------------------------------------------------------------------
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("f12_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*) envNoWarpenvNoWarp = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv("WSI_NO_WARP");
const (local variable) const(bool) warpwarp = (local variable) const(bool) autoExitautoExit && ((local variable) const(char*) envNoWarpenvNoWarp is null || (local variable) const(char*) envNoWarpenvNoWarp[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) : 8_000;
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);
// -- Theme resolution: log every input libXcursor consults --------------
const(char)* (local variable) const(char)* envThemeenvTheme = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv("XCURSOR_THEME"), (local variable) const(char)* envPathenvPath = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv("XCURSOR_PATH"),
(local variable) const(char)* envSizeenvSize = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv("XCURSOR_SIZE");
char* (local variable) char* themetheme = XcursorGetTheme(dpy);
const (local variable) const(_error_) sizesize = XcursorGetDefaultSize(dpy);
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("xcursor_env", "XCURSOR_THEME=%s XCURSOR_PATH=%s XCURSOR_SIZE=%s",
(local variable) const(char)* envThemeenvTheme ? (local variable) const(char)* envThemeenvTheme : "(unset)", (local variable) const(char)* envPathenvPath ? (local variable) const(char)* envPathenvPath : "(unset)",
(local variable) const(char)* envSizeenvSize ? (local variable) const(char)* envSizeenvSize : "(unset)");
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("xcursor_resolved", "XcursorGetTheme=%s XcursorGetDefaultSize=%d "
~ "XcursorSupportsARGB=%d",
(local variable) char* themetheme ? (local variable) char* themetheme : "(null)", size, cast(int) XcursorSupportsARGB(dpy));
// -- Load every shape via BOTH mechanisms --------------------------------
foreach (ref (parameter) ss; g_shapes)
loadShape(dpy, s);
// File-level resolution for one static and one animated shape: what size
// was actually chosen, and how many frames does 'watch' carry?
void app.logShapeImages(const(char)* name, const(char)* theme, int size) nothrow @nogcLog what the theme actually resolves a name to at the file level: frame
count, nominal size vs actual pixel size, per-frame delay. This is also
the F12 "log chosen size" requirement — the size XcursorGetDefaultSize
picked is what the library loads.
logShapeImages("default", (local variable) char* themetheme, size);
void app.logShapeImages(const(char)* name, const(char)* theme, int size) nothrow @nogcLog what the theme actually resolves a name to at the file level: frame
count, nominal size vs actual pixel size, per-frame delay. This is also
the F12 "log chosen size" requirement — the size XcursorGetDefaultSize
picked is what the library loads.
logShapeImages("watch", (local variable) char* themetheme, size);
Cursor (local variable) _error_ customcustom = makeBullseye(dpy);
// The animated cursor: count the frames ourselves, then hand ALL of them
// to the server in one cursor object — the server runs the animation.
Cursor (local variable) _error_ animatedanimated = 0;
const(char)* (local variable) const(char)* animNameanimName = "watch", (local variable) const(char)* animPathanimPath = "animated";
XcursorImages* (local variable) _error_ animanim = XcursorLibraryLoadImages("watch", theme, size);
if (anim is null || anim.nimage == 0)
{
if (anim !is null)
XcursorImagesDestroy(anim);
(local variable) const(char)* animNameanimName = "progress";
anim = XcursorLibraryLoadImages("progress", theme, size);
}
if (anim !is null && anim.nimage > 0)
{
animated = XcursorImagesLoadCursor(dpy, anim);
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("cursor_animated", "name=%s frames=%d delay_ms=%u cursor=0x%lx "
~ "animator=server (client sets it once, never ticks)",
(local variable) const(char)* animNameanimName, anim.nimage, anim.images[0].delay, animated);
XcursorImagesDestroy(anim);
}
else
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("cursor_animated frames=0 (no animated shape in reach; falls back to font XC_watch)");
if (animated == 0)
{
animated = XCreateFontCursor(dpy, XC_watch); // static fallback
(local variable) const(char)* animNameanimName = "watch";
(local variable) const(char)* animPathanimPath = "font-fallback-static";
}
// -- Window ---------------------------------------------------------------
int (local variable) int widthwidth = 480, (local variable) int heightheight = 480;
Window (local variable) _error_ winwin = XCreateSimpleWindow(dpy, root, 20, 20, width, height, 1,
XBlackPixel(dpy, screen), XWhitePixel(dpy, screen));
XStoreName(dpy, win, "Sparkles · X11 F12 cursors");
Atom (local variable) _error_ wmDeletewmDelete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
XSetWMProtocols(dpy, win, &wmDelete, 1);
XSelectInput(dpy, win, ExposureMask | KeyPressMask | StructureNotifyMask
| PointerMotionMask | EnterWindowMask | LeaveWindowMask);
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 zones=3x3", win, (local variable) int widthwidth, (local variable) int heightheight);
GC (local variable) _error_ gcgc = XDefaultGC(dpy, screen);
const (local variable) const(_error_) fdfd = XConnectionNumber(dpy);
bool (local variable) bool runningrunning = true;
int (local variable) int zonezone = -1, (local variable) int centerCyclecenterCycle = -1, (local variable) int warpStepwarpStep = 0;
int (local variable) int cursorSetscursorSets = 0;
// Centre-zone cycle: arrow -> text -> hand -> custom -> animated.
// The XWarpPointer storm re-enters the centre after each edge zone, so a
// full sweep exercises all five.
static immutable int[17] (immutable global) immutable(int[17]) app.main.warpZoneswarpZones = [0, 4, 1, 4, 2, 4, 3, 4, 5, 4, 6, 4, 7, 4, 8, 4, 0];
void void app.main.setZoneCursor(int z) pure nothrow @nogc @safesetZoneCursor(int (parameter) int zz) @nogc nothrow
{
Cursor (local variable) _error_ curcur;
const(char)* (local variable) const(char)* pathpath, (local variable) const(char)* namename;
if ((parameter) int zz == 4) // centre: advance the cycle on each entry
{
(local variable) int centerCyclecenterCycle = ((local variable) int centerCyclecenterCycle + 1) % 5;
switch ((local variable) int centerCyclecenterCycle)
{
case 0: case 1: case 2:
(local variable) const(char)* namename = g_shapes[9 + centerCycle].(field) _error_ (__error)[9 + centerCycle].namename;
(local variable) const(char)* pathpath = pick(g_shapes[9 + centerCycle], cur);
break;
case 3:
(local variable) const(char)* namename = "custom-bullseye";
(local variable) const(char)* pathpath = "custom";
cur = custom;
break;
default:
(local variable) const(char)* namename = (local variable) const(char)* animNameanimName;
(local variable) const(char)* pathpath = (local variable) const(char)* animPathanimPath;
cur = animated;
break;
}
}
else
{
(local variable) const(char)* namename = g_shapes[z].(field) _error_ (__error)[z].namename;
(local variable) const(char)* pathpath = pick(g_shapes[z], cur);
}
XDefineCursor(dpy, win, cur);
XFlush(dpy); // the request must reach the server before we sleep
++(local variable) int cursorSetscursorSets;
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("cursor_set", "zone=%d name=%s path=%s size=%d cursor=0x%lx",
(parameter) int zz, (local variable) const(char)* namename, (local variable) const(char)* pathpath, size, cur);
}
while ((local variable) bool runningrunning)
{
while (XPending(dpy) > 0)
{
XEvent (local variable) _error_ evev;
XNextEvent(dpy, &ev);
switch (ev.type)
{
case MotionNotify:
case EnterNotify:
const _error_ xx = ev.type == MotionNotify ? ev.xmotion.ev.xmotion.xx : ev.xcrossing.ev.xcrossing.xx;
const _error_ yy = ev.type == MotionNotify ? ev.xmotion.ev.xmotion.yy : ev.xcrossing.ev.xcrossing.yy;
int _error_ zxzx = x * 3 / (width > 0 ? width : 1), _error_ zyzy = y * 3 / (height > 0 ? height : 1);
zx = zx < 0 ? 0 : (zx > 2 ? 2 : zx);
zy = zy < 0 ? 0 : (zy > 2 ? 2 : zy);
const _error_ zz = zy * 3 + zx;
if (z != zone)
{
zone = z;
setZoneCursor(z);
}
break;
case LeaveNotify:
zone = -1; // re-entering the same zone counts as an entry
break;
case Expose:
if (ev.xexpose.count == 0)
{
foreach ((parameter) ii; 1 .. 3) // the 3x3 grid, server-side lines
{
XDrawLine(dpy, win, gc, width * i / 3, 0, width * i / 3, height);
XDrawLine(dpy, win, gc, 0, height * i / 3, width, height * i / 3);
}
XFlush(dpy);
}
break;
case ConfigureNotify:
width = ev.xconfigure.ev.xconfigure.widthwidth;
height = ev.xconfigure.ev.xconfigure.heightheight;
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) const(bool) autoExitautoExit)
{
const (local variable) const(long) elapsedMselapsedMs = long instrument.nowUs() nothrow @nogcMicroseconds elapsed since initInstrument.
nowUs() / 1000;
// The built-in driver: one warp every 300 ms through all zones,
// re-entering the centre between edge zones (cycles all 5 centre
// cursors). The warp generates real MotionNotify events.
if ((local variable) const(bool) warpwarp && (local variable) int warpStepwarpStep < (immutable global) immutable(int[17]) app.main.warpZoneswarpZones.(constant) ulong immutable(int[17]).length = 17LUlength && (local variable) const(long) elapsedMselapsedMs > 500 + 300 * (local variable) int warpStepwarpStep)
{
const (local variable) immutable(int) zz = (immutable global) immutable(int[17]) app.main.warpZoneswarpZones[(local variable) int warpStepwarpStep];
XWarpPointer(dpy, None, win, 0, 0, 0, 0,
(z % 3) * width / 3 + width / 6, (z / 3) * height / 3 + height / 6);
XFlush(dpy);
++(local variable) int warpStepwarpStep;
}
if ((local variable) const(long) elapsedMselapsedMs > (local variable) const(int) durationMsdurationMs)
{
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 ? 50 : -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", "cursor_sets=%d theme=%s default_size=%d",
(local variable) int cursorSetscursorSets, (local variable) char* themetheme ? (local variable) char* themetheme : "(null)", size);
XFreeCursor(dpy, custom);
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;
}