fix(kbm): Handle SendVirtualInput failures and release modifiers for text remap#9
fix(kbm): Handle SendVirtualInput failures and release modifiers for text remap#9crutkas wants to merge 5 commits into
Conversation
This comment has been minimized.
This comment has been minimized.
|
At for (int vk : modifierKeys)
if (ii.GetVirtualKeyState(vk))
// queue release + remember in releasedKeys
ii.SendVirtualInput(releaseEvents);
Helpers::SendTextInput(*remapping); // <-- user can release the physical modifier here
// unconditionally re-press releasedKeys
ii.SendVirtualInput(restoreEvents);If the user physically lets go of the modifier during Suggested fix: query for (int vk : releasedKeys)
{
if (GetAsyncKeyState(vk) & 0x8000)
Helpers::SetKeyEvent(restoreEvents, INPUT_KEYBOARD, static_cast<WORD>(vk), 0,
KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
}Tests in |
…text remap - SendVirtualInput now returns bool; callers pass through original key on failure - Release held modifiers before text injection in single-key-to-text remap to prevent Ctrl+text corruption Fixes: microsoft#17035, microsoft#29015, microsoft#9778 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…urns, add tests - Add VK_LWIN/VK_RWIN to modifierKeys[] in HandleSingleKeyToTextRemapEvent so Win key is released before text injection (prevents Win+h/e/l shortcuts) - Check SendVirtualInput return on modifier release — pass through original key if release fails (prevents Ctrl+text corruption on SendInput failure) - Add MockedInput::SetKeyboardState for test setup - Add ClearSingleKeyToTextRemaps to ResetTestEnv - Add 2 new tests: - HandleSingleKeyToTextRemapEvent_ShouldReleaseAndRestoreWinKey_WhenWinKeyIsHeld - HandleSingleKeyToTextRemapEvent_ShouldReleaseAndRestoreCtrl_WhenCtrlIsHeld All 94 KBM unit tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The single-key-to-text handler releases any held modifiers before injecting Unicode text (so Ctrl/Shift/etc. don't corrupt it) and previously re-pressed them afterwards. Re-pressing is unsafe: once we inject a modifier KEYUP, GetAsyncKeyState reports that key as up, so we cannot tell whether the user is still physically holding it or has let go. If they released it during injection, the re-press would leave the modifier stuck down - the exact bug this series fixes. Leave released modifiers released; the user taps the key again to re-engage it. Update the tests to assert modifiers are never re-pressed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
0bb92cb to
edf82ef
Compare
|
Good catch on the release/re-press race. I dug into the So I dropped the re-press entirely. Modifiers are released before the text is injected ( |
…ection Three correctness fixes for the single-key-to-text remap path plus test coverage for the stuck-key hardening: 1. Insert a dummy key event before releasing held modifiers. Releasing a lone Win or Alt key-up otherwise triggers the Start Menu / menu bar; the dummy key absorbs the modifier so the release is inert. The dummy and releases are only injected when a modifier is actually held. 2. Accept WM_SYSKEYDOWN as well as WM_KEYDOWN. While Alt is held the system sends WM_SYSKEYDOWN, so the previous WM_KEYDOWN-only guard silently dropped the remap whenever Alt was down. 3. Route Helpers::SendTextInput through the InputInterface instead of calling Win32 SendInput directly. Previously the unit tests for this handler injected real keystrokes into the OS during the test run. Text is still flushed per character to preserve the existing batching workaround. Also add a MockedInput failure seam (SetSendVirtualInputShouldFail) and tests: RemappedKey_ShouldPassOriginalKeyThrough_WhenInjectionFails verifies the handler passes the original key through when injection fails - the core stuck-key behavior introduced by this change set, previously untestable because the mock always succeeded; and HandleSingleKeyToTextRemapEvent_ShouldFireAndReleaseAlt_WhenAltIsHeld covers fix 2 by asserting the remap still fires (releasing the held Alt) when the key arrives as WM_SYSKEYDOWN. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Superseded by the upstream PR against microsoft/PowerToys: microsoft#48571 (rebased onto current main, builds/tests verified). Closing this intra-fork review PR. |
Summary
Fix Keyboard Manager stuck key bugs where
SendVirtualInputfailure causes keys to vanish, and held modifiers corrupt text injection.Changes
C1:
SendVirtualInputreturns bool — pass through on failureInputInterface::SendVirtualInputfromvoidtoboolfalsewhenSendInputinjects fewer events than expectedreturn 1) now check the return value — if injection fails,return 0passes through the original key instead of eating itMockedInputtest double to match new signatureC4: Release held modifiers before text injection
HandleSingleKeyToTextRemapEvent, detect held modifiers viaGetAsyncKeyStateSendTextInputCtrl+text,Win+textcorruptionNew Unit Tests (3 tests)
HandleSingleKeyRemapEvent_ShouldPassThrough_WhenSendVirtualInputFails— verifies key not eaten on failureHandleSingleKeyToTextRemapEvent_ShouldReleaseAndRestoreCtrl_WhenCtrlIsHeldHandleSingleKeyToTextRemapEvent_ShouldReleaseAndRestoreWinKey_WhenWinKeyIsHeldHow to Reproduce
C1 — Key eaten on failure: Hard to reproduce directly (requires SendInput to fail), but verified via unit test with mocked failure.
C4 — Modifier corruption:
How to Verify
Related Issues
PR Checklist