// F12 — Cursors, Win32 implementation (../../../features/f12-cursors.md).
// Extends the scaffold (../scaffold/app.d) into a WM_SETCURSOR observatory:
//
// * A 3×3 hover-zone grid over the client area: the 8 border cells map the
// 8 resize edges onto Win32's FOUR bidirectional resize shapes
// (IDC_SIZENWSE / IDC_SIZENS / IDC_SIZENESW / IDC_SIZEWE — the vocabulary
// has no per-edge cursors), and the center cell is subdivided 2×2 into
// IDC_ARROW / IDC_IBEAM / IDC_HAND / a custom cursor. Every WM_SETCURSOR
// is logged (the per-mouse-move storm), plus cursor_set on zone change.
// * The pointer is driven from inside the demo: SetCursorPos steps a
// 12-stop tour across the zones (winewayland has no external warp tool;
// SetCursorPos goes through wineserver's virtual cursor and works), and
// each warp's WM_MOUSEMOVE → WM_SETCURSOR cascade lands in the log.
// * One custom ARGB cursor via CreateIconIndirect (32×32 bullseye, hotspot
// 16,16: a 32bpp top-down DIB color bitmap + an all-zero monochrome mask).
// CreateCursor is NOT used — it only takes monochrome AND/XOR planes.
// * Class-cursor vs WM_SETCURSOR precedence probe: the class registers
// hCursor=IDC_CROSS; phases then answer WM_SETCURSOR differently —
// normal: SetCursor(zone)+return TRUE; set_then_def: SetCursor(IDC_HAND)
// then DefWindowProcW; class_only: straight to DefWindowProcW — and
// GetCursor() is sampled afterwards to capture who won.
// * DPI: logs GetSystemMetrics(SM_CXCURSOR/SM_CYCURSOR); animated cursors:
// attempts LoadCursorFromFileW on the prefix's C:\windows\cursors .ani
// (logged either way; Wine prefixes ship no .ani files).
// SetSystemCursor (system-wide cursor replacement) is deliberately NOT
// called — it would mutate host-global state.
//
// WSI_AUTO_EXIT=1 bounds the run (~2 s); exit 0 in all modes.
//
// Only druntime's built-in core.sys.windows bindings — no third-party packages.
module (module) appapp;
import (package) corecore.(package) core.syssys.(package) core.sys.windowswindows.(module) core.sys.windows.windowsWindows API header module
Translated from MinGW API for MS-Windows 4.0
Source
core/sys/windows/windows.d
windows;
import instrument;
// ---------------------------------------------------------------------------
// Cursor inventory: system shapes + one custom ARGB cursor.
enum (enum) app.CursorIdCursorId
{
(enum value) app.CursorId.arrow = 0arrow,
(enum value) app.CursorId.ibeam = 1ibeam,
(enum value) app.CursorId.hand = 2hand,
(enum value) app.CursorId.sizenwse = 3sizenwse, // ↖↘ — serves both the NW and SE edges
(enum value) app.CursorId.sizenesw = 4sizenesw, // ↗↙ — serves both the NE and SW edges
(enum value) app.CursorId.sizewe = 5sizewe, // ↔ — serves both the W and E edges
(enum value) app.CursorId.sizens = 6sizens, // ↕ — serves both the N and S edges
(enum value) app.CursorId.cross = 7cross, // the class cursor (precedence probe)
(enum value) app.CursorId.custom = 8custom, // 32×32 ARGB bullseye, hotspot (16,16)
}
immutable (alias) object.string = stringstring[(enum) app.CursorIdCursorId.(constant) app.CursorId app.CursorId.max = CursorId.custommax + 1] (immutable global) immutable(string[9]) app.cursorNamescursorNames = [
"IDC_ARROW", "IDC_IBEAM", "IDC_HAND", "IDC_SIZENWSE", "IDC_SIZENESW",
"IDC_SIZEWE", "IDC_SIZENS", "IDC_CROSS", "custom_bullseye",
];
__gshared HCURSOR[CursorId.CursorId.maxmax + 1] _error_ app.cursorscursors;
const(char)* app.cursorNamecursorName(HCURSOR (parameter) HCURSOR hh) nothrow
{
if (h is null)
return "null";
foreach ((parameter) ii, (parameter) cc; cursors)
if (c is h)
return cursorNames[i].ptr;
return "unknown";
}
// 32×32 ARGB bullseye with hotspot (16,16): concentric opaque rings over
// transparent ground. CreateIconIndirect with fIcon=FALSE turns the pair of
// bitmaps into a cursor; the 32bpp hbmColor carries the alpha channel, and
// the monochrome hbmMask must still be supplied (all-zero here).
HCURSOR app.createBullseyeCursorcreateBullseyeCursor() nothrow
{
enum (constant) N = 32N = 32;
BITMAPINFO _error_ bmibmi;
bmi.bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
bmi.bmiHeader.biWidth = N;
bmi.bmiHeader.biHeight = -N; // top-down
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
void* _error_ bitsbits;
HBITMAP _error_ colorcolor = CreateDIBSection(null, &bmi, DIB_RGB_COLORS, &bits, null, 0);
if (color is null)
return null;
auto _error_ pxpx = cast(uint*) bits;
foreach ((parameter) yy; 0 .. N)
foreach ((parameter) xx; 0 .. N)
{
const _error_ dxdx = x - 16, _error_ dydy = y - 16;
const _error_ r2r2 = dx * dx + dy * dy;
uint _error_ argbargb = 0; // transparent
if (r2 <= 12 || (r2 >= 64 && r2 <= 110) || (r2 >= 196 && r2 <= 256))
argb = 0xff000000 | (r2 <= 12 ? 0x00ff0000 : 0x00ffffff);
px[cast(size_t) y * N + x] = argb;
}
static immutable ubyte[N * N / 8] ubyte[N * N / 8] maskBitsmaskBits; // all zero
HBITMAP _error_ maskmask = CreateBitmap(N, N, 1, 1, maskBits.ptr);
ICONINFO _error_ iiii;
ii.fIcon = FALSE; // FALSE = cursor → hotspot fields are honored
ii.xHotspot = 16;
ii.yHotspot = 16;
ii.hbmMask = mask;
ii.hbmColor = color;
HCURSOR _error_ curcur = cast(HCURSOR) CreateIconIndirect(&ii);
DeleteObject(color); // CreateIconIndirect copies both bitmaps
DeleteObject(mask);
return cur;
}
void void app.loadCursors() nothrowloadCursors() nothrow
{
cursors[CursorId.CursorId.arrowarrow] = LoadCursorW(null, IDC_ARROW);
cursors[CursorId.CursorId.ibeamibeam] = LoadCursorW(null, IDC_IBEAM);
cursors[CursorId.CursorId.handhand] = LoadCursorW(null, IDC_HAND);
cursors[CursorId.CursorId.sizenwsesizenwse] = LoadCursorW(null, IDC_SIZENWSE);
cursors[CursorId.CursorId.sizeneswsizenesw] = LoadCursorW(null, IDC_SIZENESW);
cursors[CursorId.CursorId.sizewesizewe] = LoadCursorW(null, IDC_SIZEWE);
cursors[CursorId.CursorId.sizenssizens] = LoadCursorW(null, IDC_SIZENS);
cursors[CursorId.CursorId.crosscross] = LoadCursorW(null, IDC_CROSS);
cursors[CursorId.CursorId.customcustom] = createBullseyeCursor();
foreach ((parameter) ii, (parameter) cc; cursors)
logEvent("cursor_loaded name=%s handle=%p", cursorNames[i].ptr, c);
}
// ---------------------------------------------------------------------------
// Hover zones: 3×3 grid, the 8 border cells = the 8 resize edges, the center
// cell subdivided 2×2 (arrow / ibeam / hand / custom).
struct (struct) app.ZoneZone
{
const(char)* (field) const(char)* app.Zone.namename;
(enum) app.CursorIdCursorId (field) app.CursorId app.Zone.cursorcursor;
}
// Border cells by (col, row), center handled separately.
immutable (struct) app.ZoneZone[3][3] (immutable global) immutable(app.Zone[3][3]) app.borderZonesborderZones = [
[{"nw", (enum) app.CursorIdCursorId.(enum value) app.CursorId.sizenwse = 3sizenwse}, {"n", (enum) app.CursorIdCursorId.(enum value) app.CursorId.sizens = 6sizens}, {"ne", (enum) app.CursorIdCursorId.(enum value) app.CursorId.sizenesw = 4sizenesw}],
[{"w", (enum) app.CursorIdCursorId.(enum value) app.CursorId.sizewe = 5sizewe}, {"center", (enum) app.CursorIdCursorId.(enum value) app.CursorId.arrow = 0arrow}, {"e", (enum) app.CursorIdCursorId.(enum value) app.CursorId.sizewe = 5sizewe}],
[{"sw", (enum) app.CursorIdCursorId.(enum value) app.CursorId.sizenesw = 4sizenesw}, {"s", (enum) app.CursorIdCursorId.(enum value) app.CursorId.sizens = 6sizens}, {"se", (enum) app.CursorIdCursorId.(enum value) app.CursorId.sizenwse = 3sizenwse}],
];
immutable (struct) app.ZoneZone[4] (immutable global) immutable(app.Zone[4]) app.centerZonescenterZones = [
{"c_arrow", (enum) app.CursorIdCursorId.(enum value) app.CursorId.arrow = 0arrow}, {"c_ibeam", (enum) app.CursorIdCursorId.(enum value) app.CursorId.ibeam = 1ibeam},
{"c_hand", (enum) app.CursorIdCursorId.(enum value) app.CursorId.hand = 2hand}, {"c_custom", (enum) app.CursorIdCursorId.(enum value) app.CursorId.custom = 8custom},
];
(struct) app.ZoneZone app.Zone app.zoneForPoint(int x, int y, int w, int h) nothrowzoneForPoint(int (parameter) int xx, int (parameter) int yy, int (parameter) int ww, int (parameter) int hh) nothrow
{
if ((parameter) int ww <= 0 || (parameter) int hh <= 0)
return (struct) app.ZoneZone("outside", (enum) app.CursorIdCursorId.(enum value) app.CursorId.arrow = 0arrow);
int (local variable) int colcol = (parameter) int xx * 3 / (parameter) int ww, (local variable) int rowrow = (parameter) int yy * 3 / (parameter) int hh;
if ((local variable) int colcol < 0) (local variable) int colcol = 0; if ((local variable) int colcol > 2) (local variable) int colcol = 2;
if ((local variable) int rowrow < 0) (local variable) int rowrow = 0; if ((local variable) int rowrow > 2) (local variable) int rowrow = 2;
if ((local variable) int colcol != 1 || (local variable) int rowrow != 1)
return (immutable global) immutable(app.Zone[3][3]) app.borderZonesborderZones[(local variable) int rowrow][(local variable) int colcol];
// center cell: quadrants
const (local variable) const(int) qxqx = (parameter) int xx * 6 / (parameter) int ww >= 3 ? 1 : 0; // right half of the center cell
const (local variable) const(int) qyqy = (parameter) int yy * 6 / (parameter) int hh >= 3 ? 1 : 0; // bottom half
return (immutable global) immutable(app.Zone[4]) app.centerZonescenterZones[(local variable) const(int) qyqy * 2 + (local variable) const(int) qxqx];
}
// The 12-stop SetCursorPos tour: 8 border zones, then the 4 center quadrants.
// Coordinates are zone centers in 1/6ths of the client size.
struct (struct) app.StopStop
{
const(char)* (field) const(char)* app.Stop.namename;
int (field) int app.Stop.sxsx, (field) int app.Stop.sysy; // client position numerators over /6
}
immutable (struct) app.StopStop[12] (immutable global) immutable(app.Stop[12]) app.tourtour = [
{"nw", 1, 1}, {"n", 3, 1}, {"ne", 5, 1}, {"e", 5, 3},
{"se", 5, 5}, {"s", 3, 5}, {"sw", 1, 5}, {"w", 1, 3},
{"c_arrow", 13, 13}, {"c_ibeam", 17, 13}, // center quadrants: /30ths
{"c_hand", 13, 17}, {"c_custom", 17, 17},
];
// ---------------------------------------------------------------------------
// Demo state.
enum UINT_PTR (constant) _error_ app.TIMER_ID = __errorTIMER_ID = 1;
enum (constant) int app.TICK_MS = 16TICK_MS = 16;
enum (constant) int app.TOUR_STEP_TICKS = 6TOUR_STEP_TICKS = 6; // one SetCursorPos warp per ~96 ms
enum (enum) app.PhasePhase
{
(enum value) app.Phase.normal = 0normal, // SetCursor(zone) + return TRUE
(enum value) app.Phase.setThenDef = 1setThenDef, // SetCursor(IDC_HAND) then DefWindowProcW — who wins?
(enum value) app.Phase.classOnly = 2classOnly, // straight to DefWindowProcW → class cursor (IDC_CROSS)
}
immutable (alias) object.string = stringstring[(enum) app.PhasePhase.(constant) app.Phase app.Phase.max = Phase.classOnlymax + 1] (immutable global) immutable(string[3]) app.phaseNamesphaseNames = ["normal", "set_then_def", "class_only"];
struct (struct) app.DemoDemo
{
HDC (field) _error_ app.Demo.memDcmemDc;
HBITMAP (field) _error_ app.Demo.dibdib, (field) _error_ app.Demo.stockBmpstockBmp;
uint* (field) uint* app.Demo.pixelspixels;
int (field) int app.Demo.widthwidth, (field) int app.Demo.heightheight;
uint (field) uint app.Demo.frameframe, (field) uint app.Demo.ticksticks;
uint (field) uint app.Demo.nSetCursornSetCursor, (field) uint app.Demo.nMouseMovenMouseMove;
int (field) int app.Demo.tourIndextourIndex = -1; // last issued stop
(enum) app.PhasePhase (field) app.Phase app.Demo.phasephase;
const(char)* (field) const(char)* app.Demo.lastZonelastZone = "";
bool (field) bool app.Demo.autoExitautoExit;
bool (field) bool app.Demo.firstPaintDonefirstPaintDone;
}
__gshared (struct) app.DemoDemo _error_ app.gg;
void void app.createBackbuffer(int w, int h) nothrowcreateBackbuffer(int (parameter) int ww, int (parameter) int hh) nothrow
{
if (g.(field) _error_ g.dibdib !is null)
{
SelectObject(g.g.memDcmemDc, g.g.stockBmpstockBmp);
DeleteObject(g.g.dibdib);
g.g.dibdib = null;
g.g.pixelspixels = null;
g.g.widthwidth = g.g.heightheight = 0;
}
if ((parameter) int ww <= 0 || (parameter) int hh <= 0)
return;
BITMAPINFO (local variable) _error_ bmibmi;
bmi.bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
bmi.bmiHeader.biWidth = w;
bmi.bmiHeader.biHeight = -h;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
void* (local variable) void* bitsbits;
g.g.dibdib = CreateDIBSection(null, &bmi, DIB_RGB_COLORS, &bits, null, 0);
if (g.(field) _error_ g.dibdib is null)
{
logEvent("error what=CreateDIBSection code=%lu", GetLastError());
return;
}
g.g.pixelspixels = cast(uint*) bits;
g.g.widthwidth = w;
g.g.heightheight = h;
g.g.stockBmpstockBmp = cast(HBITMAP) SelectObject(g.g.memDcmemDc, g.g.dibdib);
logEvent("buffer_alloc size=%dx%d bytes=%d", w, h, w * h * 4);
}
// Scaffold gradient + the 3×3 grid lines so the zones are visible on screen.
void void app.drawFrame() nothrowdrawFrame() nothrow
{
if (g.(field) _error_ g.pixelspixels is null)
return;
const (local variable) const(_error_) ww = g.(field) _error_ g.widthwidth, (local variable) const(_error_) hh = g.(field) _error_ g.heightheight;
const (local variable) const(_error_) blueblue = (g.(field) _error_ g.frameframe * 4) & 0xff;
foreach ((parameter) yy; 0 .. h)
{
uint* _error_ rowrow = g.g.pixelspixels + cast(size_t) y * w;
const _error_ greengreen = h > 1 ? (y * 255) / (h - 1) : 0;
foreach ((parameter) xx; 0 .. w)
{
const _error_ redred = w > 1 ? (x * 255) / (w - 1) : 0;
row[x] = cast(uint)((red << 16) | (green << 8) | blue);
}
}
foreach ((local variable) int ii; 1 .. 3) // grid lines at 1/3 and 2/3
{
foreach ((parameter) xx; 0 .. w)
g.g.pixelspixels[cast(size_t)(h * i / 3) * w + x] = 0xffffff;
foreach ((parameter) yy; 0 .. h)
g.g.pixelspixels[cast(size_t) y * w + (w * i / 3)] = 0xffffff;
}
}
// Warp the OS cursor to a tour stop (client-relative → screen coordinates).
void app.warpTowarpTo(HWND (parameter) HWND hwndhwnd, in (struct) app.StopStop stop) nothrow
{
RECT _error_ rcrc;
GetClientRect(hwnd, &rc);
const _error_ denden = stop.stop.sxsx >= 6 ? 30 : 6; // center quadrants use /30ths
POINT _error_ pp = POINT(rc.right * stop.stop.sxsx / den, rc.bottom * stop.stop.sysy / den);
ClientToScreen(hwnd, &p);
logEvent("tour_warp zone=%s client=%d,%d screen=%d,%d",
stop.stop.namename, rc.right * stop.stop.sxsx / den, rc.bottom * stop.stop.sysy / den, p.p.xx, p.p.yy);
if (!SetCursorPos(p.p.xx, p.p.yy))
logEvent("error what=SetCursorPos code=%lu", GetLastError());
}
// ---------------------------------------------------------------------------
extern (Windows) LRESULT app.wndProcwndProc(HWND (parameter) HWND hwndhwnd, UINT (parameter) UINT msgmsg, WPARAM wParam, LPARAM lParam) nothrow
{
switch (msg)
{
case WM_CREATE:
g.g.memDcmemDc = CreateCompatibleDC(null);
return 0;
case WM_SETCURSOR:
// Sent on EVERY mouse message while the cursor is over the window
// (and on some non-mouse triggers) — the "storm". lParam: low word =
// hit-test code, high word = the triggering mouse message.
++g.g.nSetCursornSetCursor;
const _error_ hithit = cast(uint)(lParam & 0xffff);
const _error_ triggertrigger = cast(uint)((lParam >> 16) & 0xffff);
logEvent("wm_setcursor n=%u hittest=%u trigger=0x%x phase=%s",
g.g.nSetCursornSetCursor, hit, trigger, phaseNames[g.g.phasephase].ptr);
if (hit != HTCLIENT)
goto default; // non-client area: let DefWindowProc pick
if (g.g.phasephase == Phase.Phase.normalnormal)
{
POINT _error_ pp;
GetCursorPos(&p);
ScreenToClient(hwnd, &p);
RECT _error_ rcrc;
GetClientRect(hwnd, &rc);
const _error_ zonezone = zoneForPoint(p.p.xx, p.p.yy, rc.right, rc.bottom);
SetCursor(cursors[zone.zone.cursorcursor]);
if (zone.zone.namename !is g.g.lastZonelastZone)
{
g.g.lastZonelastZone = zone.zone.namename;
logEvent("cursor_set name=%s zone=%s", cursorNames[zone.zone.cursorcursor].ptr, zone.zone.namename);
}
return TRUE; // handled — DefWindowProc must not reset it
}
if (g.g.phasephase == Phase.Phase.setThenDefsetThenDef)
{
SetCursor(cursors[CursorId.CursorId.handhand]);
logEvent("cursor_set name=IDC_HAND zone=probe then=DefWindowProcW");
}
goto default; // DefWindowProc applies the class cursor — or not?
case WM_MOUSEMOVE:
++g.g.nMouseMovenMouseMove;
goto default; // DefWindowProc generates the WM_SETCURSOR for us
case WM_SIZE:
const _error_ ww = cast(int)(lParam & 0xffff);
const _error_ hh = cast(int)((lParam >> 16) & 0xffff);
if (wParam == SIZE_MINIMIZED)
return 0;
if (w != g.g.widthwidth || h != g.g.heightheight)
createBackbuffer(w, h);
return 0;
case WM_PAINT:
PAINTSTRUCT _error_ psps;
HDC _error_ hdchdc = BeginPaint(hwnd, &ps);
++g.g.frameframe;
drawFrame();
if (g.g.pixelspixels !is null)
BitBlt(hdc, 0, 0, g.g.widthwidth, g.g.heightheight, g.g.memDcmemDc, 0, 0, SRCCOPY);
if (!g.g.firstPaintDonefirstPaintDone)
{
g.g.firstPaintDonefirstPaintDone = true;
logEvent("first_pixel_presented size=%dx%d", g.g.widthwidth, g.g.heightheight);
}
EndPaint(hwnd, &ps);
return 0;
case WM_TIMER:
if (wParam != TIMER_ID)
return 0;
++g.g.ticksticks;
InvalidateRect(hwnd, null, FALSE);
if (g.g.autoExitautoExit)
runSchedule(hwnd);
return 0;
case WM_CLOSE:
logEvent("close_requested");
goto default;
case WM_DESTROY:
KillTimer(hwnd, TIMER_ID);
createBackbuffer(0, 0);
if (g.g.memDcmemDc !is null)
{
DeleteDC(g.g.memDcmemDc);
g.g.memDcmemDc = null;
}
if (cursors[CursorId.CursorId.customcustom] !is null)
DestroyCursor(cursors[CursorId.CursorId.customcustom]);
logEvent("summary wm_setcursor=%u wm_mousemove=%u", g.g.nSetCursornSetCursor, g.g.nMouseMovenMouseMove);
PostQuitMessage(0);
return 0;
default:
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
}
// The bounded-run schedule: 12 tour warps, then the precedence probe (each
// phase: a fresh warp forces a WM_SETCURSOR, GetCursor() sampled 2 ticks
// later shows which cursor survived), then DestroyWindow.
void app.runSchedulerunSchedule(HWND (parameter) HWND hwndhwnd) nothrow
{
const _error_ tt = g.g.ticksticks;
if (t % TOUR_STEP_TICKS == 0 && t / TOUR_STEP_TICKS <= tour.length)
{
const _error_ ii = cast(int)(t / TOUR_STEP_TICKS) - 1;
if (i > g.g.tourIndextourIndex)
{
g.g.tourIndextourIndex = i;
warpTo(hwnd, tour[i]);
}
return;
}
enum (constant) probeBase = (tour.length + 1) * TOUR_STEP_TICKSprobeBase = (tour.length + 1) * TOUR_STEP_TICKS; // tick 78
switch (t)
{
case probeBase: // phase 1: SetCursor then DefWindowProc
g.g.phasephase = Phase.Phase.setThenDefsetThenDef;
logEvent("precedence_begin phase=set_then_def class_cursor=IDC_CROSS");
warpTo(hwnd, tour[8]); // back to the center-arrow quadrant
break;
case probeBase + 4:
logEvent("precedence_result phase=set_then_def cursor_after=%s", cursorName(GetCursor()));
g.g.phasephase = Phase.Phase.classOnlyclassOnly;
logEvent("precedence_begin phase=class_only class_cursor=IDC_CROSS");
warpTo(hwnd, tour[9]);
break;
case probeBase + 8:
logEvent("precedence_result phase=class_only cursor_after=%s", cursorName(GetCursor()));
g.g.phasephase = Phase.Phase.normalnormal;
warpTo(hwnd, tour[10]);
break;
case probeBase + 12:
logEvent("precedence_result phase=normal cursor_after=%s", cursorName(GetCursor()));
DestroyWindow(hwnd);
break;
default:
break;
}
}
// ---------------------------------------------------------------------------
bool bool app.envFlag(const(wchar)* name) nothrowenvFlag(const(wchar)* (parameter) const(wchar)* namename) nothrow
{
WCHAR[16] (local variable) _error_ bufbuf;
const (local variable) const(_error_) nn = GetEnvironmentVariableW(name, buf.ptr, buf.length);
return n >= 1 && n < buf.length && buf[0] == '1';
}
int int D main()main()
{
instrumentInit("f12_cursors_win32");
logEvent("init_start");
g.g.autoExitautoExit = envFlag("WSI_AUTO_EXIT"w.ptr);
logEvent("mode auto_exit=%d", g.g.autoExitautoExit ? 1 : 0);
void app.loadCursors() nothrowloadCursors();
// DPI/system metrics: the nominal system cursor size. Win32 scales the
// cursor per-monitor only via the system "cursor size" accessibility
// setting / per-monitor DPI on Win10+; there is no per-window API.
logEvent("cursor_metrics sm_cxcursor=%d sm_cycursor=%d",
GetSystemMetrics(SM_CXCURSOR), GetSystemMetrics(SM_CYCURSOR));
// Animated cursor probe: Windows ships .ani files under
// C:\windows\cursors; does this (Wine) prefix?
SetLastError(0);
HCURSOR (local variable) _error_ aniani = LoadCursorFromFileW(`C:\windows\cursors\aero_busy.ani`w.ptr);
logEvent("ani_probe path=C:/windows/cursors/aero_busy.ani handle=%p err=%lu",
ani, ani is null ? GetLastError() : 0);
if (ani !is null)
DestroyCursor(ani);
HINSTANCE (local variable) _error_ hInsthInst = GetModuleHandleW(null);
auto (local variable) wstring clsNameclsName = "wsi-f12-class"w;
WNDCLASSEXW (local variable) _error_ wcwc;
wc.cbSize = WNDCLASSEXW.sizeof;
wc.lpfnWndProc = &wndProc;
wc.hInstance = hInst;
wc.lpszClassName = clsName.ptr;
// The probe target: a DISTINCT class cursor, so the log can tell whether
// DefWindowProc re-applied it over our SetCursor.
wc.hCursor = cursors[CursorId.CursorId.crosscross];
if (!RegisterClassExW(&wc))
{
logEvent("error what=RegisterClassExW code=%lu", GetLastError());
return 1;
}
HWND (local variable) _error_ hwndhwnd = CreateWindowExW(0, clsName.ptr, "wsi-f12-cursors"w.ptr,
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 480, 320,
null, null, hInst, null);
if (hwnd is null)
{
logEvent("error what=CreateWindowExW code=%lu", GetLastError());
return 1;
}
logEvent("window_created");
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
SetTimer(hwnd, TIMER_ID, TICK_MS, null);
MSG (local variable) _error_ msgmsg;
while (GetMessageW(&msg, null, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
logEvent("exit code=%d", cast(int) msg.wParam);
return cast(int) msg.wParam;
}