terminal/src/terminal/input/terminalInput.hpp
Leonard Hecker a9c9714295
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 19:09:28 +00:00

134 lines
5 KiB
C++

/*++
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;
bool HandleKey(const IInputEvent* const pInEvent);
void ChangeKeypadMode(const bool applicationMode) noexcept;
void ChangeCursorKeysMode(const bool applicationMode) noexcept;
#pragma region MouseInput
// These methods are defined in mouseInput.cpp
bool HandleMouse(const COORD position,
const unsigned int button,
const short modifierKeyState,
const short delta);
bool IsTrackingMouseInput() const noexcept;
#pragma endregion
#pragma region MouseInputState Management
// These methods are defined in mouseInputState.cpp
void SetUtf8ExtendedMode(const bool enable) noexcept;
void SetSGRExtendedMode(const bool enable) noexcept;
void EnableDefaultTracking(const bool enable) noexcept;
void EnableButtonEventTracking(const bool enable) noexcept;
void EnableAnyEventTracking(const bool enable) noexcept;
void EnableAlternateScroll(const bool enable) noexcept;
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;
bool _keypadApplicationMode = false;
bool _cursorApplicationMode = false;
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;
#pragma region MouseInputState Management
// These methods are defined in mouseInputState.cpp
enum class ExtendedMode : unsigned int
{
None,
Utf8,
Sgr,
Urxvt
};
enum class TrackingMode : unsigned int
{
None,
Default,
ButtonEvent,
AnyEvent
};
struct MouseInputState
{
ExtendedMode extendedMode{ ExtendedMode::None };
TrackingMode trackingMode{ TrackingMode::None };
bool alternateScroll{ false };
bool inAlternateBuffer{ false };
COORD lastPos{ -1, -1 };
unsigned int lastButton{ 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 unsigned int s_GetPressedButton() noexcept;
#pragma endregion
};
}