app.dhover×368 error×105all
// F15 — Popup with grab, Win32 implementation
// (../../../features/f15-popup.md). Extends the scaffold (../scaffold/app.d)
// with the capture-variant context menu the spec asks for:
//
//   * Right-click on the main window opens a WS_POPUP + WS_EX_NOACTIVATE +
//     WS_EX_TOPMOST menu window (3 items, hover highlight) at the pointer,
//     then takes SetCapture on it — Win32's "grab": all MOUSE input is routed
//     to the capture window (in its client coords, which go negative outside
//     it). Keyboard input is NOT captured — it follows focus, which
//     WS_EX_NOACTIVATE deliberately leaves on the main window, so Esc arrives
//     as the main window's WM_KEYDOWN.
//   * Outside-click dismissal = capture-routed WM_LBUTTONDOWN whose screen
//     coords hit-test outside the popup chain. Hit-testing is done in SCREEN
//     coordinates against the app-known chain rects (popup + submenu) — the
//     one-capture-owner + hit-test pattern.
//   * Placement is pure app math (no positioner object exists): anchor at the
//     click, flip-x/flip-y against the monitor work area
//     (MonitorFromPoint/GetMonitorInfoW) when the menu would overflow —
//     every term of the computation is logged (popup_place …).
//   * Submenu: hovering the last item opens a second WS_POPUP and the demo
//     deliberately moves capture to it (SetCapture(sub)) to MEASURE the
//     capture-is-single-window problem: the parent popup receives
//     WM_CAPTURECHANGED naming the thief; naive "capture lost => dismiss"
//     code would close the whole menu, so WM_CAPTURECHANGED must be filtered
//     through chain knowledge. Closing the submenu hands capture back.
//   * A theft probe (SetCapture by the main window while the menu is open)
//     shows the real fragility: any SetCapture anywhere in the session kills
//     the grab silently — the popup just gets WM_CAPTURECHANGED, logged and
//     treated as dismissal (cause=capture_lost).
//   * TrackPopupMenu probe: the system escape hatch is called once with
//     TPM_RETURNCMD; WM_ENTERMENULOOP/WM_EXITMENULOOP and its blocking
//     duration are logged. A pre-armed SetTimer fires INSIDE its modal loop
//     (the same dispatch-from-modal-loop fact as F03) and EndMenu() ends it.
//
// WSI_AUTO_EXIT=1 drives everything with real injected input (SetCursorPos
// moves + SendInput button/key events): open, hover, item click, outside
// click, Esc, edge-anchored reposition, submenu chain, capture theft,
// TrackPopupMenu — then exits 0. Without it: right-click to open, interact.
//
// Only druntime's built-in core.sys.windows bindings — no third-party packages.
module 
(module) app
app
;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.windows
windows
.
(module) core.sys.windows.windows

Windows API header module

Translated from MinGW API for MS-Windows 4.0

Source

core/sys/windows/windows.d

