alt-tab-per-monitor: rewrite v2.0.0 - state machine architecture#3465
alt-tab-per-monitor: rewrite v2.0.0 - state machine architecture#3465troshab wants to merge 1 commit intoramensoftware:mainfrom
Conversation
Replace fragile timing hack with proper state machine architecture: - State machine (AltTabPhase enum) instead of 3 unsynchronized atomics - RAII PhaseGuard for safe phase transitions - SRWLOCK synchronization for thread-safe state access - Wh_ModUninit with hook call counter for safe unloading - Configurable postCreateThresholdMs (was hardcoded 200ms) - Deduplicated common symbol hooks between Win10/Win11 - Removed per-window Wh_Log spam from IsViewVisible
|
Thanks for the submission. Are you seeing an actual problem that your submission fixes? |
Yes, with the previous version of this module enabled, switching keys with Alt Shift became hell. If you clicked very quickly instead of holding Alt for a relatively long time, it did not switch the input language. |
|
How is Alt+Shift related to Alt+Tab or to this mod? Please provide a way to reproduce the issue. And I'm sure it's possible to address it without rewriting the whole mod. |
The connection is through CVirtualDesktop::IsViewVisible - this function is not exclusive to Alt+Tab. It's called by the taskbar, Win+Tab, and other shell components including the input switcher overlay. The mod's timing-based filter (kDeltaThreshold = 200ms) can leak into those calls because g_lastThreadIdForXamlAltTabViewHost_CreateInstance is never cleared - once set, any IsViewVisible call on that thread within 200ms gets filtered. How I found it: I was debugging why Alt+Shift language switching was unreliable on Windows 11 24H2 (required holding keys instead of a quick press). After ruling out keyboard hooks (measured 0ms latency with a custom WH_KEYBOARD_LL monitor), I started disabling Windhawk mods one by one. Disabling alt-tab-per-monitor resolved the issue. Re-enabling it brought the problem back. Reproduction: Enable the mod, then rapidly press Alt+Shift to switch input language. On systems with 2+ languages and multiple monitors, the switch is unreliable - sometimes it doesn't register, sometimes it double-switches. Disable the mod - the problem goes away. As for the rewrite scope - beyond the Alt+Shift issue, the current code has a data race (g_CreateInstance_TickCount is a non-atomic ULONGLONG written and read from different contexts), no Wh_ModUninit (hooks can fire during unload), and Wh_Log in IsViewVisible that spams the log for every window. These issues are interconnected enough that a targeted fix would end up touching most of the same code. But I'm happy to split this into smaller PRs if preferred. |
Right, but it can cause issues only up to 200ms after
I can't reproduce it. I also have nothing logged when switching between languages with Alt+Shift. Can you post logs and a video recording? Perhaps you have some additional UI for one of the languages.
It can be changed to
It applies for many other mods, and implementing a refcount in each case is a hassle. Note that:
That's a matter of preference. It has no impact when logging is disabled. Surely it doesn't warrant a refactor for the whole mod.
Let's focus on understanding why the mod broke Alt+Shift for you, and fix that specifically. |
Summary
Rewrites the
alt-tab-per-monitormod (by @L3r0yThingz) with proper state machine architecture, replacing the fragile timing-based approach.Problems in v1.1.1
kDeltaThreshold = 200ms- too short on slow systems, unnecessary on fast onesg_CreateInstance_TickCountfor state trackingWh_ModUninit: hooks could fire during unload (undefined behavior)Wh_LoginIsViewVisible: called per-window, spams the logChanges in v2.0.0
AltTabPhaseenum: Inactive -> Creating -> PostCreate -> Showing -> Inactive)PhaseGuardfor safe phase transitions (no leaked flags on error paths)SRWLOCKsynchronization instead of multiple atomicsWh_ModUninitwithHookCallCounterGuardfor safe unloadingpostCreateThresholdMsin mod settings (default 200ms)commonSymbolHooks[]Wh_LogfromIsViewVisible(was spamming log)PhaseGuardin Show handles cleanupCompatibility
Test plan