Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #521 - AltGr combinations not working #1436

Merged
merged 1 commit into from
Jun 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 32 additions & 18 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -627,15 +627,18 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return;
}

auto modifiers = _GetPressedModifierKeys();

const auto modifiers = _GetPressedModifierKeys();
const auto vkey = static_cast<WORD>(e.OriginalKey());

bool handled = false;
auto bindings = _settings.KeyBindings();
if (bindings)
{
KeyChord chord(modifiers, vkey);
KeyChord chord(
WI_IsAnyFlagSet(modifiers, CTRL_PRESSED),
WI_IsAnyFlagSet(modifiers, ALT_PRESSED),
WI_IsFlagSet(modifiers, SHIFT_PRESSED),
vkey);
handled = bindings.TryKeyChord(chord);
}

Expand All @@ -645,10 +648,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// If the terminal translated the key, mark the event as handled.
// This will prevent the system from trying to get the character out
// of it and sending us a CharacterRecieved event.
handled = _terminal->SendKeyEvent(vkey,
WI_IsFlagSet(modifiers, KeyModifiers::Ctrl),
WI_IsFlagSet(modifiers, KeyModifiers::Alt),
WI_IsFlagSet(modifiers, KeyModifiers::Shift));
handled = _terminal->SendKeyEvent(vkey, modifiers);

if (_cursorTimer.has_value())
{
Expand Down Expand Up @@ -1424,8 +1424,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// find out which modifiers (ctrl, alt, shift) are pressed in events that
// don't necessarily include that state.
// Return Value:
// - a KeyModifiers value with flags set for each key that's pressed.
Settings::KeyModifiers TermControl::_GetPressedModifierKeys() const
// - The combined ControlKeyState flags as a bitfield.
DWORD TermControl::_GetPressedModifierKeys() const
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @zadjii-msft was actively avoiding using a loose number of flags for representing this.

Even though Settings::KeyModifiers is no longer appropriate (given it treats left/right alt/ctrl the same), maybe it's appropriate to either expand it to have a preference (if folks want it and set both alts or both ctrls if the regular alt/ctrl is chosen) or appropriate to make a different more strongly typed flag structure for this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be fair, I don't mind it this way.

{
CoreWindow window = CoreWindow::GetForCurrentThread();
// DONT USE
Expand All @@ -1435,17 +1435,31 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Sometimes with the key down, the state is Down | Locked.
// Sometimes with the key up, the state is Locked.
// IsFlagSet(Down) is the only correct solution.
const auto ctrlKeyState = window.GetKeyState(VirtualKey::Control);
const auto shiftKeyState = window.GetKeyState(VirtualKey::Shift);
const auto altKeyState = window.GetKeyState(VirtualKey::Menu);

const auto ctrl = WI_IsFlagSet(ctrlKeyState, CoreVirtualKeyStates::Down);
const auto shift = WI_IsFlagSet(shiftKeyState, CoreVirtualKeyStates::Down);
const auto alt = WI_IsFlagSet(altKeyState, CoreVirtualKeyStates::Down);
struct KeyModifier
{
VirtualKey vkey;
DWORD flag;
};

constexpr std::array<KeyModifier, 5> modifiers{ {
{ VirtualKey::RightMenu, RIGHT_ALT_PRESSED },
{ VirtualKey::LeftMenu, LEFT_ALT_PRESSED },
{ VirtualKey::RightControl, RIGHT_CTRL_PRESSED },
{ VirtualKey::LeftControl, LEFT_CTRL_PRESSED },
{ VirtualKey::Shift, SHIFT_PRESSED },
} };

DWORD flags = 0;

for (const auto& mod : modifiers)
{
const auto state = window.GetKeyState(mod.vkey);
const auto isDown = WI_IsFlagSet(state, CoreVirtualKeyStates::Down);
lhecker marked this conversation as resolved.
Show resolved Hide resolved
flags |= isDown ? mod.flag : 0;
}

return KeyModifiers{ (ctrl ? Settings::KeyModifiers::Ctrl : Settings::KeyModifiers::None) |
(alt ? Settings::KeyModifiers::Alt : Settings::KeyModifiers::None) |
(shift ? Settings::KeyModifiers::Shift : Settings::KeyModifiers::None) };
return flags;
}

// Method Description:
Expand Down
2 changes: 1 addition & 1 deletion src/cascadia/TerminalControl/TermControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void _ScrollbarUpdater(Windows::UI::Xaml::Controls::Primitives::ScrollBar scrollbar, const int viewTop, const int viewHeight, const int bufferSize);
static Windows::UI::Xaml::Thickness _ParseThicknessFromPadding(const hstring padding);

Settings::KeyModifiers _GetPressedModifierKeys() const;
DWORD _GetPressedModifierKeys() const;

const COORD _GetTerminalPosition(winrt::Windows::Foundation::Point cursorPosition);
};
Expand Down
2 changes: 1 addition & 1 deletion src/cascadia/TerminalCore/ITerminalInput.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Microsoft::Terminal::Core
public:
virtual ~ITerminalInput() {}

virtual bool SendKeyEvent(const WORD vkey, const bool ctrlPressed, const bool altPressed, const bool shiftPressed) = 0;
virtual bool SendKeyEvent(const WORD vkey, const DWORD modifiers) = 0;

