From 6dc051dadaa3bb9f6efc070b81e95f6711278313 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 16 Oct 2020 19:11:47 +0200 Subject: [PATCH] Fixed #5784: Key bindings won't consume dead keys --- .../actions/spell-check/dictionary/names.txt | 2 + src/cascadia/TerminalControl/TermControl.cpp | 40 +++++++++++++++++-- src/cascadia/TerminalControl/TermControl.h | 3 +- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/.github/actions/spell-check/dictionary/names.txt b/.github/actions/spell-check/dictionary/names.txt index c7f4c1f2b15..23e4f9b6430 100644 --- a/.github/actions/spell-check/dictionary/names.txt +++ b/.github/actions/spell-check/dictionary/names.txt @@ -31,9 +31,11 @@ mbadolato Mehrain mgravell michaelniksa +michkap migrie mikegr mikemaccana +miloush miniksa niksa oising diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 592cd733f7f..fc33c7603c7 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -895,7 +895,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // keybindings on the keyUp, then we'll still send the keydown to the // connected terminal application, and something like ctrl+shift+T will // emit a ^T to the pipe. - if (!modifiers.IsAltGrPressed() && keyDown && _TryHandleKeyBinding(vkey, modifiers)) + if (!modifiers.IsAltGrPressed() && keyDown && _TryHandleKeyBinding(vkey, scanCode, modifiers)) { e.Handled(true); return; @@ -917,8 +917,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // - Attempt to handle this key combination as a key binding // Arguments: // - vkey: The vkey of the key pressed. + // - scanCode: The scan code of the key pressed. // - modifiers: The ControlKeyStates representing the modifier key states. - bool TermControl::_TryHandleKeyBinding(const WORD vkey, ::Microsoft::Terminal::Core::ControlKeyStates modifiers) const + bool TermControl::_TryHandleKeyBinding(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers) const { auto bindings = _settings.KeyBindings(); if (!bindings) @@ -926,12 +927,44 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation return false; } - return bindings.TryKeyChord({ + auto success = bindings.TryKeyChord({ modifiers.IsCtrlPressed(), modifiers.IsAltPressed(), modifiers.IsShiftPressed(), vkey, }); + if (!success) + { + return false; + } + + // Let's assume the user has bound the dead key "^" to a sendInput command that sends "b". + // If the user presses the two keys "^a" it'll produce "bâ", despite us marking the key event as handled. + // The following is used to manually "consume" such dead keys and clear them from the keyboard state. + _ClearKeyboardState(vkey, scanCode); + return true; + } + + // Method Description: + // - Discards currently pressed dead keys. + // Arguments: + // - vkey: The vkey of the key pressed. + // - scanCode: The scan code of the key pressed. + void TermControl::_ClearKeyboardState(const WORD vkey, const WORD scanCode) const noexcept + { + std::array keyState; + if (!GetKeyboardState(keyState.data())) + { + return; + } + + // As described in "Sometimes you *want* to interfere with the keyboard's state buffer": + // http://archives.miloush.net/michkap/archive/2006/09/10/748775.html + // > "The key here is to keep trying to pass stuff to ToUnicode until -1 is not returned." + std::array buffer; + while (ToUnicodeEx(vkey, scanCode, keyState.data(), buffer.data(), gsl::narrow_cast(buffer.size()), 0b1, nullptr) < 0) + { + } } // Method Description: @@ -941,6 +974,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // - Makes the cursor briefly visible during typing. // Arguments: // - vkey: The vkey of the key pressed. + // - scanCode: The scan code of the key pressed. // - states: The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states. // - keyDown: If true, the key was pressed, otherwise the key was released. bool TermControl::_TrySendKeyEvent(const WORD vkey, diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index dd1118c3f1c..3d16738e020 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -285,7 +285,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation void _KeyHandler(Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e, const bool keyDown); ::Microsoft::Terminal::Core::ControlKeyStates _GetPressedModifierKeys() const; - bool _TryHandleKeyBinding(const WORD vkey, ::Microsoft::Terminal::Core::ControlKeyStates modifiers) const; + bool _TryHandleKeyBinding(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers) const; + void _ClearKeyboardState(const WORD vkey, const WORD scanCode) const noexcept; bool _TrySendKeyEvent(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const bool keyDown); bool _TrySendMouseEvent(Windows::UI::Input::PointerPoint const& point); bool _CanSendVTMouseInput();