Repository navigation
Fix various KKP encoding issues - #20052
Conversation
This comment has been minimized.
This comment has been minimized.
| } | ||
|
|
||
| // Keep track of key repeats. | ||
| key.keyRepeat = _lastVirtualKeyCode == key.virtualKey; |
There was a problem hiding this comment.
Placing the detection outside a if (key.keyDown) block meant that a single key up/down would already be detected as a key repeat.
| if (const auto flags = _controlKeyStateFromVirtualKey(key.virtualKey, key.controlKeyState)) | ||
| { | ||
| key.keyRepeat = (_previousControlKeyState & flags) != 0; | ||
| } |
There was a problem hiding this comment.
These 3 lines fix the AltGr key repeat.
| key.altGrPressed = anyAltPressed && anyCtrlPressed; | ||
| key.ctrlPressed = key.altGrPressed ? (bothCtrlPressed || (rightAltPressed && leftCtrlIsReallyPressed)) : anyCtrlPressed; | ||
| key.altPressed = key.altGrPressed ? bothAltPressed : anyAltPressed; | ||
| key.shiftPressed = WI_IsFlagSet(key.controlKeyState, SHIFT_PRESSED); |
There was a problem hiding this comment.
To make reliable AltGr detection possible throughout the code, I've made a bit of a more controversial change and removed the entire key.controlKeyState reliance throughout the code.
This crucially means that even the legacy key encoding will now treat AltGr distinct from other modifiers.
| // NOTE: MSDN states that `lpSrcStr == lpDestStr` is valid for LCMAP_LOWERCASE. | ||
| len = LCMapStringW(LOCALE_INVARIANT, LCMAP_LOWERCASE, &buf[0], len, &buf[0], ARRAYSIZE(buf)); | ||
| // NOTE: LCMapStringW returns the length including the null terminator. | ||
| len -= 1; |
There was a problem hiding this comment.
len -= 1 was wrong and only applies to encoding null-terminated strings. I have switched to a length-based buffer a long time ago and missed fixing this. 🙈
| // In the context of KKP, AltGr acts more like a keyboard "layer" toggle. | ||
| // It's not a modifier that's ever transmitted as-is and instead modifies the actual base key code. | ||
| if (key.altGrPressed) | ||
| { | ||
| controlKeyState |= LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED; | ||
| } |
There was a problem hiding this comment.
This new if condition ensures we treat AltGr like a layer shift that changes the base encoding of the key.
| { L"D Shift+Alt+a", L"\x1b[97;4u", D, true, 'A', 0x1E, L'A', Shift | Alt }, | ||
| { L"D Shift+a", L"A", D, true, 'A', 0x1E, L'A', Shift }, | ||
| { L"D Ctrl+a", L"\x1b[97;5u", D, true, 'A', 0x10, 0, Ctrl }, | ||
| { L"D Ctrl+Alt+a", L"æ", D, true, 'A', 0x10, L'æ', Ctrl | Alt }, |
There was a problem hiding this comment.
This has been rewritten to use French AZERTY events.
There was a problem hiding this comment.
all of them ??? is that safe? ... are we guaranteed that it exists on OneCore?
should we have tests in US-104 for the most part and allow a TestCase to specify a keyboard layout to set?
There was a problem hiding this comment.
all of them ??? is that safe?
Yes and yes.
are we guaranteed that it exists on OneCore?
No. But all of this code relies on APIs that are categorically unavailable in OneCore, so it probably doesn't matter.
should we have tests in US-104 for the most part and allow a TestCase to specify a keyboard layout to set?
That's how it works now, via SetTerminalInputKeyboardLayout.
This comment has been minimized.
This comment has been minimized.
| Log::Comment(L"Sending every possible VKEY at the input stream for interception during key DOWN."); | ||
|
|
||
| BYTE vkey = LOBYTE(OneCoreSafeVkKeyScanW(0)); | ||
| BYTE vkey = LOBYTE(VkKeyScanExW(0, layout)); |
There was a problem hiding this comment.
We still need to use OneCoreSaveVkKeyScanW; this test will fail to run (or, maybe the entire fixture will fail to load?) if we link VkKeyScanW.
There was a problem hiding this comment.
But it depends on LoadKeyboardLayout and ToUnicodeEx now. Won't all of these fail to work anyway? Do they have to work in the first? The TerminalInput logic is platform-independent with the exception of a single ToUnicodeEx call.
There was a problem hiding this comment.
Okay, so here's what I have come to understand.
We refer to these APIs using an extension APIset.
ext-ms-win-ntuser-keyboard-l1-1-0
GetKeyState
GetKeyboardLayout
MapVirtualKeyW
ToUnicodeEx
VkKeyScanW
OneCoreSafeEtc runs those through coniosrv or equivalent rather than ntuser.
In theory, we could have done some effort to compose onecore with a new extension apiset that fills the same contract. Instead we used runtime dynamic dispatch. Eh.
As long as the implementation calls OneCoreSafeEtc, we're fine.
We will need to prevent the kitty tests from running on OneCore, so that we do not experience test gate failures. :)
There was a problem hiding this comment.
I have added exception guards around SetTerminalInputKeyboardLayout. However, I have not checked if it results in linking failures in OneCore. I'm somewhat confused how this works in general given that we have never defined a OneCoreSafe wrapper for ToUnicodeEx.
|
It is rather annoying that the |
Unfortunately, the deal with TestHook is that the no-op implementation must be in a separate This type of test hook relies on the classic model of linking where objects are only pulled in even from libs if they are referred to for an unresolved symbol. |
|
(Whereas you have TestHook.obj in the UT project; you rather need TestHook.obj in TerminalInput which contains the no-op implementation; then you can implement the hook in the UT in whatever object file you want, including the test file that relies on the overridden hook) |
This comment has been minimized.
This comment has been minimized.
Dustin L. Howett (DHowett)
left a comment
There was a problem hiding this comment.
OK, I think I understand this now. Thanks for being patient. Let's get it in Canary and see what happens.
NOTE: This commit changes our input model to treat all Ctrl-Alt combinations as an AltGr modifier by default. Ctrl+Alt+/ will not produce `"\x1b\x1f"` anymore. This fixes the following issues: * Base-key report of non-ASCII keys was broken * AltGr on international keyboards would spam Ctrl/Alt up/down events repeatedly forever * KKP doesn't report modifier key repeats * AltGr+Key combinations were reported as Ctrl+Alt+key but should've been just plain Key-But-Modified Closes #19977 ## Validation Steps Performed This cannot be easily tested in unit tests, due to the dependence on international keyboard layouts. It appears to work correctly from what I can tell. (cherry picked from commit fbda436) Service-Card-Id: PVTI_lADOAF3p4s4BQX0-zg6CmdI Service-Version: 1.25
Windows Terminal >= 1.24 aliases every Ctrl+Alt combo to AltGr (microsoft/terminal#20052), so the legacy "ESC + control byte" pair that matchesKey() looks for (ctrl+alt+r = \x1b\x12) is never emitted there and the keystroke dies inside the terminal. WSL panes inherit that input translation, so they need the same treatment; pi's own useWindowsKeybindings() draws the line in the same place (it is not exported from the package root, so the check is mirrored). - lib/shortcuts.ts: one place for the three shortcuts (t/n/r). Windows-like platforms register ctrl+alt+<letter> and alt+<letter>; everyone else keeps ctrl+alt only (macOS: plain Option composes characters). The cheatsheet label is derived from the key id that is actually registered, so a tab can no longer advertise a key the platform never binds; on macOS it renders "Ctrl+option+r" like pi's own formatter does. - extensions/section-headers.ts registers every variant through pi.registerShortcut; extensions/footer.ts matches through the same object. Both build it once at module scope (matches() runs per keystroke). - /reload still intercepts handleInput on purpose: ExtensionContext.reload() exists in the typings but the context handed to shortcut handlers does not implement it. - README: platform notes (Windows/WSL, macOS, Linux GNOME owning ctrl+alt+t) and PI_TUI_ESC_TIMEOUT for the intermittent "Escape + stray letter" glitch — pi flushes a lone ESC after ~10 ms, which splits ESC-prefixed shortcuts on slow or multiplexed PTYs (verified: 20 ms gap -> ["\x1b","r"], Escape fired; 100 ms -> ["\x1br"], shortcut fired). Verified: npm test 10/10 (incl. new lib/shortcuts.ts self-check), platform matrix forced for win32 / WSL / linux / darwin, live registration probe ["ctrl+alt+t","alt+t"] and cheatsheet [Alt+t]/[Alt+n]/[Alt+r] on win32.
NOTE:
This commit changes our input model to treat all
Ctrl-Alt combinations as an AltGr modifier by default.
Ctrl+Alt+/ will not produce
"\x1b\x1f"anymore.This fixes the following issues:
Ctrl/Alt up/down events repeatedly forever
but should've been just plain Key-But-Modified
Closes #19977
Validation Steps Performed
This cannot be easily tested in unit tests, due to
the dependence on international keyboard layouts.
It appears to work correctly from what I can tell.