@@ -619,35 +619,41 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
619619 }
620620
621621 const auto modifiers = _GetPressedModifierKeys ();
622- const auto vkey = static_cast <WORD>(e.OriginalKey ());
623622
623+ // AltGr key combinations don't always contain any meaningful,
624+ // pretranslated unicode character during WM_KEYDOWN.
625+ // E.g. on a German keyboard AltGr+Q should result in a "@" character,
626+ // but actually results in "Q" with Alt and Ctrl modifier states.
627+ // By returning false though, we can abort handling this WM_KEYDOWN
628+ // event and let the WM_CHAR handler kick in, which will be
629+ // provided with an appropriate unicode character.
630+ //
631+ // GH#2235: Make sure to handle AltGr before trying keybindings,
632+ // so Ctrl+Alt keybindings won't eat an AltGr keypress.
633+ if (modifiers.IsAltGrPressed ())
634+ {
635+ _HandleVoidKeyEvent ();
636+ e.Handled (false );
637+ return ;
638+ }
639+
640+ const auto vkey = static_cast <WORD>(e.OriginalKey ());
624641 bool handled = false ;
642+
625643 auto bindings = _settings.KeyBindings ();
626644 if (bindings)
627645 {
628- KeyChord chord (
646+ handled = bindings. TryKeyChord ({
629647 modifiers.IsCtrlPressed (),
630648 modifiers.IsAltPressed (),
631649 modifiers.IsShiftPressed (),
632- vkey);
633- handled = bindings. TryKeyChord (chord );
650+ vkey,
651+ } );
634652 }
635653
636654 if (!handled)
637655 {
638- _terminal->ClearSelection ();
639- // If the terminal translated the key, mark the event as handled.
640- // This will prevent the system from trying to get the character out
641- // of it and sending us a CharacterRecieved event.
642- handled = _terminal->SendKeyEvent (vkey, modifiers);
643-
644- if (_cursorTimer.has_value ())
645- {
646- // Manually show the cursor when a key is pressed. Restarting
647- // the timer prevents flickering.
648- _terminal->SetCursorVisible (true );
649- _cursorTimer.value ().Start ();
650- }
656+ handled = _TrySendKeyEvent (vkey, modifiers);
651657 }
652658
653659 // Manually prevent keyboard navigation with tab. We want to send tab to
@@ -661,6 +667,45 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
661667 e.Handled (handled);
662668 }
663669
670+ // Method Description:
671+ // - Some key events cannot be handled (e.g. AltGr combinations) and are
672+ // delegated to the character handler. Just like with _TrySendKeyEvent(),
673+ // the character handler counts on us though to:
674+ // - Clears the current selection.
675+ // - Makes the cursor briefly visible during typing.
676+ void TermControl::_HandleVoidKeyEvent ()
677+ {
678+ _TrySendKeyEvent (0 , {});
679+ }
680+
681+ // Method Description:
682+ // - Send this particular key event to the terminal.
683+ // See Terminal::SendKeyEvent for more information.
684+ // - Clears the current selection.
685+ // - Makes the cursor briefly visible during typing.
686+ // Arguments:
687+ // - vkey: The vkey of the key pressed.
688+ // - states: The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
689+ bool TermControl::_TrySendKeyEvent (WORD vkey, const ControlKeyStates modifiers)
690+ {
691+ _terminal->ClearSelection ();
692+
693+ // If the terminal translated the key, mark the event as handled.
694+ // This will prevent the system from trying to get the character out
695+ // of it and sending us a CharacterRecieved event.
696+ const auto handled = vkey ? _terminal->SendKeyEvent (vkey, modifiers) : true ;
697+
698+ if (_cursorTimer.has_value ())
699+ {
700+ // Manually show the cursor when a key is pressed. Restarting
701+ // the timer prevents flickering.
702+ _terminal->SetCursorVisible (true );
703+ _cursorTimer.value ().Start ();
704+ }
705+
706+ return handled;
707+ }
708+
664709 // Method Description:
665710 // - handle a mouse click event. Begin selection process.
666711 // Arguments:
0 commit comments