v1.6.2 — the warning was in the wrong place, and nothing could have said so
Reported as "the frame sits in the top-left corner". Measured on the desktop it happened on: two monitors, 3840x2160 and 1080x1920, and the overlay's own bar windows were 1280x720 at the origin. Four of them, not eight — it had found one screen where there are two.
psc-top 0,0 -> 1280 x 31
psc-left 0,0 -> 31 x 720
psc-right 1249,0 -> 31 x 720
psc-bottom 0,689 -> 1280 x 31
It is not a scaling bug
That was the first guess, and it was wrong. SetProcessDpiAwareness(2) succeeds cleanly - HRESULT 0 - and the process reads 3840x2160 at 150% afterwards. Measured before assuming.
1280x720 is what Windows hands back to a process that asks about the desktop before the desktop is ready. The overlay asked exactly once, in its first line, and kept the answer for the rest of the session. It is started with the first block after the app launches, which on a cold boot is precisely when the answer is not ready yet.
The reading is no longer kept
It is taken again whenever the glow is about to be shown, and once a second for as long as it is up. That costs microseconds, and it covers every way screens can change without having to enumerate them: waking from sleep, a monitor switched on, resolution or scaling changed, a cable moved, a remote session - and the case above, where nothing changed at all and the first answer was simply wrong. Bars are created and retired to match.
On the message-loop thread, not the reader thread. Creating a window from the reader is the same mistake that hid the input hold for two releases; warn(), lock() and wait_on() only stamp a clock now, and tick() does the work.
And the eye is no longer the only witness
The overlay reports the rectangles it draws on, and self_test holds them against the screens Windows describes right now:
Is the warning drawn around your actual screens? OK 3840x2160+0+0;1080x1920+3840+0
A glow in the wrong place is a line of text now, not something you have to happen to notice.
Third time for this shape
1.6.0 and 1.6.1 were about a call whose failure was swallowed. This one is about an answer that was correct for nobody and was never asked again. Same core both times: something asserts a state, and nothing checks whether it still holds.
New test file tests/test_monitors.py - nineteen now, green on Windows against Python 3.9, 3.11 and 3.13.
Install
Claude Desktop - pc-screen-control.mcpb -> Settings -> Extensions -> Advanced -> Install extension. Remove the previous version first and quit Claude completely, tray icon included.
ChatGPT desktop, Codex, Cursor, VS Code, Cline, Zed - pc-screen-control-gpt.zip -> extract -> INSTALL-FOR-GPT.bat -> restart.
Windows and Python 3.9+ with "Add python.exe to PATH". Run self_test - it should report 1.6.2.
Checksums
pc-screen-control.mcpb 8239855 bytes
SHA-256 48db4afd1a8fcd9ee7506eb14565ae49acc9c5339f1c14ca06fe31867b106aef
pc-screen-control-gpt.zip 8140363 bytes
SHA-256 92152d34c8f0283f0c75bab579b161f1427c18b94f9796ca6e3611aa434f4e43