windows
;
import instrument;
unable to read module `instrument` Expected 'instrument.d' or 'instrument/package.d' in one of the following import paths:
unable to read module `instrument` Expected 'instrument.d' or 'instrument/package.d' in one of the following import paths:
enum UINT_PTR
(constant) _error_ app.TIMER_ID = __error
TIMER_ID
= 1;
undefined identifier `UINT_PTR`
enum UINT_PTR
(constant) _error_ app.MENU_TIMER_ID = __error
MENU_TIMER_ID
= 2;
undefined identifier `UINT_PTR`
enum
(constant) int app.TICK_MS = 16
TICK_MS
= 16;
enum
(constant) int app.TICKS_PER_PHASE = 20
TICKS_PER_PHASE
= 20; // ~320 ms between scripted phases
enum
(constant) int app.ITEM_W = 160
ITEM_W
= 160;
enum
(constant) int app.ITEM_H = 24
ITEM_H
= 24;
enum
(constant) int app.N_ITEMS = 3
N_ITEMS
= 3;
enum
(constant) int app.MENU_H = 72
MENU_H
=
(constant) int app.ITEM_H = 24
ITEM_H
*
(constant) int app.N_ITEMS = 3
N_ITEMS
;
struct
(struct) app.Menu
Menu
{ HWND
(field) _error_ app.Menu.hwnd
hwnd
;
undefined identifier `HWND`
RECT
(field) _error_ app.Menu.rect
rect
; // screen coords
undefined identifier `RECT`
} struct
(struct) app.Demo
Demo
{ HDC
(field) _error_ app.Demo.memDc
memDc
;
undefined identifier `HDC`
HBITMAP
(field) _error_ app.Demo.dib
dib
,
(field) _error_ app.Demo.stockBmp
stockBmp
;
undefined identifier `HBITMAP`
undefined identifier `HBITMAP`
uint*
(field) uint* app.Demo.pixels
pixels
;
int
(field) int app.Demo.width
width
,
(field) int app.Demo.height
height
;
uint
(field) uint app.Demo.frame
frame
,
(field) uint app.Demo.ticks
ticks
;
bool
(field) bool app.Demo.autoExit
autoExit
;
int
(field) int app.Demo.phase
phase
;
HWND
(field) _error_ app.Demo.hwndMain
hwndMain
;
undefined identifier `HWND`
(struct) app.Menu
Menu
[2]
(field) _error_ app.Demo.menus
menus
; // [0] = popup, [1] = submenu
int
(field) int app.Demo.nOpen
nOpen
; // 0, 1 or 2
int
(field) int app.Demo.hoverMenu
hoverMenu
= -1,
(field) int app.Demo.hoverItem
hoverItem
= -1;
bool
(field) bool app.Demo.swallowNextUp
swallowNextUp
; // the opening right-click's release
bool
(field) bool app.Demo.inMenuLoop
inMenuLoop
; // inside TrackPopupMenu (pause the phase driver)
long
(field) long app.Demo.menuLoopT0
menuLoopT0
; // TrackPopupMenu entry timestamp
} __gshared
(struct) app.Demo
Demo
_error_ app.g
g
;
// --------------------------------------------------------------------------- // Main-window backbuffer (scaffold-identical, trimmed). void
void app.createBackbuffer(int w, int h) nothrow
createBackbuffer
(int
(parameter) int w
w
, int
(parameter) int h
h
) nothrow
{ if (g.
(field) _error_ g.dib
dib
!is null)
{ SelectObject(g.
g.memDc
memDc
, g.
g.stockBmp
stockBmp
);
undefined identifier `SelectObject`
DeleteObject(g.
g.dib
dib
);
undefined identifier `DeleteObject`
g.
g.dib
dib
= null;
g.
g.pixels
pixels
= null;
g.
g.width
width
= g.
g.height
height
= 0;
} if (
(parameter) int w
w
<= 0 ||
(parameter) int h
h
<= 0)
return; BITMAPINFO
(local variable) _error_ bmi
bmi
;
undefined identifier `BITMAPINFO`
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* bits
bits
;
g.
g.dib
dib
= CreateDIBSection(null, &bmi, DIB_RGB_COLORS, &bits, null, 0);
if (g.
(field) _error_ g.dib
dib
is null)
return; g.
g.pixels
pixels
= cast(uint*) bits;
g.
g.width
width
= w;
g.
g.height
height
= h;
g.
g.stockBmp
stockBmp
= cast(HBITMAP) SelectObject(g.
g.memDc
memDc
, g.
g.dib
dib
);
} void
void app.drawGradient() nothrow
drawGradient
() nothrow
{ if (g.
(field) _error_ g.pixels
pixels
is null)
return; const
(local variable) const(_error_) w
w
= g.
(field) _error_ g.width
width
,
(local variable) const(_error_) h
h
= g.
(field) _error_ g.height
height
;
const
(local variable) const(_error_) blue
blue
= (g.
(field) _error_ g.frame
frame
* 4) & 0xff;
foreach (
(parameter) y
y
; 0 .. h)
{ uint*
_error_ row
row
= g.
g.pixels
pixels
+ cast(size_t) y * w;
const
_error_ green
green
= h > 1 ? (y * 255) / (h - 1) : 0;
foreach (
(parameter) x
x
; 0 .. w)
{ const
_error_ red
red
= w > 1 ? (x * 255) / (w - 1) : 0;
row[x] = cast(uint)((red << 16) | (green << 8) | blue); } } } // --------------------------------------------------------------------------- // Popup placement: the app IS the positioner on Win32. Anchor at the pointer, // gravity bottom-right; flip when the work area would be overflowed. RECT
app.placeMenu
placeMenu
(int
(parameter) int ax
ax
, int
(parameter) int ay
ay
) nothrow
undefined identifier `RECT`
{ POINT
_error_ p
p
= POINT(ax, ay);
MONITORINFO
_error_ mi
mi
;
mi.cbSize = MONITORINFO.sizeof; GetMonitorInfoW(MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY), &mi); const
_error_ wa
wa
= mi.rcWork;
int
_error_ x
x
= ax,
_error_ y
y
= ay;
const(char)*
_error_ adjX
adjX
= "none",
_error_ adjY
adjY
= "none";
if (x + ITEM_W > wa.right) { x = ax - ITEM_W; // flip: open leftwards adjX = "flip-x"; if (x < wa.left) { x = wa.right - ITEM_W; // slide as last resort adjX = "slide-x"; } } if (y + MENU_H > wa.bottom) { y = ay - MENU_H; adjY = "flip-y"; if (y < wa.top) { y = wa.bottom - MENU_H; adjY = "slide-y"; } } logEvent("popup_place anchor=%d,%d gravity=bottom-right size=%dx%d work=%ld,%ld-%ldx%ld final=%d,%d adjust=%s,%s", ax, ay, ITEM_W, MENU_H, wa.left, wa.top, wa.right - wa.left, wa.bottom - wa.top, x, y, adjX, adjY); return RECT(x, y, x + ITEM_W, y + MENU_H); } // --------------------------------------------------------------------------- // Chain hit-testing in screen coordinates. // Returns menu index (0/1) or -1; item receives the row index or -1. int
app.hitTest
hitTest
(POINT
(parameter) POINT s
s
, out int
(parameter) int item
item
) nothrow
undefined identifier `POINT`
{ item = -1; foreach_reverse (
(parameter) i
i
; 0 .. g.
g.nOpen
nOpen
) // submenu is on top
{ const
_error_ r
r
= g.
g.menus
menus
[i].
g.menus[i].rect
rect
;
if (s.
s.x
x
>= r.left && s.
s.x
x
< r.right && s.
s.y
y
>= r.top && s.
s.y
y
< r.bottom)
{ item = cast(int)((s.
s.y
y
- r.top) / ITEM_H);
return cast(int) i; } } return -1; } // --------------------------------------------------------------------------- // Menu windows. immutable wchar*[
(constant) int app.N_ITEMS = 3
N_ITEMS
][2]
(constant) int app.N_ITEMS = 3
itemLabels
= [
["Alpha"w.
(constant) immutable(wchar)* "Alpha"w.ptr = "Alpha"w
ptr
, "Beta"w.
(constant) immutable(wchar)* "Beta"w.ptr = "Beta"w
ptr
, "Gamma ▸"w.
(constant) immutable(wchar)* "Gamma \u25b8"w.ptr = "Gamma \u25b8"w
ptr
],
["Sub-1"w.
(constant) immutable(wchar)* "Sub-1"w.ptr = "Sub-1"w
ptr
, "Sub-2"w.
(constant) immutable(wchar)* "Sub-2"w.ptr = "Sub-2"w
ptr
, "Sub-3"w.
(constant) immutable(wchar)* "Sub-3"w.ptr = "Sub-3"w
ptr
],
]; void
void app.openMenu(int idx, int ax, int ay, const(char)* cause) nothrow
openMenu
(int
(parameter) int idx
idx
, int
(parameter) int ax
ax
, int
(parameter) int ay
ay
, const(char)*
(parameter) const(char)* cause
cause
) nothrow
{ logEvent("popup_open menu=%d anchor=%d,%d cause=%s", idx, ax, ay, cause);
undefined identifier `logEvent`
const
(local variable) const(_error_) r
r
= placeMenu(ax, ay);
HWND
(local variable) _error_ hwnd
hwnd
= CreateWindowExW(WS_EX_TOPMOST | WS_EX_NOACTIVATE,
undefined identifier `HWND`
undefined identifier `CreateWindowExW`
"wsi-f15-menu"w.
"wsi-f15-menu"w.ptr
ptr
, null, WS_POPUP | WS_BORDER,
r.left, r.top, ITEM_W, MENU_H, g.
g.hwndMain
hwndMain
, null,
GetModuleHandleW(null), null); g.
(field) _error_ g.menus
menus
[idx].
(__error)[idx].hwnd
hwnd
= hwnd;
ShowWindow(hwnd, SW_SHOWNOACTIVATE);
undefined identifier `ShowWindow`
GetWindowRect(hwnd, &g.
g.menus
menus
[idx].
g.menus[idx].rect
rect
); // authoritative placement
undefined identifier `GetWindowRect`
const
(local variable) const(_error_) rr
rr
= g.
(field) _error_ g.menus
menus
[idx].
(field) _error_ (__error)[idx].rect
rect
;
logEvent("popup_placed menu=%d rect=%ld,%ld-%ldx%ld", idx, rr.left, rr.top,
undefined identifier `logEvent`
rr.right - rr.left, rr.bottom - rr.top); g.
g.nOpen
nOpen
= idx + 1;
// The grab: route all mouse input to this menu window. Capture is a // single per-queue slot — taking it for the submenu STEALS it from the // parent popup (measured via the parent's WM_CAPTURECHANGED). HWND
(local variable) _error_ prev
prev
= SetCapture(hwnd);
undefined identifier `HWND`
undefined identifier `SetCapture`
logEvent("grab state=acquired menu=%d owner=%p prev=%p readback=%p",
undefined identifier `logEvent`
idx, hwnd, prev, GetCapture()); } void
void app.closeSubmenu(const(char)* cause) nothrow
closeSubmenu
(const(char)*
(parameter) const(char)* cause
cause
) nothrow
{ if (g.
(field) _error_ g.nOpen
nOpen
< 2)
return; logEvent("popup_dismiss menu=1 cause=%s", cause);
undefined identifier `logEvent`
HWND
(local variable) _error_ h
h
= g.
(field) _error_ g.menus
menus
[1].
(field) _error_ (__error)[1].hwnd
hwnd
;
undefined identifier `HWND`
g.
(field) _error_ g.menus
menus
[1].
(__error)[1].hwnd
hwnd
= null;
g.
g.nOpen
nOpen
= 1;
SetCapture(g.
g.menus
menus
[0].
g.menus[0].hwnd
hwnd
); // hand the grab back to the parent
undefined identifier `SetCapture`
logEvent("grab state=returned_to_parent owner=%p", GetCapture());
undefined identifier `logEvent`
DestroyWindow(h);
undefined identifier `DestroyWindow`
} void
void app.dismissChain(const(char)* cause) nothrow
dismissChain
(const(char)*
(parameter) const(char)* cause
cause
) nothrow
{ if (g.
(field) _error_ g.nOpen
nOpen
== 0)
return; logEvent("popup_dismiss cause=%s open=%d", cause, g.
g.nOpen
nOpen
);
undefined identifier `logEvent`
// Destroy top-down; release capture first so the destroys do not generate // misleading WM_CAPTURECHANGED noise (one is sent anyway on ReleaseCapture). g.
g.nOpen
nOpen
= 0;
g.
g.hoverMenu
hoverMenu
= g.
g.hoverItem
hoverItem
= -1;
ReleaseCapture();
undefined identifier `ReleaseCapture`
foreach_reverse (
(local variable) int i
i
; 0 .. 2)
if (g.
(field) _error_ g.menus
menus
[i].
(field) _error_ (__error)[i].hwnd
hwnd
!is null)
{ DestroyWindow(g.
g.menus
menus
[i].
g.menus[i].hwnd
hwnd
);
undefined identifier `DestroyWindow`
g.
(field) _error_ g.menus
menus
[i].
(__error)[i].hwnd
hwnd
= null;
} logEvent("grab state=released readback=%p", GetCapture());
undefined identifier `logEvent`
} int
app.menuIndexOf
menuIndexOf
(HWND
(parameter) HWND hwnd
hwnd
) nothrow @nogc
undefined identifier `HWND`
{ if (hwnd is null) return -1; foreach (
(parameter) i
i
; 0 .. 2)
if (g.
g.menus
menus
[i].
g.menus[i].hwnd
hwnd
is hwnd)
return cast(int) i; return -1; } // --------------------------------------------------------------------------- // Menu WndProc: paint + capture-routed mouse handling. extern (Windows) LRESULT
app.menuProc
menuProc
(HWND
(parameter) HWND hwnd
hwnd
, UINT
(parameter) UINT msg
msg
, WPARAM wParam, LPARAM lParam) nothrow
undefined identifier `LRESULT`
undefined identifier `HWND`
undefined identifier `UINT`
undefined identifier `WPARAM`
undefined identifier `LPARAM`
{ const
_error_ self
self
= menuIndexOf(hwnd);
switch (msg) { case WM_PAINT: PAINTSTRUCT
_error_ ps
ps
;
HDC
_error_ hdc
hdc
= BeginPaint(hwnd, &ps);
HBRUSH
_error_ normal
normal
= CreateSolidBrush(RGB(232, 232, 232));
HBRUSH
_error_ hot
hot
= CreateSolidBrush(RGB(60, 120, 216));
SetBkMode(hdc, TRANSPARENT); foreach (
(parameter) i
i
; 0 .. N_ITEMS)
{ RECT
_error_ r
r
= RECT(0, i * ITEM_H, ITEM_W, (i + 1) * ITEM_H);
const
_error_ isHot
isHot
= self == g.
g.hoverMenu
hoverMenu
&& i == g.
g.hoverItem
hoverItem
;
FillRect(hdc, &r, isHot ? hot : normal); SetTextColor(hdc, isHot ? RGB(255, 255, 255) : RGB(20, 20, 20)); r.left += 8; if (self >= 0) DrawTextW(hdc, itemLabels[self][i], -1, &r, DT_SINGLELINE | DT_VCENTER | DT_LEFT); } DeleteObject(normal); DeleteObject(hot); EndPaint(hwnd, &ps); return 0; case WM_MOUSEMOVE: // Capture-routed: coords are THIS window's client coords, possibly // negative / beyond its size. Convert to screen and chain-hit-test. POINT
_error_ s
s
= POINT(cast(short)(lParam & 0xffff), cast(short)((lParam >> 16) & 0xffff));
ClientToScreen(hwnd, &s); int
_error_ item
item
;
const
_error_ m
m
= hitTest(s, item);
if (m != g.
g.hoverMenu
hoverMenu
|| item != g.
g.hoverItem
hoverItem
)
{ g.
g.hoverMenu
hoverMenu
= m;
g.
g.hoverItem
hoverItem
= item;
logEvent("hover menu=%d item=%d screen=%ld,%ld routed_to=%d", m, item, s.
s.x
x
, s.
s.y
y
, self);
foreach (
(parameter) i
i
; 0 .. g.
g.nOpen
nOpen
)
InvalidateRect(g.
g.menus
menus
[i].
g.menus[i].hwnd
hwnd
, null, FALSE);
// Submenu opens on hovering the parent's last item, closes when // the hover returns to a different parent item. if (m == 0 && item == N_ITEMS - 1 && g.
g.nOpen
nOpen
== 1)
{ const
_error_ pr
pr
= g.
g.menus
menus
[0].
g.menus[0].rect
rect
;
openMenu(1, pr.right, pr.top + (N_ITEMS - 1) * ITEM_H, "submenu_hover"); } else if (m == 0 && item != N_ITEMS - 1 && g.
g.nOpen
nOpen
== 2)
closeSubmenu("parent_hover"); } return 0; case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: POINT
_error_ sc
sc
= POINT(cast(short)(lParam & 0xffff), cast(short)((lParam >> 16) & 0xffff));
ClientToScreen(hwnd, &sc); int
_error_ it
it
;
const
_error_ mh
mh
= hitTest(sc, it);
if (mh < 0) { logEvent("button state=down screen=%ld,%ld routed_to=%d hit=outside", sc.
sc.x
x
, sc.
sc.y
y
, self);
dismissChain("outside_click"); } else { logEvent("button state=down screen=%ld,%ld routed_to=%d hit=menu%d_item%d", sc.
sc.x
x
, sc.
sc.y
y
, self, mh, it);
if (!(mh == 0 && it == N_ITEMS - 1)) // submenu anchor item only hovers { logEvent("item_activated menu=%d item=%d", mh, it); dismissChain("item_activated"); } } return 0; case WM_RBUTTONUP: case WM_LBUTTONUP: if (g.
g.swallowNextUp
swallowNextUp
)
{ // The release of the click that OPENED the menu arrives capture- // routed; activating an item from it would be wrong. g.
g.swallowNextUp
swallowNextUp
= false;
logEvent("button state=release swallowed=open_click"); } return 0; case WM_CAPTURECHANGED: // lParam = the window that NOW has capture. Sent to the previous // owner whenever anyone calls SetCapture/ReleaseCapture — this is the // single-slot fragility. Filter through chain knowledge. HWND
_error_ thief
thief
= cast(HWND) lParam;
const
_error_ thiefIdx
thiefIdx
= menuIndexOf(thief);
logEvent("msg name=WM_CAPTURECHANGED menu=%d new_owner=%p chain_member=%d", self, thief, thiefIdx); if (thiefIdx < 0 && self == 0 && g.
g.nOpen
nOpen
> 0)
{ // Someone outside the menu chain took (or released) capture: // the grab is gone and we cannot see outside clicks any more. dismissChain("capture_lost"); } return 0; case WM_MOUSEACTIVATE: return MA_NOACTIVATE; // belt & braces with WS_EX_NOACTIVATE default: return DefWindowProcW(hwnd, msg, wParam, lParam); } } // --------------------------------------------------------------------------- // Input injection (auto mode): real cursor warps + real button/key events, so // the capture routing is exercised, not simulated. void
void app.warpTo(int x, int y) nothrow
warpTo
(int
(parameter) int x
x
, int
(parameter) int y
y
) nothrow
{ SetCursorPos(x, y);
undefined identifier `SetCursorPos`
logEvent("inject method=SetCursorPos screen=%d,%d", x, y);
undefined identifier `logEvent`
} void
app.click
click
(DWORD downFlag, DWORD upFlag, const(char)* name) nothrow
undefined identifier `DWORD`
undefined identifier `DWORD`
{ INPUT[2]
_error_ inp
inp
;
inp[0].type = INPUT_MOUSE; inp[0].
inp[0].mi
mi
.dwFlags = downFlag;
inp[1].type = INPUT_MOUSE; inp[1].
inp[1].mi
mi
.dwFlags = upFlag;
const
_error_ n
n
= SendInput(2, inp.
inp.ptr
ptr
, INPUT.sizeof);
logEvent("inject method=SendInput kind=%s sent=%u", name, n); } void
void app.pressEsc() nothrow
pressEsc
() nothrow
{ INPUT[2]
(local variable) _error_ inp
inp
;
undefined identifier `INPUT`
inp[0].type = INPUT_KEYBOARD; inp[0].ki.wVk = VK_ESCAPE; inp[1].type = INPUT_KEYBOARD; inp[1].ki.wVk = VK_ESCAPE; inp[1].ki.dwFlags = KEYEVENTF_KEYUP; const
(local variable) const(_error_) n
n
= SendInput(2, inp.
inp.ptr
ptr
, INPUT.sizeof);
undefined identifier `SendInput`
logEvent("inject method=SendInput kind=esc sent=%u", n);
undefined identifier `logEvent`
} POINT
app.mainCenter
mainCenter
() nothrow
undefined identifier `POINT`
{ POINT
_error_ p
p
= POINT(g.
g.width
width
/ 2, g.
g.height
height
/ 2);
ClientToScreen(g.
g.hwndMain
hwndMain
, &p);
return p; } POINT
app.itemCenter
itemCenter
(int
(parameter) int menu
menu
, int
(parameter) int item
item
) nothrow @nogc
undefined identifier `POINT`
{ const
_error_ r
r
= g.
g.menus
menus
[menu].
g.menus[menu].rect
rect
;
return POINT(r.left + ITEM_W / 2, r.top + item * ITEM_H + ITEM_H / 2); } // --------------------------------------------------------------------------- // TrackPopupMenu probe — the system escape hatch, measured. void
void app.probeTrackPopupMenu() nothrow
probeTrackPopupMenu
() nothrow
{ HMENU
(local variable) _error_ menu
menu
= CreatePopupMenu();
undefined identifier `HMENU`
undefined identifier `CreatePopupMenu`
AppendMenuW(menu, MF_STRING, 101, "Sys-Alpha"w.
"Sys-Alpha"w.ptr
ptr
);
undefined identifier `AppendMenuW`
AppendMenuW(menu, MF_STRING, 102, "Sys-Beta"w.
"Sys-Beta"w.ptr
ptr
);
undefined identifier `AppendMenuW`
AppendMenuW(menu, MF_STRING, 103, "Sys-Gamma"w.
"Sys-Gamma"w.ptr
ptr
);
undefined identifier `AppendMenuW`
const
(local variable) const(_error_) p
p
= mainCenter();
// The pre-armed timer fires INSIDE TrackPopupMenu's modal loop (same // dispatch behavior as the F03 size/move loop) and calls EndMenu(). SetTimer(g.
g.hwndMain
hwndMain
, MENU_TIMER_ID, 400, null);
undefined identifier `SetTimer`
g.
g.menuLoopT0
menuLoopT0
= nowUs();
g.
g.inMenuLoop
inMenuLoop
= true;
logEvent("trackpopupmenu state=calling pos=%ld,%ld", p.
p.x
x
, p.
p.y
y
);
undefined identifier `logEvent`
const
(local variable) const(_error_) r
r
= TrackPopupMenu(menu, TPM_RETURNCMD | TPM_LEFTALIGN | TPM_TOPALIGN,
undefined identifier `TrackPopupMenu`
p.
p.x
x
, p.
p.y
y
, 0, g.
g.hwndMain
hwndMain
, null);
g.
g.inMenuLoop
inMenuLoop
= false;
logEvent("trackpopupmenu state=returned cmd=%d blocked_us=%lld err=%lu",
undefined identifier `logEvent`
cast(int) r, nowUs() - g.
g.menuLoopT0
menuLoopT0
, GetLastError());
KillTimer(g.
g.hwndMain
hwndMain
, MENU_TIMER_ID);
undefined identifier `KillTimer`
DestroyMenu(menu);
undefined identifier `DestroyMenu`
} // --------------------------------------------------------------------------- // Scripted tour. void
void app.runPhase(int n) nothrow
runPhase
(int
(parameter) int n
n
) nothrow
{ POINT
(local variable) _error_ c
c
= mainCenter();
undefined identifier `POINT`
switch (
(parameter) int n
n
)
{ case 0: // open at center via a real right-click
void app.warpTo(int x, int y) nothrow
warpTo
(c.
(field) _error_ c.x
x
, c.
(field) _error_ c.y
y
);
break; case 1: click(MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP, "right_click"); break; case 2: // hover item 0, then 1 if (g.
(field) _error_ g.nOpen
nOpen
> 0)
{ const
(local variable) const(_error_) p
p
= itemCenter(0, 0);
void app.warpTo(int x, int y) nothrow
warpTo
(p.
(field) _error_ p.x
x
, p.
(field) _error_ p.y
y
);
} break; case 3: if (g.
(field) _error_ g.nOpen
nOpen
> 0)
{ const
(local variable) const(_error_) p
p
= itemCenter(0, 1);
void app.warpTo(int x, int y) nothrow
warpTo
(p.
(field) _error_ p.x
x
, p.
(field) _error_ p.y
y
);
} break; case 4: // activate item 1 click(MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, "left_click"); break; case 5: // reopen
void app.warpTo(int x, int y) nothrow
warpTo
(c.
(field) _error_ c.x
x
, c.
(field) _error_ c.y
y
);
click(MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP, "right_click"); break; case 6: // outside click (top-left of the main client, away from the menu) POINT
(local variable) _error_ o
o
= POINT(8, 8);
undefined identifier `POINT`
undefined identifier `POINT`
ClientToScreen(g.
g.hwndMain
hwndMain
, &o);
undefined identifier `ClientToScreen`
void app.warpTo(int x, int y) nothrow
warpTo
(o.
(field) _error_ o.x
x
, o.
(field) _error_ o.y
y
);
break; case 7: click(MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, "left_click"); break; case 8: // reopen, dismiss via Esc (keyboard follows FOCUS, not capture)
void app.warpTo(int x, int y) nothrow
warpTo
(c.
(field) _error_ c.x
x
, c.
(field) _error_ c.y
y
);
click(MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP, "right_click"); break; case 9: logEvent("focus_probe focus=%p main=%p (keyboard goes to focus, not capture)",
undefined identifier `logEvent`
GetFocus(), g.
g.hwndMain
hwndMain
);
void app.pressEsc() nothrow
pressEsc
();
break; case 10: // edge probe: anchor at the work-area bottom-right corner MONITORINFO
(local variable) _error_ mi
mi
;
undefined identifier `MONITORINFO`
mi.cbSize = MONITORINFO.sizeof; GetMonitorInfoW(MonitorFromWindow(g.
g.hwndMain
hwndMain
, MONITOR_DEFAULTTOPRIMARY), &mi);
undefined identifier `GetMonitorInfoW`
g.
g.swallowNextUp
swallowNextUp
= false;
void app.openMenu(int idx, int ax, int ay, const(char)* cause) nothrow
openMenu
(0, mi.rcWork.right - 4, mi.rcWork.bottom - 4, "edge_probe");
break; case 11: // May the popup exceed the output bounds at all? Move it half off // the bottom-right corner and read the rect back (no WM/compositor // veto expected on Win32 — measured, not assumed). if (g.
(field) _error_ g.nOpen
nOpen
> 0)
{ MONITORINFO
(local variable) _error_ mi2
mi2
;
undefined identifier `MONITORINFO`
mi2.cbSize = MONITORINFO.sizeof; GetMonitorInfoW(MonitorFromWindow(g.
g.hwndMain
hwndMain
, MONITOR_DEFAULTTOPRIMARY), &mi2);
undefined identifier `GetMonitorInfoW`
const
(local variable) const(_error_) want
want
= POINT(mi2.rcMonitor.right - ITEM_W / 2,
undefined identifier `POINT`
mi2.rcMonitor.bottom - MENU_H / 2); SetWindowPos(g.
g.menus
menus
[0].
g.menus[0].hwnd
hwnd
, null, want.
want.x
x
, want.
want.y
y
, 0, 0,
undefined identifier `SetWindowPos`
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); RECT
(local variable) _error_ rb
rb
;
undefined identifier `RECT`
GetWindowRect(g.
g.menus
menus
[0].
g.menus[0].hwnd
hwnd
, &rb);
undefined identifier `GetWindowRect`
g.
(field) _error_ g.menus
menus
[0].
(__error)[0].rect
rect
= rb;
logEvent("offscreen_probe requested=%ld,%ld readback=%ld,%ld-%ldx%ld monitor_br=%ld,%ld",
undefined identifier `logEvent`
want.
want.x
x
, want.
want.y
y
, rb.left, rb.top, rb.right - rb.left,
rb.bottom - rb.top, mi2.rcMonitor.right, mi2.rcMonitor.bottom); }
void app.pressEsc() nothrow
pressEsc
();
break; case 12: // reopen; walk to the submenu anchor item
void app.warpTo(int x, int y) nothrow
warpTo
(c.
(field) _error_ c.x
x
, c.
(field) _error_ c.y
y
);
click(MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP, "right_click"); break; case 13: if (g.
(field) _error_ g.nOpen
nOpen
> 0)
{ const
(local variable) const(_error_) p
p
= itemCenter(0, N_ITEMS - 1); // opens the submenu
void app.warpTo(int x, int y) nothrow
warpTo
(p.
(field) _error_ p.x
x
, p.
(field) _error_ p.y
y
);
} break; case 14: if (g.
(field) _error_ g.nOpen
nOpen
> 1)
{ const
(local variable) const(_error_) p
p
= itemCenter(1, 1); // hover Sub-2
void app.warpTo(int x, int y) nothrow
warpTo
(p.
(field) _error_ p.x
x
, p.
(field) _error_ p.y
y
);
} break; case 15: click(MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, "left_click"); break; case 16: // capture-theft probe
void app.warpTo(int x, int y) nothrow
warpTo
(c.
(field) _error_ c.x
x
, c.
(field) _error_ c.y
y
);
click(MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP, "right_click"); break; case 17: if (g.
(field) _error_ g.nOpen
nOpen
> 0)
{ logEvent("capture_theft_probe api=SetCapture(main)");
undefined identifier `logEvent`
SetCapture(g.
g.hwndMain
hwndMain
); // any window may steal — silently
undefined identifier `SetCapture`
ReleaseCapture();
undefined identifier `ReleaseCapture`
} break; case 18:
void app.probeTrackPopupMenu() nothrow
probeTrackPopupMenu
();
break; case 19: DestroyWindow(g.
g.hwndMain
hwndMain
);
undefined identifier `DestroyWindow`
break; default: break; } } // --------------------------------------------------------------------------- // Main WndProc. extern (Windows) LRESULT
app.wndProc
wndProc
(HWND
(parameter) HWND hwnd
hwnd
, UINT
(parameter) UINT msg
msg
, WPARAM wParam, LPARAM lParam) nothrow
undefined identifier `LRESULT`
undefined identifier `HWND`
undefined identifier `UINT`
undefined identifier `WPARAM`
undefined identifier `LPARAM`
{ switch (msg) { case WM_CREATE: g.
g.memDc
memDc
= CreateCompatibleDC(null);
return 0; case WM_SIZE: const
_error_ w
w
= cast(int)(lParam & 0xffff);
const
_error_ h
h
= cast(int)((lParam >> 16) & 0xffff);
if (wParam != SIZE_MINIMIZED && (w != g.
g.width
width
|| h != g.
g.height
height
))
createBackbuffer(w, h); return 0; case WM_RBUTTONDOWN: // Open the menu at the pointer (screen coords). POINT
_error_ p
p
= POINT(cast(short)(lParam & 0xffff), cast(short)((lParam >> 16) & 0xffff));
ClientToScreen(hwnd, &p); if (g.
g.nOpen
nOpen
== 0)
{ g.
g.swallowNextUp
swallowNextUp
= true; // the matching button-up is capture-routed
openMenu(0, p.
p.x
x
, p.
p.y
y
, "right_click");
} return 0; case WM_KEYDOWN: if (wParam == VK_ESCAPE && g.
g.nOpen
nOpen
> 0)
{ logEvent("key vk=VK_ESCAPE routed_to=main_focus_window"); dismissChain("esc"); } return 0; case WM_ACTIVATE: logEvent("focus state=%s reason=WM_ACTIVATE other=%p", LOWORD(wParam) == WA_INACTIVE ? "out".
"out".ptr
ptr
: "in".
"in".ptr
ptr
, cast(void*) lParam);
goto default; case WM_KILLFOCUS: logEvent("focus state=out reason=WM_KILLFOCUS next=%p", cast(void*) wParam); return 0; case WM_SETFOCUS: logEvent("focus state=in reason=WM_SETFOCUS prev=%p", cast(void*) wParam); return 0; case WM_CAPTURECHANGED: logEvent("msg name=WM_CAPTURECHANGED menu=main new_owner=%p", cast(void*) lParam); return 0; case WM_ENTERMENULOOP: logEvent("msg name=WM_ENTERMENULOOP track=%d dt_us=%lld", cast(int) wParam, nowUs() - g.
g.menuLoopT0
menuLoopT0
);
return 0; case WM_EXITMENULOOP: logEvent("msg name=WM_EXITMENULOOP track=%d dt_us=%lld", cast(int) wParam, nowUs() - g.
g.menuLoopT0
menuLoopT0
);
return 0; case WM_INITMENUPOPUP: logEvent("msg name=WM_INITMENUPOPUP menu=%p", cast(void*) wParam); goto default; case WM_ERASEBKGND: return 1; case WM_PAINT: PAINTSTRUCT
_error_ ps
ps
;
HDC
_error_ hdc
hdc
= BeginPaint(hwnd, &ps);
++g.
g.frame
frame
;
drawGradient(); if (g.
g.pixels
pixels
!is null)
BitBlt(hdc, 0, 0, g.
g.width
width
, g.
g.height
height
, g.
g.memDc
memDc
, 0, 0, SRCCOPY);
EndPaint(hwnd, &ps); return 0; case WM_TIMER: if (wParam == MENU_TIMER_ID) { // Fires inside TrackPopupMenu's modal loop — proof + exit lever. logEvent("timer id=menu inside_menu_loop dt_us=%lld", nowUs() - g.
g.menuLoopT0
menuLoopT0
);
EndMenu(); return 0; } if (wParam != TIMER_ID) return 0; ++g.
g.ticks
ticks
;
InvalidateRect(hwnd, null, FALSE); // Regular ticks are dispatched inside TrackPopupMenu's modal loop // too — pause the phase driver there so only the MENU_TIMER acts. if (g.
g.autoExit
autoExit
&& !g.
g.inMenuLoop
inMenuLoop
&& g.
g.ticks
ticks
% TICKS_PER_PHASE == 0)
runPhase(g.
g.phase
phase
++);
return 0; case WM_DESTROY: KillTimer(hwnd, TIMER_ID); createBackbuffer(0, 0); if (g.
g.memDc
memDc
!is null)
{ DeleteDC(g.
g.memDc
memDc
);
g.
g.memDc
memDc
= null;
} PostQuitMessage(0); return 0; default: return DefWindowProcW(hwnd, msg, wParam, lParam); } } // --------------------------------------------------------------------------- bool
bool app.wantAutoExit() nothrow
wantAutoExit
() nothrow
{ WCHAR[8]
(local variable) _error_ buf
buf
;
undefined identifier `WCHAR`
const
(local variable) const(_error_) n
n
= GetEnvironmentVariableW("WSI_AUTO_EXIT"w.
"WSI_AUTO_EXIT"w.ptr
ptr
, buf.
buf.ptr
ptr
, buf.length);
undefined identifier `GetEnvironmentVariableW`
return n >= 1 && n < buf.length && buf[0] == '1'; } int
int D main()
main
()
{ instrumentInit("f15_popup_win32");
undefined identifier `instrumentInit`
logEvent("init_start");
undefined identifier `logEvent`
g.
g.autoExit
autoExit
= wantAutoExit();
logEvent("mode auto_exit=%d", g.
g.autoExit
autoExit
? 1 : 0);
undefined identifier `logEvent`
HINSTANCE
(local variable) _error_ hInst
hInst
= GetModuleHandleW(null);
undefined identifier `HINSTANCE`
undefined identifier `GetModuleHandleW`
HCURSOR
(local variable) _error_ arrow
arrow
= LoadCursorW(null, IDC_ARROW);
undefined identifier `HCURSOR`
undefined identifier `LoadCursorW`
WNDCLASSEXW
(local variable) _error_ wc
wc
;
undefined identifier `WNDCLASSEXW`
wc.cbSize = WNDCLASSEXW.sizeof; wc.lpfnWndProc = &wndProc; wc.hInstance = hInst; wc.lpszClassName = "wsi-f15-class"w.
"wsi-f15-class"w.ptr
ptr
;
wc.hCursor = arrow; if (!RegisterClassExW(&wc))
undefined identifier `RegisterClassExW`
return 1; WNDCLASSEXW
(local variable) _error_ mc
mc
;
undefined identifier `WNDCLASSEXW`
mc.cbSize = WNDCLASSEXW.sizeof; mc.lpfnWndProc = &menuProc; mc.hInstance = hInst; mc.lpszClassName = "wsi-f15-menu"w.
"wsi-f15-menu"w.ptr
ptr
;
mc.hCursor = arrow; if (!RegisterClassExW(&mc))
undefined identifier `RegisterClassExW`
return 1; g.
g.hwndMain
hwndMain
= CreateWindowExW(0, "wsi-f15-class"w.
"wsi-f15-class"w.ptr
ptr
, "wsi-f15-popup"w.
"wsi-f15-popup"w.ptr
ptr
,
WS_OVERLAPPEDWINDOW, 60, 40, 480, 320, null, null, hInst, null); if (g.
(field) _error_ g.hwndMain
hwndMain
is null)
{ logEvent("error what=CreateWindowExW code=%lu", GetLastError());
undefined identifier `logEvent`
return 1; } logEvent("window_created hwnd=%p", g.
g.hwndMain
hwndMain
);
undefined identifier `logEvent`
ShowWindow(g.
g.hwndMain
hwndMain
, SW_SHOW);
undefined identifier `ShowWindow`
UpdateWindow(g.
g.hwndMain
hwndMain
);
undefined identifier `UpdateWindow`
SetTimer(g.
g.hwndMain
hwndMain
, TIMER_ID, TICK_MS, null);
undefined identifier `SetTimer`
MSG
(local variable) _error_ msg
msg
;
undefined identifier `MSG`
while (GetMessageW(&msg, null, 0, 0) > 0)
undefined identifier `GetMessageW`
{ TranslateMessage(&msg);
undefined identifier `TranslateMessage`
DispatchMessageW(&msg);
undefined identifier `DispatchMessageW`
} logEvent("exit code=%d", cast(int) msg.wParam);
undefined identifier `logEvent`
return cast(int) msg.wParam; }