// void SendMouseEvent(uint row, uint col, KeyModifiers modifiers);
[[nodiscard]] virtual HRESULT UserResize(const COORD size) noexcept = 0;
Expand Down
34 changes: 21 additions & 13 deletions src/cascadia/TerminalCore/Terminal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,16 +195,11 @@ void Terminal::Write(std::wstring_view stringView)
// real character out of the event.
// Arguments:
// - vkey: The vkey of the key pressed.
// - ctrlPressed: true iff either ctrl key is pressed.
// - altPressed: true iff either alt key is pressed.
// - shiftPressed: true iff either shift key is pressed.
// - modifiers: The current ControlKeyState flags.
// Return Value:
// - true if we translated the key event, and it should not be processed any further.
// - false if we did not translate the key, and it should be processed into a character.
bool Terminal::SendKeyEvent(const WORD vkey,
const bool ctrlPressed,
const bool altPressed,
const bool shiftPressed)
bool Terminal::SendKeyEvent(const WORD vkey, const DWORD modifiers)
{
if (_snapOnInput && _scrollOffset != 0)
{
Expand All @@ -213,10 +208,23 @@ bool Terminal::SendKeyEvent(const WORD vkey,
_NotifyScrollEvent();
}

DWORD modifiers = 0 |
(ctrlPressed ? LEFT_CTRL_PRESSED : 0) |
(altPressed ? LEFT_ALT_PRESSED : 0) |
(shiftPressed ? SHIFT_PRESSED : 0);
KeyEvent keyEv{ true, 0, vkey, 0, UNICODE_NULL, modifiers };

// AltGr key combinations don't always contain any meaningful,
// pretranslated unicode character during WM_KEYDOWN.
// E.g. on a German keyboard AltGr+Q should result in a "@" character,
// but actually results in "Q" with Alt and Ctrl modifier states.
// By returning false though, we can abort handling this WM_KEYDOWN
// event and let the WM_CHAR handler kick in, which will be
// provided with an appropriate unicode character.
if (keyEv.IsAltGrPressed())
{
return false;
}

const auto ctrlPressed = keyEv.IsCtrlPressed();
const auto altPressed = keyEv.IsAltPressed();
const auto shiftPressed = keyEv.IsShiftPressed();

// Alt key sequences _require_ the char to be in the keyevent. If alt is
// pressed, manually get the character that's being typed, and put it in the
Expand Down Expand Up @@ -250,9 +258,9 @@ bool Terminal::SendKeyEvent(const WORD vkey,
ch = UNICODE_SPACE;
}

const bool manuallyHandled = ch != UNICODE_NULL;
keyEv.SetCharData(ch);

KeyEvent keyEv{ true, 0, vkey, 0, ch, modifiers };
const bool manuallyHandled = ch != UNICODE_NULL;
const bool translated = _terminalInput->HandleKey(&keyEv);

return translated && manuallyHandled;
Expand Down
5 changes: 1 addition & 4 deletions src/cascadia/TerminalCore/Terminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,7 @@ class Microsoft::Terminal::Core::Terminal final :

#pragma region ITerminalInput
// These methods are defined in Terminal.cpp
bool SendKeyEvent(const WORD vkey,
const bool ctrlPressed,
const bool altPressed,
const bool shiftPressed) override;
bool SendKeyEvent(const WORD vkey, const DWORD modifiers) override;
[[nodiscard]] HRESULT UserResize(const COORD viewportSize) noexcept override;
void UserScrollViewport(const int viewTop) override;
int GetScrollOffset() override;
Expand Down
6 changes: 3 additions & 3 deletions src/cascadia/UnitTests_TerminalCore/InputTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,19 @@ namespace TerminalCoreUnitTests
// Verify that Alt+a generates a lowercase a on the input
expectedinput = L"\x1b"
"a";
VERIFY_IS_TRUE(term.SendKeyEvent(L'A', false, true, false));
VERIFY_IS_TRUE(term.SendKeyEvent(L'A', LEFT_ALT_PRESSED));

// Verify that Alt+shift+a generates a uppercase a on the input
expectedinput = L"\x1b"
"A";
VERIFY_IS_TRUE(term.SendKeyEvent(L'A', false, true, true));
VERIFY_IS_TRUE(term.SendKeyEvent(L'A', LEFT_ALT_PRESSED | SHIFT_PRESSED));
}

void InputTest::AltSpace()
{
// Make sure we don't handle Alt+Space. The system will use this to
// bring up the system menu for restore, min/maximimize, size, move,
// close
VERIFY_IS_FALSE(term.SendKeyEvent(L' ', false, true, false));
VERIFY_IS_FALSE(term.SendKeyEvent(L' ', LEFT_ALT_PRESSED));
}
}
9 changes: 7 additions & 2 deletions src/terminal/adapter/ut_adapter/inputTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ void InputTest::TerminalInputModifierKeyTests()
unsigned int uiKeystate = uiActualKeystate;

const TerminalInput* const pInput = new TerminalInput(s_TerminalInputTestCallback);
const BYTE slashVkey = LOBYTE(VkKeyScanW(L'/'));

Log::Comment(L"Sending every possible VKEY at the input stream for interception during key DOWN.");
for (BYTE vkey = 0; vkey < BYTE_MAX; vkey++)
Expand All @@ -356,6 +357,12 @@ void InputTest::TerminalInputModifierKeyTests()
irTest.Event.KeyEvent.wVirtualKeyCode = vkey;
irTest.Event.KeyEvent.bKeyDown = TRUE;

// Ctrl-/ is handled in another test, because it's weird.
if (ControlPressed(uiKeystate) && (vkey == VK_DIVIDE || vkey == slashVkey))
{
continue;
}

// Set up expected result
switch (vkey)
{
Expand All @@ -373,9 +380,7 @@ void InputTest::TerminalInputModifierKeyTests()
continue;
case VK_BACK:
// Backspace is kinda different from other keys - we'll handle in another test.
case VK_DIVIDE:
case VK_OEM_2:
// Ctrl-/ is also handled in another test, because it's weird.
// VK_OEM_2 is typically the '/?' key
continue;
// wcscpy_s(s_pwsInputBuffer, L"\x7f");
Expand Down