terminal/src/terminal/input/terminalInput.hpp

137 lines
4.9 KiB
C++
Raw Normal View History

/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- terminalInput.hpp
Abstract:
- This serves as an adapter between virtual key input from a user and the virtual terminal sequences that are
typically emitted by an xterm-compatible console.
Author(s):
- Michael Niksa (MiNiksa) 30-Oct-2015
--*/
#include <functional>
#include "../../types/inc/IInputEvent.hpp"
#pragma once
namespace Microsoft::Console::VirtualTerminal
{
class TerminalInput final
{
public:
TerminalInput(_In_ std::function<void(std::deque<std::unique_ptr<IInputEvent>>&)> pfn);
TerminalInput() = delete;
TerminalInput(const TerminalInput& old) = default;
TerminalInput(TerminalInput&& moved) = default;
TerminalInput& operator=(const TerminalInput& old) = default;
TerminalInput& operator=(TerminalInput&& moved) = default;
~TerminalInput() = default;
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
bool HandleKey(const IInputEvent* const pInEvent);
Consolidate the interfaces for setting VT input modes (#11384) Instead of having a separate method for setting each mouse and keyboard mode, this PR consolidates them all into a single method which takes a mode parameter, and stores the modes in a `til::enumset` rather than having a separate `bool` for each mode. This enables us to get rid of a lot of boilerplate code, and makes the code easier to extend when we want to introduce additional modes in the future. It'll also makes it easier to read back the state of the various modes when implementing the `DECRQM` query. Most of the complication is in the `TerminalInput` class, which had to be adjusted to work with an `enumset` in place of all the `bool` fields. For the rest, it was largely a matter of replacing calls to all the old mode setting methods with the new `SetInputMode` method, and deleting a bunch of unused code. One thing worth mentioning is that the `AdaptDispatch` implementation used to have a `_ShouldPassThroughInputModeChange` method that was called after every mode change. This code has now been moved up into the `SetInputMode` implementation in `ConhostInternalGetSet` so it's just handled in one place. Keeping this out of the dispatch class will also be beneficial for sharing the implementation with `TerminalDispatch`. ## Validation The updated interface necessitated some adjustments to the tests in `AdapterTest` and `MouseInputTest`, but the essential structure of the tests remains unchanged, and everything still passes. I've also tested the keyboard and mouse modes in Vttest and confirmed they still work at least as well as they did before (both conhost and Windows Terminal), and I tested the alternate scroll mode manually (conhost only). Simplifying the `ConGetSet` and `ITerminalApi` is also part of the plan to de-duplicate the `AdaptDispatch` and `TerminalDispatch` implementation (#3849).
2021-10-26 23:12:22 +02:00
enum class Mode : size_t
{
Ansi,
Keypad,
CursorKey,
Win32,
Utf8MouseEncoding,
SgrMouseEncoding,
DefaultMouseTracking,
ButtonEventMouseTracking,
AnyEventMouseTracking,
AlternateScroll
};
void SetInputMode(const Mode mode, const bool enabled) noexcept;
bool GetInputMode(const Mode mode) const noexcept;
void ForceDisableWin32InputMode(const bool win32InputMode) noexcept;
#pragma region MouseInput
// These methods are defined in mouseInput.cpp
struct MouseButtonState
{
bool isLeftButtonDown;
bool isMiddleButtonDown;
bool isRightButtonDown;
};
bool HandleMouse(const COORD position,
const unsigned int button,
const short modifierKeyState,
const short delta,
const MouseButtonState state);
bool IsTrackingMouseInput() const noexcept;
#pragma endregion
#pragma region MouseInputState Management
// These methods are defined in mouseInputState.cpp
void UseAlternateScreenBuffer() noexcept;
void UseMainScreenBuffer() noexcept;
#pragma endregion
private:
std::function<void(std::deque<std::unique_ptr<IInputEvent>>&)> _pfnWriteEvents;
// storage location for the leading surrogate of a utf-16 surrogate pair
std::optional<wchar_t> _leadingSurrogate;
Consolidate the interfaces for setting VT input modes (#11384) Instead of having a separate method for setting each mouse and keyboard mode, this PR consolidates them all into a single method which takes a mode parameter, and stores the modes in a `til::enumset` rather than having a separate `bool` for each mode. This enables us to get rid of a lot of boilerplate code, and makes the code easier to extend when we want to introduce additional modes in the future. It'll also makes it easier to read back the state of the various modes when implementing the `DECRQM` query. Most of the complication is in the `TerminalInput` class, which had to be adjusted to work with an `enumset` in place of all the `bool` fields. For the rest, it was largely a matter of replacing calls to all the old mode setting methods with the new `SetInputMode` method, and deleting a bunch of unused code. One thing worth mentioning is that the `AdaptDispatch` implementation used to have a `_ShouldPassThroughInputModeChange` method that was called after every mode change. This code has now been moved up into the `SetInputMode` implementation in `ConhostInternalGetSet` so it's just handled in one place. Keeping this out of the dispatch class will also be beneficial for sharing the implementation with `TerminalDispatch`. ## Validation The updated interface necessitated some adjustments to the tests in `AdapterTest` and `MouseInputTest`, but the essential structure of the tests remains unchanged, and everything still passes. I've also tested the keyboard and mouse modes in Vttest and confirmed they still work at least as well as they did before (both conhost and Windows Terminal), and I tested the alternate scroll mode manually (conhost only). Simplifying the `ConGetSet` and `ITerminalApi` is also part of the plan to de-duplicate the `AdaptDispatch` and `TerminalDispatch` implementation (#3849).
2021-10-26 23:12:22 +02:00
til::enumset<Mode> _inputMode{ Mode::Ansi };
bool _forceDisableWin32InputMode{ 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
void _SendChar(const wchar_t ch);
void _SendNullInputSequence(const DWORD dwControlKeyState) const;
void _SendInputSequence(const std::wstring_view sequence) const noexcept;
void _SendEscapedInputSequence(const wchar_t wch) const;
static std::wstring _GenerateWin32KeySequence(const KeyEvent& key);
#pragma region MouseInputState Management
// These methods are defined in mouseInputState.cpp
struct MouseInputState
{
bool inAlternateBuffer{ false };
COORD lastPos{ -1, -1 };
unsigned int lastButton{ 0 };
int accumulatedDelta{ 0 };
};
MouseInputState _mouseInputState;
#pragma endregion
#pragma region MouseInput
static std::wstring _GenerateDefaultSequence(const COORD position,
const unsigned int button,
const bool isHover,
const short modifierKeyState,
const short delta);
static std::wstring _GenerateUtf8Sequence(const COORD position,
const unsigned int button,
const bool isHover,
const short modifierKeyState,
const short delta);
static std::wstring _GenerateSGRSequence(const COORD position,
const unsigned int button,
const bool isDown,
const bool isHover,
const short modifierKeyState,
const short delta);
bool _ShouldSendAlternateScroll(const unsigned int button, const short delta) const noexcept;
bool _SendAlternateScroll(const short delta) const noexcept;
static constexpr unsigned int s_GetPressedButton(const MouseButtonState state) noexcept;
#pragma endregion
};
}