Toggle a game's profile from a hotkey (#143) - #14
Merged
Conversation
The issue is a title and nothing else - "[Suggestion] Add reset/toggle keybind" - so the shape was decided rather than transcribed, and every choice below is an assumption a maintainer may want to argue with. Press the key while a configured game holds the foreground and that display flips between the game's level and the Windows level. Press it anywhere else and nothing happens at all. The choice then wins over the automatic behaviour for that game until pressed again or the app restarts, so alt-tabbing back in does not quietly undo it. RegisterHotKey, never a low-level keyboard hook. The readme names CS:GO, and a program installing a system-wide keyboard hook while someone plays a competitive shooter is the exact shape anti-cheat heuristics look for. Nothing here is loaded into, injected into or hooked onto another process. The cost is real and documented: a game can suppress the key by registering raw input against hotkeys, or by installing its own hook after ours. The only mechanism that could reach those cases is the one ruled out, so they are stated rather than chased. The far more common cause of a dead hotkey - another program already owning the combination - is reported inline the moment it is set, where the old behaviour was to store the failure in a boolean and tell nobody. The state is a set of suppressed profile names, deliberately not a set of enabled ones, so an empty set means today's behaviour and none of the existing checks change meaning. It is keyed by name rather than by the setting object, because editing a profile replaces that object. Nothing persists it: the helper has no I/O at all, so a game silently left off cannot survive a restart and be discovered weeks later. Toggling off restores one display through the single-display path, not the one that walks the whole work list and the primary as well. On AMD with the default configuration that path has to widen, because there the automatic apply writes every display - restoring one would leave the others saturated while the notification claimed otherwise. Two things had to change underneath. Restoring one display carries no guard against the Windows level being unknown - that lives a level up - so reaching it directly would have written zero to a display during startup, which is the defect fixed three changes ago arriving through a new door. The check now sits in the decision itself. And AMD's set- saturation call returned void, so there was no way to honour "only report success when the write landed": it now returns whether a display actually matched and every call succeeded, which also distinguishes a name that matched nothing from a write that worked. Fifty-three checks behind a fake registrar, a fake foreground reader and the existing fake devices. No test registers a real hotkey or touches a real display. Every check was proven by breaking the line it guards - including two that were rewritten after review because they exercised a copy of the logic rather than the logic, which the whole suite could not distinguish. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0187tGqyEw4frZzDYPPJfUMd
This was referenced Aug 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Upstream issue #143 is a title and nothing else — "[Suggestion] Add reset/toggle keybind", empty body, no comments. So the product shape was decided rather than transcribed, and every choice below is an assumption a maintainer may want to argue with. They're listed explicitly at the end.
What it does
Press the key while a configured game holds the foreground → that display flips between the game's level and your Windows level. Press it anywhere else → nothing happens at all: no writes, no state change.
The choice then wins over the automatic behaviour for that game until pressed again or the app restarts — so alt-tabbing back into the game does not quietly undo it.
RegisterHotKey, never a low-level keyboard hookThe README names CS:GO. A program installing a system-wide keyboard hook while someone plays a competitive shooter is the exact shape anti-cheat heuristics look for, so this is a product constraint, not an implementation detail. Nothing here is loaded into, injected into, or hooked onto another process — the OS posts
WM_HOTKEYto our own thread's queue.grepforSetWindowsHookExreturns zero hits repo-wide; the property is structural.The cost is real and stated rather than chased. A game can suppress the key by registering raw input with
RIDEV_NOHOTKEYS, or by installing its own low-level hook after ours. Exclusive fullscreen alone does not cause this. The only mechanism that could reach those cases is the one the constraint rules out — a low-level hook is strictly worse here, not a trade: it loses the same install-ordering race, and UIPI additionally blocks a non-elevated hook from seeing input bound for an elevated foreground process.The far more common cause of a dead hotkey — another program already owning the combination — is reported inline in Settings, synchronously, at the moment it's set.
The state
A set of suppressed profile names, deliberately not a set of enabled ones, so an empty set means today's behaviour and none of the existing checks change meaning. Keyed by name rather than by the
ApplicationSettingobject, because editing a profile removes and re-adds a new one.Nothing persists it. The helper contains no I/O at all, so there is no read path to review — a game silently left off cannot survive a restart and be discovered weeks later.
Two things had to change underneath
Restoring a single display carries no guard against the Windows level being unknown. That guard lives one level up, and every pre-existing caller reaches the single-display path through it. Calling it directly — the natural implementation — would write 0 to a display during startup, which is the defect fixed in #11 arriving through a new door, and the existing check wouldn't catch it because it drives the wrapper. The check now sits in the decision function itself.
AMD's
SetSaturationOnDisplayreturnedvoid, so "only report success when the write landed" was unbuildable there. It now returns whether a display actually matched and every call succeeded — which also distinguishes "the name matched nothing, so nothing was called" from a write that worked. Those were previously indistinguishable. The underlying ADL call always returned a status; it was being discarded. A latent null-reference on the same line (the delegate resolves lazily and can be null) is guarded in passing.AMD's existing restore loop still ignores the new bool, so the checks pinning its behaviour keep their meaning.
Testing
53 checks behind a fake registrar, a fake foreground reader, and the existing fake devices. Suite total 328. No test registers a real hotkey or touches a real display, and the fixture must never grow a hardware variant.
Every check was proven by breaking the line it guards. Two findings from that process are worth recording:
Two checks were rewritten after review because they exercised a copy of the logic rather than the logic. A fixture-local helper mirrored the real gate verbatim. QA then mutated the real production line — and the entire 322-check suite still passed, because nothing anywhere reflected into that class. The expression is now hoisted into a method both the app and the tests call, so the copy is gone.
Two checks that look redundant are not. Moving the suppression gate to after the screen assignment leaves the "zero device calls" check passing and only fails the "screen untouched" check. Both halves reproduced independently.
The B2 fix also broke three pre-existing checks that had been implicitly relying on the old unconditional behaviour. They were repaired with explicit setup rather than adjusted assertions.
Assumptions a maintainer may disagree with
Known gaps
-p:TargetFrameworkVersion=v4.8. The csproj still declaresv4.0and was not edited.Relationship to PR juv#153
harisonw's #153 implements an adjacent feature and its mechanism choice was right —
RegisterHotKeyover a hook — which this adopts and credits. Its product decisions differ (it persists toggle state; it matches profiles on name alone, so a profile matched by install directory would be reachable by the automatic path and invisible to the hotkey), and it bundles a .NET 4.8 framework upgrade across 15 files, which this deliberately does not.Reviewing it also corrected a claim that would otherwise have appeared here: an earlier draft asserted that clicking the tray icon recreates the form's handle and orphans juv#153's registration. That is false —
ShowInTaskbaris alreadytrueand its setter no-ops, measured. The handle is still cached at registration time in this implementation because that is unconditionally more correct, but as a defensive choice, not a fix for a defect anyone has.