// F09 — Output enumeration & hotplug, Win32 implementation
// (../../../features/f09-outputs.md). Extends the scaffold (../scaffold/app.d)
// into a monitor observatory:
//
// * Enumerates outputs BEFORE any window exists (EnumDisplayMonitors needs
// only user32, proving enumeration is global), then again after window
// creation and on every hotplug signal. Per output: GetMonitorInfoW with
// MONITORINFOEXW (rcMonitor / rcWork / MONITORINFOF_PRIMARY / szDevice),
// GetDpiForMonitor(MDT_EFFECTIVE_DPI) (shcore, resolved at runtime —
// druntime predates it), and EnumDisplaySettingsW(ENUM_CURRENT_SETTINGS)
// for the raw mode (dmPelsWidth/Height, dmBitsPerPel, dmDisplayFrequency,
// dmPosition).
// * Occupancy: MonitorFromWindow(MONITOR_DEFAULTTONULL) on WM_MOVE/WM_SIZE,
// logged as surface_output enter/leave-style transitions (the derivation
// Win32 forces on apps — no wl_surface.enter equivalent). A scripted
// off-screen SetWindowPos probes the MONITOR_DEFAULTTONULL vs
// MONITOR_DEFAULTTONEAREST contract when the window intersects no monitor.
// * Hotplug: WM_DISPLAYCHANGE / WM_SETTINGCHANGE / WM_DEVICECHANGE are
// logged (WM_DEVICECHANGE registered for GUID_DEVINTERFACE_MONITOR via
// RegisterDeviceNotificationW), each triggering a re-enumeration that is
// diffed by device name into output_added / output_removed. A ~0.5 s
// poll re-enumerates and diffs too, so a topology change Wine does NOT
// announce is still caught (via=poll vs via=msg in the log).
//
// WSI_AUTO_EXIT=1 bounds the run (~1.5 s default; WSI_RUN_MS overrides — the
// hotplug hunt uses a longer run while `swaymsg create_output` adds a second
// headless output under winewayland). 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 (package) corecore.(package) core.syssys.(package) core.sys.windowswindows.(module) core.sys.windows.dbtWindows API header module
Translated from MinGW Windows headers
Source
core/sys/windows/dbt.d
dbt;
import instrument;
// ---------------------------------------------------------------------------
// GetDpiForMonitor (shcore.dll, Win 8.1+) — absent from druntime; resolved at
// runtime so a missing export is a logged finding, not a loader failure.
enum (constant) int app.MDT_EFFECTIVE_DPI = 0MDT_EFFECTIVE_DPI = 0;
extern (Windows) alias (alias) app.GetDpiForMonitorT = _error_GetDpiForMonitorT =
HRESULT function(HMONITOR, int, UINT*, UINT*) nothrow @nogc;
__gshared (alias) app.GetDpiForMonitorT = _error_GetDpiForMonitorT _error_ app.pGetDpiForMonitorpGetDpiForMonitor;
void void app.loadDpiApi() nothrowloadDpiApi() nothrow
{
HMODULE (local variable) _error_ shcoreshcore = LoadLibraryW("shcore"w.ptr);
if (shcore !is null)
pGetDpiForMonitor =
cast((unresolved type) GetDpiForMonitorTGetDpiForMonitorT) GetProcAddress(shcore, "GetDpiForMonitor");
logEvent("api name=GetDpiForMonitor present=%d", pGetDpiForMonitor !is null ? 1 : 0);
}
// GUID_DEVINTERFACE_MONITOR {E6F07B5F-EE97-4a90-B076-33F57BF4EAA7} — the
// device-interface class RegisterDeviceNotificationW filters on.
__gshared GUID _error_ app.monitorInterfaceGuidmonitorInterfaceGuid = GUID(0xE6F07B5F, 0xEE97, 0x4A90,
[0xB0, 0x76, 0x33, 0xF5, 0x7B, 0xF4, 0xEA, 0xA7]);
// ---------------------------------------------------------------------------
// Output snapshot: enumeration result kept for diffing on hotplug signals.
enum (constant) int app.MAX_OUTPUTS = 16MAX_OUTPUTS = 16;
struct (struct) app.OutputOutput
{
HMONITOR (field) _error_ app.Output.hmonhmon;
WCHAR[32] (field) _error_ app.Output.devicedevice = 0; // szDevice from MONITORINFOEXW (e.g. \\.\DISPLAY1)
RECT (field) _error_ app.Output.rcMonitorrcMonitor, (field) _error_ app.Output.rcWorkrcWork;
RECT (field) _error_ app.Output.rcEnumrcEnum; // the rect EnumDisplayMonitors itself hands the callback
bool (field) bool app.Output.primaryprimary;
uint (field) uint app.Output.dpidpi; // MDT_EFFECTIVE_DPI (0 = unavailable)
uint (field) uint app.Output.modeWmodeW, (field) uint app.Output.modeHmodeH, (field) uint app.Output.bppbpp, (field) uint app.Output.hzhz; // EnumDisplaySettingsW(ENUM_CURRENT_SETTINGS)
POINTL (field) _error_ app.Output.pospos; // dmPosition (desktop coordinates of the mode)
}
struct (struct) app.SnapshotSnapshot
{
(struct) app.OutputOutput[(constant) int app.MAX_OUTPUTS = 16MAX_OUTPUTS] (field) _error_ app.Snapshot.outputsoutputs;
int (field) int app.Snapshot.countcount;
}
extern (Windows) BOOL app.enumProcenumProc(HMONITOR (parameter) HMONITOR hmonhmon, HDC, LPRECT rc, LPARAM lParam) nothrow
{
auto _error_ snapsnap = cast((unresolved type) SnapshotSnapshot*) lParam;
if (snap.snap.countcount >= MAX_OUTPUTS)
return TRUE;
(unresolved type) OutputOutput* _error_ oo = &snap.snap.outputsoutputs[snap.snap.countcount];
o.o.hmonhmon = hmon;
if (rc !is null)
o.o.rcEnumrcEnum = *rc;
MONITORINFOEXW _error_ mimi;
mi.cbSize = MONITORINFOEXW.sizeof;
if (GetMonitorInfoW(hmon, &mi))
{
o.o.rcMonitorrcMonitor = mi.mi.rcMonitorrcMonitor;
o.o.rcWorkrcWork = mi.mi.rcWorkrcWork;
o.o.primaryprimary = (mi.dwFlags & MONITORINFOF_PRIMARY) != 0;
o.o.devicedevice = mi.szDevice;
}
if (pGetDpiForMonitor !is null)
{
UINT _error_ dxdx, _error_ dydy;
if (pGetDpiForMonitor(hmon, MDT_EFFECTIVE_DPI, &dx, &dy) == S_OK)
o.o.dpidpi = dx;
}
DEVMODEW _error_ dmdm;
dm.dmSize = DEVMODEW.sizeof;
if (EnumDisplaySettingsW(o.o.devicedevice.ptr, ENUM_CURRENT_SETTINGS, &dm))
{
o.o.modeWmodeW = dm.dmPelsWidth;
o.o.modeHmodeH = dm.dmPelsHeight;
o.o.bppbpp = dm.dmBitsPerPel;
o.o.hzhz = dm.dmDisplayFrequency;
o.o.pospos = dm.dmPosition;
}
++snap.snap.countcount;
return TRUE;
}
void void app.enumerate(app.Snapshot* snap, const(char)* when, bool logEach) nothrowenumerate((struct) app.SnapshotSnapshot* (parameter) app.Snapshot* snapsnap, const(char)* (parameter) const(char)* whenwhen, bool (parameter) bool logEachlogEach) nothrow
{
(parameter) app.Snapshot* snapsnap.(field) int app.Snapshot.countcount = 0;
EnumDisplayMonitors(null, null, &enumProc, cast(LPARAM) snap);
if (!(parameter) bool logEachlogEach)
return;
logEvent("enum_pass when=%s count=%d", when, snap.snap.countcount);
foreach ((local variable) int ii; 0 .. (parameter) app.Snapshot* snapsnap.(field) int app.Snapshot.countcount)
{
const (local variable) const(_error_) oo = &(parameter) app.Snapshot* snapsnap.(field) _error_ app.Snapshot*.outputsoutputs[i];
logEvent("output id=%d device=%ls rect=%dx%d+%d+%d enum_rect=%dx%d+%d+%d "
~ "work=%dx%d+%d+%d primary=%d dpi=%u mode=%ux%u bpp=%u hz=%u pos=%d,%d",
i, o.o.devicedevice.ptr,
o.o.rcMonitorrcMonitor.right - o.o.rcMonitorrcMonitor.left, o.o.rcMonitorrcMonitor.bottom - o.o.rcMonitorrcMonitor.top,
o.o.rcMonitorrcMonitor.left, o.o.rcMonitorrcMonitor.top,
o.o.rcEnumrcEnum.right - o.o.rcEnumrcEnum.left, o.o.rcEnumrcEnum.bottom - o.o.rcEnumrcEnum.top,
o.o.rcEnumrcEnum.left, o.o.rcEnumrcEnum.top,
o.o.rcWorkrcWork.right - o.o.rcWorkrcWork.left, o.o.rcWorkrcWork.bottom - o.o.rcWorkrcWork.top,
o.o.rcWorkrcWork.left, o.o.rcWorkrcWork.top,
o.o.primaryprimary ? 1 : 0, o.o.dpidpi, o.o.modeWmodeW, o.o.modeHmodeH, o.o.bppbpp, o.o.hzhz,
o.o.pospos.o.pos.xx, o.o.pospos.o.pos.yy);
}
}
// Diff two snapshots by device name → output_added / output_removed.
// Returns true if anything changed (device set OR geometry).
bool bool app.diffSnapshots(const(app.Snapshot)* old, const(app.Snapshot)* now, const(char)* via) nothrowdiffSnapshots(const((struct) app.SnapshotSnapshot)* (parameter) const(app.Snapshot)* oldold, const((struct) app.SnapshotSnapshot)* (parameter) const(app.Snapshot)* nownow, const(char)* (parameter) const(char)* viavia) nothrow
{
static bool app.diffSnapshots.hasDevicehasDevice(const((struct) app.SnapshotSnapshot)* s, const(WCHAR)* dev) nothrow
{
foreach ((parameter) ii; 0 .. s.s.countcount)
if (lstrcmpW(s.s.outputsoutputs[i].s.outputs[i].devicedevice.ptr, dev) == 0)
return true;
return false;
}
bool (local variable) bool changedchanged;
foreach ((local variable) int ii; 0 .. (parameter) const(app.Snapshot)* nownow.(field) int app.Snapshot.countcount)
if (!hasDevice(old, now.now.outputsoutputs[i].now.outputs[i].devicedevice.ptr))
{
logEvent("output_added device=%ls via=%s", now.now.outputsoutputs[i].now.outputs[i].devicedevice.ptr, via);
(local variable) bool changedchanged = true;
}
foreach ((local variable) int ii; 0 .. (parameter) const(app.Snapshot)* oldold.(field) int app.Snapshot.countcount)
if (!hasDevice(now, old.old.outputsoutputs[i].old.outputs[i].devicedevice.ptr))
{
logEvent("output_removed device=%ls via=%s", old.old.outputsoutputs[i].old.outputs[i].devicedevice.ptr, via);
(local variable) bool changedchanged = true;
}
if (!(local variable) bool changedchanged)
foreach ((local variable) int ii; 0 .. (parameter) const(app.Snapshot)* nownow.(field) int app.Snapshot.countcount) // same devices — geometry/mode change?
foreach ((local variable) int jj; 0 .. (parameter) const(app.Snapshot)* oldold.(field) int app.Snapshot.countcount)
if (lstrcmpW(now.now.outputsoutputs[i].now.outputs[i].devicedevice.ptr, old.old.outputsoutputs[j].old.outputs[j].devicedevice.ptr) == 0
&& ((parameter) const(app.Snapshot)* nownow.(field) _error_ const(app.Snapshot)*.outputsoutputs[i].(field) _error_ (__error)[i].modeWmodeW != (parameter) const(app.Snapshot)* oldold.(field) _error_ const(app.Snapshot)*.outputsoutputs[j].(field) _error_ (__error)[j].modeWmodeW
|| (parameter) const(app.Snapshot)* nownow.(field) _error_ const(app.Snapshot)*.outputsoutputs[i].(field) _error_ (__error)[i].modeHmodeH != (parameter) const(app.Snapshot)* oldold.(field) _error_ const(app.Snapshot)*.outputsoutputs[j].(field) _error_ (__error)[j].modeHmodeH
|| (parameter) const(app.Snapshot)* nownow.(field) _error_ const(app.Snapshot)*.outputsoutputs[i].(field) _error_ (__error)[i].rcMonitorrcMonitor != (parameter) const(app.Snapshot)* oldold.(field) _error_ const(app.Snapshot)*.outputsoutputs[j].(field) _error_ (__error)[j].rcMonitorrcMonitor))
{
logEvent("output_changed device=%ls via=%s mode=%ux%u",
now.now.outputsoutputs[i].now.outputs[i].devicedevice.ptr, via,
now.now.outputsoutputs[i].now.outputs[i].modeWmodeW, now.now.outputsoutputs[i].now.outputs[i].modeHmodeH);
(local variable) bool changedchanged = true;
}
return (local variable) bool changedchanged;
}
// ---------------------------------------------------------------------------
// 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.OFFSCREEN_AT_TICK = 30OFFSCREEN_AT_TICK = 30; // ~0.5 s: off-screen probe
enum (constant) int app.RESTORE_AT_TICK = 45RESTORE_AT_TICK = 45; // ~0.7 s: move back on-screen
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.runMsrunMs = 1500;
uint (field) uint app.Demo.nDisplayChangenDisplayChange, (field) uint app.Demo.nSettingChangenSettingChange, (field) uint app.Demo.nDeviceChangenDeviceChange;
bool (field) bool app.Demo.autoExitautoExit;
bool (field) bool app.Demo.firstPaintDonefirstPaintDone;
HMONITOR (field) _error_ app.Demo.currentMoncurrentMon; // occupancy as last derived
(struct) app.SnapshotSnapshot (field) app.Snapshot app.Demo.snapsnap; // last enumeration (diff base)
HDEVNOTIFY (field) _error_ app.Demo.devNotifydevNotify;
}
__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);
}
void void app.drawGradient() nothrowdrawGradient() 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);
}
}
}
// Occupancy derivation (F09 req. 2): Win32 has no surface-enters-output event;
// the app re-derives via MonitorFromWindow whenever its geometry changes.
void app.trackOccupancytrackOccupancy(HWND (parameter) HWND hwndhwnd, const(char)* why) nothrow
{
HMONITOR _error_ monmon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
if (mon is g.g.currentMoncurrentMon)
return;
if (g.g.currentMoncurrentMon !is null)
logEvent("surface_output leave hmon=%p", g.g.currentMoncurrentMon);
g.g.currentMoncurrentMon = mon;
if (mon is null)
{
logEvent("surface_output none why=%s", why);
return;
}
MONITORINFOEXW _error_ mimi;
mi.cbSize = MONITORINFOEXW.sizeof;
GetMonitorInfoW(mon, &mi);
RECT _error_ wrwr;
GetWindowRect(hwnd, &wr);
logEvent("surface_output enter device=%ls why=%s window=%dx%d+%d+%d",
mi.szDevice.ptr, why, wr.right - wr.left, wr.bottom - wr.top, wr.left, wr.top);
}
// The MONITOR_DEFAULTTONULL vs MONITOR_DEFAULTTONEAREST contract, probed with
// the window parked at +20000+20000 (intersecting no monitor).
void app.offscreenProbeoffscreenProbe(HWND (parameter) HWND hwndhwnd) nothrow
{
HMONITOR _error_ nullMonnullMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
HMONITOR _error_ nearMonnearMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
HMONITOR _error_ priMonpriMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
MONITORINFOEXW _error_ mimi;
mi.cbSize = MONITORINFOEXW.sizeof;
if (nearMon !is null)
GetMonitorInfoW(nearMon, &mi);
logEvent("offscreen_probe defaulttonull=%p defaulttonearest=%ls same_as_primary=%d",
nullMon, nearMon !is null ? mi.szDevice.ptr : "none"w.ptr,
nearMon is priMon ? 1 : 0);
}
void app.reEnumeratereEnumerate(HWND (parameter) HWND hwndhwnd, const(char)* (parameter) const(char)* viavia) nothrow
{
(unresolved type) SnapshotSnapshot _error_ nownow;
enumerate(&now, via, false);
if (diffSnapshots(&g.g.snapsnap, &now, via))
{
g.g.snapsnap = now;
enumerate(&g.g.snapsnap, via, true); // log the new full state
g.g.currentMoncurrentMon = null; // HMONITORs may be stale — re-derive occupancy
trackOccupancy(hwnd, via);
}
else
g.g.snapsnap = now; // keep hmonitor handles fresh; nothing to report
}
// ---------------------------------------------------------------------------
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_DISPLAYCHANGE:
// "sent to all windows when the display resolution has changed";
// wParam = new bit depth, lParam = new primary resolution.
++g.g.nDisplayChangenDisplayChange;
logEvent("msg name=WM_DISPLAYCHANGE bpp=%u res=%ux%u",
cast(uint) wParam, cast(uint)(lParam & 0xffff),
cast(uint)((lParam >> 16) & 0xffff));
reEnumerate(hwnd, "WM_DISPLAYCHANGE");
return 0;
case WM_SETTINGCHANGE:
++g.g.nSettingChangenSettingChange;
logEvent("msg name=WM_SETTINGCHANGE wparam=%u area=%ls",
cast(uint) wParam, lParam ? cast(const(wchar)*) lParam : ""w.ptr);
return 0;
case WM_DEVICECHANGE:
++g.g.nDeviceChangenDeviceChange;
logEvent("msg name=WM_DEVICECHANGE event=0x%x", cast(uint) wParam);
if (wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE
|| wParam == DBT_DEVNODES_CHANGED)
reEnumerate(hwnd, "WM_DEVICECHANGE");
return TRUE;
case WM_MOVE:
trackOccupancy(hwnd, "WM_MOVE");
return 0;
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);
trackOccupancy(hwnd, "WM_SIZE");
return 0;
case WM_PAINT:
PAINTSTRUCT _error_ psps;
HDC _error_ hdchdc = BeginPaint(hwnd, &ps);
++g.g.frameframe;
drawGradient();
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.ticksticks % 31 == 0) // ~0.5 s topology poll (catches silent changes)
reEnumerate(hwnd, "poll");
if (g.g.autoExitautoExit)
{
if (g.g.ticksticks == OFFSCREEN_AT_TICK)
{
logEvent("step name=SetWindowPos offscreen=1 pos=20000,20000");
SetWindowPos(hwnd, null, 20000, 20000, 0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
offscreenProbe(hwnd);
}
else if (g.g.ticksticks == RESTORE_AT_TICK)
{
logEvent("step name=SetWindowPos offscreen=0 pos=80,80");
SetWindowPos(hwnd, null, 80, 80, 0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
if (g.g.ticksticks * TICK_MS >= g.g.runMsrunMs)
DestroyWindow(hwnd);
}
return 0;
case WM_CLOSE:
logEvent("close_requested");
goto default;
case WM_DESTROY:
KillTimer(hwnd, TIMER_ID);
if (g.g.devNotifydevNotify !is null)
UnregisterDeviceNotification(g.g.devNotifydevNotify);
createBackbuffer(0, 0);
if (g.g.memDcmemDc !is null)
{
DeleteDC(g.g.memDcmemDc);
g.g.memDcmemDc = null;
}
logEvent("summary displaychange=%u settingchange=%u devicechange=%u outputs=%d",
g.g.nDisplayChangenDisplayChange, g.g.nSettingChangenSettingChange, g.g.nDeviceChangenDeviceChange, g.g.snapsnap.g.snap.countcount);
PostQuitMessage(0);
return 0;
default:
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
}
// ---------------------------------------------------------------------------
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';
}
uint uint app.envUint(const(wchar)* name, uint def) nothrowenvUint(const(wchar)* (parameter) const(wchar)* namename, uint (parameter) uint defdef) nothrow
{
WCHAR[16] (local variable) _error_ bufbuf;
const (local variable) const(_error_) nn = GetEnvironmentVariableW(name, buf.ptr, buf.length);
if (n < 1 || n >= buf.length)
return (parameter) uint defdef;
uint (local variable) uint vv;
foreach ((parameter) ii; 0 .. n)
{
if (buf[i] < '0' || buf[i] > '9')
return def;
v = v * 10 + (buf[i] - '0');
}
return (local variable) uint vv;
}
int int D main()main()
{
instrumentInit("f09_outputs_win32");
logEvent("init_start");
g.g.autoExitautoExit = envFlag("WSI_AUTO_EXIT"w.ptr);
g.g.runMsrunMs = envUint("WSI_RUN_MS"w.ptr, 1500);
logEvent("mode auto_exit=%d run_ms=%u", g.g.autoExitautoExit ? 1 : 0, g.g.runMsrunMs);
void app.loadDpiApi() nothrowloadDpiApi();
// F09 finding "does enumeration require a window?": this pass runs before
// RegisterClassExW — only user32 (and its session connection) is needed.
void app.enumerate(app.Snapshot* snap, const(char)* when, bool logEach) nothrowenumerate(&g.(field) _error_ g.snapsnap, "pre_window", true);
HINSTANCE (local variable) _error_ hInsthInst = GetModuleHandleW(null);
auto (local variable) wstring clsNameclsName = "wsi-f09-class"w;
WNDCLASSEXW (local variable) _error_ wcwc;
wc.cbSize = WNDCLASSEXW.sizeof;
wc.lpfnWndProc = &wndProc;
wc.hInstance = hInst;
wc.lpszClassName = clsName.ptr;
wc.hCursor = LoadCursorW(null, IDC_ARROW);
if (!RegisterClassExW(&wc))
{
logEvent("error what=RegisterClassExW code=%lu", GetLastError());
return 1;
}
HWND (local variable) _error_ hwndhwnd = CreateWindowExW(0, clsName.ptr, "wsi-f09-outputs"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");
// Hotplug signal #3: WM_DEVICECHANGE only carries device-interface events
// if the window registers for the interface class (monitors here).
DEV_BROADCAST_DEVICEINTERFACE_W (local variable) _error_ filterfilter;
filter.dbcc_size = DEV_BROADCAST_DEVICEINTERFACE_W.sizeof;
filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
filter.dbcc_classguid = monitorInterfaceGuid;
g.g.devNotifydevNotify = RegisterDeviceNotificationW(hwnd, &filter, DEVICE_NOTIFY_WINDOW_HANDLE);
logEvent("step name=RegisterDeviceNotificationW handle=%p err=%lu",
g.g.devNotifydevNotify, g.g.devNotifydevNotify is null ? GetLastError() : 0);
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;
}