terminal/src/terminal/input/terminalInput.cpp

768 lines
32 KiB
C++
Raw Normal View History

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include <windows.h>
#include "terminalInput.hpp"
#include "strsafe.h"
#define WIL_SUPPORT_BITOPERATION_PASCAL_NAMES
#include <wil\Common.h>
#ifdef BUILD_ONECORE_INTERACTIVITY
#include "..\..\interactivity\inc\VtApiRedirection.hpp"
#endif
#include "..\..\inc\unicode.hpp"
#include "..\..\types\inc\Utf16Parser.hpp"
using namespace Microsoft::Console::VirtualTerminal;
DWORD const dwAltGrFlags = LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED;
TerminalInput::TerminalInput(_In_ std::function<void(std::deque<std::unique_ptr<IInputEvent>>&)> pfn) :
_leadingSurrogate{}
{
_pfnWriteEvents = pfn;
}
struct TermKeyMap
{
const WORD vkey;
const std::wstring_view sequence;
const DWORD modifiers;
constexpr TermKeyMap(WORD vkey, std::wstring_view sequence) noexcept :
TermKeyMap(vkey, 0, sequence)
{
}
constexpr TermKeyMap(const WORD vkey, const DWORD modifiers, std::wstring_view sequence) noexcept :
vkey(vkey),
sequence(sequence),
modifiers(modifiers)
{
}
};
// See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-PC-Style-Function-Keys
// For the source for these tables.
// Also refer to the values in terminfo for kcub1, kcud1, kcuf1, kcuu1, kend, khome.
// the 'xterm' setting lists the application mode versions of these sequences.
static constexpr std::array<TermKeyMap, 6> s_cursorKeysNormalMapping = {
TermKeyMap{ VK_UP, L"\x1b[A" },
TermKeyMap{ VK_DOWN, L"\x1b[B" },
TermKeyMap{ VK_RIGHT, L"\x1b[C" },
TermKeyMap{ VK_LEFT, L"\x1b[D" },
TermKeyMap{ VK_HOME, L"\x1b[H" },
TermKeyMap{ VK_END, L"\x1b[F" },
};
static constexpr std::array<TermKeyMap, 6> s_cursorKeysApplicationMapping{
TermKeyMap{ VK_UP, L"\x1bOA" },
TermKeyMap{ VK_DOWN, L"\x1bOB" },
TermKeyMap{ VK_RIGHT, L"\x1bOC" },
TermKeyMap{ VK_LEFT, L"\x1bOD" },
TermKeyMap{ VK_HOME, L"\x1bOH" },
TermKeyMap{ VK_END, L"\x1bOF" },
};
Add support for VT52 emulation (#4789) ## Summary of the Pull Request This PR adds support for the core VT52 commands, and implements the `DECANM` private mode sequence, which switches the terminal between ANSI mode and VT52-compatible mode. ## References PR #2017 defined the initial specification for VT52 support. PR #4044 removed the original VT52 cursor ops that conflicted with VT100 sequences. ## PR Checklist * [x] Closes #976 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed * [ ] Requires documentation to be updated * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #2017 ## Detailed Description of the Pull Request / Additional comments Most of the work involves updates to the parsing state machine, which behaves differently in VT52 mode. `CSI`, `OSC`, and `SS3` sequences are not applicable, and there is one special-case escape sequence (_Direct Cursor Address_), which requires an additional state to handle parameters that come _after_ the final character. Once the parsing is handled though, it's mostly just a matter of dispatching the commands to existing methods in the `ITermDispatch` interface. Only one new method was required in the interface to handle the _Identify_ command. The only real new functionality is in the `TerminalInput` class, which needs to generate different escape sequences for certain keys in VT52 mode. This does not yet support _all_ of the VT52 key sequences, because the VT100 support is itself not yet complete. But the basics are in place, and I think the rest is best left for a follow-up issue, and potentially a refactor of the `TerminalInput` class. I should point out that the original spec called for a new _Graphic Mode_ character set, but I've since discovered that the VT terminals that _emulate_ VT52 just use the existing VT100 _Special Graphics_ set, so that is really what we should be doing too. We can always consider adding the VT52 graphic set as a option later, if there is demand for strict VT52 compatibility. ## Validation Steps Performed I've added state machine and adapter tests to confirm that the `DECANM` mode changing sequences are correctly dispatched and forwarded to the `ConGetSet` handler. I've also added state machine tests that confirm the VT52 escape sequences are dispatched correctly when the ANSI mode is reset. For fuzzing support, I've extended the VT command fuzzer to generate the different kinds of VT52 sequences, as well as mode change sequences to switch between the ANSI and VT52 modes. In terms of manual testing, I've confirmed that the _Test of VT52 mode_ in Vttest now works as expected.
2020-06-01 23:20:40 +02:00
static constexpr std::array<TermKeyMap, 6> s_cursorKeysVt52Mapping{
TermKeyMap{ VK_UP, L"\033A" },
TermKeyMap{ VK_DOWN, L"\033B" },
TermKeyMap{ VK_RIGHT, L"\033C" },
TermKeyMap{ VK_LEFT, L"\033D" },
TermKeyMap{ VK_HOME, L"\033H" },
TermKeyMap{ VK_END, L"\033F" },
};
static constexpr std::array<TermKeyMap, 20> s_keypadNumericMapping{
TermKeyMap{ VK_TAB, L"\x09" },
TermKeyMap{ VK_BACK, L"\x7f" },
TermKeyMap{ VK_PAUSE, L"\x1a" },
TermKeyMap{ VK_ESCAPE, L"\x1b" },
TermKeyMap{ VK_INSERT, L"\x1b[2~" },
TermKeyMap{ VK_DELETE, L"\x1b[3~" },
TermKeyMap{ VK_PRIOR, L"\x1b[5~" },
TermKeyMap{ VK_NEXT, L"\x1b[6~" },
TermKeyMap{ VK_F1, L"\x1bOP" }, // also \x1b[11~, PuTTY uses \x1b\x1b[A
TermKeyMap{ VK_F2, L"\x1bOQ" }, // also \x1b[12~, PuTTY uses \x1b\x1b[B
TermKeyMap{ VK_F3, L"\x1bOR" }, // also \x1b[13~, PuTTY uses \x1b\x1b[C
TermKeyMap{ VK_F4, L"\x1bOS" }, // also \x1b[14~, PuTTY uses \x1b\x1b[D
TermKeyMap{ VK_F5, L"\x1b[15~" },
TermKeyMap{ VK_F6, L"\x1b[17~" },
TermKeyMap{ VK_F7, L"\x1b[18~" },
TermKeyMap{ VK_F8, L"\x1b[19~" },
TermKeyMap{ VK_F9, L"\x1b[20~" },
TermKeyMap{ VK_F10, L"\x1b[21~" },
TermKeyMap{ VK_F11, L"\x1b[23~" },
TermKeyMap{ VK_F12, L"\x1b[24~" },
};
//Application mode - Some terminals support both a "Numeric" input mode, and an "Application" mode
// The standards vary on what each key translates to in the various modes, so I tried to make it as close
// to the VT220 standard as possible.
// The notable difference is in the arrow keys, which in application mode translate to "^[0A" (etc) as opposed to "^[[A" in numeric
//Some very unclear documentation at http://invisible-island.net/xterm/ctlseqs/ctlseqs.html also suggests alternate encodings for F1-4
// which I have left in the comments on those entries as something to possibly add in the future, if need be.
//It seems to me as though this was used for early numpad implementations, where presently numlock would enable
// "numeric" mode, outputting the numbers on the keys, while "application" mode does things like pgup/down, arrow keys, etc.
//These keys aren't translated at all in numeric mode, so I figured I'd leave them out of the numeric table.
static constexpr std::array<TermKeyMap, 20> s_keypadApplicationMapping{
TermKeyMap{ VK_TAB, L"\x09" },
TermKeyMap{ VK_BACK, L"\x7f" },
TermKeyMap{ VK_PAUSE, L"\x1a" },
TermKeyMap{ VK_ESCAPE, L"\x1b" },
TermKeyMap{ VK_INSERT, L"\x1b[2~" },
TermKeyMap{ VK_DELETE, L"\x1b[3~" },
TermKeyMap{ VK_PRIOR, L"\x1b[5~" },
TermKeyMap{ VK_NEXT, L"\x1b[6~" },
TermKeyMap{ VK_F1, L"\x1bOP" }, // also \x1b[11~, PuTTY uses \x1b\x1b[A
TermKeyMap{ VK_F2, L"\x1bOQ" }, // also \x1b[12~, PuTTY uses \x1b\x1b[B
TermKeyMap{ VK_F3, L"\x1bOR" }, // also \x1b[13~, PuTTY uses \x1b\x1b[C
TermKeyMap{ VK_F4, L"\x1bOS" }, // also \x1b[14~, PuTTY uses \x1b\x1b[D
TermKeyMap{ VK_F5, L"\x1b[15~" },
TermKeyMap{ VK_F6, L"\x1b[17~" },
TermKeyMap{ VK_F7, L"\x1b[18~" },
TermKeyMap{ VK_F8, L"\x1b[19~" },
TermKeyMap{ VK_F9, L"\x1b[20~" },
TermKeyMap{ VK_F10, L"\x1b[21~" },
TermKeyMap{ VK_F11, L"\x1b[23~" },
TermKeyMap{ VK_F12, L"\x1b[24~" },
// The numpad has a variety of mappings, none of which seem standard or really configurable by the OS.
// See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-PC-Style-Function-Keys
// to see just how convoluted this all is.
// PuTTY uses a set of mappings that don't work in ViM without reamapping them back to the numpad
// (see http://vim.wikia.com/wiki/PuTTY_numeric_keypad_mappings#Comments)
// I think the best solution is to just not do any for the time being.
// Putty also provides configuration for choosing which of the 5 mappings it has through the settings, which is more work than we can manage now.
// TermKeyMap{ VK_MULTIPLY, L"\x1bOj" }, // PuTTY: \x1bOR (I believe putty is treating the top row of the numpad as PF1-PF4)
// TermKeyMap{ VK_ADD, L"\x1bOk" }, // PuTTY: \x1bOl, \x1bOm (with shift)
// TermKeyMap{ VK_SEPARATOR, L"\x1bOl" }, // ? I'm not sure which key this is...
// TermKeyMap{ VK_SUBTRACT, L"\x1bOm" }, // \x1bOS
// TermKeyMap{ VK_DECIMAL, L"\x1bOn" }, // \x1bOn
// TermKeyMap{ VK_DIVIDE, L"\x1bOo" }, // \x1bOQ
// TermKeyMap{ VK_NUMPAD0, L"\x1bOp" },
// TermKeyMap{ VK_NUMPAD1, L"\x1bOq" },
// TermKeyMap{ VK_NUMPAD2, L"\x1bOr" },
// TermKeyMap{ VK_NUMPAD3, L"\x1bOs" },
// TermKeyMap{ VK_NUMPAD4, L"\x1bOt" },
// TermKeyMap{ VK_NUMPAD5, L"\x1bOu" }, // \x1b0E
// TermKeyMap{ VK_NUMPAD5, L"\x1bOE" }, // PuTTY \x1b[G
// TermKeyMap{ VK_NUMPAD6, L"\x1bOv" },
// TermKeyMap{ VK_NUMPAD7, L"\x1bOw" },
// TermKeyMap{ VK_NUMPAD8, L"\x1bOx" },
// TermKeyMap{ VK_NUMPAD9, L"\x1bOy" },
// TermKeyMap{ '=', L"\x1bOX" }, // I've also seen these codes mentioned in some documentation,
// TermKeyMap{ VK_SPACE, L"\x1bO " }, // but I wasn't really sure if they should be included or not...
// TermKeyMap{ VK_TAB, L"\x1bOI" }, // So I left them here as a reference just in case.
};
Add support for VT52 emulation (#4789) ## Summary of the Pull Request This PR adds support for the core VT52 commands, and implements the `DECANM` private mode sequence, which switches the terminal between ANSI mode and VT52-compatible mode. ## References PR #2017 defined the initial specification for VT52 support. PR #4044 removed the original VT52 cursor ops that conflicted with VT100 sequences. ## PR Checklist * [x] Closes #976 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed * [ ] Requires documentation to be updated * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #2017 ## Detailed Description of the Pull Request / Additional comments Most of the work involves updates to the parsing state machine, which behaves differently in VT52 mode. `CSI`, `OSC`, and `SS3` sequences are not applicable, and there is one special-case escape sequence (_Direct Cursor Address_), which requires an additional state to handle parameters that come _after_ the final character. Once the parsing is handled though, it's mostly just a matter of dispatching the commands to existing methods in the `ITermDispatch` interface. Only one new method was required in the interface to handle the _Identify_ command. The only real new functionality is in the `TerminalInput` class, which needs to generate different escape sequences for certain keys in VT52 mode. This does not yet support _all_ of the VT52 key sequences, because the VT100 support is itself not yet complete. But the basics are in place, and I think the rest is best left for a follow-up issue, and potentially a refactor of the `TerminalInput` class. I should point out that the original spec called for a new _Graphic Mode_ character set, but I've since discovered that the VT terminals that _emulate_ VT52 just use the existing VT100 _Special Graphics_ set, so that is really what we should be doing too. We can always consider adding the VT52 graphic set as a option later, if there is demand for strict VT52 compatibility. ## Validation Steps Performed I've added state machine and adapter tests to confirm that the `DECANM` mode changing sequences are correctly dispatched and forwarded to the `ConGetSet` handler. I've also added state machine tests that confirm the VT52 escape sequences are dispatched correctly when the ANSI mode is reset. For fuzzing support, I've extended the VT command fuzzer to generate the different kinds of VT52 sequences, as well as mode change sequences to switch between the ANSI and VT52 modes. In terms of manual testing, I've confirmed that the _Test of VT52 mode_ in Vttest now works as expected.
2020-06-01 23:20:40 +02:00
static constexpr std::array<TermKeyMap, 20> s_keypadVt52Mapping{
TermKeyMap{ VK_TAB, L"\x09" },
TermKeyMap{ VK_BACK, L"\x7f" },
TermKeyMap{ VK_PAUSE, L"\x1a" },
TermKeyMap{ VK_ESCAPE, L"\x1b" },
TermKeyMap{ VK_INSERT, L"\x1b[2~" },
TermKeyMap{ VK_DELETE, L"\x1b[3~" },
TermKeyMap{ VK_PRIOR, L"\x1b[5~" },
TermKeyMap{ VK_NEXT, L"\x1b[6~" },
TermKeyMap{ VK_F1, L"\x1bP" },
TermKeyMap{ VK_F2, L"\x1bQ" },
TermKeyMap{ VK_F3, L"\x1bR" },
TermKeyMap{ VK_F4, L"\x1bS" },
TermKeyMap{ VK_F5, L"\x1b[15~" },
TermKeyMap{ VK_F6, L"\x1b[17~" },
TermKeyMap{ VK_F7, L"\x1b[18~" },
TermKeyMap{ VK_F8, L"\x1b[19~" },
TermKeyMap{ VK_F9, L"\x1b[20~" },
TermKeyMap{ VK_F10, L"\x1b[21~" },
TermKeyMap{ VK_F11, L"\x1b[23~" },
TermKeyMap{ VK_F12, L"\x1b[24~" },
};
// Sequences to send when a modifier is pressed with any of these keys
// Basically, the 'm' will be replaced with a character indicating which
// modifier keys are pressed.
static constexpr std::array<TermKeyMap, 22> s_modifierKeyMapping{
TermKeyMap{ VK_UP, L"\x1b[1;mA" },
TermKeyMap{ VK_DOWN, L"\x1b[1;mB" },
TermKeyMap{ VK_RIGHT, L"\x1b[1;mC" },
TermKeyMap{ VK_LEFT, L"\x1b[1;mD" },
TermKeyMap{ VK_HOME, L"\x1b[1;mH" },
TermKeyMap{ VK_END, L"\x1b[1;mF" },
TermKeyMap{ VK_F1, L"\x1b[1;mP" },
TermKeyMap{ VK_F2, L"\x1b[1;mQ" },
TermKeyMap{ VK_F3, L"\x1b[1;mR" },
TermKeyMap{ VK_F4, L"\x1b[1;mS" },
TermKeyMap{ VK_INSERT, L"\x1b[2;m~" },
TermKeyMap{ VK_DELETE, L"\x1b[3;m~" },
TermKeyMap{ VK_PRIOR, L"\x1b[5;m~" },
TermKeyMap{ VK_NEXT, L"\x1b[6;m~" },
TermKeyMap{ VK_F5, L"\x1b[15;m~" },
TermKeyMap{ VK_F6, L"\x1b[17;m~" },
TermKeyMap{ VK_F7, L"\x1b[18;m~" },
TermKeyMap{ VK_F8, L"\x1b[19;m~" },
TermKeyMap{ VK_F9, L"\x1b[20;m~" },
TermKeyMap{ VK_F10, L"\x1b[21;m~" },
TermKeyMap{ VK_F11, L"\x1b[23;m~" },
TermKeyMap{ VK_F12, L"\x1b[24;m~" },
// Ubuntu's inputrc also defines \x1b[5C, \x1b\x1bC (and D) as 'forward/backward-word' mappings
// I believe '\x1b\x1bC' is listed because the C1 ESC (x9B) gets encoded as
// \xC2\x9B, but then translated to \x1b\x1b if the C1 codepoint isn't supported by the current encoding
};
// Sequences to send when a modifier is pressed with any of these keys
// These sequences are not later updated to encode the modifier state in the
// sequence itself, they are just weird exceptional cases to the general
// rules above.
static constexpr std::array<TermKeyMap, 14> s_simpleModifiedKeyMapping{
TermKeyMap{ VK_BACK, CTRL_PRESSED, L"\x8" },
TermKeyMap{ VK_BACK, ALT_PRESSED, L"\x1b\x7f" },
TermKeyMap{ VK_BACK, CTRL_PRESSED | ALT_PRESSED, L"\x1b\x8" },
TermKeyMap{ VK_TAB, CTRL_PRESSED, L"\t" },
TermKeyMap{ VK_TAB, SHIFT_PRESSED, L"\x1b[Z" },
TermKeyMap{ VK_DIVIDE, CTRL_PRESSED, L"\x1F" },
// GH#3507 - We should also be encoding Ctrl+# according to the following table:
// https://vt100.net/docs/vt220-rm/table3-5.html
// * 1 and 9 do not send any special characters, but they _should_ send
// through the character unmodified.
// * 0 doesn't seem to send even an unmodified '0' through.
// * Ctrl+2 is already special-cased below in `HandleKey`, so it's not
// included here.
TermKeyMap{ static_cast<WORD>('1'), CTRL_PRESSED, L"1" },
// TermKeyMap{ static_cast<WORD>('2'), CTRL_PRESSED, L"\x00" },
TermKeyMap{ static_cast<WORD>('3'), CTRL_PRESSED, L"\x1B" },
TermKeyMap{ static_cast<WORD>('4'), CTRL_PRESSED, L"\x1C" },
TermKeyMap{ static_cast<WORD>('5'), CTRL_PRESSED, L"\x1D" },
TermKeyMap{ static_cast<WORD>('6'), CTRL_PRESSED, L"\x1E" },
TermKeyMap{ static_cast<WORD>('7'), CTRL_PRESSED, L"\x1F" },
TermKeyMap{ static_cast<WORD>('8'), CTRL_PRESSED, L"\x7F" },
TermKeyMap{ static_cast<WORD>('9'), CTRL_PRESSED, L"9" },
// These two are not implemented here, because they are system keys.
// TermKeyMap{ VK_TAB, ALT_PRESSED, L""}, This is the Windows system shortcut for switching windows.
// TermKeyMap{ VK_ESCAPE, ALT_PRESSED, L""}, This is another Windows system shortcut for switching windows.
};
const wchar_t* const CTRL_SLASH_SEQUENCE = L"\x1f";
const wchar_t* const CTRL_QUESTIONMARK_SEQUENCE = L"\x7F";
const wchar_t* const CTRL_ALT_SLASH_SEQUENCE = L"\x1b\x1f";
const wchar_t* const CTRL_ALT_QUESTIONMARK_SEQUENCE = L"\x1b\x7F";
Add support for VT52 emulation (#4789) ## Summary of the Pull Request This PR adds support for the core VT52 commands, and implements the `DECANM` private mode sequence, which switches the terminal between ANSI mode and VT52-compatible mode. ## References PR #2017 defined the initial specification for VT52 support. PR #4044 removed the original VT52 cursor ops that conflicted with VT100 sequences. ## PR Checklist * [x] Closes #976 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed * [ ] Requires documentation to be updated * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #2017 ## Detailed Description of the Pull Request / Additional comments Most of the work involves updates to the parsing state machine, which behaves differently in VT52 mode. `CSI`, `OSC`, and `SS3` sequences are not applicable, and there is one special-case escape sequence (_Direct Cursor Address_), which requires an additional state to handle parameters that come _after_ the final character. Once the parsing is handled though, it's mostly just a matter of dispatching the commands to existing methods in the `ITermDispatch` interface. Only one new method was required in the interface to handle the _Identify_ command. The only real new functionality is in the `TerminalInput` class, which needs to generate different escape sequences for certain keys in VT52 mode. This does not yet support _all_ of the VT52 key sequences, because the VT100 support is itself not yet complete. But the basics are in place, and I think the rest is best left for a follow-up issue, and potentially a refactor of the `TerminalInput` class. I should point out that the original spec called for a new _Graphic Mode_ character set, but I've since discovered that the VT terminals that _emulate_ VT52 just use the existing VT100 _Special Graphics_ set, so that is really what we should be doing too. We can always consider adding the VT52 graphic set as a option later, if there is demand for strict VT52 compatibility. ## Validation Steps Performed I've added state machine and adapter tests to confirm that the `DECANM` mode changing sequences are correctly dispatched and forwarded to the `ConGetSet` handler. I've also added state machine tests that confirm the VT52 escape sequences are dispatched correctly when the ANSI mode is reset. For fuzzing support, I've extended the VT command fuzzer to generate the different kinds of VT52 sequences, as well as mode change sequences to switch between the ANSI and VT52 modes. In terms of manual testing, I've confirmed that the _Test of VT52 mode_ in Vttest now works as expected.
2020-06-01 23:20:40 +02:00
void TerminalInput::ChangeAnsiMode(const bool ansiMode) noexcept
{
_ansiMode = ansiMode;
}
void TerminalInput::ChangeKeypadMode(const bool applicationMode) noexcept
{
_keypadApplicationMode = applicationMode;
}
void TerminalInput::ChangeCursorKeysMode(const bool applicationMode) noexcept
{
_cursorApplicationMode = applicationMode;
}
void TerminalInput::ChangeWin32InputMode(const bool win32InputMode) noexcept
{
_win32InputMode = win32InputMode;
}
void TerminalInput::ForceDisableWin32InputMode(const bool win32InputMode) noexcept
{
_forceDisableWin32InputMode = win32InputMode;
}
static const gsl::span<const TermKeyMap> _getKeyMapping(const KeyEvent& keyEvent,
const bool ansiMode,
const bool cursorApplicationMode,
const bool keypadApplicationMode) noexcept
{
Add support for VT52 emulation (#4789) ## Summary of the Pull Request This PR adds support for the core VT52 commands, and implements the `DECANM` private mode sequence, which switches the terminal between ANSI mode and VT52-compatible mode. ## References PR #2017 defined the initial specification for VT52 support. PR #4044 removed the original VT52 cursor ops that conflicted with VT100 sequences. ## PR Checklist * [x] Closes #976 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed * [ ] Requires documentation to be updated * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #2017 ## Detailed Description of the Pull Request / Additional comments Most of the work involves updates to the parsing state machine, which behaves differently in VT52 mode. `CSI`, `OSC`, and `SS3` sequences are not applicable, and there is one special-case escape sequence (_Direct Cursor Address_), which requires an additional state to handle parameters that come _after_ the final character. Once the parsing is handled though, it's mostly just a matter of dispatching the commands to existing methods in the `ITermDispatch` interface. Only one new method was required in the interface to handle the _Identify_ command. The only real new functionality is in the `TerminalInput` class, which needs to generate different escape sequences for certain keys in VT52 mode. This does not yet support _all_ of the VT52 key sequences, because the VT100 support is itself not yet complete. But the basics are in place, and I think the rest is best left for a follow-up issue, and potentially a refactor of the `TerminalInput` class. I should point out that the original spec called for a new _Graphic Mode_ character set, but I've since discovered that the VT terminals that _emulate_ VT52 just use the existing VT100 _Special Graphics_ set, so that is really what we should be doing too. We can always consider adding the VT52 graphic set as a option later, if there is demand for strict VT52 compatibility. ## Validation Steps Performed I've added state machine and adapter tests to confirm that the `DECANM` mode changing sequences are correctly dispatched and forwarded to the `ConGetSet` handler. I've also added state machine tests that confirm the VT52 escape sequences are dispatched correctly when the ANSI mode is reset. For fuzzing support, I've extended the VT command fuzzer to generate the different kinds of VT52 sequences, as well as mode change sequences to switch between the ANSI and VT52 modes. In terms of manual testing, I've confirmed that the _Test of VT52 mode_ in Vttest now works as expected.
2020-06-01 23:20:40 +02:00
if (ansiMode)
{
Add support for VT52 emulation (#4789) ## Summary of the Pull Request This PR adds support for the core VT52 commands, and implements the `DECANM` private mode sequence, which switches the terminal between ANSI mode and VT52-compatible mode. ## References PR #2017 defined the initial specification for VT52 support. PR #4044 removed the original VT52 cursor ops that conflicted with VT100 sequences. ## PR Checklist * [x] Closes #976 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed * [ ] Requires documentation to be updated * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #2017 ## Detailed Description of the Pull Request / Additional comments Most of the work involves updates to the parsing state machine, which behaves differently in VT52 mode. `CSI`, `OSC`, and `SS3` sequences are not applicable, and there is one special-case escape sequence (_Direct Cursor Address_), which requires an additional state to handle parameters that come _after_ the final character. Once the parsing is handled though, it's mostly just a matter of dispatching the commands to existing methods in the `ITermDispatch` interface. Only one new method was required in the interface to handle the _Identify_ command. The only real new functionality is in the `TerminalInput` class, which needs to generate different escape sequences for certain keys in VT52 mode. This does not yet support _all_ of the VT52 key sequences, because the VT100 support is itself not yet complete. But the basics are in place, and I think the rest is best left for a follow-up issue, and potentially a refactor of the `TerminalInput` class. I should point out that the original spec called for a new _Graphic Mode_ character set, but I've since discovered that the VT terminals that _emulate_ VT52 just use the existing VT100 _Special Graphics_ set, so that is really what we should be doing too. We can always consider adding the VT52 graphic set as a option later, if there is demand for strict VT52 compatibility. ## Validation Steps Performed I've added state machine and adapter tests to confirm that the `DECANM` mode changing sequences are correctly dispatched and forwarded to the `ConGetSet` handler. I've also added state machine tests that confirm the VT52 escape sequences are dispatched correctly when the ANSI mode is reset. For fuzzing support, I've extended the VT command fuzzer to generate the different kinds of VT52 sequences, as well as mode change sequences to switch between the ANSI and VT52 modes. In terms of manual testing, I've confirmed that the _Test of VT52 mode_ in Vttest now works as expected.
2020-06-01 23:20:40 +02:00
if (keyEvent.IsCursorKey())
{
Add support for VT52 emulation (#4789) ## Summary of the Pull Request This PR adds support for the core VT52 commands, and implements the `DECANM` private mode sequence, which switches the terminal between ANSI mode and VT52-compatible mode. ## References PR #2017 defined the initial specification for VT52 support. PR #4044 removed the original VT52 cursor ops that conflicted with VT100 sequences. ## PR Checklist * [x] Closes #976 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed * [ ] Requires documentation to be updated * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #2017 ## Detailed Description of the Pull Request / Additional comments Most of the work involves updates to the parsing state machine, which behaves differently in VT52 mode. `CSI`, `OSC`, and `SS3` sequences are not applicable, and there is one special-case escape sequence (_Direct Cursor Address_), which requires an additional state to handle parameters that come _after_ the final character. Once the parsing is handled though, it's mostly just a matter of dispatching the commands to existing methods in the `ITermDispatch` interface. Only one new method was required in the interface to handle the _Identify_ command. The only real new functionality is in the `TerminalInput` class, which needs to generate different escape sequences for certain keys in VT52 mode. This does not yet support _all_ of the VT52 key sequences, because the VT100 support is itself not yet complete. But the basics are in place, and I think the rest is best left for a follow-up issue, and potentially a refactor of the `TerminalInput` class. I should point out that the original spec called for a new _Graphic Mode_ character set, but I've since discovered that the VT terminals that _emulate_ VT52 just use the existing VT100 _Special Graphics_ set, so that is really what we should be doing too. We can always consider adding the VT52 graphic set as a option later, if there is demand for strict VT52 compatibility. ## Validation Steps Performed I've added state machine and adapter tests to confirm that the `DECANM` mode changing sequences are correctly dispatched and forwarded to the `ConGetSet` handler. I've also added state machine tests that confirm the VT52 escape sequences are dispatched correctly when the ANSI mode is reset. For fuzzing support, I've extended the VT command fuzzer to generate the different kinds of VT52 sequences, as well as mode change sequences to switch between the ANSI and VT52 modes. In terms of manual testing, I've confirmed that the _Test of VT52 mode_ in Vttest now works as expected.
2020-06-01 23:20:40 +02:00
if (cursorApplicationMode)
{
return { s_cursorKeysApplicationMapping.data(), s_cursorKeysApplicationMapping.size() };
}
else
{
return { s_cursorKeysNormalMapping.data(), s_cursorKeysNormalMapping.size() };
}
}
else
{
Add support for VT52 emulation (#4789) ## Summary of the Pull Request This PR adds support for the core VT52 commands, and implements the `DECANM` private mode sequence, which switches the terminal between ANSI mode and VT52-compatible mode. ## References PR #2017 defined the initial specification for VT52 support. PR #4044 removed the original VT52 cursor ops that conflicted with VT100 sequences. ## PR Checklist * [x] Closes #976 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed * [ ] Requires documentation to be updated * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #2017 ## Detailed Description of the Pull Request / Additional comments Most of the work involves updates to the parsing state machine, which behaves differently in VT52 mode. `CSI`, `OSC`, and `SS3` sequences are not applicable, and there is one special-case escape sequence (_Direct Cursor Address_), which requires an additional state to handle parameters that come _after_ the final character. Once the parsing is handled though, it's mostly just a matter of dispatching the commands to existing methods in the `ITermDispatch` interface. Only one new method was required in the interface to handle the _Identify_ command. The only real new functionality is in the `TerminalInput` class, which needs to generate different escape sequences for certain keys in VT52 mode. This does not yet support _all_ of the VT52 key sequences, because the VT100 support is itself not yet complete. But the basics are in place, and I think the rest is best left for a follow-up issue, and potentially a refactor of the `TerminalInput` class. I should point out that the original spec called for a new _Graphic Mode_ character set, but I've since discovered that the VT terminals that _emulate_ VT52 just use the existing VT100 _Special Graphics_ set, so that is really what we should be doing too. We can always consider adding the VT52 graphic set as a option later, if there is demand for strict VT52 compatibility. ## Validation Steps Performed I've added state machine and adapter tests to confirm that the `DECANM` mode changing sequences are correctly dispatched and forwarded to the `ConGetSet` handler. I've also added state machine tests that confirm the VT52 escape sequences are dispatched correctly when the ANSI mode is reset. For fuzzing support, I've extended the VT command fuzzer to generate the different kinds of VT52 sequences, as well as mode change sequences to switch between the ANSI and VT52 modes. In terms of manual testing, I've confirmed that the _Test of VT52 mode_ in Vttest now works as expected.
2020-06-01 23:20:40 +02:00
if (keypadApplicationMode)
{
return { s_keypadApplicationMapping.data(), s_keypadApplicationMapping.size() };
}
else
{
return { s_keypadNumericMapping.data(), s_keypadNumericMapping.size() };
}
}
}
else
{
Add support for VT52 emulation (#4789) ## Summary of the Pull Request This PR adds support for the core VT52 commands, and implements the `DECANM` private mode sequence, which switches the terminal between ANSI mode and VT52-compatible mode. ## References PR #2017 defined the initial specification for VT52 support. PR #4044 removed the original VT52 cursor ops that conflicted with VT100 sequences. ## PR Checklist * [x] Closes #976 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed * [ ] Requires documentation to be updated * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #2017 ## Detailed Description of the Pull Request / Additional comments Most of the work involves updates to the parsing state machine, which behaves differently in VT52 mode. `CSI`, `OSC`, and `SS3` sequences are not applicable, and there is one special-case escape sequence (_Direct Cursor Address_), which requires an additional state to handle parameters that come _after_ the final character. Once the parsing is handled though, it's mostly just a matter of dispatching the commands to existing methods in the `ITermDispatch` interface. Only one new method was required in the interface to handle the _Identify_ command. The only real new functionality is in the `TerminalInput` class, which needs to generate different escape sequences for certain keys in VT52 mode. This does not yet support _all_ of the VT52 key sequences, because the VT100 support is itself not yet complete. But the basics are in place, and I think the rest is best left for a follow-up issue, and potentially a refactor of the `TerminalInput` class. I should point out that the original spec called for a new _Graphic Mode_ character set, but I've since discovered that the VT terminals that _emulate_ VT52 just use the existing VT100 _Special Graphics_ set, so that is really what we should be doing too. We can always consider adding the VT52 graphic set as a option later, if there is demand for strict VT52 compatibility. ## Validation Steps Performed I've added state machine and adapter tests to confirm that the `DECANM` mode changing sequences are correctly dispatched and forwarded to the `ConGetSet` handler. I've also added state machine tests that confirm the VT52 escape sequences are dispatched correctly when the ANSI mode is reset. For fuzzing support, I've extended the VT command fuzzer to generate the different kinds of VT52 sequences, as well as mode change sequences to switch between the ANSI and VT52 modes. In terms of manual testing, I've confirmed that the _Test of VT52 mode_ in Vttest now works as expected.
2020-06-01 23:20:40 +02:00
if (keyEvent.IsCursorKey())
{
Add support for VT52 emulation (#4789) ## Summary of the Pull Request This PR adds support for the core VT52 commands, and implements the `DECANM` private mode sequence, which switches the terminal between ANSI mode and VT52-compatible mode. ## References PR #2017 defined the initial specification for VT52 support. PR #4044 removed the original VT52 cursor ops that conflicted with VT100 sequences. ## PR Checklist * [x] Closes #976 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed * [ ] Requires documentation to be updated * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #2017 ## Detailed Description of the Pull Request / Additional comments Most of the work involves updates to the parsing state machine, which behaves differently in VT52 mode. `CSI`, `OSC`, and `SS3` sequences are not applicable, and there is one special-case escape sequence (_Direct Cursor Address_), which requires an additional state to handle parameters that come _after_ the final character. Once the parsing is handled though, it's mostly just a matter of dispatching the commands to existing methods in the `ITermDispatch` interface. Only one new method was required in the interface to handle the _Identify_ command. The only real new functionality is in the `TerminalInput` class, which needs to generate different escape sequences for certain keys in VT52 mode. This does not yet support _all_ of the VT52 key sequences, because the VT100 support is itself not yet complete. But the basics are in place, and I think the rest is best left for a follow-up issue, and potentially a refactor of the `TerminalInput` class. I should point out that the original spec called for a new _Graphic Mode_ character set, but I've since discovered that the VT terminals that _emulate_ VT52 just use the existing VT100 _Special Graphics_ set, so that is really what we should be doing too. We can always consider adding the VT52 graphic set as a option later, if there is demand for strict VT52 compatibility. ## Validation Steps Performed I've added state machine and adapter tests to confirm that the `DECANM` mode changing sequences are correctly dispatched and forwarded to the `ConGetSet` handler. I've also added state machine tests that confirm the VT52 escape sequences are dispatched correctly when the ANSI mode is reset. For fuzzing support, I've extended the VT command fuzzer to generate the different kinds of VT52 sequences, as well as mode change sequences to switch between the ANSI and VT52 modes. In terms of manual testing, I've confirmed that the _Test of VT52 mode_ in Vttest now works as expected.
2020-06-01 23:20:40 +02:00
return { s_cursorKeysVt52Mapping.data(), s_cursorKeysVt52Mapping.size() };
}
else
{
Add support for VT52 emulation (#4789) ## Summary of the Pull Request This PR adds support for the core VT52 commands, and implements the `DECANM` private mode sequence, which switches the terminal between ANSI mode and VT52-compatible mode. ## References PR #2017 defined the initial specification for VT52 support. PR #4044 removed the original VT52 cursor ops that conflicted with VT100 sequences. ## PR Checklist * [x] Closes #976 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed * [ ] Requires documentation to be updated * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #2017 ## Detailed Description of the Pull Request / Additional comments Most of the work involves updates to the parsing state machine, which behaves differently in VT52 mode. `CSI`, `OSC`, and `SS3` sequences are not applicable, and there is one special-case escape sequence (_Direct Cursor Address_), which requires an additional state to handle parameters that come _after_ the final character. Once the parsing is handled though, it's mostly just a matter of dispatching the commands to existing methods in the `ITermDispatch` interface. Only one new method was required in the interface to handle the _Identify_ command. The only real new functionality is in the `TerminalInput` class, which needs to generate different escape sequences for certain keys in VT52 mode. This does not yet support _all_ of the VT52 key sequences, because the VT100 support is itself not yet complete. But the basics are in place, and I think the rest is best left for a follow-up issue, and potentially a refactor of the `TerminalInput` class. I should point out that the original spec called for a new _Graphic Mode_ character set, but I've since discovered that the VT terminals that _emulate_ VT52 just use the existing VT100 _Special Graphics_ set, so that is really what we should be doing too. We can always consider adding the VT52 graphic set as a option later, if there is demand for strict VT52 compatibility. ## Validation Steps Performed I've added state machine and adapter tests to confirm that the `DECANM` mode changing sequences are correctly dispatched and forwarded to the `ConGetSet` handler. I've also added state machine tests that confirm the VT52 escape sequences are dispatched correctly when the ANSI mode is reset. For fuzzing support, I've extended the VT command fuzzer to generate the different kinds of VT52 sequences, as well as mode change sequences to switch between the ANSI and VT52 modes. In terms of manual testing, I've confirmed that the _Test of VT52 mode_ in Vttest now works as expected.
2020-06-01 23:20:40 +02:00
return { s_keypadVt52Mapping.data(), s_keypadVt52Mapping.size() };
}
}
}
// Routine Description:
// - Searches the keyMapping for a entry corresponding to this key event, and returns it.
// Arguments:
// - keyEvent - Key event to translate
// - keyMapping - Array of key mappings to search
// Return Value:
// - Has value if there was a match to a key translation.
static std::optional<const TermKeyMap> _searchKeyMapping(const KeyEvent& keyEvent,
gsl::span<const TermKeyMap> keyMapping) noexcept
{
for (auto& map : keyMapping)
{
if (map.vkey == keyEvent.GetVirtualKeyCode())
{
// If the mapping has no modifiers set, then it doesn't really care
// what the modifiers are on the key. The caller will likely do
// something with them.
// However, if there are modifiers set, then we only want to match
// if the key's modifiers are the same as the modifiers in the
// mapping.
bool modifiersMatch = WI_AreAllFlagsClear(map.modifiers, MOD_PRESSED);
if (!modifiersMatch)
{
// The modifier mapping expects certain modifier keys to be
// pressed. Check those as well.
modifiersMatch =
(WI_IsFlagSet(map.modifiers, SHIFT_PRESSED) == keyEvent.IsShiftPressed()) &&
(WI_IsAnyFlagSet(map.modifiers, ALT_PRESSED) == keyEvent.IsAltPressed()) &&
(WI_IsAnyFlagSet(map.modifiers, CTRL_PRESSED) == keyEvent.IsCtrlPressed());
}
if (modifiersMatch)
{
return map;
}
}
}
return std::nullopt;
}
typedef std::function<void(const std::wstring_view)> InputSender;
// Routine Description:
// - Searches the s_modifierKeyMapping for a entry corresponding to this key event.
// Changes the second to last byte to correspond to the currently pressed modifier keys
// before sending to the input.
// Arguments:
// - keyEvent - Key event to translate
// - sender - Function to use to dispatch translated event
// Return Value:
// - True if there was a match to a key translation, and we successfully modified and sent it to the input
static bool _searchWithModifier(const KeyEvent& keyEvent, InputSender sender)
{
bool success = false;
const auto match = _searchKeyMapping(keyEvent,
{ s_modifierKeyMapping.data(), s_modifierKeyMapping.size() });
if (match)
{
const auto& v = match.value();
if (!v.sequence.empty())
{
std::wstring modified{ v.sequence }; // Make a copy so we can modify it.
const bool shift = keyEvent.IsShiftPressed();
const bool alt = keyEvent.IsAltPressed();
const bool ctrl = keyEvent.IsCtrlPressed();
modified.at(modified.size() - 2) = L'1' + (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0);
sender(modified);
success = true;
}
}
else
{
// We didn't find the key in the map of modified keys that need editing,
// maybe it's in the other map of modified keys with sequences that
// don't need editing before sending.
const auto match2 = _searchKeyMapping(keyEvent,
{ s_simpleModifiedKeyMapping.data(), s_simpleModifiedKeyMapping.size() });
if (match2)
{
// This mapping doesn't need to be changed at all.
sender(match2.value().sequence);
success = true;
}
else
{
// One last check:
// * C-/ is supposed to be ^_ (the C0 character US)
// * C-? is supposed to be DEL
// * C-M-/ is supposed to be ^[^_
// * C-M-? is supposed to be ^[^?
//
// But this whole scenario is tricky. '/' is not the same VKEY on
// all keyboards. On USASCII keyboards, '/' and '?' share the _same_
// key. So we have to figure out the vkey at runtime, and we have to
// determine if the key that was pressed was '?' with some
// modifiers, or '/' with some modifiers.
//
// These translations are not in s_simpleModifiedKeyMapping, because
// the aforementioned fact that they aren't the same VKEY on all
// keyboards.
//
// See GH#3079 for details.
// Also see https://github.com/microsoft/terminal/pull/4947#issuecomment-600382856
// VkKeyScan will give us both the Vkey of the key needed for this
// character, and the modifiers the user might need to press to get
// this character.
const auto slashKeyScan = VkKeyScan(L'/'); // On USASCII: 0x00bf
const auto questionMarkKeyScan = VkKeyScan(L'?'); //On USASCII: 0x01bf
const auto slashVkey = LOBYTE(slashKeyScan);
const auto questionMarkVkey = LOBYTE(questionMarkKeyScan);
const auto ctrl = keyEvent.IsCtrlPressed();
const auto alt = keyEvent.IsAltPressed();
const bool shift = keyEvent.IsShiftPressed();
// From the KeyEvent we're translating, synthesize the equivalent VkKeyScan result
const auto vkey = keyEvent.GetVirtualKeyCode();
const short keyScanFromEvent = vkey |
(shift ? 0x100 : 0) |
(ctrl ? 0x200 : 0) |
(alt ? 0x400 : 0);
// Make sure the VKEY is an _exact_ match, and that the modifier
// bits also match. This handles the hypothetical case we get a
// keyscan back that's ctrl+alt+some_random_VK, and some_random_VK
// has bits that are a superset of the bits set for question mark.
const bool wasQuestionMark = vkey == questionMarkVkey && WI_AreAllFlagsSet(keyScanFromEvent, questionMarkKeyScan);
const bool wasSlash = vkey == slashVkey && WI_AreAllFlagsSet(keyScanFromEvent, slashKeyScan);
// If the key pressed was exactly the ? key, then try to send the
// appropriate sequence for a modified '?'. Otherwise, check if this
// was a modified '/' keypress. These mappings don't need to be
// changed at all.
if ((ctrl && alt) && wasQuestionMark)
{
sender(CTRL_ALT_QUESTIONMARK_SEQUENCE);
success = true;
}
else if (ctrl && wasQuestionMark)
{
sender(CTRL_QUESTIONMARK_SEQUENCE);
success = true;
}
else if ((ctrl && alt) && wasSlash)
{
sender(CTRL_ALT_SLASH_SEQUENCE);
success = true;
}
else if (ctrl && wasSlash)
{
sender(CTRL_SLASH_SEQUENCE);
success = true;
}
}
}
return success;
}
// Routine Description:
// - Searches the input array of mappings, and sends it to the input if a match was found.
// Arguments:
// - keyEvent - Key event to translate
// - keyMapping - Array of key mappings to search
// - sender - Function to use to dispatch translated event
// Return Value:
// - True if there was a match to a key translation, and we successfully sent it to the input
static bool _translateDefaultMapping(const KeyEvent& keyEvent,
const gsl::span<const TermKeyMap> keyMapping,
InputSender sender)
{
const auto match = _searchKeyMapping(keyEvent, keyMapping);
if (match)
{
sender(match->sequence);
}
return match.has_value();
}
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
// Routine Description:
// - Sends the given input event to the shell.
// - The caller should attempt to fill the char data in pInEvent if possible.
// The char data should already be translated in accordance to Ctrl/Alt/Shift
// modifiers, like the characters given by the WM_CHAR event.
// - The caller doesn't need to fill in any char data for:
// - Tab key
// - Alt+key combinations
// - This method will alias Ctrl+Space as a synonym for Ctrl+@ - the null byte.
// Arguments:
// - keyEvent - Key event to translate
// Return Value:
// - True if the event was handled.
bool TerminalInput::HandleKey(const IInputEvent* const pInEvent)
{
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
if (!pInEvent)
{
return false;
}
// On key presses, prepare to translate to VT compatible sequences
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
if (pInEvent->EventType() != InputEventType::KeyEvent)
{
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
return false;
}
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
auto keyEvent = *static_cast<const KeyEvent* const>(pInEvent);
// GH#4999 - If we're in win32-input mode, skip straight to doing that.
// Since this mode handles all types of key events, do nothing else.
// Only do this if win32-input-mode support isn't manually disabled.
if (_win32InputMode && !_forceDisableWin32InputMode)
{
const auto seq = _GenerateWin32KeySequence(keyEvent);
_SendInputSequence(seq);
return true;
}
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
// Only need to handle key down. See raw key handler (see RawReadWaitRoutine in stream.cpp)
if (!keyEvent.IsKeyDown())
{
return false;
}
// Many keyboard layouts have an AltGr key, which makes widely used characters accessible.
// For instance on a German keyboard layout "[" is written by pressing AltGr+8.
// Furthermore Ctrl+Alt is traditionally treated as an alternative way to AltGr by Windows.
// When AltGr is pressed, the caller needs to make sure to send us a pretranslated character in GetCharData().
// --> Strip out the AltGr flags, in order for us to not step into the Alt/Ctrl conditions below.
if (keyEvent.IsAltGrPressed())
{
keyEvent.DeactivateModifierKey(ModifierKeyState::LeftCtrl);
keyEvent.DeactivateModifierKey(ModifierKeyState::RightAlt);
}
// The Alt modifier initiates a so called "escape sequence".
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#Escape_sequences
// See: ECMA-48, section 5.3, http://www.ecma-international.org/publications/standards/Ecma-048.htm
//
// This section in particular handles Alt+Ctrl combinations though.
// The Ctrl modifier causes all of the char code's bits except
// for the 5 least significant ones to be zeroed out.
if (keyEvent.IsAltPressed() && keyEvent.IsCtrlPressed())
{
auto ch = keyEvent.GetCharData();
if (ch == UNICODE_NULL)
{
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
// For Alt+Ctrl+Key messages GetCharData() returns 0.
// The values of the ASCII characters and virtual key codes
// of <Space>, A-Z (as used below) are numerically identical.
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
// -> Get the char from the virtual key.
ch = keyEvent.GetVirtualKeyCode();
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
}
// Alt+Ctrl acts as a substitute for AltGr on Windows.
// For instance using a German keyboard both AltGr+< and Alt+Ctrl+< produce a | (pipe) character.
// The below condition primitively ensures that we allow all common Alt+Ctrl combinations
// while preserving most of the functionality of Alt+Ctrl as a substitute for AltGr.
if (ch == UNICODE_SPACE || (ch > 0x40 && ch <= 0x5A))
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
{
// Pressing the control key causes all bits but the 5 least
// significant ones to be zeroed out (when using ASCII).
ch &= 0b11111;
_SendEscapedInputSequence(ch);
return true;
}
}
const auto senderFunc = [this](const std::wstring_view seq) noexcept {
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
_SendInputSequence(seq);
};
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
// If a modifier key was pressed, then we need to try and send the modified sequence.
if (keyEvent.IsModifierPressed() && _searchWithModifier(keyEvent, senderFunc))
{
return true;
}
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
// This section is similar to the Alt modifier section above,
// but handles cases without Ctrl modifiers.
if (keyEvent.IsAltPressed() && !keyEvent.IsCtrlPressed() && keyEvent.GetCharData() != 0)
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
{
_SendEscapedInputSequence(keyEvent.GetCharData());
return true;
}
// Pressing the control key causes all bits but the 5 least
// significant ones to be zeroed out (when using ASCII).
// This results in Ctrl+Space and Ctrl+@ being equal to a null byte.
// Normally the C0 control code set only defines Ctrl+@,
// but Ctrl+Space is also widely accepted by most terminals.
// -> Send a "null input sequence" in that case.
// We don't need to handle other kinds of Ctrl combinations,
// as we rely on the caller to pretranslate those to characters for us.
if (!keyEvent.IsAltPressed() && keyEvent.IsCtrlPressed())
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
{
const auto ch = keyEvent.GetCharData();
const auto vkey = keyEvent.GetVirtualKeyCode();
// Currently, when we're called with Ctrl+@, ch will be 0, since Ctrl+@ equals a null byte.
// VkKeyScanW(0) in turn returns the vkey for the null character (ASCII @).
// -> Use the vkey to alternatively determine if Ctrl+@ is being pressed.
if (ch == UNICODE_SPACE || (ch == UNICODE_NULL && vkey == LOBYTE(VkKeyScanW(0))))
{
_SendNullInputSequence(keyEvent.GetActiveModifierKeys());
return true;
}
}
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
// Check any other key mappings (like those for the F1-F12 keys).
Add support for VT52 emulation (#4789) ## Summary of the Pull Request This PR adds support for the core VT52 commands, and implements the `DECANM` private mode sequence, which switches the terminal between ANSI mode and VT52-compatible mode. ## References PR #2017 defined the initial specification for VT52 support. PR #4044 removed the original VT52 cursor ops that conflicted with VT100 sequences. ## PR Checklist * [x] Closes #976 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed * [ ] Requires documentation to be updated * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #2017 ## Detailed Description of the Pull Request / Additional comments Most of the work involves updates to the parsing state machine, which behaves differently in VT52 mode. `CSI`, `OSC`, and `SS3` sequences are not applicable, and there is one special-case escape sequence (_Direct Cursor Address_), which requires an additional state to handle parameters that come _after_ the final character. Once the parsing is handled though, it's mostly just a matter of dispatching the commands to existing methods in the `ITermDispatch` interface. Only one new method was required in the interface to handle the _Identify_ command. The only real new functionality is in the `TerminalInput` class, which needs to generate different escape sequences for certain keys in VT52 mode. This does not yet support _all_ of the VT52 key sequences, because the VT100 support is itself not yet complete. But the basics are in place, and I think the rest is best left for a follow-up issue, and potentially a refactor of the `TerminalInput` class. I should point out that the original spec called for a new _Graphic Mode_ character set, but I've since discovered that the VT terminals that _emulate_ VT52 just use the existing VT100 _Special Graphics_ set, so that is really what we should be doing too. We can always consider adding the VT52 graphic set as a option later, if there is demand for strict VT52 compatibility. ## Validation Steps Performed I've added state machine and adapter tests to confirm that the `DECANM` mode changing sequences are correctly dispatched and forwarded to the `ConGetSet` handler. I've also added state machine tests that confirm the VT52 escape sequences are dispatched correctly when the ANSI mode is reset. For fuzzing support, I've extended the VT command fuzzer to generate the different kinds of VT52 sequences, as well as mode change sequences to switch between the ANSI and VT52 modes. In terms of manual testing, I've confirmed that the _Test of VT52 mode_ in Vttest now works as expected.
2020-06-01 23:20:40 +02:00
const auto mapping = _getKeyMapping(keyEvent, _ansiMode, _cursorApplicationMode, _keypadApplicationMode);
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
if (_translateDefaultMapping(keyEvent, mapping, senderFunc))
{
return true;
}
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
// If all else fails we can finally try to send the character itself if there is any.
if (keyEvent.GetCharData() != 0)
{
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
_SendChar(keyEvent.GetCharData());
return true;
}
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
return false;
}
// Routine Description:
// - Sends the given character to the shell.
// - Surrogate pairs are being aggregated by this function before being sent.
// Arguments:
// - ch: The UTF-16 character to send.
void TerminalInput::_SendChar(const wchar_t ch)
{
if (Utf16Parser::IsLeadingSurrogate(ch))
{
if (_leadingSurrogate.has_value())
{
// we already were storing a leading surrogate but we got another one. Go ahead and send the
// saved surrogate piece and save the new one
const auto formatted = wil::str_printf<std::wstring>(L"%I32u", _leadingSurrogate.value());
_SendInputSequence(formatted);
}
// save the leading portion of a surrogate pair so that they can be sent at the same time
_leadingSurrogate.emplace(ch);
}
else if (_leadingSurrogate.has_value())
{
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
std::array<wchar_t, 2> wstr{ { _leadingSurrogate.value(), ch } };
_leadingSurrogate.reset();
Delegate all character input to the character event handler (#4192) My basic idea was that `WM_CHAR` is just the better `WM_KEYDOWN`. The latter fails to properly support common dead key sequences like in #3516. As such I added some logic to `Terminal::SendKeyEvent` to make it return false if the pressed key represents a printable character. This causes us to receive a character event with a (hopefully) correctly composed code unit, which then gets sent to `Terminal::SendCharEvent`. `Terminal::SendCharEvent` in turn had to be modified to support potentially pressed modifier keys, since `Terminal::SendKeyEvent` isn't doing that for us anymore. Lastly `TerminalInput` had to be modified heavily to support character events with modifier key states. In order to do so I merged its `HandleKey` and `HandleChar` methods into a single one, that now handles both cases. Since key events will now contain character data and character events key codes the decision logic in `TerminalInput::HandleKey` had to be rewritten. ## PR Checklist * [x] CLA signed * [x] Tests added/passed * [x] I've discussed this with core contributors already. ## Validation Steps Performed * See #3516. * I don't have any keyboard that generates surrogate characters. Due to this I modified `TermControl::_SendPastedTextToConnection` to send the data to `_terminal->SendCharEvent()` instead. I then pasted the test string ""𐐌𐐜𐐬" and ensured that the new `TerminalInput::_SendChar` method still correctly assembles surrogate pairs. Closes #3516 Closes #3554 (obsoleted by this PR) Potentially impacts #391, which sounds like a duplicate of #3516
2020-04-07 21:09:28 +02:00
_SendInputSequence({ wstr.data(), wstr.size() });
}
else
{
_SendInputSequence({ &ch, 1 });
}
}
// Routine Description:
// - Sends the given char as a sequence representing Alt+wch, also the same as
// Meta+wch.
// Arguments:
// - wch - character to send to input paired with Esc
// Return Value:
// - None
void TerminalInput::_SendEscapedInputSequence(const wchar_t wch) const
{
try
{
std::deque<std::unique_ptr<IInputEvent>> inputEvents;
inputEvents.push_back(std::make_unique<KeyEvent>(true, 1ui16, 0ui16, 0ui16, L'\x1b', 0));
inputEvents.push_back(std::make_unique<KeyEvent>(true, 1ui16, 0ui16, 0ui16, wch, 0));
_pfnWriteEvents(inputEvents);
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
}
}
void TerminalInput::_SendNullInputSequence(const DWORD controlKeyState) const
{
try
{
std::deque<std::unique_ptr<IInputEvent>> inputEvents;
inputEvents.push_back(std::make_unique<KeyEvent>(true,
1ui16,
LOBYTE(VkKeyScanW(0)),
0ui16,
L'\x0',
controlKeyState));
_pfnWriteEvents(inputEvents);
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
}
}
void TerminalInput::_SendInputSequence(const std::wstring_view sequence) const noexcept
{
if (!sequence.empty())
{
try
{
std::deque<std::unique_ptr<IInputEvent>> inputEvents;
for (const auto& wch : sequence)
{
inputEvents.push_back(std::make_unique<KeyEvent>(true, 1ui16, 0ui16, 0ui16, wch, 0));
}
_pfnWriteEvents(inputEvents);
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
}
}
}
// Method Description:
// - Synthesize a win32-input-mode sequence for the given keyevent.
// Arguments:
// - key: the KeyEvent to serialize.
// Return Value:
// - the formatted string representation of this key
std::wstring TerminalInput::_GenerateWin32KeySequence(const KeyEvent& key)
{
// Sequences are formatted as follows:
//
// ^[ [ Vk ; Sc ; Uc ; Kd ; Cs ; Rc _
//
// Vk: the value of wVirtualKeyCode - any number. If omitted, defaults to '0'.
// Sc: the value of wVirtualScanCode - any number. If omitted, defaults to '0'.
// Uc: the decimal value of UnicodeChar - for example, NUL is "0", LF is
// "10", the character 'A' is "65". If omitted, defaults to '0'.
// Kd: the value of bKeyDown - either a '0' or '1'. If omitted, defaults to '0'.
// Cs: the value of dwControlKeyState - any number. If omitted, defaults to '0'.
// Rc: the value of wRepeatCount - any number. If omitted, defaults to '1'.
return fmt::format(L"\x1b[{};{};{};{};{};{}_",
key.GetVirtualKeyCode(),
key.GetVirtualScanCode(),
static_cast<int>(key.GetCharData()),
key.IsKeyDown() ? 1 : 0,
key.GetActiveModifierKeys(),
key.GetRepeatCount());
}