From fcbbe08d1d6f8ffa4aeaae6e594d3fc5e8d3c002 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 12 Jul 2023 19:24:40 +0200 Subject: [PATCH 1/8] Replace IInputEvent with INPUT_RECORD --- src/cascadia/TerminalControl/ControlCore.cpp | 2 +- src/cascadia/TerminalCore/Terminal.cpp | 10 +- src/cascadia/TerminalCore/Terminal.hpp | 16 + src/host/cmdline.cpp | 89 +++ src/host/cmdline.h | 3 + src/host/conimeinfo.cpp | 13 +- src/host/directio.cpp | 15 +- src/host/ft_host/Message_KeyPressTests.cpp | 1 - src/host/input.cpp | 39 +- src/host/input.h | 2 +- src/host/inputBuffer.cpp | 191 ++---- src/host/inputBuffer.hpp | 56 +- src/host/output.cpp | 2 +- src/host/outputStream.cpp | 11 +- src/host/readDataDirect.cpp | 6 +- src/host/readDataDirect.hpp | 2 +- src/host/stream.cpp | 95 ++- src/host/ut_host/ClipboardTests.cpp | 157 ++--- src/host/ut_host/Host.UnitTests.vcxproj | 1 - .../ut_host/Host.UnitTests.vcxproj.filters | 3 - src/host/ut_host/InputBufferTests.cpp | 163 +++-- src/inc/til/small_vector.h | 6 + src/interactivity/base/EventSynthesis.cpp | 201 ++---- src/interactivity/inc/EventSynthesis.hpp | 12 +- src/interactivity/onecore/ConIoSrvComm.cpp | 4 +- src/interactivity/win32/Clipboard.cpp | 20 +- src/interactivity/win32/clipboard.hpp | 6 +- src/interactivity/win32/windowio.cpp | 41 +- src/server/ApiDispatchers.cpp | 14 +- src/server/WaitBlock.cpp | 12 +- src/terminal/adapter/IInteractDispatch.hpp | 4 +- src/terminal/adapter/InteractDispatch.cpp | 18 +- src/terminal/adapter/InteractDispatch.hpp | 4 +- .../adapter/ut_adapter/MouseInputTest.cpp | 3 +- src/terminal/adapter/ut_adapter/inputTest.cpp | 96 +-- src/terminal/input/mouseInput.cpp | 1 + src/terminal/input/terminalInput.cpp | 109 ++-- src/terminal/input/terminalInput.hpp | 24 +- .../parser/InputStateMachineEngine.cpp | 93 +-- .../parser/InputStateMachineEngine.hpp | 6 +- .../parser/ut_parser/InputEngineTest.cpp | 165 +++-- src/types/FocusEvent.cpp | 38 -- src/types/IInputEvent.cpp | 68 -- src/types/IInputEventStreams.cpp | 113 ---- src/types/KeyEvent.cpp | 189 ------ src/types/MenuEvent.cpp | 25 - src/types/ModifierKeyState.cpp | 137 ---- src/types/MouseEvent.cpp | 43 -- src/types/WindowBufferSizeEvent.cpp | 26 - src/types/inc/IInputEvent.hpp | 602 ++---------------- src/types/lib/types.vcxproj | 9 +- src/types/lib/types.vcxproj.filters | 45 -- src/types/sources.inc | 6 - tools/ConsoleTypes.natvis | 24 +- 54 files changed, 773 insertions(+), 2268 deletions(-) delete mode 100644 src/types/FocusEvent.cpp delete mode 100644 src/types/IInputEvent.cpp delete mode 100644 src/types/IInputEventStreams.cpp delete mode 100644 src/types/KeyEvent.cpp delete mode 100644 src/types/MenuEvent.cpp delete mode 100644 src/types/ModifierKeyState.cpp delete mode 100644 src/types/MouseEvent.cpp delete mode 100644 src/types/WindowBufferSizeEvent.cpp diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 870b8f9fed5..7c460666a9d 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -511,7 +511,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // modifier key. We'll wait for a real keystroke to dismiss the // GH #7395 - don't update selection when taking PrintScreen // selection. - return HasSelection() && !KeyEvent::IsModifierKey(vkey) && vkey != VK_SNAPSHOT; + return HasSelection() && ::Microsoft::Terminal::Core::Terminal::IsInputKey(vkey); } bool ControlCore::TryMarkModeKeybinding(const WORD vkey, diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index d475966350e..de33990e365 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -661,7 +661,7 @@ bool Terminal::SendKeyEvent(const WORD vkey, // modifier key. We'll wait for a real keystroke to snap to the bottom. // GH#6481 - Additionally, make sure the key was actually pressed. This // check will make sure we behave the same as before GH#6309 - if (!KeyEvent::IsModifierKey(vkey) && keyDown) + if (IsInputKey(vkey) && keyDown) { TrySnapOnInput(); } @@ -714,8 +714,8 @@ bool Terminal::SendKeyEvent(const WORD vkey, return false; } - const KeyEvent keyEv{ keyDown, 1, vkey, sc, ch, states.Value() }; - return _handleTerminalInputResult(_terminalInput.HandleKey(&keyEv)); + const auto keyEv = SynthesizeKeyEvent(keyDown, 1, vkey, sc, ch, states.Value()); + return _handleTerminalInputResult(_terminalInput.HandleKey(keyEv)); } // Method Description: @@ -791,8 +791,8 @@ bool Terminal::SendCharEvent(const wchar_t ch, const WORD scanCode, const Contro MarkOutputStart(); } - const KeyEvent keyDown{ true, 1, vkey, scanCode, ch, states.Value() }; - return _handleTerminalInputResult(_terminalInput.HandleKey(&keyDown)); + const auto keyDown = SynthesizeKeyEvent(true, 1, vkey, scanCode, ch, states.Value()); + return _handleTerminalInputResult(_terminalInput.HandleKey(keyDown)); } // Method Description: diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index fc392bc7e0c..faa6e12b1a3 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -60,6 +60,22 @@ class Microsoft::Terminal::Core::Terminal final : using RenderSettings = Microsoft::Console::Render::RenderSettings; public: + static constexpr bool IsInputKey(WORD vkey) + { + return vkey != VK_CONTROL && + vkey != VK_LCONTROL && + vkey != VK_RCONTROL && + vkey != VK_MENU && + vkey != VK_LMENU && + vkey != VK_RMENU && + vkey != VK_SHIFT && + vkey != VK_LSHIFT && + vkey != VK_RSHIFT && + vkey != VK_LWIN && + vkey != VK_RWIN && + vkey != VK_SNAPSHOT; + } + Terminal(); void Create(til::size viewportSize, diff --git a/src/host/cmdline.cpp b/src/host/cmdline.cpp index 4b48f4bd24b..0ed97f11f81 100644 --- a/src/host/cmdline.cpp +++ b/src/host/cmdline.cpp @@ -301,6 +301,95 @@ void SetCurrentCommandLine(COOKED_READ_DATA& cookedReadData, _In_ SHORT Index) / cookedReadData.SetBufferCurrentPtr(cookedReadData.BufferStartPtr() + CharsToWrite); } +bool IsCommandLinePopupKey(const KEY_EVENT_RECORD& event) +{ + if (WI_AreAllFlagsClear(event.dwControlKeyState, RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED | RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) + { + switch (event.wVirtualKeyCode) + { + case VK_ESCAPE: + case VK_PRIOR: + case VK_NEXT: + case VK_END: + case VK_HOME: + case VK_LEFT: + case VK_UP: + case VK_RIGHT: + case VK_DOWN: + case VK_F2: + case VK_F4: + case VK_F7: + case VK_F9: + case VK_DELETE: + return true; + default: + break; + } + } + + return false; +} + +bool IsCommandLineEditingKey(const KEY_EVENT_RECORD& event) +{ + if (WI_AreAllFlagsClear(event.dwControlKeyState, RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED | RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) + { + switch (event.wVirtualKeyCode) + { + case VK_ESCAPE: + case VK_PRIOR: + case VK_NEXT: + case VK_END: + case VK_HOME: + case VK_LEFT: + case VK_UP: + case VK_RIGHT: + case VK_DOWN: + case VK_INSERT: + case VK_DELETE: + case VK_F1: + case VK_F2: + case VK_F3: + case VK_F4: + case VK_F5: + case VK_F6: + case VK_F7: + case VK_F8: + case VK_F9: + return true; + default: + break; + } + } + if (WI_AreAllFlagsClear(event.dwControlKeyState, RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) + { + switch (event.wVirtualKeyCode) + { + case VK_END: + case VK_HOME: + case VK_LEFT: + case VK_RIGHT: + return true; + default: + break; + } + } + + if (WI_AreAllFlagsClear(event.dwControlKeyState, RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) + { + switch (event.wVirtualKeyCode) + { + case VK_F7: + case VK_F10: + return true; + default: + break; + } + } + + return false; +} + // Routine Description: // - This routine handles the command list popup. It puts up the popup, then calls ProcessCommandListInput to get and process input. // Return Value: diff --git a/src/host/cmdline.h b/src/host/cmdline.h index a8669edadde..35bcdf3847d 100644 --- a/src/host/cmdline.h +++ b/src/host/cmdline.h @@ -141,3 +141,6 @@ bool IsWordDelim(const std::wstring_view charData); bool IsValidStringBuffer(_In_ bool Unicode, _In_reads_bytes_(Size) PVOID Buffer, _In_ ULONG Size, _In_ ULONG Count, ...); void SetCurrentCommandLine(COOKED_READ_DATA& cookedReadData, _In_ SHORT Index); + +bool IsCommandLinePopupKey(const KEY_EVENT_RECORD& event); +bool IsCommandLineEditingKey(const KEY_EVENT_RECORD& event); diff --git a/src/host/conimeinfo.cpp b/src/host/conimeinfo.cpp index 9ab47a80029..fc2ce5ae74c 100644 --- a/src/host/conimeinfo.cpp +++ b/src/host/conimeinfo.cpp @@ -462,18 +462,13 @@ void ConsoleImeInfo::_InsertConvertedString(const std::wstring_view text) } const auto dwControlKeyState = GetControlKeyState(0); - std::deque> inEvents; - KeyEvent keyEvent{ TRUE, // keydown - 1, // repeatCount - 0, // virtualKeyCode - 0, // virtualScanCode - 0, // charData - dwControlKeyState }; // activeModifierKeys + InputEventQueue inEvents; + auto keyEvent = SynthesizeKeyEvent(true, 1, 0, 0, 0, dwControlKeyState); for (const auto& ch : text) { - keyEvent.SetCharData(ch); - inEvents.push_back(std::make_unique(keyEvent)); + keyEvent.Event.KeyEvent.uChar.UnicodeChar = ch; + inEvents.push_back(keyEvent); } gci.pInputBuffer->Write(inEvents); diff --git a/src/host/directio.cpp b/src/host/directio.cpp index b5ababfe4b4..1187bd4faae 100644 --- a/src/host/directio.cpp +++ b/src/host/directio.cpp @@ -101,7 +101,7 @@ using Microsoft::Console::Interactivity::ServiceLocator; // Return Value: // - HRESULT indicating success or failure [[nodiscard]] static HRESULT _WriteConsoleInputWImplHelper(InputBuffer& context, - std::deque>& events, + const std::span& events, size_t& written, const bool append) noexcept { @@ -151,7 +151,7 @@ try auto Unlock = wil::scope_exit([&] { UnlockConsole(); }); const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - til::small_vector events; + InputEventQueue events; auto it = buffer.begin(); const auto end = buffer.end(); @@ -160,7 +160,7 @@ try // the next call to WriteConsoleInputAImpl to join it with the now available trailing DBCS. if (context.IsWritePartialByteSequenceAvailable()) { - auto lead = context.FetchWritePartialByteSequence(false)->ToInputRecord(); + auto lead = context.FetchWritePartialByteSequence(); const auto& trail = *it; if (trail.EventType == KEY_EVENT) @@ -200,7 +200,7 @@ try if (it == end) { // Missing trailing DBCS -> Store the lead for the next call to WriteConsoleInputAImpl. - context.StoreWritePartialByteSequence(IInputEvent::Create(lead)); + context.StoreWritePartialByteSequence(lead); break; } @@ -225,8 +225,7 @@ try } } - auto result = IInputEvent::Create(std::span{ events.data(), events.size() }); - return _WriteConsoleInputWImplHelper(context, result, written, append); + return _WriteConsoleInputWImplHelper(context, events, written, append); } CATCH_RETURN(); @@ -252,9 +251,7 @@ CATCH_RETURN(); try { - auto events = IInputEvent::Create(buffer); - - return _WriteConsoleInputWImplHelper(context, events, written, append); + return _WriteConsoleInputWImplHelper(context, buffer, written, append); } CATCH_RETURN(); } diff --git a/src/host/ft_host/Message_KeyPressTests.cpp b/src/host/ft_host/Message_KeyPressTests.cpp index 53d8b9dade8..92f52eeeca0 100644 --- a/src/host/ft_host/Message_KeyPressTests.cpp +++ b/src/host/ft_host/Message_KeyPressTests.cpp @@ -86,7 +86,6 @@ class KeyPressTests expectedRecord.Event.KeyEvent.uChar.UnicodeChar = 0x0; expectedRecord.Event.KeyEvent.bKeyDown = true; expectedRecord.Event.KeyEvent.dwControlKeyState = ENHANCED_KEY; - expectedRecord.Event.KeyEvent.dwControlKeyState |= (GetKeyState(VK_NUMLOCK) & KEY_STATE_TOGGLED) ? NUMLOCK_ON : 0; expectedRecord.Event.KeyEvent.wRepeatCount = SINGLE_KEY_REPEAT; expectedRecord.Event.KeyEvent.wVirtualKeyCode = VK_APPS; expectedRecord.Event.KeyEvent.wVirtualScanCode = (WORD)scanCode; diff --git a/src/host/input.cpp b/src/host/input.cpp index 94e58d1407a..d48192daabb 100644 --- a/src/host/input.cpp +++ b/src/host/input.cpp @@ -105,17 +105,18 @@ bool ShouldTakeOverKeyboardShortcuts() // Routine Description: // - handles key events without reference to Win32 elements. -void HandleGenericKeyEvent(_In_ KeyEvent keyEvent, const bool generateBreak) +void HandleGenericKeyEvent(INPUT_RECORD event, const bool generateBreak) { + auto& keyEvent = event.Event.KeyEvent; const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); auto ContinueProcessing = true; - if (keyEvent.IsCtrlPressed() && - !keyEvent.IsAltPressed() && - keyEvent.IsKeyDown()) + if (WI_IsAnyFlagSet(keyEvent.dwControlKeyState, CTRL_PRESSED) && + WI_AreAllFlagsClear(keyEvent.dwControlKeyState, ALT_PRESSED) && + keyEvent.bKeyDown) { // check for ctrl-c, if in line input mode. - if (keyEvent.GetVirtualKeyCode() == 'C' && IsInProcessedInputMode()) + if (keyEvent.wVirtualKeyCode == 'C' && IsInProcessedInputMode()) { HandleCtrlEvent(CTRL_C_EVENT); if (gci.PopupCount == 0) @@ -130,7 +131,7 @@ void HandleGenericKeyEvent(_In_ KeyEvent keyEvent, const bool generateBreak) } // Check for ctrl-break. - else if (keyEvent.GetVirtualKeyCode() == VK_CANCEL) + else if (keyEvent.wVirtualKeyCode == VK_CANCEL) { gci.pInputBuffer->Flush(); HandleCtrlEvent(CTRL_BREAK_EVENT); @@ -146,33 +147,25 @@ void HandleGenericKeyEvent(_In_ KeyEvent keyEvent, const bool generateBreak) } // don't write ctrl-esc to the input buffer - else if (keyEvent.GetVirtualKeyCode() == VK_ESCAPE) + else if (keyEvent.wVirtualKeyCode == VK_ESCAPE) { ContinueProcessing = false; } } - else if (keyEvent.IsAltPressed() && - keyEvent.IsKeyDown() && - keyEvent.GetVirtualKeyCode() == VK_ESCAPE) + else if (WI_IsAnyFlagSet(keyEvent.dwControlKeyState, ALT_PRESSED) && + keyEvent.bKeyDown && + keyEvent.wVirtualKeyCode == VK_ESCAPE) { ContinueProcessing = false; } if (ContinueProcessing) { - size_t EventsWritten = 0; - try + gci.pInputBuffer->Write(event); + if (generateBreak) { - EventsWritten = gci.pInputBuffer->Write(std::make_unique(keyEvent)); - if (EventsWritten && generateBreak) - { - keyEvent.SetKeyDown(false); - EventsWritten = gci.pInputBuffer->Write(std::make_unique(keyEvent)); - } - } - catch (...) - { - LOG_HR(wil::ResultFromCaughtException()); + keyEvent.bKeyDown = false; + gci.pInputBuffer->Write(event); } } } @@ -210,7 +203,7 @@ void HandleMenuEvent(const DWORD wParam) size_t EventsWritten = 0; try { - EventsWritten = gci.pInputBuffer->Write(std::make_unique(wParam)); + EventsWritten = gci.pInputBuffer->Write(SynthesizeMenuEvent(wParam)); if (EventsWritten != 1) { RIPMSG0(RIP_WARNING, "PutInputInBuffer: EventsWritten != 1, 1 expected"); diff --git a/src/host/input.h b/src/host/input.h index 4b7667aa063..7e4509f6a1f 100644 --- a/src/host/input.h +++ b/src/host/input.h @@ -78,7 +78,7 @@ bool ShouldTakeOverKeyboardShortcuts(); void HandleMenuEvent(const DWORD wParam); void HandleFocusEvent(const BOOL fSetFocus); void HandleCtrlEvent(const DWORD EventType); -void HandleGenericKeyEvent(_In_ KeyEvent keyEvent, const bool generateBreak); +void HandleGenericKeyEvent(INPUT_RECORD event, const bool generateBreak); void ProcessCtrlEvents(); diff --git a/src/host/inputBuffer.cpp b/src/host/inputBuffer.cpp index de272bc1dc2..a4ed5000a89 100644 --- a/src/host/inputBuffer.cpp +++ b/src/host/inputBuffer.cpp @@ -183,7 +183,7 @@ size_t InputBuffer::PeekCached(bool isUnicode, size_t count, InputEventQueue& ta break; } - target.push_back(IInputEvent::Create(e->ToInputRecord())); + target.push_back(e); i++; } @@ -222,7 +222,7 @@ void InputBuffer::_switchReadingModeSlowPath(ReadingMode mode) _cachedTextW = std::wstring{}; _cachedTextReaderW = {}; - _cachedInputEvents = std::deque>{}; + _cachedInputEvents = std::deque{}; _readingMode = mode; } @@ -234,9 +234,9 @@ void InputBuffer::_switchReadingModeSlowPath(ReadingMode mode) // - None // Return Value: // - true if partial char data is available, false otherwise -bool InputBuffer::IsWritePartialByteSequenceAvailable() +bool InputBuffer::IsWritePartialByteSequenceAvailable() const noexcept { - return _writePartialByteSequence.get() != nullptr; + return _writePartialByteSequenceAvailable; } // Routine Description: @@ -245,23 +245,10 @@ bool InputBuffer::IsWritePartialByteSequenceAvailable() // - peek - if true, data will not be removed after being fetched // Return Value: // - the partial char data. may be nullptr if no data is available -std::unique_ptr InputBuffer::FetchWritePartialByteSequence(_In_ bool peek) +const INPUT_RECORD& InputBuffer::FetchWritePartialByteSequence() noexcept { - if (!IsWritePartialByteSequenceAvailable()) - { - return std::unique_ptr(); - } - - if (peek) - { - return IInputEvent::Create(_writePartialByteSequence->ToInputRecord()); - } - else - { - std::unique_ptr outEvent; - outEvent.swap(_writePartialByteSequence); - return outEvent; - } + _writePartialByteSequenceAvailable = false; + return _writePartialByteSequence; } // Routine Description: @@ -271,9 +258,10 @@ std::unique_ptr InputBuffer::FetchWritePartialByteSequence(_In_ boo // - event - The event to store // Return Value: // - None -void InputBuffer::StoreWritePartialByteSequence(std::unique_ptr event) +void InputBuffer::StoreWritePartialByteSequence(const INPUT_RECORD& event) noexcept { - _writePartialByteSequence.swap(event); + _writePartialByteSequenceAvailable = true; + _writePartialByteSequence = event; } // Routine Description: @@ -348,8 +336,8 @@ void InputBuffer::Flush() // - The console lock must be held when calling this routine. void InputBuffer::FlushAllButKeys() { - auto newEnd = std::remove_if(_storage.begin(), _storage.end(), [](const std::unique_ptr& event) { - return event->EventType() != InputEventType::KeyEvent; + auto newEnd = std::remove_if(_storage.begin(), _storage.end(), [](const INPUT_RECORD& event) { + return event.EventType != KEY_EVENT; }); _storage.erase(newEnd, _storage.end()); } @@ -391,7 +379,7 @@ void InputBuffer::PassThroughWin32MouseRequest(bool enable) // - STATUS_SUCCESS if records were read into the client buffer and everything is OK. // - CONSOLE_STATUS_WAIT if there weren't enough records to satisfy the request (and waits are allowed) // - otherwise a suitable memory/math/string error in NTSTATUS form. -[[nodiscard]] NTSTATUS InputBuffer::Read(_Out_ std::deque>& OutEvents, +[[nodiscard]] NTSTATUS InputBuffer::Read(_Out_ InputEventQueue& OutEvents, const size_t AmountToRead, const bool Peek, const bool WaitForData, @@ -417,31 +405,29 @@ try while (it != end && OutEvents.size() < AmountToRead) { - auto event = IInputEvent::Create((*it)->ToInputRecord()); - - if (event->EventType() == InputEventType::KeyEvent) + if (it->EventType == KEY_EVENT) { - const auto keyEvent = static_cast(event.get()); + auto event = *it; WORD repeat = 1; // for stream reads we need to split any key events that have been coalesced if (Stream) { - repeat = keyEvent->GetRepeatCount(); - keyEvent->SetRepeatCount(1); + repeat = std::max(1, event.Event.KeyEvent.wRepeatCount); + event.Event.KeyEvent.wRepeatCount = 1; } if (Unicode) { do { - OutEvents.push_back(std::make_unique(*keyEvent)); + OutEvents.push_back(event); repeat--; } while (repeat > 0 && OutEvents.size() < AmountToRead); } else { - const auto wch = keyEvent->GetCharData(); + const auto wch = event.Event.KeyEvent.uChar.UnicodeChar; char buffer[8]; const auto length = WideCharToMultiByte(cp, 0, &wch, 1, &buffer[0], sizeof(buffer), nullptr, nullptr); @@ -453,9 +439,10 @@ try { for (const auto& ch : str) { - auto tempEvent = std::make_unique(*keyEvent); - tempEvent->SetCharData(ch); - OutEvents.push_back(std::move(tempEvent)); + // char is signed and assigning it to UnicodeChar would cause sign-extension. + // unsigned char doesn't have this problem. + event.Event.KeyEvent.uChar.UnicodeChar = til::bit_cast(ch); + OutEvents.push_back(event); } repeat--; } while (repeat > 0 && OutEvents.size() < AmountToRead); @@ -463,14 +450,13 @@ try if (repeat && !Peek) { - const auto originalKeyEvent = static_cast((*it).get()); - originalKeyEvent->SetRepeatCount(repeat); + it->Event.KeyEvent.wRepeatCount = repeat; break; } } else { - OutEvents.push_back(std::move(event)); + OutEvents.push_back(*it); } ++it; @@ -498,51 +484,6 @@ catch (...) return NTSTATUS_FROM_HRESULT(wil::ResultFromCaughtException()); } -// Routine Description: -// - This routine reads a single event from the input buffer. -// - It can convert returned data to through the currently set Input CP, it can optionally return a wait condition -// if there isn't enough data in the buffer, and it can be set to not remove records as it reads them out. -// Note: -// - The console lock must be held when calling this routine. -// Arguments: -// - outEvent - where the read event is stored -// - Peek - If true, copy events to pInputRecord but don't remove them from the input buffer. -// - WaitForData - if true, wait until an event is input (if there aren't enough to fill client buffer). if false, return immediately -// - Unicode - true if the data in key events should be treated as unicode. false if they should be converted by the current input CP. -// - Stream - true if read should unpack KeyEvents that have a >1 repeat count. -// Return Value: -// - STATUS_SUCCESS if records were read into the client buffer and everything is OK. -// - CONSOLE_STATUS_WAIT if there weren't enough records to satisfy the request (and waits are allowed) -// - otherwise a suitable memory/math/string error in NTSTATUS form. -[[nodiscard]] NTSTATUS InputBuffer::Read(_Out_ std::unique_ptr& outEvent, - const bool Peek, - const bool WaitForData, - const bool Unicode, - const bool Stream) -{ - NTSTATUS Status; - try - { - std::deque> outEvents; - Status = Read(outEvents, - 1, - Peek, - WaitForData, - Unicode, - Stream); - if (!outEvents.empty()) - { - outEvent.swap(outEvents.front()); - } - } - catch (...) - { - Status = NTSTATUS_FROM_HRESULT(wil::ResultFromCaughtException()); - } - - return Status; -} - // Routine Description: // - Writes events to the beginning of the input buffer. // Arguments: @@ -552,7 +493,7 @@ catch (...) // S_OK if successful. // Note: // - The console lock must be held when calling this routine. -size_t InputBuffer::Prepend(_Inout_ std::deque>& inEvents) +size_t InputBuffer::Prepend(const std::span& inEvents) { try { @@ -569,7 +510,7 @@ size_t InputBuffer::Prepend(_Inout_ std::deque>& in // this way to handle any coalescing that might occur. // get all of the existing records, "emptying" the buffer - std::deque> existingStorage; + std::deque existingStorage; existingStorage.swap(_storage); // We will need this variable to pass to _WriteBuffer so it can attempt to determine wait status. @@ -583,10 +524,10 @@ size_t InputBuffer::Prepend(_Inout_ std::deque>& in _WriteBuffer(inEvents, prependEventsWritten, unusedWaitStatus); FAIL_FAST_IF(!(unusedWaitStatus)); - // write all previously existing records - size_t existingEventsWritten; - _WriteBuffer(existingStorage, existingEventsWritten, unusedWaitStatus); - FAIL_FAST_IF(!(!unusedWaitStatus)); + for (const auto& event : existingStorage) + { + _storage.push_back(event); + } // We need to set the wait event if there were 0 events in the // input queue when we started. @@ -621,19 +562,9 @@ size_t InputBuffer::Prepend(_Inout_ std::deque>& in // - The console lock must be held when calling this routine. // - any outside references to inEvent will ben invalidated after // calling this method. -size_t InputBuffer::Write(_Inout_ std::unique_ptr inEvent) +size_t InputBuffer::Write(const INPUT_RECORD& inEvent) { - try - { - std::deque> inEvents; - inEvents.push_back(std::move(inEvent)); - return Write(inEvents); - } - catch (...) - { - LOG_HR(wil::ResultFromCaughtException()); - return 0; - } + return Write(std::span{ &inEvent, 1 }); } // Routine Description: @@ -645,7 +576,7 @@ size_t InputBuffer::Write(_Inout_ std::unique_ptr inEvent) // - The number of events that were written to input buffer. // Note: // - The console lock must be held when calling this routine. -size_t InputBuffer::Write(_Inout_ std::deque>& inEvents) +size_t InputBuffer::Write(const std::span& inEvents) { try { @@ -694,7 +625,7 @@ void InputBuffer::WriteFocusEvent(bool focused) noexcept { // This is a mini-version of Write(). const auto wasEmpty = _storage.empty(); - _storage.push_back(std::make_unique(focused)); + _storage.push_back(SynthesizeFocusEvent(focused)); if (wasEmpty) { ServiceLocator::LocateGlobals().hInputEvent.SetEvent(); @@ -760,9 +691,7 @@ static bool IsPauseKey(const KEY_EVENT_RECORD& event) // Note: // - The console lock must be held when calling this routine. // - will throw on failure -void InputBuffer::_WriteBuffer(_Inout_ std::deque>& inEvents, - _Out_ size_t& eventsWritten, - _Out_ bool& setWaitEvent) +void InputBuffer::_WriteBuffer(const std::span& inEvents, _Out_ size_t& eventsWritten, _Out_ bool& setWaitEvent) { auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); @@ -772,18 +701,18 @@ void InputBuffer::_WriteBuffer(_Inout_ std::deque>& const auto initialInEventsSize = inEvents.size(); const auto vtInputMode = IsInVirtualTerminalInputMode(); - for (auto& inEvent : inEvents) + for (const auto& inEvent : inEvents) { - if (inEvent->EventType() == InputEventType::KeyEvent && static_cast(inEvent.get())->IsKeyDown()) + if (inEvent.EventType == KEY_EVENT && inEvent.Event.KeyEvent.bKeyDown) { // if output is suspended, any keyboard input releases it. - if (WI_IsFlagSet(gci.Flags, CONSOLE_SUSPENDED) && !IsSystemKey(static_cast(inEvent.get())->GetVirtualKeyCode())) + if (WI_IsFlagSet(gci.Flags, CONSOLE_SUSPENDED) && !IsSystemKey(inEvent.Event.KeyEvent.wVirtualKeyCode)) { UnblockWriteConsole(CONSOLE_OUTPUT_SUSPENDED); continue; } // intercept control-s - if (WI_IsFlagSet(InputMode, ENABLE_LINE_INPUT) && IsPauseKey(inEvent->ToInputRecord().Event.KeyEvent)) + if (WI_IsFlagSet(InputMode, ENABLE_LINE_INPUT) && IsPauseKey(inEvent.Event.KeyEvent)) { WI_SetFlag(gci.Flags, CONSOLE_SUSPENDED); continue; @@ -797,7 +726,7 @@ void InputBuffer::_WriteBuffer(_Inout_ std::deque>& if (vtInputMode) { // GH#11682: TerminalInput::HandleKey can handle both KeyEvents and Focus events seamlessly - if (const auto out = _termInput.HandleKey(inEvent.get())) + if (const auto out = _termInput.HandleKey(inEvent)) { _HandleTerminalInputCallback(*out); eventsWritten++; @@ -816,7 +745,7 @@ void InputBuffer::_WriteBuffer(_Inout_ std::deque>& } // At this point, the event was neither coalesced, nor processed by VT. - _storage.push_back(std::move(inEvent)); + _storage.push_back(inEvent); ++eventsWritten; } if (initiallyEmptyQueue && !_storage.empty()) @@ -839,39 +768,39 @@ void InputBuffer::_WriteBuffer(_Inout_ std::deque>& // the buffer with updated values from an incoming event, instead of // storing the incoming event (which would make the original one // redundant/out of date with the most current state). -bool InputBuffer::_CoalesceEvent(const std::unique_ptr& inEvent) const noexcept +bool InputBuffer::_CoalesceEvent(const INPUT_RECORD& inEvent) noexcept { auto& lastEvent = _storage.back(); - if (lastEvent->EventType() == InputEventType::MouseEvent && inEvent->EventType() == InputEventType::MouseEvent) + if (lastEvent.EventType == MOUSE_EVENT && inEvent.EventType == MOUSE_EVENT) { - const auto& inMouse = *static_cast(inEvent.get()); - auto& lastMouse = *static_cast(lastEvent.get()); + const auto& inMouse = inEvent.Event.MouseEvent; + auto& lastMouse = lastEvent.Event.MouseEvent; - if (lastMouse.IsMouseMoveEvent() && inMouse.IsMouseMoveEvent()) + if (lastMouse.dwEventFlags == MOUSE_MOVED && inMouse.dwEventFlags == MOUSE_MOVED) { - lastMouse.SetPosition(inMouse.GetPosition()); + lastMouse.dwMousePosition = inMouse.dwMousePosition; return true; } } - else if (lastEvent->EventType() == InputEventType::KeyEvent && inEvent->EventType() == InputEventType::KeyEvent) + else if (lastEvent.EventType == KEY_EVENT && inEvent.EventType == KEY_EVENT) { - const auto& inKey = *static_cast(inEvent.get()); - auto& lastKey = *static_cast(lastEvent.get()); - - if (lastKey.IsKeyDown() && inKey.IsKeyDown() && - (lastKey.GetVirtualScanCode() == inKey.GetVirtualScanCode() || WI_IsFlagSet(inKey.GetActiveModifierKeys(), NLS_IME_CONVERSION)) && - lastKey.GetCharData() == inKey.GetCharData() && - lastKey.GetActiveModifierKeys() == inKey.GetActiveModifierKeys() && - // TODO: This behavior is an import from old conhost v1 and has been broken for decades. + const auto& inKey = inEvent.Event.KeyEvent; + auto& lastKey = lastEvent.Event.KeyEvent; + + if (lastKey.bKeyDown && inKey.bKeyDown && + (lastKey.wVirtualScanCode == inKey.wVirtualScanCode || WI_IsFlagSet(inKey.dwControlKeyState, NLS_IME_CONVERSION)) && + lastKey.uChar.UnicodeChar == inKey.uChar.UnicodeChar && + lastKey.dwControlKeyState == inKey.dwControlKeyState && + // TODO:GH#8000 This behavior is an import from old conhost v1 and has been broken for decades. // This is probably the outdated idea that any wide glyph is being represented by 2 characters (DBCS) and likely // resulted from conhost originally being split into a ASCII/OEM and a DBCS variant with preprocessor flags. // You can't update the repeat count of such a A,B pair, because they're stored as A,A,B,B (down-down, up-up). // I believe the proper approach is to store pairs of characters as pairs, update their combined // repeat count and only when they're being read de-coalesce them into their alternating form. - !IsGlyphFullWidth(inKey.GetCharData())) + !IsGlyphFullWidth(inKey.uChar.UnicodeChar)) { - lastKey.SetRepeatCount(lastKey.GetRepeatCount() + inKey.GetRepeatCount()); + lastKey.wRepeatCount += inKey.wRepeatCount; return true; } } @@ -908,7 +837,7 @@ void InputBuffer::_HandleTerminalInputCallback(const TerminalInput::StringType& for (const auto& wch : text) { - _storage.push_back(std::make_unique(true, 1ui16, 0ui16, 0ui16, wch, 0)); + _storage.push_back(SynthesizeKeyEvent(true, 1, 0, 0, wch, 0)); } if (!_vtInputShouldSuppress) diff --git a/src/host/inputBuffer.hpp b/src/host/inputBuffer.hpp index 5fde60431f9..0044cb89d27 100644 --- a/src/host/inputBuffer.hpp +++ b/src/host/inputBuffer.hpp @@ -1,20 +1,5 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- inputBuffer.hpp - -Abstract: -- storage area for incoming input events. - -Author: -- Therese Stowell (Thereses) 12-Nov-1990. Adapted from OS/2 subsystem server\srvpipe.c - -Revision History: -- Moved from input.h/input.cpp. (AustDi, 2017) -- Refactored to class, added stl container usage (AustDi, 2017) ---*/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. #pragma once @@ -52,9 +37,9 @@ class InputBuffer final : public ConsoleObjectHeader void Cache(bool isUnicode, InputEventQueue& source, size_t expectedSourceSize); // storage API for partial dbcs bytes being written to the buffer - bool IsWritePartialByteSequenceAvailable(); - std::unique_ptr FetchWritePartialByteSequence(_In_ bool peek); - void StoreWritePartialByteSequence(std::unique_ptr event); + bool IsWritePartialByteSequenceAvailable() const noexcept; + const INPUT_RECORD& FetchWritePartialByteSequence() noexcept; + void StoreWritePartialByteSequence(const INPUT_RECORD& event) noexcept; void ReinitializeInputBuffer(); void WakeUpReadersWaitingForData(); @@ -63,24 +48,16 @@ class InputBuffer final : public ConsoleObjectHeader void Flush(); void FlushAllButKeys(); - [[nodiscard]] NTSTATUS Read(_Out_ std::deque>& OutEvents, + [[nodiscard]] NTSTATUS Read(_Out_ InputEventQueue& OutEvents, const size_t AmountToRead, const bool Peek, const bool WaitForData, const bool Unicode, const bool Stream); - [[nodiscard]] NTSTATUS Read(_Out_ std::unique_ptr& inEvent, - const bool Peek, - const bool WaitForData, - const bool Unicode, - const bool Stream); - - size_t Prepend(_Inout_ std::deque>& inEvents); - - size_t Write(_Inout_ std::unique_ptr inEvent); - size_t Write(_Inout_ std::deque>& inEvents); - + size_t Prepend(const std::span& inEvents); + size_t Write(const INPUT_RECORD& inEvent); + size_t Write(const std::span& inEvents); void WriteFocusEvent(bool focused) noexcept; bool WriteMouseEvent(til::point position, unsigned int button, short keyState, short wheelDelta); @@ -102,11 +79,12 @@ class InputBuffer final : public ConsoleObjectHeader std::string_view _cachedTextReaderA; std::wstring _cachedTextW; std::wstring_view _cachedTextReaderW; - std::deque> _cachedInputEvents; + std::deque _cachedInputEvents; ReadingMode _readingMode = ReadingMode::StringA; - std::deque> _storage; - std::unique_ptr _writePartialByteSequence; + std::deque _storage; + INPUT_RECORD _writePartialByteSequence{}; + bool _writePartialByteSequenceAvailable = false; Microsoft::Console::VirtualTerminal::TerminalInput _termInput; Microsoft::Console::Render::VtEngine* _pTtyConnection; @@ -118,12 +96,8 @@ class InputBuffer final : public ConsoleObjectHeader void _switchReadingMode(ReadingMode mode); void _switchReadingModeSlowPath(ReadingMode mode); - - void _WriteBuffer(_Inout_ std::deque>& inRecords, - _Out_ size_t& eventsWritten, - _Out_ bool& setWaitEvent); - - bool _CoalesceEvent(const std::unique_ptr& inEvent) const noexcept; + void _WriteBuffer(const std::span& inRecords, _Out_ size_t& eventsWritten, _Out_ bool& setWaitEvent); + bool _CoalesceEvent(const INPUT_RECORD& inEvent) noexcept; void _HandleTerminalInputCallback(const Microsoft::Console::VirtualTerminal::TerminalInput::StringType& text); #ifdef UNIT_TESTING diff --git a/src/host/output.cpp b/src/host/output.cpp index 75f522de314..beae8a8ef98 100644 --- a/src/host/output.cpp +++ b/src/host/output.cpp @@ -257,7 +257,7 @@ void ScreenBufferSizeChange(const til::size coordNewSize) try { - gci.pInputBuffer->Write(std::make_unique(coordNewSize)); + gci.pInputBuffer->Write(SynthesizeWindowBufferSizeEvent(coordNewSize)); } catch (...) { diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index e20f1e73f99..72d5353f4c8 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -33,18 +33,17 @@ ConhostInternalGetSet::ConhostInternalGetSet(_In_ IIoProvider& io) : // - void ConhostInternalGetSet::ReturnResponse(const std::wstring_view response) { - std::deque> inEvents; + InputEventQueue inEvents; // generate a paired key down and key up event for every // character to be sent into the console's input buffer for (const auto& wch : response) { // This wasn't from a real keyboard, so we're leaving key/scan codes blank. - KeyEvent keyEvent{ TRUE, 1, 0, 0, wch, 0 }; - - inEvents.push_back(std::make_unique(keyEvent)); - keyEvent.SetKeyDown(false); - inEvents.push_back(std::make_unique(keyEvent)); + auto keyEvent = SynthesizeKeyEvent(true, 1, 0, 0, wch, 0); + inEvents.push_back(keyEvent); + keyEvent.Event.KeyEvent.bKeyDown = false; + inEvents.push_back(keyEvent); } // TODO GH#4954 During the input refactor we may want to add a "priority" input list diff --git a/src/host/readDataDirect.cpp b/src/host/readDataDirect.cpp index 9f8edc3daad..778b585c4cd 100644 --- a/src/host/readDataDirect.cpp +++ b/src/host/readDataDirect.cpp @@ -47,7 +47,7 @@ DirectReadData::DirectReadData(_In_ InputBuffer* const pInputBuffer, // - pControlKeyState - For certain types of reads, this specifies // which modifier keys were held. // - pOutputData - a pointer to a -// std::deque> that is used to the read +// InputEventQueue that is used to the read // input events back to the server // Return Value: // - true if the wait is done and result buffer/status code can be sent back to the client. @@ -69,8 +69,6 @@ try *pControlKeyState = 0; *pNumBytes = 0; - std::deque> readEvents; - // If ctrl-c or ctrl-break was seen, ignore it. if (WI_IsAnyFlagSet(TerminationReason, (WaitTerminationReason::CtrlC | WaitTerminationReason::CtrlBreak))) { @@ -119,7 +117,7 @@ try } // move events to pOutputData - const auto pOutputDeque = static_cast>* const>(pOutputData); + const auto pOutputDeque = static_cast(pOutputData); *pNumBytes = _outEvents.size() * sizeof(INPUT_RECORD); *pOutputDeque = std::move(_outEvents); diff --git a/src/host/readDataDirect.hpp b/src/host/readDataDirect.hpp index 399b8a99ca8..44768c09d10 100644 --- a/src/host/readDataDirect.hpp +++ b/src/host/readDataDirect.hpp @@ -47,5 +47,5 @@ class DirectReadData final : public ReadData private: const size_t _eventReadCount; - std::deque> _outEvents; + InputEventQueue _outEvents; }; diff --git a/src/host/stream.cpp b/src/host/stream.cpp index 9ecccd2fadb..6600f804c45 100644 --- a/src/host/stream.cpp +++ b/src/host/stream.cpp @@ -16,8 +16,6 @@ #include "../interactivity/inc/ServiceLocator.hpp" -#pragma hdrstop - using Microsoft::Console::Interactivity::ServiceLocator; // Routine Description: @@ -56,57 +54,50 @@ using Microsoft::Console::Interactivity::ServiceLocator; *pdwKeyState = 0; } - NTSTATUS Status; for (;;) { - std::unique_ptr inputEvent; - Status = pInputBuffer->Read(inputEvent, - false, // peek - Wait, - true, // unicode - true); // stream - + InputEventQueue events; + const auto Status = pInputBuffer->Read(events, 1, false, Wait, true, true); if (FAILED_NTSTATUS(Status)) { return Status; } - else if (inputEvent.get() == nullptr) + if (events.empty()) { - FAIL_FAST_IF(Wait); + assert(!Wait); return STATUS_UNSUCCESSFUL; } - if (inputEvent->EventType() == InputEventType::KeyEvent) + const auto& Event = events[0]; + if (Event.EventType == KEY_EVENT) { - auto keyEvent = std::unique_ptr(static_cast(inputEvent.release())); - auto commandLineEditKey = false; if (pCommandLineEditingKeys) { - commandLineEditKey = keyEvent->IsCommandLineEditingKey(); + commandLineEditKey = IsCommandLineEditingKey(Event.Event.KeyEvent); } else if (pPopupKeys) { - commandLineEditKey = keyEvent->IsPopupKey(); + commandLineEditKey = IsCommandLinePopupKey(Event.Event.KeyEvent); } if (pdwKeyState) { - *pdwKeyState = keyEvent->GetActiveModifierKeys(); + *pdwKeyState = Event.Event.KeyEvent.dwControlKeyState; } - if (keyEvent->GetCharData() != 0 && !commandLineEditKey) + if (Event.Event.KeyEvent.uChar.UnicodeChar != 0 && !commandLineEditKey) { // chars that are generated using alt + numpad - if (!keyEvent->IsKeyDown() && keyEvent->GetVirtualKeyCode() == VK_MENU) + if (!Event.Event.KeyEvent.bKeyDown && Event.Event.KeyEvent.wVirtualKeyCode == VK_MENU) { - if (keyEvent->IsAltNumpadSet()) + if (WI_IsFlagSet(Event.Event.KeyEvent.dwControlKeyState, ALTNUMPAD_BIT)) { - if (HIBYTE(keyEvent->GetCharData())) + if (HIBYTE(Event.Event.KeyEvent.uChar.UnicodeChar)) { - char chT[2] = { - static_cast(HIBYTE(keyEvent->GetCharData())), - static_cast(LOBYTE(keyEvent->GetCharData())), + const char chT[2] = { + static_cast(HIBYTE(Event.Event.KeyEvent.uChar.UnicodeChar)), + static_cast(LOBYTE(Event.Event.KeyEvent.uChar.UnicodeChar)), }; *pwchOut = CharToWchar(chT, 2); } @@ -115,64 +106,54 @@ using Microsoft::Console::Interactivity::ServiceLocator; // Because USER doesn't know our codepage, // it gives us the raw OEM char and we // convert it to a Unicode character. - char chT = LOBYTE(keyEvent->GetCharData()); + char chT = LOBYTE(Event.Event.KeyEvent.uChar.UnicodeChar); *pwchOut = CharToWchar(&chT, 1); } } else { - *pwchOut = keyEvent->GetCharData(); + *pwchOut = Event.Event.KeyEvent.uChar.UnicodeChar; } return STATUS_SUCCESS; } + // Ignore Escape and Newline chars - else if (keyEvent->IsKeyDown() && - (WI_IsFlagSet(pInputBuffer->InputMode, ENABLE_VIRTUAL_TERMINAL_INPUT) || - (keyEvent->GetVirtualKeyCode() != VK_ESCAPE && - keyEvent->GetCharData() != UNICODE_LINEFEED))) + if (Event.Event.KeyEvent.bKeyDown && + (WI_IsFlagSet(pInputBuffer->InputMode, ENABLE_VIRTUAL_TERMINAL_INPUT) || + (Event.Event.KeyEvent.wVirtualKeyCode != VK_ESCAPE && + Event.Event.KeyEvent.uChar.UnicodeChar != UNICODE_LINEFEED))) { - *pwchOut = keyEvent->GetCharData(); + *pwchOut = Event.Event.KeyEvent.uChar.UnicodeChar; return STATUS_SUCCESS; } } - if (keyEvent->IsKeyDown()) + if (Event.Event.KeyEvent.bKeyDown) { if (pCommandLineEditingKeys && commandLineEditKey) { *pCommandLineEditingKeys = true; - *pwchOut = static_cast(keyEvent->GetVirtualKeyCode()); + *pwchOut = static_cast(Event.Event.KeyEvent.wVirtualKeyCode); return STATUS_SUCCESS; } - else if (pPopupKeys && commandLineEditKey) + + if (pPopupKeys && commandLineEditKey) { *pPopupKeys = true; - *pwchOut = static_cast(keyEvent->GetVirtualKeyCode()); + *pwchOut = static_cast(Event.Event.KeyEvent.wVirtualKeyCode); return STATUS_SUCCESS; } - else - { - const auto zeroVkeyData = OneCoreSafeVkKeyScanW(0); - const auto zeroVKey = LOBYTE(zeroVkeyData); - const auto zeroControlKeyState = HIBYTE(zeroVkeyData); - try - { - // Convert real Windows NT modifier bit into bizarre Console bits - auto consoleModKeyState = FromVkKeyScan(zeroControlKeyState); + const auto zeroKey = OneCoreSafeVkKeyScanW(0); - if (zeroVKey == keyEvent->GetVirtualKeyCode() && - keyEvent->DoActiveModifierKeysMatch(consoleModKeyState)) - { - // This really is the character 0x0000 - *pwchOut = keyEvent->GetCharData(); - return STATUS_SUCCESS; - } - } - catch (...) - { - LOG_HR(wil::ResultFromCaughtException()); - } + if (LOBYTE(zeroKey) == Event.Event.KeyEvent.wVirtualKeyCode && + WI_IsAnyFlagSet(Event.Event.KeyEvent.dwControlKeyState, ALT_PRESSED) == WI_IsFlagSet(zeroKey, 0x400) && + WI_IsAnyFlagSet(Event.Event.KeyEvent.dwControlKeyState, CTRL_PRESSED) == WI_IsFlagSet(zeroKey, 0x200) && + WI_IsAnyFlagSet(Event.Event.KeyEvent.dwControlKeyState, SHIFT_PRESSED) == WI_IsFlagSet(zeroKey, 0x100)) + { + // This really is the character 0x0000 + *pwchOut = Event.Event.KeyEvent.uChar.UnicodeChar; + return STATUS_SUCCESS; } } } diff --git a/src/host/ut_host/ClipboardTests.cpp b/src/host/ut_host/ClipboardTests.cpp index 3c32b6efc85..2f9e03c88bb 100644 --- a/src/host/ut_host/ClipboardTests.cpp +++ b/src/host/ut_host/ClipboardTests.cpp @@ -153,107 +153,46 @@ class ClipboardTests VERIFY_IS_NOT_NULL(ptr); } - TEST_METHOD(CanConvertTextToInputEvents) + TEST_METHOD(CanConvertText) { - std::wstring wstr = L"hello world"; - auto events = Clipboard::Instance().TextToKeyEvents(wstr.c_str(), - wstr.size()); - VERIFY_ARE_EQUAL(wstr.size() * 2, events.size()); - for (auto wch : wstr) + static constexpr std::wstring_view input{ L"HeLlO WoRlD" }; + const auto events = Clipboard::Instance().TextToKeyEvents(input.data(), input.size()); + + const auto shiftSC = static_cast(OneCoreSafeMapVirtualKeyW(VK_SHIFT, MAPVK_VK_TO_VSC)); + const auto shiftDown = SynthesizeKeyEvent(true, 1, VK_SHIFT, shiftSC, 0, SHIFT_PRESSED); + const auto shiftUp = SynthesizeKeyEvent(false, 1, VK_SHIFT, shiftSC, 0, 0); + + InputEventQueue expectedEvents; + + for (auto wch : input) { - std::deque keydownPattern{ true, false }; - for (auto isKeyDown : keydownPattern) + const auto state = OneCoreSafeVkKeyScanW(wch); + const auto vk = LOBYTE(state); + const auto sc = static_cast(OneCoreSafeMapVirtualKeyW(vk, MAPVK_VK_TO_VSC)); + const auto shift = WI_IsFlagSet(state, 0x100); + auto event = SynthesizeKeyEvent(true, 1, vk, sc, wch, shift ? SHIFT_PRESSED : 0); + + if (shift) { - VERIFY_ARE_EQUAL(InputEventType::KeyEvent, events.front()->EventType()); - std::unique_ptr keyEvent; - keyEvent.reset(static_cast(events.front().release())); - events.pop_front(); - - const auto keyState = OneCoreSafeVkKeyScanW(wch); - VERIFY_ARE_NOT_EQUAL(-1, keyState); - const auto virtualScanCode = static_cast(OneCoreSafeMapVirtualKeyW(LOBYTE(keyState), MAPVK_VK_TO_VSC)); - - VERIFY_ARE_EQUAL(wch, keyEvent->GetCharData()); - VERIFY_ARE_EQUAL(isKeyDown, keyEvent->IsKeyDown()); - VERIFY_ARE_EQUAL(1, keyEvent->GetRepeatCount()); - VERIFY_ARE_EQUAL(static_cast(0), keyEvent->GetActiveModifierKeys()); - VERIFY_ARE_EQUAL(virtualScanCode, keyEvent->GetVirtualScanCode()); - VERIFY_ARE_EQUAL(LOBYTE(keyState), keyEvent->GetVirtualKeyCode()); + expectedEvents.push_back(shiftDown); } - } - } - TEST_METHOD(CanConvertUppercaseText) - { - std::wstring wstr = L"HeLlO WoRlD"; - size_t uppercaseCount = 0; - for (auto wch : wstr) - { - std::isupper(wch) ? ++uppercaseCount : 0; - } - auto events = Clipboard::Instance().TextToKeyEvents(wstr.c_str(), - wstr.size()); + expectedEvents.push_back(event); + event.Event.KeyEvent.bKeyDown = FALSE; + expectedEvents.push_back(event); - VERIFY_ARE_EQUAL((wstr.size() + uppercaseCount) * 2, events.size()); - for (auto wch : wstr) - { - std::deque keydownPattern{ true, false }; - for (auto isKeyDown : keydownPattern) + if (shift) { - Log::Comment(NoThrowString().Format(L"testing char: %C; keydown: %d", wch, isKeyDown)); - - VERIFY_ARE_EQUAL(InputEventType::KeyEvent, events.front()->EventType()); - std::unique_ptr keyEvent; - keyEvent.reset(static_cast(events.front().release())); - events.pop_front(); - - const short keyScanError = -1; - const auto keyState = OneCoreSafeVkKeyScanW(wch); - VERIFY_ARE_NOT_EQUAL(keyScanError, keyState); - const auto virtualScanCode = static_cast(OneCoreSafeMapVirtualKeyW(LOBYTE(keyState), MAPVK_VK_TO_VSC)); - - if (std::isupper(wch)) - { - // uppercase letters have shift key events - // surrounding them, making two events per letter - // (and another two for the keyup) - VERIFY_IS_FALSE(events.empty()); - - VERIFY_ARE_EQUAL(InputEventType::KeyEvent, events.front()->EventType()); - std::unique_ptr keyEvent2; - keyEvent2.reset(static_cast(events.front().release())); - events.pop_front(); - - const auto keyState2 = OneCoreSafeVkKeyScanW(wch); - VERIFY_ARE_NOT_EQUAL(keyScanError, keyState2); - const auto virtualScanCode2 = static_cast(OneCoreSafeMapVirtualKeyW(LOBYTE(keyState2), MAPVK_VK_TO_VSC)); - - if (isKeyDown) - { - // shift then letter - const KeyEvent shiftDownEvent{ TRUE, 1, VK_SHIFT, leftShiftScanCode, L'\0', SHIFT_PRESSED }; - VERIFY_ARE_EQUAL(shiftDownEvent, *keyEvent); - - const KeyEvent expectedKeyEvent{ TRUE, 1, LOBYTE(keyState2), virtualScanCode2, wch, SHIFT_PRESSED }; - VERIFY_ARE_EQUAL(expectedKeyEvent, *keyEvent2); - } - else - { - // letter then shift - const KeyEvent expectedKeyEvent{ FALSE, 1, LOBYTE(keyState), virtualScanCode, wch, SHIFT_PRESSED }; - VERIFY_ARE_EQUAL(expectedKeyEvent, *keyEvent); - - const KeyEvent shiftUpEvent{ FALSE, 1, VK_SHIFT, leftShiftScanCode, L'\0', 0 }; - VERIFY_ARE_EQUAL(shiftUpEvent, *keyEvent2); - } - } - else - { - const KeyEvent expectedKeyEvent{ !!isKeyDown, 1, LOBYTE(keyState), virtualScanCode, wch, 0 }; - VERIFY_ARE_EQUAL(expectedKeyEvent, *keyEvent); - } + expectedEvents.push_back(shiftUp); } } + + VERIFY_ARE_EQUAL(expectedEvents.size(), events.size()); + + for (size_t i = 0; i < events.size(); ++i) + { + VERIFY_ARE_EQUAL(expectedEvents[i], events[i]); + } } TEST_METHOD(CanConvertCharsRequiringAltGr) @@ -274,23 +213,22 @@ class ClipboardTests auto events = Clipboard::Instance().TextToKeyEvents(wstr.c_str(), wstr.size()); - std::deque expectedEvents; + InputEventQueue expectedEvents; // should be converted to: // 1. AltGr keydown // 2. € keydown // 3. € keyup // 4. AltGr keyup - expectedEvents.push_back({ TRUE, 1, VK_MENU, altScanCode, L'\0', (ENHANCED_KEY | LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED) }); - expectedEvents.push_back({ TRUE, 1, virtualKeyCode, virtualScanCode, wstr[0], (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED) }); - expectedEvents.push_back({ FALSE, 1, virtualKeyCode, virtualScanCode, wstr[0], (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED) }); - expectedEvents.push_back({ FALSE, 1, VK_MENU, altScanCode, L'\0', ENHANCED_KEY }); + expectedEvents.push_back(SynthesizeKeyEvent(true, 1, VK_MENU, altScanCode, L'\0', (ENHANCED_KEY | LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED))); + expectedEvents.push_back(SynthesizeKeyEvent(true, 1, virtualKeyCode, virtualScanCode, wstr[0], (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED))); + expectedEvents.push_back(SynthesizeKeyEvent(false, 1, virtualKeyCode, virtualScanCode, wstr[0], (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED))); + expectedEvents.push_back(SynthesizeKeyEvent(false, 1, VK_MENU, altScanCode, L'\0', ENHANCED_KEY)); VERIFY_ARE_EQUAL(expectedEvents.size(), events.size()); for (size_t i = 0; i < events.size(); ++i) { - const auto currentKeyEvent = *reinterpret_cast(events[i].get()); - VERIFY_ARE_EQUAL(expectedEvents[i], currentKeyEvent, NoThrowString().Format(L"i == %d", i)); + VERIFY_ARE_EQUAL(expectedEvents[i], events[i]); } } @@ -302,7 +240,7 @@ class ClipboardTests auto events = Clipboard::Instance().TextToKeyEvents(wstr.c_str(), wstr.size()); - std::deque expectedEvents; + InputEventQueue expectedEvents; if constexpr (Feature_UseNumpadEventsForClipboardInput::IsEnabled()) { // Inside Windows, where numpad events are enabled, this generated numpad events. @@ -313,26 +251,25 @@ class ClipboardTests // 4. 2nd numpad keydown // 5. 2nd numpad keyup // 6. left alt keyup - expectedEvents.push_back({ TRUE, 1, VK_MENU, altScanCode, L'\0', LEFT_ALT_PRESSED }); - expectedEvents.push_back({ TRUE, 1, 0x66, 0x4D, L'\0', LEFT_ALT_PRESSED }); - expectedEvents.push_back({ FALSE, 1, 0x66, 0x4D, L'\0', LEFT_ALT_PRESSED }); - expectedEvents.push_back({ TRUE, 1, 0x63, 0x51, L'\0', LEFT_ALT_PRESSED }); - expectedEvents.push_back({ FALSE, 1, 0x63, 0x51, L'\0', LEFT_ALT_PRESSED }); - expectedEvents.push_back({ FALSE, 1, VK_MENU, altScanCode, wstr[0], 0 }); + expectedEvents.push_back(SynthesizeKeyEvent(true, 1, VK_MENU, altScanCode, L'\0', LEFT_ALT_PRESSED)); + expectedEvents.push_back(SynthesizeKeyEvent(true, 1, 0x66, 0x4D, L'\0', LEFT_ALT_PRESSED)); + expectedEvents.push_back(SynthesizeKeyEvent(false, 1, 0x66, 0x4D, L'\0', LEFT_ALT_PRESSED)); + expectedEvents.push_back(SynthesizeKeyEvent(true, 1, 0x63, 0x51, L'\0', LEFT_ALT_PRESSED)); + expectedEvents.push_back(SynthesizeKeyEvent(false, 1, 0x63, 0x51, L'\0', LEFT_ALT_PRESSED)); + expectedEvents.push_back(SynthesizeKeyEvent(false, 1, VK_MENU, altScanCode, wstr[0], 0)); } else { // Outside Windows, without numpad events, we just emit the key with a nonzero UnicodeChar - expectedEvents.push_back({ TRUE, 1, 0, 0, wstr[0], 0 }); - expectedEvents.push_back({ FALSE, 1, 0, 0, wstr[0], 0 }); + expectedEvents.push_back(SynthesizeKeyEvent(true, 1, 0, 0, wstr[0], 0)); + expectedEvents.push_back(SynthesizeKeyEvent(false, 1, 0, 0, wstr[0], 0)); } VERIFY_ARE_EQUAL(expectedEvents.size(), events.size()); for (size_t i = 0; i < events.size(); ++i) { - const auto currentKeyEvent = *reinterpret_cast(events[i].get()); - VERIFY_ARE_EQUAL(expectedEvents[i], currentKeyEvent, NoThrowString().Format(L"i == %d", i)); + VERIFY_ARE_EQUAL(expectedEvents[i], events[i]); } } }; diff --git a/src/host/ut_host/Host.UnitTests.vcxproj b/src/host/ut_host/Host.UnitTests.vcxproj index 31f0f8e5a95..36afffca49c 100644 --- a/src/host/ut_host/Host.UnitTests.vcxproj +++ b/src/host/ut_host/Host.UnitTests.vcxproj @@ -39,7 +39,6 @@ - Create diff --git a/src/host/ut_host/Host.UnitTests.vcxproj.filters b/src/host/ut_host/Host.UnitTests.vcxproj.filters index 2dcabcef1d4..d7045f877f0 100644 --- a/src/host/ut_host/Host.UnitTests.vcxproj.filters +++ b/src/host/ut_host/Host.UnitTests.vcxproj.filters @@ -63,9 +63,6 @@ Source Files - - Source Files - Source Files diff --git a/src/host/ut_host/InputBufferTests.cpp b/src/host/ut_host/InputBufferTests.cpp index 6a3c859ca8c..f81d304bda7 100644 --- a/src/host/ut_host/InputBufferTests.cpp +++ b/src/host/ut_host/InputBufferTests.cpp @@ -61,12 +61,12 @@ class InputBufferTests { InputBuffer inputBuffer; auto record = MakeKeyEvent(true, 1, L'a', 0, L'a', 0); - VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(record)), 0u); + VERIFY_IS_GREATER_THAN(inputBuffer.Write(record), 0u); VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 1u); // add another event, check again INPUT_RECORD record2; record2.EventType = MENU_EVENT; - VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(record2)), 0u); + VERIFY_IS_GREATER_THAN(inputBuffer.Write(record2), 0u); VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 2u); } @@ -77,8 +77,8 @@ class InputBufferTests { INPUT_RECORD record; record.EventType = MENU_EVENT; - VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(record)), 0u); - VERIFY_ARE_EQUAL(record, inputBuffer._storage.back()->ToInputRecord()); + VERIFY_IS_GREATER_THAN(inputBuffer.Write(record), 0u); + VERIFY_ARE_EQUAL(record, inputBuffer._storage.back()); } VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT); } @@ -86,19 +86,19 @@ class InputBufferTests TEST_METHOD(CanBulkInsertIntoInputBuffer) { InputBuffer inputBuffer; - std::deque> events; + InputEventQueue events; INPUT_RECORD record; record.EventType = MENU_EVENT; for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i) { - events.push_back(IInputEvent::Create(record)); + events.push_back(record); } VERIFY_IS_GREATER_THAN(inputBuffer.Write(events), 0u); VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT); // verify that the events are the same in storage for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i) { - VERIFY_ARE_EQUAL(inputBuffer._storage[i]->ToInputRecord(), record); + VERIFY_ARE_EQUAL(inputBuffer._storage[i], record); } } @@ -115,23 +115,22 @@ class InputBufferTests { mouseRecord.Event.MouseEvent.dwMousePosition.X = static_cast(i + 1); mouseRecord.Event.MouseEvent.dwMousePosition.Y = static_cast(i + 1) * 2; - VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(mouseRecord)), 0u); + VERIFY_IS_GREATER_THAN(inputBuffer.Write(mouseRecord), 0u); } // check that they coalesced VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 1u); // check that the mouse position is being updated correctly - const IInputEvent* const pOutEvent = inputBuffer._storage.front().get(); - const auto pMouseEvent = static_cast(pOutEvent); - VERIFY_ARE_EQUAL(pMouseEvent->GetPosition().x, static_cast(RECORD_INSERT_COUNT)); - VERIFY_ARE_EQUAL(pMouseEvent->GetPosition().y, static_cast(RECORD_INSERT_COUNT * 2)); + const auto& pMouseEvent = inputBuffer._storage.front().Event.MouseEvent; + VERIFY_ARE_EQUAL(pMouseEvent.dwMousePosition.X, static_cast(RECORD_INSERT_COUNT)); + VERIFY_ARE_EQUAL(pMouseEvent.dwMousePosition.Y, static_cast(RECORD_INSERT_COUNT * 2)); // add a key event and another mouse event to make sure that // an event between two mouse events stopped the coalescing. INPUT_RECORD keyRecord; keyRecord.EventType = KEY_EVENT; - VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(keyRecord)), 0u); - VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(mouseRecord)), 0u); + VERIFY_IS_GREATER_THAN(inputBuffer.Write(keyRecord), 0u); + VERIFY_IS_GREATER_THAN(inputBuffer.Write(mouseRecord), 0u); // verify VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 3u); @@ -143,29 +142,25 @@ class InputBufferTests InputBuffer inputBuffer; INPUT_RECORD mouseRecords[RECORD_INSERT_COUNT]; - std::deque> events; + InputEventQueue events; for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i) { mouseRecords[i].EventType = MOUSE_EVENT; mouseRecords[i].Event.MouseEvent.dwEventFlags = MOUSE_MOVED; - events.push_back(IInputEvent::Create(mouseRecords[i])); + events.push_back(mouseRecords[i]); } - // add an extra event - events.push_front(IInputEvent::Create(mouseRecords[0])); - inputBuffer.Flush(); // send one mouse event to possibly coalesce into later - VERIFY_IS_GREATER_THAN(inputBuffer.Write(std::move(events.front())), 0u); - events.pop_front(); + VERIFY_IS_GREATER_THAN(inputBuffer.Write(mouseRecords[0]), 0u); // write the others in bulk VERIFY_IS_GREATER_THAN(inputBuffer.Write(events), 0u); // no events should have been coalesced VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT + 1); // check that the events stored match those inserted - VERIFY_ARE_EQUAL(inputBuffer._storage.front()->ToInputRecord(), mouseRecords[0]); + VERIFY_ARE_EQUAL(inputBuffer._storage.front(), mouseRecords[0]); for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i) { - VERIFY_ARE_EQUAL(inputBuffer._storage[i + 1]->ToInputRecord(), mouseRecords[i]); + VERIFY_ARE_EQUAL(inputBuffer._storage[i + 1], mouseRecords[i]); } } @@ -180,7 +175,7 @@ class InputBufferTests inputBuffer.Flush(); for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i) { - VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(record)), 0u); + VERIFY_IS_GREATER_THAN(inputBuffer.Write(record), 0u); } // all events should have been coalesced into one @@ -188,16 +183,12 @@ class InputBufferTests // the single event should have a repeat count for each // coalesced event - std::unique_ptr outEvent; - VERIFY_NT_SUCCESS(inputBuffer.Read(outEvent, - true, - false, - false, - false)); + InputEventQueue outEvents; + VERIFY_NT_SUCCESS(inputBuffer.Read(outEvents, 1, true, false, false, false)); - VERIFY_ARE_NOT_EQUAL(nullptr, outEvent.get()); - const auto pKeyEvent = static_cast(outEvent.get()); - VERIFY_ARE_EQUAL(pKeyEvent->GetRepeatCount(), RECORD_INSERT_COUNT); + VERIFY_IS_FALSE(outEvents.empty()); + const auto& pKeyEvent = outEvents.front().Event.KeyEvent; + VERIFY_ARE_EQUAL(pKeyEvent.wRepeatCount, RECORD_INSERT_COUNT); } TEST_METHOD(InputBufferDoesNotCoalesceBulkKeyEvents) @@ -206,25 +197,25 @@ class InputBufferTests InputBuffer inputBuffer; INPUT_RECORD keyRecords[RECORD_INSERT_COUNT]; - std::deque> events; + InputEventQueue events; for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i) { keyRecords[i] = MakeKeyEvent(true, 1, L'a', 0, L'a', 0); - events.push_back(IInputEvent::Create(keyRecords[i])); + events.push_back(keyRecords[i]); } inputBuffer.Flush(); // send one key event to possibly coalesce into later - VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(keyRecords[0])), 0u); + VERIFY_IS_GREATER_THAN(inputBuffer.Write(keyRecords[0]), 0u); // write the others in bulk VERIFY_IS_GREATER_THAN(inputBuffer.Write(events), 0u); // no events should have been coalesced VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT + 1); // check that the events stored match those inserted - VERIFY_ARE_EQUAL(inputBuffer._storage.front()->ToInputRecord(), keyRecords[0]); + VERIFY_ARE_EQUAL(inputBuffer._storage.front(), keyRecords[0]); for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i) { - VERIFY_ARE_EQUAL(inputBuffer._storage[i + 1]->ToInputRecord(), keyRecords[i]); + VERIFY_ARE_EQUAL(inputBuffer._storage[i + 1], keyRecords[i]); } } @@ -238,8 +229,8 @@ class InputBufferTests inputBuffer.Flush(); for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i) { - VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(record)), 0u); - VERIFY_ARE_EQUAL(inputBuffer._storage.back()->ToInputRecord(), record); + VERIFY_IS_GREATER_THAN(inputBuffer.Write(record), 0u); + VERIFY_ARE_EQUAL(inputBuffer._storage.back(), record); } // The events shouldn't be coalesced @@ -249,14 +240,14 @@ class InputBufferTests TEST_METHOD(CanFlushAllOutput) { InputBuffer inputBuffer; - std::deque> events; + InputEventQueue events; // put some events in the buffer so we can remove them INPUT_RECORD record; record.EventType = MENU_EVENT; for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i) { - events.push_back(IInputEvent::Create(record)); + events.push_back(record); } VERIFY_IS_GREATER_THAN(inputBuffer.Write(events), 0u); VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT); @@ -270,13 +261,13 @@ class InputBufferTests { InputBuffer inputBuffer; INPUT_RECORD records[RECORD_INSERT_COUNT] = { 0 }; - std::deque> inEvents; + InputEventQueue inEvents; // create alternating mouse and key events for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i) { records[i].EventType = (i % 2 == 0) ? MENU_EVENT : KEY_EVENT; - inEvents.push_back(IInputEvent::Create(records[i])); + inEvents.push_back(records[i]); } VERIFY_IS_GREATER_THAN(inputBuffer.Write(inEvents), 0u); VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT); @@ -286,7 +277,7 @@ class InputBufferTests VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT / 2); // make sure that the non key events were the ones removed - std::deque> outEvents; + InputEventQueue outEvents; auto amountToRead = RECORD_INSERT_COUNT / 2; VERIFY_NT_SUCCESS(inputBuffer.Read(outEvents, amountToRead, @@ -298,7 +289,7 @@ class InputBufferTests for (size_t i = 0; i < outEvents.size(); ++i) { - VERIFY_ARE_EQUAL(outEvents[i]->EventType(), InputEventType::KeyEvent); + VERIFY_ARE_EQUAL(outEvents[i].EventType, KEY_EVENT); } } @@ -306,18 +297,18 @@ class InputBufferTests { InputBuffer inputBuffer; INPUT_RECORD records[RECORD_INSERT_COUNT]; - std::deque> inEvents; + InputEventQueue inEvents; // write some input records for (unsigned int i = 0; i < RECORD_INSERT_COUNT; ++i) { records[i] = MakeKeyEvent(TRUE, 1, static_cast(L'A' + i), 0, static_cast(L'A' + i), 0); - inEvents.push_back(IInputEvent::Create(records[i])); + inEvents.push_back(records[i]); } VERIFY_IS_GREATER_THAN(inputBuffer.Write(inEvents), 0u); // read them back out - std::deque> outEvents; + InputEventQueue outEvents; auto amountToRead = RECORD_INSERT_COUNT; VERIFY_NT_SUCCESS(inputBuffer.Read(outEvents, amountToRead, @@ -329,7 +320,7 @@ class InputBufferTests VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 0u); for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i) { - VERIFY_ARE_EQUAL(records[i], outEvents[i]->ToInputRecord()); + VERIFY_ARE_EQUAL(records[i], outEvents[i]); } } @@ -339,16 +330,16 @@ class InputBufferTests // add some events so that we have something to peek at INPUT_RECORD records[RECORD_INSERT_COUNT]; - std::deque> inEvents; + InputEventQueue inEvents; for (unsigned int i = 0; i < RECORD_INSERT_COUNT; ++i) { records[i] = MakeKeyEvent(TRUE, 1, static_cast(L'A' + i), 0, static_cast(L'A' + i), 0); - inEvents.push_back(IInputEvent::Create(records[i])); + inEvents.push_back(records[i]); } VERIFY_IS_GREATER_THAN(inputBuffer.Write(inEvents), 0u); // peek at events - std::deque> outEvents; + InputEventQueue outEvents; auto amountToRead = RECORD_INSERT_COUNT; VERIFY_NT_SUCCESS(inputBuffer.Read(outEvents, amountToRead, @@ -361,7 +352,7 @@ class InputBufferTests VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT); for (unsigned int i = 0; i < RECORD_INSERT_COUNT; ++i) { - VERIFY_ARE_EQUAL(records[i], outEvents[i]->ToInputRecord()); + VERIFY_ARE_EQUAL(records[i], outEvents[i]); } } @@ -373,11 +364,11 @@ class InputBufferTests // add some events so that we have something to stick in front of INPUT_RECORD records[RECORD_INSERT_COUNT]; - std::deque> inEvents; + InputEventQueue inEvents; for (unsigned int i = 0; i < RECORD_INSERT_COUNT; ++i) { records[i] = MakeKeyEvent(TRUE, 1, static_cast(L'A' + i), 0, static_cast(L'A' + i), 0); - inEvents.push_back(IInputEvent::Create(records[i])); + inEvents.push_back(records[i]); } VERIFY_IS_GREATER_THAN(inputBuffer.Write(inEvents), 0u); @@ -385,7 +376,7 @@ class InputBufferTests waitEvent.SetEvent(); // read one record, hInputEvent should still be signaled - std::deque> outEvents; + InputEventQueue outEvents; VERIFY_NT_SUCCESS(inputBuffer.Read(outEvents, 1, false, @@ -435,17 +426,11 @@ class InputBufferTests outRecordsExpected[3] = MakeKeyEvent(TRUE, 1, 0x3042, 0, 0xa0, 0); outRecordsExpected[4].EventType = MOUSE_EVENT; - std::deque> inEvents; - for (size_t i = 0; i < inRecords.size(); ++i) - { - inEvents.push_back(IInputEvent::Create(inRecords[i])); - } - inputBuffer.Flush(); - VERIFY_IS_GREATER_THAN(inputBuffer.Write(inEvents), 0u); + VERIFY_IS_GREATER_THAN(inputBuffer.Write(inRecords), 0u); // read them out non-unicode style and compare - std::deque> outEvents; + InputEventQueue outEvents; VERIFY_NT_SUCCESS(inputBuffer.Read(outEvents, outRecordsExpected.size(), false, @@ -455,7 +440,7 @@ class InputBufferTests VERIFY_ARE_EQUAL(outEvents.size(), outRecordsExpected.size()); for (size_t i = 0; i < outEvents.size(); ++i) { - VERIFY_ARE_EQUAL(outEvents[i]->ToInputRecord(), outRecordsExpected[i]); + VERIFY_ARE_EQUAL(outEvents[i], outRecordsExpected[i]); } } @@ -465,11 +450,11 @@ class InputBufferTests // add some events so that we have something to stick in front of INPUT_RECORD records[RECORD_INSERT_COUNT]; - std::deque> inEvents; + InputEventQueue inEvents; for (unsigned int i = 0; i < RECORD_INSERT_COUNT; ++i) { records[i] = MakeKeyEvent(TRUE, 1, static_cast(L'A' + i), 0, static_cast(L'A' + i), 0); - inEvents.push_back(IInputEvent::Create(records[i])); + inEvents.push_back(records[i]); } VERIFY_IS_GREATER_THAN(inputBuffer.Write(inEvents), 0u); @@ -479,13 +464,13 @@ class InputBufferTests for (unsigned int i = 0; i < RECORD_INSERT_COUNT; ++i) { prependRecords[i] = MakeKeyEvent(TRUE, 1, static_cast(L'a' + i), 0, static_cast(L'a' + i), 0); - inEvents.push_back(IInputEvent::Create(prependRecords[i])); + inEvents.push_back(prependRecords[i]); } auto eventsWritten = inputBuffer.Prepend(inEvents); VERIFY_ARE_EQUAL(eventsWritten, RECORD_INSERT_COUNT); // grab the first set of events and ensure they match prependRecords - std::deque> outEvents; + InputEventQueue outEvents; auto amountToRead = RECORD_INSERT_COUNT; VERIFY_NT_SUCCESS(inputBuffer.Read(outEvents, amountToRead, @@ -497,7 +482,7 @@ class InputBufferTests VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT); for (unsigned int i = 0; i < RECORD_INSERT_COUNT; ++i) { - VERIFY_ARE_EQUAL(prependRecords[i], outEvents[i]->ToInputRecord()); + VERIFY_ARE_EQUAL(prependRecords[i], outEvents[i]); } outEvents.clear(); @@ -512,7 +497,7 @@ class InputBufferTests VERIFY_ARE_EQUAL(amountToRead, outEvents.size()); for (unsigned int i = 0; i < RECORD_INSERT_COUNT; ++i) { - VERIFY_ARE_EQUAL(records[i], outEvents[i]->ToInputRecord()); + VERIFY_ARE_EQUAL(records[i], outEvents[i]); } } @@ -524,7 +509,7 @@ class InputBufferTests // change the buffer's state a bit INPUT_RECORD record; record.EventType = MENU_EVENT; - VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(record)), 0u); + VERIFY_IS_GREATER_THAN(inputBuffer.Write(record), 0u); VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 1u); inputBuffer.InputMode = 0x0; inputBuffer.ReinitializeInputBuffer(); @@ -544,7 +529,7 @@ class InputBufferTests VERIFY_IS_FALSE(WI_IsFlagSet(gci.Flags, CONSOLE_OUTPUT_SUSPENDED)); VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 0u); - VERIFY_ARE_EQUAL(inputBuffer.Write(IInputEvent::Create(pauseRecord)), 0u); + VERIFY_ARE_EQUAL(inputBuffer.Write(pauseRecord), 0u); // we should now be paused and the input record should be discarded VERIFY_IS_TRUE(WI_IsFlagSet(gci.Flags, CONSOLE_OUTPUT_SUSPENDED)); @@ -552,7 +537,7 @@ class InputBufferTests // the next key press should unpause us but be discarded auto unpauseRecord = MakeKeyEvent(true, 1, L'a', 0, L'a', 0); - VERIFY_ARE_EQUAL(inputBuffer.Write(IInputEvent::Create(unpauseRecord)), 0u); + VERIFY_ARE_EQUAL(inputBuffer.Write(unpauseRecord), 0u); VERIFY_IS_FALSE(WI_IsFlagSet(gci.Flags, CONSOLE_OUTPUT_SUSPENDED)); VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 0u); @@ -569,7 +554,7 @@ class InputBufferTests VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 0u); // pause the screen - VERIFY_ARE_EQUAL(inputBuffer.Write(IInputEvent::Create(pauseRecord)), 0u); + VERIFY_ARE_EQUAL(inputBuffer.Write(pauseRecord), 0u); // we should now be paused and the input record should be discarded VERIFY_IS_TRUE(WI_IsFlagSet(gci.Flags, CONSOLE_OUTPUT_SUSPENDED)); @@ -578,12 +563,12 @@ class InputBufferTests // sending a system key event should not stop the pause and // the record should be stored in the input buffer auto systemRecord = MakeKeyEvent(true, 1, VK_CONTROL, 0, 0, 0); - VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(systemRecord)), 0u); + VERIFY_IS_GREATER_THAN(inputBuffer.Write(systemRecord), 0u); VERIFY_IS_TRUE(WI_IsFlagSet(gci.Flags, CONSOLE_OUTPUT_SUSPENDED)); VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 1u); - std::deque> outEvents; + InputEventQueue outEvents; size_t amountToRead = 2; VERIFY_NT_SUCCESS(inputBuffer.Read(outEvents, amountToRead, @@ -597,18 +582,18 @@ class InputBufferTests { InputBuffer inputBuffer; auto record = MakeKeyEvent(true, 1, L'a', 0, L'a', 0); - auto inputEvent = IInputEvent::Create(record); + auto inputEvent = record; size_t eventsWritten; auto waitEvent = false; inputBuffer.Flush(); // write one event to an empty buffer - std::deque> storage; + InputEventQueue storage; storage.push_back(std::move(inputEvent)); inputBuffer._WriteBuffer(storage, eventsWritten, waitEvent); VERIFY_IS_TRUE(waitEvent); // write another, it shouldn't signal this time auto record2 = MakeKeyEvent(true, 1, L'b', 0, L'b', 0); - auto inputEvent2 = IInputEvent::Create(record2); + auto inputEvent2 = record2; // write another event to a non-empty buffer waitEvent = false; storage.clear(); @@ -623,9 +608,9 @@ class InputBufferTests InputBuffer inputBuffer; const WORD repeatCount = 5; auto record = MakeKeyEvent(true, repeatCount, L'a', 0, L'a', 0); - std::deque> outEvents; + InputEventQueue outEvents; - VERIFY_ARE_EQUAL(inputBuffer.Write(IInputEvent::Create(record)), 1u); + VERIFY_ARE_EQUAL(inputBuffer.Write(record), 1u); VERIFY_NT_SUCCESS(inputBuffer.Read(outEvents, 1, false, @@ -634,8 +619,8 @@ class InputBufferTests true)); VERIFY_ARE_EQUAL(outEvents.size(), 1u); VERIFY_ARE_EQUAL(inputBuffer._storage.size(), 1u); - VERIFY_ARE_EQUAL(static_cast(*inputBuffer._storage.front()).GetRepeatCount(), repeatCount - 1); - VERIFY_ARE_EQUAL(static_cast(*outEvents.front()).GetRepeatCount(), 1u); + VERIFY_ARE_EQUAL(inputBuffer._storage.front().Event.KeyEvent.wRepeatCount, repeatCount - 1); + VERIFY_ARE_EQUAL(outEvents.front().Event.KeyEvent.wRepeatCount, 1u); } TEST_METHOD(StreamPeekingDeCoalesces) @@ -643,9 +628,9 @@ class InputBufferTests InputBuffer inputBuffer; const WORD repeatCount = 5; auto record = MakeKeyEvent(true, repeatCount, L'a', 0, L'a', 0); - std::deque> outEvents; + InputEventQueue outEvents; - VERIFY_ARE_EQUAL(inputBuffer.Write(IInputEvent::Create(record)), 1u); + VERIFY_ARE_EQUAL(inputBuffer.Write(record), 1u); VERIFY_NT_SUCCESS(inputBuffer.Read(outEvents, 1, true, @@ -654,7 +639,7 @@ class InputBufferTests true)); VERIFY_ARE_EQUAL(outEvents.size(), 1u); VERIFY_ARE_EQUAL(inputBuffer._storage.size(), 1u); - VERIFY_ARE_EQUAL(static_cast(*inputBuffer._storage.front()).GetRepeatCount(), repeatCount); - VERIFY_ARE_EQUAL(static_cast(*outEvents.front()).GetRepeatCount(), 1u); + VERIFY_ARE_EQUAL(inputBuffer._storage.front().Event.KeyEvent.wRepeatCount, repeatCount); + VERIFY_ARE_EQUAL(outEvents.front().Event.KeyEvent.wRepeatCount, 1u); } }; diff --git a/src/inc/til/small_vector.h b/src/inc/til/small_vector.h index 2a10dcb6b36..a88927e7159 100644 --- a/src/inc/til/small_vector.h +++ b/src/inc/til/small_vector.h @@ -278,6 +278,12 @@ namespace til return tmp; } + [[nodiscard]] friend constexpr small_vector_iterator operator+(const difference_type off, small_vector_iterator next) noexcept + { + next += off; + return next; + } + constexpr small_vector_iterator& operator-=(const difference_type off) noexcept { base::operator-=(off); diff --git a/src/interactivity/base/EventSynthesis.cpp b/src/interactivity/base/EventSynthesis.cpp index 3b283b001ab..1258468b1cb 100644 --- a/src/interactivity/base/EventSynthesis.cpp +++ b/src/interactivity/base/EventSynthesis.cpp @@ -3,10 +3,6 @@ #include "precomp.h" #include "../inc/EventSynthesis.hpp" -#include "../../types/inc/convert.hpp" -#include "../inc/VtApiRedirection.hpp" - -#pragma hdrstop // TODO: MSFT 14150722 - can these const values be generated at // runtime without breaking compatibility? @@ -16,35 +12,29 @@ static constexpr WORD leftShiftScanCode = 0x2A; // Routine Description: // - naively determines the width of a UCS2 encoded wchar (with caveats noted above) #pragma warning(suppress : 4505) // this function will be deleted if numpad events are disabled -static CodepointWidth GetQuickCharWidthLegacyForNumpadEventSynthesis(const wchar_t wch) noexcept +static bool GetQuickCharWidthLegacyForNumpadEventSynthesis(const wchar_t wch) noexcept { - if ((0x1100 <= wch && wch <= 0x115f) // From Unicode 9.0, Hangul Choseong is wide - || (0x2e80 <= wch && wch <= 0x303e) // From Unicode 9.0, this range is wide (assorted languages) - || (0x3041 <= wch && wch <= 0x3094) // Hiragana - || (0x30a1 <= wch && wch <= 0x30f6) // Katakana - || (0x3105 <= wch && wch <= 0x312c) // Bopomofo - || (0x3131 <= wch && wch <= 0x318e) // Hangul Elements - || (0x3190 <= wch && wch <= 0x3247) // From Unicode 9.0, this range is wide - || (0x3251 <= wch && wch <= 0x4dbf) // Unicode 9.0 CJK Unified Ideographs, Yi, Reserved, Han Ideograph (hexagrams from 4DC0..4DFF are ignored - || (0x4e00 <= wch && wch <= 0xa4c6) // Unicode 9.0 CJK Unified Ideographs, Yi, Reserved, Han Ideograph (hexagrams from 4DC0..4DFF are ignored - || (0xa960 <= wch && wch <= 0xa97c) // Wide Hangul Choseong - || (0xac00 <= wch && wch <= 0xd7a3) // Korean Hangul Syllables - || (0xf900 <= wch && wch <= 0xfaff) // From Unicode 9.0, this range is wide [CJK Compatibility Ideographs, Includes Han Compatibility Ideographs] - || (0xfe10 <= wch && wch <= 0xfe1f) // From Unicode 9.0, this range is wide [Presentation forms] - || (0xfe30 <= wch && wch <= 0xfe6b) // From Unicode 9.0, this range is wide [Presentation forms] - || (0xff01 <= wch && wch <= 0xff5e) // Fullwidth ASCII variants - || (0xffe0 <= wch && wch <= 0xffe6)) // Fullwidth symbol variants - { - return CodepointWidth::Wide; - } - - return CodepointWidth::Narrow; + return (0x1100 <= wch && wch <= 0x115f) || // From Unicode 9.0, Hangul Choseong is wide + (0x2e80 <= wch && wch <= 0x303e) || // From Unicode 9.0, this range is wide (assorted languages) + (0x3041 <= wch && wch <= 0x3094) || // Hiragana + (0x30a1 <= wch && wch <= 0x30f6) || // Katakana + (0x3105 <= wch && wch <= 0x312c) || // Bopomofo + (0x3131 <= wch && wch <= 0x318e) || // Hangul Elements + (0x3190 <= wch && wch <= 0x3247) || // From Unicode 9.0, this range is wide + (0x3251 <= wch && wch <= 0x4dbf) || // Unicode 9.0 CJK Unified Ideographs, Yi, Reserved, Han Ideograph (hexagrams from 4DC0..4DFF are ignored + (0x4e00 <= wch && wch <= 0xa4c6) || // Unicode 9.0 CJK Unified Ideographs, Yi, Reserved, Han Ideograph (hexagrams from 4DC0..4DFF are ignored + (0xa960 <= wch && wch <= 0xa97c) || // Wide Hangul Choseong + (0xac00 <= wch && wch <= 0xd7a3) || // Korean Hangul Syllables + (0xf900 <= wch && wch <= 0xfaff) || // From Unicode 9.0, this range is wide [CJK Compatibility Ideographs, Includes Han Compatibility Ideographs] + (0xfe10 <= wch && wch <= 0xfe1f) || // From Unicode 9.0, this range is wide [Presentation forms] + (0xfe30 <= wch && wch <= 0xfe6b) || // From Unicode 9.0, this range is wide [Presentation forms] + (0xff01 <= wch && wch <= 0xff5e) || // Fullwidth ASCII variants + (0xffe0 <= wch && wch <= 0xffe6); // Fullwidth symbol variants } -std::deque> Microsoft::Console::Interactivity::CharToKeyEvents(const wchar_t wch, - const unsigned int codepage) +void Microsoft::Console::Interactivity::CharToKeyEvents(const wchar_t wch, const unsigned int codepage, InputEventQueue& keyEvents) { - const short invalidKey = -1; + static constexpr short invalidKey = -1; auto keyState = OneCoreSafeVkKeyScanW(wch); if (keyState == invalidKey) @@ -57,18 +47,19 @@ std::deque> Microsoft::Console::Interactivity::CharToK WORD CharType = 0; GetStringTypeW(CT_CTYPE3, &wch, 1, &CharType); - if (!(WI_IsFlagSet(CharType, C3_ALPHA) || GetQuickCharWidthLegacyForNumpadEventSynthesis(wch) == CodepointWidth::Wide)) + if (WI_IsFlagClear(CharType, C3_ALPHA) && !GetQuickCharWidthLegacyForNumpadEventSynthesis(wch)) { // It wasn't alphanumeric or determined to be wide by the old algorithm // if VkKeyScanW fails (char is not in kbd layout), we must // emulate the key being input through the numpad - return SynthesizeNumpadEvents(wch, codepage); + SynthesizeNumpadEvents(wch, codepage, keyEvents); + return; } } keyState = 0; // SynthesizeKeyboardEvents would rather get 0 than -1 } - return SynthesizeKeyboardEvents(wch, keyState); + SynthesizeKeyboardEvents(wch, keyState, keyEvents); } // Routine Description: @@ -80,80 +71,43 @@ std::deque> Microsoft::Console::Interactivity::CharToK // - deque of KeyEvents that represent the wchar_t being typed // Note: // - will throw exception on error -std::deque> Microsoft::Console::Interactivity::SynthesizeKeyboardEvents(const wchar_t wch, const short keyState) +void Microsoft::Console::Interactivity::SynthesizeKeyboardEvents(const wchar_t wch, const short keyState, InputEventQueue& keyEvents) { + const auto vk = LOBYTE(keyState); + const auto sc = gsl::narrow(OneCoreSafeMapVirtualKeyW(vk, MAPVK_VK_TO_VSC)); const auto modifierState = HIBYTE(keyState); + const auto shiftSet = WI_IsFlagSet(modifierState, 1); + const auto ctrlSet = WI_IsFlagSet(modifierState, 2); + const auto altSet = WI_IsFlagSet(modifierState, 4); + const auto altGrSet = WI_AreAllFlagsSet(modifierState, 4 | 2); - auto altGrSet = false; - auto shiftSet = false; - std::deque> keyEvents; - - // add modifier key event if necessary - if (WI_AreAllFlagsSet(modifierState, VkKeyScanModState::CtrlAndAltPressed)) + if (altGrSet) { - altGrSet = true; - keyEvents.push_back(std::make_unique(true, - 1ui16, - static_cast(VK_MENU), - altScanCode, - UNICODE_NULL, - (ENHANCED_KEY | LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED))); + keyEvents.push_back(SynthesizeKeyEvent(true, 1, VK_MENU, altScanCode, 0, ENHANCED_KEY | LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED)); } - else if (WI_IsFlagSet(modifierState, VkKeyScanModState::ShiftPressed)) + else if (shiftSet) { - shiftSet = true; - keyEvents.push_back(std::make_unique(true, - 1ui16, - static_cast(VK_SHIFT), - leftShiftScanCode, - UNICODE_NULL, - SHIFT_PRESSED)); + keyEvents.push_back(SynthesizeKeyEvent(true, 1, VK_SHIFT, leftShiftScanCode, 0, SHIFT_PRESSED)); } - const auto vk = LOBYTE(keyState); - const auto virtualScanCode = gsl::narrow(OneCoreSafeMapVirtualKeyW(vk, MAPVK_VK_TO_VSC)); - KeyEvent keyEvent{ true, 1, LOBYTE(keyState), virtualScanCode, wch, 0 }; - - // add modifier flags if necessary - if (WI_IsFlagSet(modifierState, VkKeyScanModState::ShiftPressed)) - { - keyEvent.ActivateModifierKey(ModifierKeyState::Shift); - } - if (WI_IsFlagSet(modifierState, VkKeyScanModState::CtrlPressed)) - { - keyEvent.ActivateModifierKey(ModifierKeyState::LeftCtrl); - } - if (WI_AreAllFlagsSet(modifierState, VkKeyScanModState::CtrlAndAltPressed)) - { - keyEvent.ActivateModifierKey(ModifierKeyState::RightAlt); - } + auto keyEvent = SynthesizeKeyEvent(true, 1, vk, sc, wch, 0); + WI_SetFlagIf(keyEvent.Event.KeyEvent.dwControlKeyState, SHIFT_PRESSED, shiftSet); + WI_SetFlagIf(keyEvent.Event.KeyEvent.dwControlKeyState, LEFT_CTRL_PRESSED, ctrlSet); + WI_SetFlagIf(keyEvent.Event.KeyEvent.dwControlKeyState, RIGHT_ALT_PRESSED, altSet); - // add key event down and up - keyEvents.push_back(std::make_unique(keyEvent)); - keyEvent.SetKeyDown(false); - keyEvents.push_back(std::make_unique(keyEvent)); + keyEvents.push_back(keyEvent); + keyEvent.Event.KeyEvent.bKeyDown = FALSE; + keyEvents.push_back(keyEvent); - // add modifier key up event + // handle yucky alt-gr keys if (altGrSet) { - keyEvents.push_back(std::make_unique(false, - 1ui16, - static_cast(VK_MENU), - altScanCode, - UNICODE_NULL, - ENHANCED_KEY)); + keyEvents.push_back(SynthesizeKeyEvent(false, 1, VK_MENU, altScanCode, 0, ENHANCED_KEY)); } else if (shiftSet) { - keyEvents.push_back(std::make_unique(false, - 1ui16, - static_cast(VK_SHIFT), - leftShiftScanCode, - UNICODE_NULL, - 0)); + keyEvents.push_back(SynthesizeKeyEvent(false, 1, VK_SHIFT, leftShiftScanCode, 0, 0)); } - - return keyEvents; } // Routine Description: @@ -166,64 +120,35 @@ std::deque> Microsoft::Console::Interactivity::Synthes // alt + numpad // Note: // - will throw exception on error -std::deque> Microsoft::Console::Interactivity::SynthesizeNumpadEvents(const wchar_t wch, const unsigned int codepage) +void Microsoft::Console::Interactivity::SynthesizeNumpadEvents(const wchar_t wch, const unsigned int codepage, InputEventQueue& keyEvents) { - std::deque> keyEvents; - - //alt keydown - keyEvents.push_back(std::make_unique(true, - 1ui16, - static_cast(VK_MENU), - altScanCode, - UNICODE_NULL, - LEFT_ALT_PRESSED)); - - std::wstring wstr{ wch }; - const auto convertedChars = ConvertToA(codepage, wstr); - if (convertedChars.size() == 1) + char converted = 0; + const auto result = WideCharToMultiByte(codepage, 0, &wch, 1, &converted, 1, nullptr, nullptr); + + if (result == 1) { + // alt keydown + keyEvents.push_back(SynthesizeKeyEvent(true, 1, VK_MENU, altScanCode, 0, LEFT_ALT_PRESSED)); + // It is OK if the char is "signed -1", we want to interpret that as "unsigned 255" for the // "integer to character" conversion below with ::to_string, thus the static_cast. // Prime example is nonbreaking space U+00A0 will convert to OEM by codepage 437 to 0xFF which is -1 signed. // But it is absolutely valid as 0xFF or 255 unsigned as the correct CP437 character. // We need to treat it as unsigned because we're going to pretend it was a keypad entry // and you don't enter negative numbers on the keypad. - const auto uch = static_cast(convertedChars.at(0)); - - // unsigned char values are in the range [0, 255] so we need to be - // able to store up to 4 chars from the conversion (including the end of string char) - auto charString = std::to_string(uch); + const auto charString = std::to_string(static_cast(converted)); - for (auto& ch : std::string_view(charString)) + for (const auto& ch : charString) { - if (ch == 0) - { - break; - } - const WORD virtualKey = ch - '0' + VK_NUMPAD0; - const auto virtualScanCode = gsl::narrow(OneCoreSafeMapVirtualKeyW(virtualKey, MAPVK_VK_TO_VSC)); - - keyEvents.push_back(std::make_unique(true, - 1ui16, - virtualKey, - virtualScanCode, - UNICODE_NULL, - LEFT_ALT_PRESSED)); - keyEvents.push_back(std::make_unique(false, - 1ui16, - virtualKey, - virtualScanCode, - UNICODE_NULL, - LEFT_ALT_PRESSED)); + const WORD vk = ch - '0' + VK_NUMPAD0; + const auto sc = gsl::narrow(OneCoreSafeMapVirtualKeyW(vk, MAPVK_VK_TO_VSC)); + auto keyEvent = SynthesizeKeyEvent(true, 1, vk, sc, 0, LEFT_ALT_PRESSED); + keyEvents.push_back(keyEvent); + keyEvent.Event.KeyEvent.bKeyDown = FALSE; + keyEvents.push_back(keyEvent); } - } - // alt keyup - keyEvents.push_back(std::make_unique(false, - 1ui16, - static_cast(VK_MENU), - altScanCode, - wch, - 0)); - return keyEvents; + // alt keyup + keyEvents.push_back(SynthesizeKeyEvent(false, 1, VK_MENU, altScanCode, wch, 0)); + } } diff --git a/src/interactivity/inc/EventSynthesis.hpp b/src/interactivity/inc/EventSynthesis.hpp index a1ebb64f1a0..0a114c4369b 100644 --- a/src/interactivity/inc/EventSynthesis.hpp +++ b/src/interactivity/inc/EventSynthesis.hpp @@ -14,16 +14,10 @@ Module Name: --*/ #pragma once -#include -#include -#include "../../types/inc/IInputEvent.hpp" namespace Microsoft::Console::Interactivity { - std::deque> CharToKeyEvents(const wchar_t wch, const unsigned int codepage); - - std::deque> SynthesizeKeyboardEvents(const wchar_t wch, - const short keyState); - - std::deque> SynthesizeNumpadEvents(const wchar_t wch, const unsigned int codepage); + void CharToKeyEvents(wchar_t wch, unsigned int codepage, InputEventQueue& out); + void SynthesizeKeyboardEvents(wchar_t wch, short keyState, InputEventQueue& out); + void SynthesizeNumpadEvents(wchar_t wch, unsigned int codepage, InputEventQueue& out); } diff --git a/src/interactivity/onecore/ConIoSrvComm.cpp b/src/interactivity/onecore/ConIoSrvComm.cpp index 642e1c1f29e..3c543e442ff 100644 --- a/src/interactivity/onecore/ConIoSrvComm.cpp +++ b/src/interactivity/onecore/ConIoSrvComm.cpp @@ -266,9 +266,7 @@ VOID ConIoSrvComm::ServiceInputPipe() case CIS_EVENT_TYPE_INPUT: try { - const auto keyRecord = Event.InputEvent.Record.Event.KeyEvent; - const KeyEvent keyEvent{ keyRecord }; - HandleGenericKeyEvent(keyEvent, false); + HandleGenericKeyEvent(Event.InputEvent.Record, false); } catch (...) { diff --git a/src/interactivity/win32/Clipboard.cpp b/src/interactivity/win32/Clipboard.cpp index 1be0b11f2d7..30138f6dad6 100644 --- a/src/interactivity/win32/Clipboard.cpp +++ b/src/interactivity/win32/Clipboard.cpp @@ -134,17 +134,17 @@ void Clipboard::StringPaste(_In_reads_(cchData) const wchar_t* const pData, // - deque of KeyEvents that represent the string passed in // Note: // - will throw exception on error -std::deque> Clipboard::TextToKeyEvents(_In_reads_(cchData) const wchar_t* const pData, - const size_t cchData, - const bool bracketedPaste) +InputEventQueue Clipboard::TextToKeyEvents(_In_reads_(cchData) const wchar_t* const pData, + const size_t cchData, + const bool bracketedPaste) { THROW_HR_IF_NULL(E_INVALIDARG, pData); - std::deque> keyEvents; + InputEventQueue keyEvents; const auto pushControlSequence = [&](const std::wstring_view sequence) { std::for_each(sequence.begin(), sequence.end(), [&](const auto wch) { - keyEvents.push_back(std::make_unique(true, 1ui16, 0ui16, 0ui16, wch, 0)); - keyEvents.push_back(std::make_unique(false, 1ui16, 0ui16, 0ui16, wch, 0)); + keyEvents.push_back(SynthesizeKeyEvent(true, 1, 0, 0, wch, 0)); + keyEvents.push_back(SynthesizeKeyEvent(false, 1, 0, 0, wch, 0)); }); }; @@ -194,18 +194,14 @@ std::deque> Clipboard::TextToKeyEvents(_In_reads_(c } const auto codepage = ServiceLocator::LocateGlobals().getConsoleInformation().OutputCP; - auto convertedEvents = CharToKeyEvents(currentChar, codepage); - while (!convertedEvents.empty()) - { - keyEvents.push_back(std::move(convertedEvents.front())); - convertedEvents.pop_front(); - } + CharToKeyEvents(currentChar, codepage, keyEvents); } if (bracketedPaste) { pushControlSequence(L"\x1b[201~"); } + return keyEvents; } diff --git a/src/interactivity/win32/clipboard.hpp b/src/interactivity/win32/clipboard.hpp index 31413eb61ed..f6bab9254cf 100644 --- a/src/interactivity/win32/clipboard.hpp +++ b/src/interactivity/win32/clipboard.hpp @@ -35,9 +35,9 @@ namespace Microsoft::Console::Interactivity::Win32 void Paste(); private: - std::deque> TextToKeyEvents(_In_reads_(cchData) const wchar_t* const pData, - const size_t cchData, - const bool bracketedPaste = false); + InputEventQueue TextToKeyEvents(_In_reads_(cchData) const wchar_t* const pData, + const size_t cchData, + const bool bracketedPaste = false); void StoreSelectionToClipboard(_In_ const bool fAlsoCopyFormatting); diff --git a/src/interactivity/win32/windowio.cpp b/src/interactivity/win32/windowio.cpp index 5b882feeeef..90ddd58f1e1 100644 --- a/src/interactivity/win32/windowio.cpp +++ b/src/interactivity/win32/windowio.cpp @@ -167,23 +167,19 @@ void HandleKeyEvent(const HWND hWnd, // --- END LOAD BEARING CODE --- } - KeyEvent keyEvent{ !!bKeyDown, RepeatCount, VirtualKeyCode, VirtualScanCode, UNICODE_NULL, 0 }; + auto keyEvent = SynthesizeKeyEvent(bKeyDown, RepeatCount, VirtualKeyCode, VirtualScanCode, UNICODE_NULL, 0); if (Message == WM_CHAR || Message == WM_SYSCHAR || Message == WM_DEADCHAR || Message == WM_SYSDEADCHAR) { // If this is a fake character, zero the scancode. if (lParam & 0x02000000) { - keyEvent.SetVirtualScanCode(0); + keyEvent.Event.KeyEvent.wVirtualScanCode = 0; } - keyEvent.SetActiveModifierKeys(GetControlKeyState(lParam)); + keyEvent.Event.KeyEvent.dwControlKeyState = GetControlKeyState(lParam); if (Message == WM_CHAR || Message == WM_SYSCHAR) { - keyEvent.SetCharData(static_cast(wParam)); - } - else - { - keyEvent.SetCharData(L'\0'); + keyEvent.Event.KeyEvent.uChar.UnicodeChar = static_cast(wParam); } } else @@ -193,8 +189,7 @@ void HandleKeyEvent(const HWND hWnd, { return; } - keyEvent.SetActiveModifierKeys(ControlKeyState); - keyEvent.SetCharData(L'\0'); + keyEvent.Event.KeyEvent.dwControlKeyState = ControlKeyState; } const INPUT_KEY_INFO inputKeyInfo(VirtualKeyCode, ControlKeyState); @@ -903,26 +898,12 @@ BOOL HandleMouseEvent(const SCREEN_INFORMATION& ScreenInfo, break; } - ULONG EventsWritten = 0; - try - { - auto mouseEvent = std::make_unique( - MousePosition, - ConvertMouseButtonState(ButtonFlags, static_cast(wParam)), - GetControlKeyState(0), - EventFlags); - EventsWritten = static_cast(gci.pInputBuffer->Write(std::move(mouseEvent))); - } - catch (...) - { - LOG_HR(wil::ResultFromCaughtException()); - EventsWritten = 0; - } - - if (EventsWritten != 1) - { - RIPMSG1(RIP_WARNING, "PutInputInBuffer: EventsWritten != 1 (0x%x), 1 expected", EventsWritten); - } + const auto mouseEvent = SynthesizeMouseEvent( + MousePosition, + ConvertMouseButtonState(ButtonFlags, static_cast(wParam)), + GetControlKeyState(0), + EventFlags); + gci.pInputBuffer->Write(mouseEvent); return FALSE; } diff --git a/src/server/ApiDispatchers.cpp b/src/server/ApiDispatchers.cpp index ce3b81b8212..87b0e41c2d4 100644 --- a/src/server/ApiDispatchers.cpp +++ b/src/server/ApiDispatchers.cpp @@ -212,19 +212,7 @@ static DWORD TraceGetThreadId(CONSOLE_API_MSG* const m) } else { - try - { - for (size_t i = 0; i < cRecords; ++i) - { - if (outEvents.empty()) - { - break; - } - rgRecords[i] = outEvents.front()->ToInputRecord(); - outEvents.pop_front(); - } - } - CATCH_RETURN(); + std::ranges::copy(outEvents, rgRecords); } if (SUCCEEDED(hr)) diff --git a/src/server/WaitBlock.cpp b/src/server/WaitBlock.cpp index 74ef76b5362..49a4663f1cd 100644 --- a/src/server/WaitBlock.cpp +++ b/src/server/WaitBlock.cpp @@ -135,7 +135,7 @@ bool ConsoleWaitBlock::Notify(const WaitTerminationReason TerminationReason) DWORD dwControlKeyState; auto fIsUnicode = true; - std::deque> outEvents; + InputEventQueue outEvents; // TODO: MSFT 14104228 - get rid of this void* and get the data // out of the read wait object properly. void* pOutputData = nullptr; @@ -193,15 +193,7 @@ bool ConsoleWaitBlock::Notify(const WaitTerminationReason TerminationReason) const auto pRecordBuffer = static_cast(buffer); a->NumRecords = static_cast(outEvents.size()); - for (size_t i = 0; i < a->NumRecords; ++i) - { - if (outEvents.empty()) - { - break; - } - pRecordBuffer[i] = outEvents.front()->ToInputRecord(); - outEvents.pop_front(); - } + std::ranges::copy(outEvents, pRecordBuffer); } else if (API_NUMBER_READCONSOLE == _WaitReplyMessage.msgHeader.ApiNumber) { diff --git a/src/terminal/adapter/IInteractDispatch.hpp b/src/terminal/adapter/IInteractDispatch.hpp index 33b9659c849..36278bd2e1a 100644 --- a/src/terminal/adapter/IInteractDispatch.hpp +++ b/src/terminal/adapter/IInteractDispatch.hpp @@ -28,9 +28,9 @@ namespace Microsoft::Console::VirtualTerminal virtual ~IInteractDispatch() = default; #pragma warning(pop) - virtual bool WriteInput(std::deque>& inputEvents) = 0; + virtual bool WriteInput(const std::span& inputEvents) = 0; - virtual bool WriteCtrlKey(const KeyEvent& event) = 0; + virtual bool WriteCtrlKey(const INPUT_RECORD& event) = 0; virtual bool WriteString(const std::wstring_view string) = 0; diff --git a/src/terminal/adapter/InteractDispatch.cpp b/src/terminal/adapter/InteractDispatch.cpp index 1dc9f446b54..cb06c985c91 100644 --- a/src/terminal/adapter/InteractDispatch.cpp +++ b/src/terminal/adapter/InteractDispatch.cpp @@ -17,7 +17,6 @@ #include "../../interactivity/inc/ServiceLocator.hpp" #include "../../interactivity/inc/EventSynthesis.hpp" #include "../../types/inc/Viewport.hpp" -#include "../../inc/unicode.hpp" using namespace Microsoft::Console::Interactivity; using namespace Microsoft::Console::Types; @@ -38,7 +37,7 @@ InteractDispatch::InteractDispatch() : // - inputEvents: a collection of IInputEvents // Return Value: // - True. -bool InteractDispatch::WriteInput(std::deque>& inputEvents) +bool InteractDispatch::WriteInput(const std::span& inputEvents) { const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); gci.GetActiveInputBuffer()->Write(inputEvents); @@ -52,17 +51,14 @@ bool InteractDispatch::WriteInput(std::deque>& inpu // client application. // Arguments: // - event: The key to send to the host. -// Return Value: -// - True. -bool InteractDispatch::WriteCtrlKey(const KeyEvent& event) +bool InteractDispatch::WriteCtrlKey(const INPUT_RECORD& event) { HandleGenericKeyEvent(event, false); return true; } // Method Description: -// - Writes a string of input to the host. The string is converted to keystrokes -// that will faithfully represent the input by CharToKeyEvents. +// - Writes a string of input to the host. // Arguments: // - string : a string to write to the console. // Return Value: @@ -72,15 +68,11 @@ bool InteractDispatch::WriteString(const std::wstring_view string) if (!string.empty()) { const auto codepage = _api.GetConsoleOutputCP(); - std::deque> keyEvents; + InputEventQueue keyEvents; for (const auto& wch : string) { - auto convertedEvents = CharToKeyEvents(wch, codepage); - - std::move(convertedEvents.begin(), - convertedEvents.end(), - std::back_inserter(keyEvents)); + CharToKeyEvents(wch, codepage, keyEvents); } WriteInput(keyEvents); diff --git a/src/terminal/adapter/InteractDispatch.hpp b/src/terminal/adapter/InteractDispatch.hpp index 8fd34868211..56fd9b2dd80 100644 --- a/src/terminal/adapter/InteractDispatch.hpp +++ b/src/terminal/adapter/InteractDispatch.hpp @@ -25,8 +25,8 @@ namespace Microsoft::Console::VirtualTerminal public: InteractDispatch(); - bool WriteInput(std::deque>& inputEvents) override; - bool WriteCtrlKey(const KeyEvent& event) override; + bool WriteInput(const std::span& inputEvents) override; + bool WriteCtrlKey(const INPUT_RECORD& event) override; bool WriteString(const std::wstring_view string) override; bool WindowManipulation(const DispatchTypes::WindowManipulationType function, const VTParameter parameter1, diff --git a/src/terminal/adapter/ut_adapter/MouseInputTest.cpp b/src/terminal/adapter/ut_adapter/MouseInputTest.cpp index 9995d15d107..f3df50a0a6e 100644 --- a/src/terminal/adapter/ut_adapter/MouseInputTest.cpp +++ b/src/terminal/adapter/ut_adapter/MouseInputTest.cpp @@ -2,9 +2,8 @@ // Licensed under the MIT license. #include "precomp.h" -#include -#include "../../inc/consoletaeftemplates.hpp" +#include "../types/inc/IInputEvent.hpp" #include "../terminal/input/terminalInput.hpp" using namespace WEX::Common; diff --git a/src/terminal/adapter/ut_adapter/inputTest.cpp b/src/terminal/adapter/ut_adapter/inputTest.cpp index 9977cb609cf..581f03ed925 100644 --- a/src/terminal/adapter/ut_adapter/inputTest.cpp +++ b/src/terminal/adapter/ut_adapter/inputTest.cpp @@ -2,14 +2,10 @@ // Licensed under the MIT license. #include "precomp.h" -#include "../precomp.h" -#include -#include -#include "../../inc/consoletaeftemplates.hpp" - -#include "../../input/terminalInput.hpp" #include "../../../interactivity/inc/VtApiRedirection.hpp" +#include "../../input/terminalInput.hpp" +#include "../types/inc/IInputEvent.hpp" using namespace WEX::Common; using namespace WEX::Logging; @@ -182,9 +178,8 @@ void InputTest::TerminalInputTests() break; } - auto inputEvent = IInputEvent::Create(irTest); // Send key into object (will trigger callback and verification) - VERIFY_ARE_EQUAL(expected, input.HandleKey(inputEvent.get()), L"Verify key was handled if it should have been."); + VERIFY_ARE_EQUAL(expected, input.HandleKey(irTest), L"Verify key was handled if it should have been."); } Log::Comment(L"Sending every possible VKEY at the input stream for interception during key UP."); @@ -198,9 +193,8 @@ void InputTest::TerminalInputTests() irTest.Event.KeyEvent.wVirtualKeyCode = vkey; irTest.Event.KeyEvent.bKeyDown = FALSE; - auto inputEvent = IInputEvent::Create(irTest); // Send key into object (will trigger callback and verification) - VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleKey(inputEvent.get()), L"Verify key was NOT handled."); + VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleKey(irTest), L"Verify key was NOT handled."); } Log::Comment(L"Verify other types of events are not handled/intercepted."); @@ -209,18 +203,15 @@ void InputTest::TerminalInputTests() Log::Comment(L"Testing MOUSE_EVENT"); irUnhandled.EventType = MOUSE_EVENT; - auto inputEvent = IInputEvent::Create(irUnhandled); - VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleKey(inputEvent.get()), L"Verify MOUSE_EVENT was NOT handled."); + VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleKey(irUnhandled), L"Verify MOUSE_EVENT was NOT handled."); Log::Comment(L"Testing WINDOW_BUFFER_SIZE_EVENT"); irUnhandled.EventType = WINDOW_BUFFER_SIZE_EVENT; - inputEvent = IInputEvent::Create(irUnhandled); - VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleKey(inputEvent.get()), L"Verify WINDOW_BUFFER_SIZE_EVENT was NOT handled."); + VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleKey(irUnhandled), L"Verify WINDOW_BUFFER_SIZE_EVENT was NOT handled."); Log::Comment(L"Testing MENU_EVENT"); irUnhandled.EventType = MENU_EVENT; - inputEvent = IInputEvent::Create(irUnhandled); - VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleKey(inputEvent.get()), L"Verify MENU_EVENT was NOT handled."); + VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleKey(irUnhandled), L"Verify MENU_EVENT was NOT handled."); // Testing FOCUS_EVENTs is handled by TestFocusEvents } @@ -232,40 +223,13 @@ void InputTest::TestFocusEvents() // We're relying on the fact that the INPUT_RECORD version of the ctor is only called by the API TerminalInput input; - INPUT_RECORD irTest = { 0 }; - irTest.EventType = FOCUS_EVENT; - - { - irTest.Event.FocusEvent.bSetFocus = false; - auto inputEvent = IInputEvent::Create(irTest); - VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleKey(inputEvent.get()), L"Verify FOCUS_EVENT from API was NOT handled."); - } - { - irTest.Event.FocusEvent.bSetFocus = true; - auto inputEvent = IInputEvent::Create(irTest); - VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleKey(inputEvent.get()), L"Verify FOCUS_EVENT from API was NOT handled."); - } - - VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleFocus(false), L"Verify FocusEvent from any other source was NOT handled."); - VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleFocus(true), L"Verify FocusEvent from any other source was NOT handled."); - - Log::Comment(L"Enable focus event handling"); + VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleFocus(false)); + VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleFocus(true)); input.SetInputMode(TerminalInput::Mode::FocusEvent, true); - { - irTest.Event.FocusEvent.bSetFocus = false; - auto inputEvent = IInputEvent::Create(irTest); - VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleKey(inputEvent.get()), L"Verify FOCUS_EVENT from API was NOT handled."); - } - { - irTest.Event.FocusEvent.bSetFocus = true; - auto inputEvent = IInputEvent::Create(irTest); - VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleKey(inputEvent.get()), L"Verify FOCUS_EVENT from API was NOT handled."); - } - - VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b[O"), input.HandleFocus(false), L"Verify FocusEvent from any other source was handled."); - VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b[I"), input.HandleFocus(true), L"Verify FocusEvent from any other source was handled."); + VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b[O"), input.HandleFocus(false)); + VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b[I"), input.HandleFocus(true)); } void InputTest::TerminalInputModifierKeyTests() @@ -482,9 +446,8 @@ void InputTest::TerminalInputModifierKeyTests() str[str.size() - 2] = L'1' + (fShift ? 1 : 0) + (fAlt ? 2 : 0) + (fCtrl ? 4 : 0); } - auto inputEvent = IInputEvent::Create(irTest); // Send key into object (will trigger callback and verification) - VERIFY_ARE_EQUAL(expected, input.HandleKey(inputEvent.get()), L"Verify key was handled if it should have been."); + VERIFY_ARE_EQUAL(expected, input.HandleKey(irTest), L"Verify key was handled if it should have been."); } } @@ -509,27 +472,23 @@ void InputTest::TerminalInputNullKeyTests() irTest.Event.KeyEvent.bKeyDown = TRUE; // Send key into object (will trigger callback and verification) - auto inputEvent = IInputEvent::Create(irTest); - VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\0"sv), input.HandleKey(inputEvent.get()), L"Verify key was handled if it should have been."); + VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\0"sv), input.HandleKey(irTest), L"Verify key was handled if it should have been."); vkey = VK_SPACE; Log::Comment(NoThrowString().Format(L"Testing key, state =0x%x, 0x%x", vkey, uiKeystate)); irTest.Event.KeyEvent.wVirtualKeyCode = vkey; irTest.Event.KeyEvent.uChar.UnicodeChar = vkey; - inputEvent = IInputEvent::Create(irTest); - VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\0"sv), input.HandleKey(inputEvent.get()), L"Verify key was handled if it should have been."); + VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\0"sv), input.HandleKey(irTest), L"Verify key was handled if it should have been."); uiKeystate = LEFT_CTRL_PRESSED | LEFT_ALT_PRESSED; Log::Comment(NoThrowString().Format(L"Testing key, state =0x%x, 0x%x", vkey, uiKeystate)); irTest.Event.KeyEvent.dwControlKeyState = uiKeystate; - inputEvent = IInputEvent::Create(irTest); - VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b\0"sv), input.HandleKey(inputEvent.get()), L"Verify key was handled if it should have been."); + VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b\0"sv), input.HandleKey(irTest), L"Verify key was handled if it should have been."); uiKeystate = RIGHT_CTRL_PRESSED | LEFT_ALT_PRESSED; Log::Comment(NoThrowString().Format(L"Testing key, state =0x%x, 0x%x", vkey, uiKeystate)); irTest.Event.KeyEvent.dwControlKeyState = uiKeystate; - inputEvent = IInputEvent::Create(irTest); - VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b\0"sv), input.HandleKey(inputEvent.get()), L"Verify key was handled if it should have been."); + VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b\0"sv), input.HandleKey(irTest), L"Verify key was handled if it should have been."); } static void TestKey(const TerminalInput::OutputType& expected, TerminalInput& input, const unsigned int uiKeystate, const BYTE vkey, const wchar_t wch = 0) @@ -545,8 +504,7 @@ static void TestKey(const TerminalInput::OutputType& expected, TerminalInput& in irTest.Event.KeyEvent.uChar.UnicodeChar = wch; // Send key into object (will trigger callback and verification) - auto inputEvent = IInputEvent::Create(irTest); - VERIFY_ARE_EQUAL(expected, input.HandleKey(inputEvent.get()), L"Verify key was handled if it should have been."); + VERIFY_ARE_EQUAL(expected, input.HandleKey(irTest), L"Verify key was handled if it should have been."); } void InputTest::DifferentModifiersTest() @@ -706,23 +664,23 @@ void InputTest::BackarrowKeyModeTest() void InputTest::AutoRepeatModeTest() { - const auto down = std::make_unique(true, 1ui16, 'A', 0ui16, 'A', 0ui16); - const auto up = std::make_unique(false, 1ui16, 'A', 0ui16, 'A', 0ui16); + static constexpr auto down = SynthesizeKeyEvent(true, 1, 'A', 0, 'A', 0); + static constexpr auto up = SynthesizeKeyEvent(false, 1, 'A', 0, 'A', 0); TerminalInput input; Log::Comment(L"Sending repeating keypresses with DECARM disabled."); input.SetInputMode(TerminalInput::Mode::AutoRepeat, false); - VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"A"), input.HandleKey(down.get())); - VERIFY_ARE_EQUAL(TerminalInput::MakeOutput({}), input.HandleKey(down.get())); - VERIFY_ARE_EQUAL(TerminalInput::MakeOutput({}), input.HandleKey(down.get())); - VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleKey(up.get())); + VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"A"), input.HandleKey(down)); + VERIFY_ARE_EQUAL(TerminalInput::MakeOutput({}), input.HandleKey(down)); + VERIFY_ARE_EQUAL(TerminalInput::MakeOutput({}), input.HandleKey(down)); + VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleKey(up)); Log::Comment(L"Sending repeating keypresses with DECARM enabled."); input.SetInputMode(TerminalInput::Mode::AutoRepeat, true); - VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"A"), input.HandleKey(down.get())); - VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"A"), input.HandleKey(down.get())); - VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"A"), input.HandleKey(down.get())); - VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleKey(up.get())); + VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"A"), input.HandleKey(down)); + VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"A"), input.HandleKey(down)); + VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"A"), input.HandleKey(down)); + VERIFY_ARE_EQUAL(TerminalInput::MakeUnhandled(), input.HandleKey(up)); } diff --git a/src/terminal/input/mouseInput.cpp b/src/terminal/input/mouseInput.cpp index 1fe64047b80..ddcb81b7e9b 100644 --- a/src/terminal/input/mouseInput.cpp +++ b/src/terminal/input/mouseInput.cpp @@ -4,6 +4,7 @@ #include "precomp.h" #include "terminalInput.hpp" +#include "../types/inc/IInputEvent.hpp" #include "../types/inc/utils.hpp" using namespace Microsoft::Console::VirtualTerminal; diff --git a/src/terminal/input/terminalInput.cpp b/src/terminal/input/terminalInput.cpp index 1a736bd9ed4..7c90ec383ec 100644 --- a/src/terminal/input/terminalInput.cpp +++ b/src/terminal/input/terminalInput.cpp @@ -6,8 +6,9 @@ #include -#include "../../interactivity/inc/VtApiRedirection.hpp" #include "../../inc/unicode.hpp" +#include "../../interactivity/inc/VtApiRedirection.hpp" +#include "../types/inc/IInputEvent.hpp" using namespace Microsoft::Console::VirtualTerminal; @@ -268,14 +269,14 @@ void TerminalInput::ForceDisableWin32InputMode(const bool win32InputMode) noexce _forceDisableWin32InputMode = win32InputMode; } -static const std::span _getKeyMapping(const KeyEvent& keyEvent, +static const std::span _getKeyMapping(const KEY_EVENT_RECORD& keyEvent, const bool ansiMode, const bool cursorApplicationMode, const bool keypadApplicationMode) noexcept { if (ansiMode) { - if (keyEvent.IsCursorKey()) + if (keyEvent.wVirtualKeyCode >= VK_END && keyEvent.wVirtualKeyCode <= VK_DOWN) { if (cursorApplicationMode) { @@ -300,7 +301,7 @@ static const std::span _getKeyMapping(const KeyEvent& keyEvent } else { - if (keyEvent.IsCursorKey()) + if (keyEvent.wVirtualKeyCode >= VK_END && keyEvent.wVirtualKeyCode <= VK_DOWN) { return { s_cursorKeysVt52Mapping.data(), s_cursorKeysVt52Mapping.size() }; } @@ -318,12 +319,12 @@ static const std::span _getKeyMapping(const KeyEvent& keyEvent // - keyMapping - Array of key mappings to search // Return Value: // - Has value if there was a match to a key translation. -static std::optional _searchKeyMapping(const KeyEvent& keyEvent, +static std::optional _searchKeyMapping(const KEY_EVENT_RECORD& keyEvent, std::span keyMapping) noexcept { for (auto& map : keyMapping) { - if (map.vkey == keyEvent.GetVirtualKeyCode()) + if (map.vkey == keyEvent.wVirtualKeyCode) { // 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 @@ -337,9 +338,9 @@ static std::optional _searchKeyMapping(const KeyEvent& keyEven // 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()); + WI_IsAnyFlagSet(map.modifiers, SHIFT_PRESSED) == WI_IsAnyFlagSet(keyEvent.dwControlKeyState, SHIFT_PRESSED) && + WI_IsAnyFlagSet(map.modifiers, ALT_PRESSED) == WI_IsAnyFlagSet(keyEvent.dwControlKeyState, ALT_PRESSED) && + WI_IsAnyFlagSet(map.modifiers, CTRL_PRESSED) == WI_IsAnyFlagSet(keyEvent.dwControlKeyState, CTRL_PRESSED); } if (modifiersMatch) @@ -353,16 +354,16 @@ static std::optional _searchKeyMapping(const KeyEvent& keyEven // 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. -TerminalInput::OutputType TerminalInput::_searchWithModifier(const KeyEvent& keyEvent) +TerminalInput::OutputType TerminalInput::_searchWithModifier(const KEY_EVENT_RECORD& keyEvent) { if (const auto match = _searchKeyMapping(keyEvent, s_modifierKeyMapping)) { const auto& v = match.value(); if (!v.sequence.empty()) { - const auto shift = keyEvent.IsShiftPressed(); - const auto alt = keyEvent.IsAltPressed(); - const auto ctrl = keyEvent.IsCtrlPressed(); + const auto shift = WI_IsAnyFlagSet(keyEvent.dwControlKeyState, SHIFT_PRESSED); + const auto alt = WI_IsAnyFlagSet(keyEvent.dwControlKeyState, ALT_PRESSED); + const auto ctrl = WI_IsAnyFlagSet(keyEvent.dwControlKeyState, CTRL_PRESSED); StringType str{ v.sequence }; str.at(str.size() - 2) = L'1' + (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0); return str; @@ -407,12 +408,12 @@ TerminalInput::OutputType TerminalInput::_searchWithModifier(const KeyEvent& key const auto slashVkey = LOBYTE(slashKeyScan); const auto questionMarkVkey = LOBYTE(questionMarkKeyScan); - const auto ctrl = keyEvent.IsCtrlPressed(); - const auto alt = keyEvent.IsAltPressed(); - const auto shift = keyEvent.IsShiftPressed(); + const auto ctrl = WI_IsAnyFlagSet(keyEvent.dwControlKeyState, CTRL_PRESSED); + const auto alt = WI_IsAnyFlagSet(keyEvent.dwControlKeyState, ALT_PRESSED); + const auto shift = WI_IsAnyFlagSet(keyEvent.dwControlKeyState, SHIFT_PRESSED); // From the KeyEvent we're translating, synthesize the equivalent VkKeyScan result - const auto vkey = keyEvent.GetVirtualKeyCode(); + const auto vkey = keyEvent.wVirtualKeyCode; const short keyScanFromEvent = vkey | (shift ? 0x100 : 0) | (ctrl ? 0x200 : 0) | @@ -474,20 +475,15 @@ TerminalInput::OutputType TerminalInput::MakeOutput(const std::wstring_view& str // Return Value: // - Returns an empty optional if we didn't handle the key event and the caller can opt to handle it in some other way. // - Returns a string if we successfully translated it into a VT input sequence. -TerminalInput::OutputType TerminalInput::HandleKey(const IInputEvent* const pInEvent) +TerminalInput::OutputType TerminalInput::HandleKey(const INPUT_RECORD& event) { - if (!pInEvent) - { - return MakeUnhandled(); - } - // On key presses, prepare to translate to VT compatible sequences - if (pInEvent->EventType() != InputEventType::KeyEvent) + if (event.EventType != KEY_EVENT) { return MakeUnhandled(); } - auto keyEvent = *static_cast(pInEvent); + auto keyEvent = event.Event.KeyEvent; // 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. @@ -498,10 +494,10 @@ TerminalInput::OutputType TerminalInput::HandleKey(const IInputEvent* const pInE } // Check if this key matches the last recorded key code. - const auto matchingLastKeyPress = _lastVirtualKeyCode == keyEvent.GetVirtualKeyCode(); + const auto matchingLastKeyPress = _lastVirtualKeyCode == keyEvent.wVirtualKeyCode; // Only need to handle key down. See raw key handler (see RawReadWaitRoutine in stream.cpp) - if (!keyEvent.IsKeyDown()) + if (!keyEvent.bKeyDown) { // If this is a release of the last recorded key press, we can reset that. if (matchingLastKeyPress) @@ -519,17 +515,17 @@ TerminalInput::OutputType TerminalInput::HandleKey(const IInputEvent* const pInE // the event, otherwise the key press can still end up being submitted. return MakeOutput({}); } - _lastVirtualKeyCode = keyEvent.GetVirtualKeyCode(); + _lastVirtualKeyCode = keyEvent.wVirtualKeyCode; // The VK_BACK key depends on the state of Backarrow Key mode (DECBKM). // If the mode is set, we should send BS. If reset, we should send DEL. - if (keyEvent.GetVirtualKeyCode() == VK_BACK) + if (keyEvent.wVirtualKeyCode == VK_BACK) { // The Ctrl modifier reverses the interpretation of DECBKM. - const auto backarrowMode = _inputMode.test(Mode::BackarrowKey) != keyEvent.IsCtrlPressed(); + const auto backarrowMode = _inputMode.test(Mode::BackarrowKey) != WI_IsAnyFlagSet(keyEvent.dwControlKeyState, CTRL_PRESSED); const auto seq = backarrowMode ? L'\x08' : L'\x7f'; // The Alt modifier adds an escape prefix. - if (keyEvent.IsAltPressed()) + if (WI_IsAnyFlagSet(keyEvent.dwControlKeyState, ALT_PRESSED)) { return _makeEscapedOutput(seq); } @@ -542,7 +538,7 @@ TerminalInput::OutputType TerminalInput::HandleKey(const IInputEvent* const pInE // When the Line Feed mode is set, a VK_RETURN key should send both CR and LF. // When reset, we fall through to the default behavior, which is to send just // CR, or when the Ctrl modifier is pressed, just LF. - if (keyEvent.GetVirtualKeyCode() == VK_RETURN && _inputMode.test(Mode::LineFeed)) + if (keyEvent.wVirtualKeyCode == VK_RETURN && _inputMode.test(Mode::LineFeed)) { return MakeOutput(L"\r\n"); } @@ -550,12 +546,11 @@ TerminalInput::OutputType TerminalInput::HandleKey(const IInputEvent* const pInE // 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(). + // When AltGr is pressed, the caller needs to make sure to send us a pretranslated character in uChar.UnicodeChar. // --> Strip out the AltGr flags, in order for us to not step into the Alt/Ctrl conditions below. - if (keyEvent.IsAltGrPressed()) + if (WI_AreAllFlagsSet(keyEvent.dwControlKeyState, LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED)) { - keyEvent.DeactivateModifierKey(ModifierKeyState::LeftCtrl); - keyEvent.DeactivateModifierKey(ModifierKeyState::RightAlt); + WI_ClearAllFlags(keyEvent.dwControlKeyState, LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED); } // The Alt modifier initiates a so called "escape sequence". @@ -565,16 +560,16 @@ TerminalInput::OutputType TerminalInput::HandleKey(const IInputEvent* const pInE // 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()) + if (WI_IsAnyFlagSet(keyEvent.dwControlKeyState, ALT_PRESSED) && WI_IsAnyFlagSet(keyEvent.dwControlKeyState, CTRL_PRESSED)) { - const auto ch = keyEvent.GetCharData(); - const auto vkey = keyEvent.GetVirtualKeyCode(); + const auto ch = keyEvent.uChar.UnicodeChar; + const auto vkey = keyEvent.wVirtualKeyCode; - // For Alt+Ctrl+Key messages GetCharData() usually returns 0. + // For Alt+Ctrl+Key messages uChar.UnicodeChar usually returns 0. // Luckily the numerical values of the ASCII characters and virtual key codes // of and A-Z, as used below, are numerically identical. // -> Get the char from the virtual key if it's 0. - const auto ctrlAltChar = keyEvent.GetCharData() != 0 ? keyEvent.GetCharData() : keyEvent.GetVirtualKeyCode(); + const auto ctrlAltChar = keyEvent.uChar.UnicodeChar != 0 ? keyEvent.uChar.UnicodeChar : keyEvent.wVirtualKeyCode; // 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. @@ -597,7 +592,7 @@ TerminalInput::OutputType TerminalInput::HandleKey(const IInputEvent* const pInE } // If a modifier key was pressed, then we need to try and send the modified sequence. - if (keyEvent.IsModifierPressed()) + if (WI_IsAnyFlagSet(keyEvent.dwControlKeyState, MOD_PRESSED)) { if (auto out = _searchWithModifier(keyEvent)) { @@ -607,9 +602,9 @@ TerminalInput::OutputType TerminalInput::HandleKey(const IInputEvent* const pInE // This section is similar to the Alt modifier section above, // but handles cases without Ctrl modifiers. - if (keyEvent.IsAltPressed() && !keyEvent.IsCtrlPressed() && keyEvent.GetCharData() != 0) + if (WI_IsAnyFlagSet(keyEvent.dwControlKeyState, ALT_PRESSED) && !WI_IsAnyFlagSet(keyEvent.dwControlKeyState, CTRL_PRESSED) && keyEvent.uChar.UnicodeChar != 0) { - return _makeEscapedOutput(keyEvent.GetCharData()); + return _makeEscapedOutput(keyEvent.uChar.UnicodeChar); } // Pressing the control key causes all bits but the 5 least @@ -620,10 +615,10 @@ TerminalInput::OutputType TerminalInput::HandleKey(const IInputEvent* const pInE // -> 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()) + if (!WI_IsAnyFlagSet(keyEvent.dwControlKeyState, ALT_PRESSED) && WI_IsAnyFlagSet(keyEvent.dwControlKeyState, CTRL_PRESSED)) { - const auto ch = keyEvent.GetCharData(); - const auto vkey = keyEvent.GetVirtualKeyCode(); + const auto ch = keyEvent.uChar.UnicodeChar; + const auto vkey = keyEvent.wVirtualKeyCode; // 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 @). @@ -639,7 +634,7 @@ TerminalInput::OutputType TerminalInput::HandleKey(const IInputEvent* const pInE if (ch == UNICODE_NULL) { // -> Try to infer the character from the vkey. - auto mappedChar = LOWORD(OneCoreSafeMapVirtualKeyW(keyEvent.GetVirtualKeyCode(), MAPVK_VK_TO_CHAR)); + auto mappedChar = LOWORD(OneCoreSafeMapVirtualKeyW(keyEvent.wVirtualKeyCode, MAPVK_VK_TO_CHAR)); if (mappedChar) { // Pressing the control key causes all bits but the 5 least @@ -660,9 +655,9 @@ TerminalInput::OutputType TerminalInput::HandleKey(const IInputEvent* const pInE } // If all else fails we can finally try to send the character itself if there is any. - if (keyEvent.GetCharData() != 0) + if (keyEvent.uChar.UnicodeChar != 0) { - return _makeCharOutput(keyEvent.GetCharData()); + return _makeCharOutput(keyEvent.uChar.UnicodeChar); } return MakeUnhandled(); @@ -720,16 +715,16 @@ TerminalInput::OutputType TerminalInput::_makeEscapedOutput(const wchar_t wch) // Turns an KEY_EVENT_RECORD into a win32-input-mode VT sequence. // It allows us to send KEY_EVENT_RECORD data losslessly to conhost. -TerminalInput::OutputType TerminalInput::_makeWin32Output(const KeyEvent& key) +TerminalInput::OutputType TerminalInput::_makeWin32Output(const KEY_EVENT_RECORD& key) { // .uChar.UnicodeChar must be cast to an integer because we want its numerical value. // Casting the rest to uint16_t as well doesn't hurt because that's MAX_PARAMETER_VALUE anyways. - const auto kd = gsl::narrow_cast(key.IsKeyDown() ? 1 : 0); - const auto rc = gsl::narrow_cast(key.GetRepeatCount()); - const auto vk = gsl::narrow_cast(key.GetVirtualKeyCode()); - const auto sc = gsl::narrow_cast(key.GetVirtualScanCode()); - const auto uc = gsl::narrow_cast(key.GetCharData()); - const auto cs = gsl::narrow_cast(key.GetActiveModifierKeys()); + const auto kd = gsl::narrow_cast(key.bKeyDown ? 1 : 0); + const auto rc = gsl::narrow_cast(key.wRepeatCount); + const auto vk = gsl::narrow_cast(key.wVirtualKeyCode); + const auto sc = gsl::narrow_cast(key.wVirtualScanCode); + const auto uc = gsl::narrow_cast(key.uChar.UnicodeChar); + const auto cs = gsl::narrow_cast(key.dwControlKeyState); // Sequences are formatted as follows: // diff --git a/src/terminal/input/terminalInput.hpp b/src/terminal/input/terminalInput.hpp index 8fcabe48987..820d22f8a90 100644 --- a/src/terminal/input/terminalInput.hpp +++ b/src/terminal/input/terminalInput.hpp @@ -1,22 +1,8 @@ -/*+ -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 ---*/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. #pragma once -#include "../../types/inc/IInputEvent.hpp" - namespace Microsoft::Console::VirtualTerminal { class TerminalInput final @@ -34,7 +20,7 @@ namespace Microsoft::Console::VirtualTerminal static [[nodiscard]] OutputType MakeUnhandled() noexcept; static [[nodiscard]] OutputType MakeOutput(const std::wstring_view& str); - [[nodiscard]] OutputType HandleKey(const IInputEvent* const pInEvent); + [[nodiscard]] OutputType HandleKey(const INPUT_RECORD& pInEvent); [[nodiscard]] OutputType HandleFocus(bool focused) const; [[nodiscard]] OutputType HandleMouse(til::point position, unsigned int button, short modifierKeyState, short delta, MouseButtonState state); @@ -89,8 +75,8 @@ namespace Microsoft::Console::VirtualTerminal [[nodiscard]] OutputType _makeCharOutput(wchar_t ch); static [[nodiscard]] OutputType _makeEscapedOutput(wchar_t wch); - static [[nodiscard]] OutputType _makeWin32Output(const KeyEvent& key); - static [[nodiscard]] OutputType _searchWithModifier(const KeyEvent& keyEvent); + static [[nodiscard]] OutputType _makeWin32Output(const KEY_EVENT_RECORD& key); + static [[nodiscard]] OutputType _searchWithModifier(const KEY_EVENT_RECORD& keyEvent); #pragma region MouseInputState Management // These methods are defined in mouseInputState.cpp diff --git a/src/terminal/parser/InputStateMachineEngine.cpp b/src/terminal/parser/InputStateMachineEngine.cpp index 5d0a6c51db0..95fb5a3e509 100644 --- a/src/terminal/parser/InputStateMachineEngine.cpp +++ b/src/terminal/parser/InputStateMachineEngine.cpp @@ -132,8 +132,11 @@ bool InputStateMachineEngine::_DoControlCharacter(const wchar_t wch, const bool if (wch == UNICODE_ETX && !writeAlt) { // This is Ctrl+C, which is handled specially by the host. - const auto [keyDown, keyUp] = KeyEvent::MakePair(1, 'C', 0, UNICODE_ETX, LEFT_CTRL_PRESSED); - success = _pDispatch->WriteCtrlKey(keyDown) && _pDispatch->WriteCtrlKey(keyUp); + static constexpr auto keyDown = SynthesizeKeyEvent(true, 1, L'C', 0, UNICODE_ETX, LEFT_CTRL_PRESSED); + static constexpr auto keyUp = SynthesizeKeyEvent(false, 1, L'C', 0, UNICODE_ETX, LEFT_CTRL_PRESSED); + _pDispatch->WriteCtrlKey(keyDown); + _pDispatch->WriteCtrlKey(keyUp); + success = true; } else if (wch >= '\x0' && wch < '\x20') { @@ -278,10 +281,10 @@ bool InputStateMachineEngine::ActionPassThroughString(const std::wstring_view st // similar to TerminalInput::_SendInputSequence if (!string.empty()) { - std::deque> inputEvents; + InputEventQueue inputEvents; for (const auto& wch : string) { - inputEvents.push_back(std::make_unique(true, 1ui16, 0ui16, 0ui16, wch, 0)); + inputEvents.push_back(SynthesizeKeyEvent(true, 1, 0, 0, wch, 0)); } return _pDispatch->WriteInput(inputEvents); } @@ -558,7 +561,7 @@ bool InputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/, void InputStateMachineEngine::_GenerateWrappedSequence(const wchar_t wch, const short vkey, const DWORD modifierState, - std::vector& input) + InputEventQueue& input) { input.reserve(input.size() + 8); @@ -570,44 +573,31 @@ void InputStateMachineEngine::_GenerateWrappedSequence(const wchar_t wch, const auto ctrl = WI_IsFlagSet(modifierState, LEFT_CTRL_PRESSED); const auto alt = WI_IsFlagSet(modifierState, LEFT_ALT_PRESSED); - INPUT_RECORD next{ 0 }; - + auto next = SynthesizeKeyEvent(true, 1, 0, 0, 0, 0); DWORD currentModifiers = 0; if (shift) { WI_SetFlag(currentModifiers, SHIFT_PRESSED); - next.EventType = KEY_EVENT; - next.Event.KeyEvent.bKeyDown = TRUE; next.Event.KeyEvent.dwControlKeyState = currentModifiers; - next.Event.KeyEvent.wRepeatCount = 1; next.Event.KeyEvent.wVirtualKeyCode = VK_SHIFT; next.Event.KeyEvent.wVirtualScanCode = gsl::narrow_cast(OneCoreSafeMapVirtualKeyW(VK_SHIFT, MAPVK_VK_TO_VSC)); - next.Event.KeyEvent.uChar.UnicodeChar = 0x0; input.push_back(next); } if (alt) { WI_SetFlag(currentModifiers, LEFT_ALT_PRESSED); - next.EventType = KEY_EVENT; - next.Event.KeyEvent.bKeyDown = TRUE; next.Event.KeyEvent.dwControlKeyState = currentModifiers; - next.Event.KeyEvent.wRepeatCount = 1; next.Event.KeyEvent.wVirtualKeyCode = VK_MENU; next.Event.KeyEvent.wVirtualScanCode = gsl::narrow_cast(OneCoreSafeMapVirtualKeyW(VK_MENU, MAPVK_VK_TO_VSC)); - next.Event.KeyEvent.uChar.UnicodeChar = 0x0; input.push_back(next); } if (ctrl) { WI_SetFlag(currentModifiers, LEFT_CTRL_PRESSED); - next.EventType = KEY_EVENT; - next.Event.KeyEvent.bKeyDown = TRUE; next.Event.KeyEvent.dwControlKeyState = currentModifiers; - next.Event.KeyEvent.wRepeatCount = 1; next.Event.KeyEvent.wVirtualKeyCode = VK_CONTROL; next.Event.KeyEvent.wVirtualScanCode = gsl::narrow_cast(OneCoreSafeMapVirtualKeyW(VK_CONTROL, MAPVK_VK_TO_VSC)); - next.Event.KeyEvent.uChar.UnicodeChar = 0x0; input.push_back(next); } @@ -616,40 +606,30 @@ void InputStateMachineEngine::_GenerateWrappedSequence(const wchar_t wch, // through on the KeyPress. _GetSingleKeypress(wch, vkey, modifierState, input); + next.Event.KeyEvent.bKeyDown = FALSE; + if (ctrl) { WI_ClearFlag(currentModifiers, LEFT_CTRL_PRESSED); - next.EventType = KEY_EVENT; - next.Event.KeyEvent.bKeyDown = FALSE; next.Event.KeyEvent.dwControlKeyState = currentModifiers; - next.Event.KeyEvent.wRepeatCount = 1; next.Event.KeyEvent.wVirtualKeyCode = VK_CONTROL; next.Event.KeyEvent.wVirtualScanCode = gsl::narrow_cast(OneCoreSafeMapVirtualKeyW(VK_CONTROL, MAPVK_VK_TO_VSC)); - next.Event.KeyEvent.uChar.UnicodeChar = 0x0; input.push_back(next); } if (alt) { WI_ClearFlag(currentModifiers, LEFT_ALT_PRESSED); - next.EventType = KEY_EVENT; - next.Event.KeyEvent.bKeyDown = FALSE; next.Event.KeyEvent.dwControlKeyState = currentModifiers; - next.Event.KeyEvent.wRepeatCount = 1; next.Event.KeyEvent.wVirtualKeyCode = VK_MENU; next.Event.KeyEvent.wVirtualScanCode = gsl::narrow_cast(OneCoreSafeMapVirtualKeyW(VK_MENU, MAPVK_VK_TO_VSC)); - next.Event.KeyEvent.uChar.UnicodeChar = 0x0; input.push_back(next); } if (shift) { WI_ClearFlag(currentModifiers, SHIFT_PRESSED); - next.EventType = KEY_EVENT; - next.Event.KeyEvent.bKeyDown = FALSE; next.Event.KeyEvent.dwControlKeyState = currentModifiers; - next.Event.KeyEvent.wRepeatCount = 1; next.Event.KeyEvent.wVirtualKeyCode = VK_SHIFT; next.Event.KeyEvent.wVirtualScanCode = gsl::narrow_cast(OneCoreSafeMapVirtualKeyW(VK_SHIFT, MAPVK_VK_TO_VSC)); - next.Event.KeyEvent.uChar.UnicodeChar = 0x0; input.push_back(next); } } @@ -668,21 +648,14 @@ void InputStateMachineEngine::_GenerateWrappedSequence(const wchar_t wch, void InputStateMachineEngine::_GetSingleKeypress(const wchar_t wch, const short vkey, const DWORD modifierState, - std::vector& input) + InputEventQueue& input) { input.reserve(input.size() + 2); - INPUT_RECORD rec; + const auto sc = gsl::narrow_cast(OneCoreSafeMapVirtualKeyW(vkey, MAPVK_VK_TO_VSC)); + auto rec = SynthesizeKeyEvent(true, 1, vkey, sc, wch, modifierState); - rec.EventType = KEY_EVENT; - rec.Event.KeyEvent.bKeyDown = TRUE; - rec.Event.KeyEvent.dwControlKeyState = modifierState; - rec.Event.KeyEvent.wRepeatCount = 1; - rec.Event.KeyEvent.wVirtualKeyCode = vkey; - rec.Event.KeyEvent.wVirtualScanCode = gsl::narrow_cast(OneCoreSafeMapVirtualKeyW(vkey, MAPVK_VK_TO_VSC)); - rec.Event.KeyEvent.uChar.UnicodeChar = wch; input.push_back(rec); - rec.Event.KeyEvent.bKeyDown = FALSE; input.push_back(rec); } @@ -701,10 +674,8 @@ void InputStateMachineEngine::_GetSingleKeypress(const wchar_t wch, bool InputStateMachineEngine::_WriteSingleKey(const wchar_t wch, const short vkey, const DWORD modifierState) { // At most 8 records - 2 for each of shift,ctrl,alt up and down, and 2 for the actual key up and down. - std::vector input; - _GenerateWrappedSequence(wch, vkey, modifierState, input); - auto inputEvents = IInputEvent::Create(std::span{ input }); - + InputEventQueue inputEvents; + _GenerateWrappedSequence(wch, vkey, modifierState, inputEvents); return _pDispatch->WriteInput(inputEvents); } @@ -734,18 +705,8 @@ bool InputStateMachineEngine::_WriteSingleKey(const short vkey, const DWORD modi // - true iff we successfully wrote the keypress to the input callback. bool InputStateMachineEngine::_WriteMouseEvent(const til::point uiPos, const DWORD buttonState, const DWORD controlKeyState, const DWORD eventFlags) { - INPUT_RECORD rgInput; - rgInput.EventType = MOUSE_EVENT; - rgInput.Event.MouseEvent.dwMousePosition.X = ::base::saturated_cast(uiPos.x); - rgInput.Event.MouseEvent.dwMousePosition.Y = ::base::saturated_cast(uiPos.y); - rgInput.Event.MouseEvent.dwButtonState = buttonState; - rgInput.Event.MouseEvent.dwControlKeyState = controlKeyState; - rgInput.Event.MouseEvent.dwEventFlags = eventFlags; - - // pack and write input record - // 1 record - the modifiers don't get their own events - auto inputEvents = IInputEvent::Create(std::span{ &rgInput, 1 }); - return _pDispatch->WriteInput(inputEvents); + const auto rgInput = SynthesizeMouseEvent(uiPos, buttonState, controlKeyState, eventFlags); + return _pDispatch->WriteInput({ &rgInput, 1 }); } // Method Description: @@ -1127,7 +1088,7 @@ bool InputStateMachineEngine::_GetWindowManipulationType(const std::span(parameters.at(0).value_or(0))); - key.SetVirtualScanCode(::base::saturated_cast(parameters.at(1).value_or(0))); - key.SetCharData(::base::saturated_cast(parameters.at(2).value_or(0))); - key.SetKeyDown(parameters.at(3).value_or(0)); - key.SetActiveModifierKeys(::base::saturated_cast(parameters.at(4).value_or(0))); - key.SetRepeatCount(::base::saturated_cast(parameters.at(5).value_or(1))); - return key; + return SynthesizeKeyEvent( + parameters.at(3).value_or(0), + ::base::saturated_cast(parameters.at(5).value_or(1)), + ::base::saturated_cast(parameters.at(0).value_or(0)), + ::base::saturated_cast(parameters.at(1).value_or(0)), + ::base::saturated_cast(parameters.at(2).value_or(0)), + ::base::saturated_cast(parameters.at(4).value_or(0))); } diff --git a/src/terminal/parser/InputStateMachineEngine.hpp b/src/terminal/parser/InputStateMachineEngine.hpp index a2ab34623fd..74af20a9b39 100644 --- a/src/terminal/parser/InputStateMachineEngine.hpp +++ b/src/terminal/parser/InputStateMachineEngine.hpp @@ -197,17 +197,17 @@ namespace Microsoft::Console::VirtualTerminal void _GenerateWrappedSequence(const wchar_t wch, const short vkey, const DWORD modifierState, - std::vector& input); + InputEventQueue& input); void _GetSingleKeypress(const wchar_t wch, const short vkey, const DWORD modifierState, - std::vector& input); + InputEventQueue& input); bool _GetWindowManipulationType(const std::span parameters, unsigned int& function) const noexcept; - KeyEvent _GenerateWin32Key(const VTParameters parameters); + static INPUT_RECORD _GenerateWin32Key(const VTParameters& parameters); bool _DoControlCharacter(const wchar_t wch, const bool writeAlt); diff --git a/src/terminal/parser/ut_parser/InputEngineTest.cpp b/src/terminal/parser/ut_parser/InputEngineTest.cpp index 5ae36992574..77b734895a2 100644 --- a/src/terminal/parser/ut_parser/InputEngineTest.cpp +++ b/src/terminal/parser/ut_parser/InputEngineTest.cpp @@ -75,11 +75,10 @@ class TestState { } - void RoundtripTerminalInputCallback(_In_ std::deque>& inEvents) + void RoundtripTerminalInputCallback(_In_ const std::span& inputRecords) { // Take all the characters out of the input records here, and put them into // the input state machine. - auto inputRecords = IInputEvent::ToInputRecords(inEvents); std::wstring vtseq = L""; for (auto& inRec : inputRecords) { @@ -96,10 +95,8 @@ class TestState Log::Comment(L"String processed"); } - void TestInputCallback(std::deque>& inEvents) + void TestInputCallback(const std::span& records) { - auto records = IInputEvent::ToInputRecords(inEvents); - // This callback doesn't work super well for the Ctrl+C iteration of the // C0Test. For ^C, we always send a keydown and a key up event, however, // both calls to WriteCtrlKey happen in one single call to @@ -146,10 +143,8 @@ class TestState vExpectedInput.clear(); } - void TestInputStringCallback(std::deque>& inEvents) + void TestInputStringCallback(const std::span& records) { - auto records = IInputEvent::ToInputRecords(inEvents); - for (auto expected : vExpectedInput) { Log::Comment( @@ -211,9 +206,9 @@ class Microsoft::Console::VirtualTerminal::InputEngineTest TestState testState; - void RoundtripTerminalInputCallback(std::deque>& inEvents); - void TestInputCallback(std::deque>& inEvents); - void TestInputStringCallback(std::deque>& inEvents); + void RoundtripTerminalInputCallback(const std::span& inEvents); + void TestInputCallback(const std::span& inEvents); + void TestInputStringCallback(const std::span& inEvents); std::wstring GenerateSgrMouseSequence(const CsiMouseButtonCodes button, const unsigned short modifiers, const til::point position, @@ -318,11 +313,11 @@ void InputEngineTest::VerifyExpectedInputDrained() class Microsoft::Console::VirtualTerminal::TestInteractDispatch final : public IInteractDispatch { public: - TestInteractDispatch(_In_ std::function>&)> pfn, + TestInteractDispatch(_In_ std::function&)> pfn, _In_ TestState* testState); - virtual bool WriteInput(_In_ std::deque>& inputEvents) override; + virtual bool WriteInput(_In_ const std::span& inputEvents) override; - virtual bool WriteCtrlKey(const KeyEvent& event) override; + virtual bool WriteCtrlKey(const INPUT_RECORD& event) override; virtual bool WindowManipulation(const DispatchTypes::WindowManipulationType function, const VTParameter parameter1, const VTParameter parameter2) override; // DTTERM_WindowManipulation @@ -336,29 +331,27 @@ class Microsoft::Console::VirtualTerminal::TestInteractDispatch final : public I virtual bool FocusChanged(const bool focused) const override; private: - std::function>&)> _pfnWriteInputCallback; + std::function&)> _pfnWriteInputCallback; TestState* _testState; // non-ownership pointer }; -TestInteractDispatch::TestInteractDispatch(_In_ std::function>&)> pfn, +TestInteractDispatch::TestInteractDispatch(_In_ std::function&)> pfn, _In_ TestState* testState) : _pfnWriteInputCallback(pfn), _testState(testState) { } -bool TestInteractDispatch::WriteInput(_In_ std::deque>& inputEvents) +bool TestInteractDispatch::WriteInput(_In_ const std::span& inputEvents) { _pfnWriteInputCallback(inputEvents); return true; } -bool TestInteractDispatch::WriteCtrlKey(const KeyEvent& event) +bool TestInteractDispatch::WriteCtrlKey(const INPUT_RECORD& event) { VERIFY_IS_TRUE(_testState->_expectSendCtrlC); - std::deque> inputEvents; - inputEvents.push_back(std::make_unique(event)); - return WriteInput(inputEvents); + return WriteInput({ &event, 1 }); } bool TestInteractDispatch::WindowManipulation(const DispatchTypes::WindowManipulationType function, @@ -374,16 +367,13 @@ bool TestInteractDispatch::WindowManipulation(const DispatchTypes::WindowManipul bool TestInteractDispatch::WriteString(const std::wstring_view string) { - std::deque> keyEvents; + InputEventQueue keyEvents; for (const auto& wch : string) { // We're forcing the translation to CP_USA, so that it'll be constant // regardless of the CP the test is running in - auto convertedEvents = Microsoft::Console::Interactivity::CharToKeyEvents(wch, CP_USA); - std::move(convertedEvents.begin(), - convertedEvents.end(), - std::back_inserter(keyEvents)); + Microsoft::Console::Interactivity::CharToKeyEvents(wch, CP_USA, keyEvents); } return WriteInput(keyEvents); @@ -966,13 +956,10 @@ void InputEngineTest::AltIntermediateTest() // Create the callback that's fired when the state machine wants to write // input. We'll take the events and put them straight into the // TerminalInput. - auto pfnInputStateMachineCallback = [&](std::deque>& inEvents) { + auto pfnInputStateMachineCallback = [&](const std::span& inEvents) { for (auto& ev : inEvents) { - if (const auto out = terminalInput.HandleKey(ev.get())) - { - translation.append(*out); - } + terminalInput.HandleKey(ev); } }; auto dispatch = std::make_unique(pfnInputStateMachineCallback, &testState); @@ -1474,73 +1461,73 @@ void InputEngineTest::TestWin32InputParsing() { std::vector params{ 1 }; - auto key = engine->_GenerateWin32Key({ params.data(), params.size() }); - VERIFY_ARE_EQUAL(1, key.GetVirtualKeyCode()); - VERIFY_ARE_EQUAL(0, key.GetVirtualScanCode()); - VERIFY_ARE_EQUAL(L'\0', key.GetCharData()); - VERIFY_ARE_EQUAL(false, key.IsKeyDown()); - VERIFY_ARE_EQUAL(0u, key.GetActiveModifierKeys()); - VERIFY_ARE_EQUAL(1, key.GetRepeatCount()); + auto key = engine->_GenerateWin32Key({ params.data(), params.size() }).Event.KeyEvent; + VERIFY_ARE_EQUAL(1, key.wVirtualKeyCode); + VERIFY_ARE_EQUAL(0, key.wVirtualScanCode); + VERIFY_ARE_EQUAL(L'\0', key.uChar.UnicodeChar); + VERIFY_ARE_EQUAL(FALSE, key.bKeyDown); + VERIFY_ARE_EQUAL(0u, key.dwControlKeyState); + VERIFY_ARE_EQUAL(1, key.wRepeatCount); } { std::vector params{ 1, 2 }; - auto key = engine->_GenerateWin32Key({ params.data(), params.size() }); - VERIFY_ARE_EQUAL(1, key.GetVirtualKeyCode()); - VERIFY_ARE_EQUAL(2, key.GetVirtualScanCode()); - VERIFY_ARE_EQUAL(L'\0', key.GetCharData()); - VERIFY_ARE_EQUAL(false, key.IsKeyDown()); - VERIFY_ARE_EQUAL(0u, key.GetActiveModifierKeys()); - VERIFY_ARE_EQUAL(1, key.GetRepeatCount()); + auto key = engine->_GenerateWin32Key({ params.data(), params.size() }).Event.KeyEvent; + VERIFY_ARE_EQUAL(1, key.wVirtualKeyCode); + VERIFY_ARE_EQUAL(2, key.wVirtualScanCode); + VERIFY_ARE_EQUAL(L'\0', key.uChar.UnicodeChar); + VERIFY_ARE_EQUAL(FALSE, key.bKeyDown); + VERIFY_ARE_EQUAL(0u, key.dwControlKeyState); + VERIFY_ARE_EQUAL(1, key.wRepeatCount); } { std::vector params{ 1, 2, 3 }; - auto key = engine->_GenerateWin32Key({ params.data(), params.size() }); - VERIFY_ARE_EQUAL(1, key.GetVirtualKeyCode()); - VERIFY_ARE_EQUAL(2, key.GetVirtualScanCode()); - VERIFY_ARE_EQUAL(L'\x03', key.GetCharData()); - VERIFY_ARE_EQUAL(false, key.IsKeyDown()); - VERIFY_ARE_EQUAL(0u, key.GetActiveModifierKeys()); - VERIFY_ARE_EQUAL(1, key.GetRepeatCount()); + auto key = engine->_GenerateWin32Key({ params.data(), params.size() }).Event.KeyEvent; + VERIFY_ARE_EQUAL(1, key.wVirtualKeyCode); + VERIFY_ARE_EQUAL(2, key.wVirtualScanCode); + VERIFY_ARE_EQUAL(L'\x03', key.uChar.UnicodeChar); + VERIFY_ARE_EQUAL(FALSE, key.bKeyDown); + VERIFY_ARE_EQUAL(0u, key.dwControlKeyState); + VERIFY_ARE_EQUAL(1, key.wRepeatCount); } { std::vector params{ 1, 2, 3, 4 }; - auto key = engine->_GenerateWin32Key({ params.data(), params.size() }); - VERIFY_ARE_EQUAL(1, key.GetVirtualKeyCode()); - VERIFY_ARE_EQUAL(2, key.GetVirtualScanCode()); - VERIFY_ARE_EQUAL(L'\x03', key.GetCharData()); - VERIFY_ARE_EQUAL(true, key.IsKeyDown()); - VERIFY_ARE_EQUAL(0u, key.GetActiveModifierKeys()); - VERIFY_ARE_EQUAL(1, key.GetRepeatCount()); + auto key = engine->_GenerateWin32Key({ params.data(), params.size() }).Event.KeyEvent; + VERIFY_ARE_EQUAL(1, key.wVirtualKeyCode); + VERIFY_ARE_EQUAL(2, key.wVirtualScanCode); + VERIFY_ARE_EQUAL(L'\x03', key.uChar.UnicodeChar); + VERIFY_ARE_EQUAL(TRUE, key.bKeyDown); + VERIFY_ARE_EQUAL(0u, key.dwControlKeyState); + VERIFY_ARE_EQUAL(1, key.wRepeatCount); } { std::vector params{ 1, 2, 3, 1 }; - auto key = engine->_GenerateWin32Key({ params.data(), params.size() }); - VERIFY_ARE_EQUAL(1, key.GetVirtualKeyCode()); - VERIFY_ARE_EQUAL(2, key.GetVirtualScanCode()); - VERIFY_ARE_EQUAL(L'\x03', key.GetCharData()); - VERIFY_ARE_EQUAL(true, key.IsKeyDown()); - VERIFY_ARE_EQUAL(0u, key.GetActiveModifierKeys()); - VERIFY_ARE_EQUAL(1, key.GetRepeatCount()); + auto key = engine->_GenerateWin32Key({ params.data(), params.size() }).Event.KeyEvent; + VERIFY_ARE_EQUAL(1, key.wVirtualKeyCode); + VERIFY_ARE_EQUAL(2, key.wVirtualScanCode); + VERIFY_ARE_EQUAL(L'\x03', key.uChar.UnicodeChar); + VERIFY_ARE_EQUAL(TRUE, key.bKeyDown); + VERIFY_ARE_EQUAL(0u, key.dwControlKeyState); + VERIFY_ARE_EQUAL(1, key.wRepeatCount); } { std::vector params{ 1, 2, 3, 4, 5 }; - auto key = engine->_GenerateWin32Key({ params.data(), params.size() }); - VERIFY_ARE_EQUAL(1, key.GetVirtualKeyCode()); - VERIFY_ARE_EQUAL(2, key.GetVirtualScanCode()); - VERIFY_ARE_EQUAL(L'\x03', key.GetCharData()); - VERIFY_ARE_EQUAL(true, key.IsKeyDown()); - VERIFY_ARE_EQUAL(0x5u, key.GetActiveModifierKeys()); - VERIFY_ARE_EQUAL(1, key.GetRepeatCount()); + auto key = engine->_GenerateWin32Key({ params.data(), params.size() }).Event.KeyEvent; + VERIFY_ARE_EQUAL(1, key.wVirtualKeyCode); + VERIFY_ARE_EQUAL(2, key.wVirtualScanCode); + VERIFY_ARE_EQUAL(L'\x03', key.uChar.UnicodeChar); + VERIFY_ARE_EQUAL(TRUE, key.bKeyDown); + VERIFY_ARE_EQUAL(0x5u, key.dwControlKeyState); + VERIFY_ARE_EQUAL(1, key.wRepeatCount); } { std::vector params{ 1, 2, 3, 4, 5, 6 }; - auto key = engine->_GenerateWin32Key({ params.data(), params.size() }); - VERIFY_ARE_EQUAL(1, key.GetVirtualKeyCode()); - VERIFY_ARE_EQUAL(2, key.GetVirtualScanCode()); - VERIFY_ARE_EQUAL(L'\x03', key.GetCharData()); - VERIFY_ARE_EQUAL(true, key.IsKeyDown()); - VERIFY_ARE_EQUAL(0x5u, key.GetActiveModifierKeys()); - VERIFY_ARE_EQUAL(6, key.GetRepeatCount()); + auto key = engine->_GenerateWin32Key({ params.data(), params.size() }).Event.KeyEvent; + VERIFY_ARE_EQUAL(1, key.wVirtualKeyCode); + VERIFY_ARE_EQUAL(2, key.wVirtualScanCode); + VERIFY_ARE_EQUAL(L'\x03', key.uChar.UnicodeChar); + VERIFY_ARE_EQUAL(TRUE, key.bKeyDown); + VERIFY_ARE_EQUAL(0x5u, key.dwControlKeyState); + VERIFY_ARE_EQUAL(6, key.wRepeatCount); } } @@ -1580,26 +1567,26 @@ void InputEngineTest::TestWin32InputOptionals() provideRepeatCount ? 6 : 0, }; - auto key = engine->_GenerateWin32Key({ params.data(), numParams }); + auto key = engine->_GenerateWin32Key({ params.data(), numParams }).Event.KeyEvent; VERIFY_ARE_EQUAL((provideVirtualKeyCode && numParams > 0) ? 1 : 0, - key.GetVirtualKeyCode()); + key.wVirtualKeyCode); VERIFY_ARE_EQUAL((provideVirtualScanCode && numParams > 1) ? 2 : 0, - key.GetVirtualScanCode()); + key.wVirtualScanCode); VERIFY_ARE_EQUAL((provideCharData && numParams > 2) ? L'\x03' : L'\0', - key.GetCharData()); - VERIFY_ARE_EQUAL((provideKeyDown && numParams > 3) ? true : false, - key.IsKeyDown()); + key.uChar.UnicodeChar); + VERIFY_ARE_EQUAL((provideKeyDown && numParams > 3) ? TRUE : FALSE, + key.bKeyDown); VERIFY_ARE_EQUAL((provideActiveModifierKeys && numParams > 4) ? 5u : 0u, - key.GetActiveModifierKeys()); + key.dwControlKeyState); if (numParams == 6) { VERIFY_ARE_EQUAL((provideRepeatCount) ? 6 : 0, - key.GetRepeatCount()); + key.wRepeatCount); } else { VERIFY_ARE_EQUAL((provideRepeatCount && numParams > 5) ? 6 : 1, - key.GetRepeatCount()); + key.wRepeatCount); } } } diff --git a/src/types/FocusEvent.cpp b/src/types/FocusEvent.cpp deleted file mode 100644 index 09a93b2f4ae..00000000000 --- a/src/types/FocusEvent.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -// -// BODGY: GH#13238 -// -// It appears that some applications (libuv) like to send a FOCUS_EVENT_RECORD -// as a way to jiggle the input handle. Focus events really shouldn't ever be -// sent via the API, they don't really do anything internally. However, focus -// events in the input buffer do get translated by the TerminalInput to VT -// sequences if we're in the right input mode. -// -// To not prevent libuv from jiggling the handle with a focus event, and also -// make sure that we don't erroneously translate that to a sequence of -// characters, we're going to filter out focus events that came from the API -// when translating to VT. - -#include "precomp.h" -#include "inc/IInputEvent.hpp" - -FocusEvent::~FocusEvent() = default; - -INPUT_RECORD FocusEvent::ToInputRecord() const noexcept -{ - INPUT_RECORD record{ 0 }; - record.EventType = FOCUS_EVENT; - record.Event.FocusEvent.bSetFocus = !!_focus; - return record; -} - -InputEventType FocusEvent::EventType() const noexcept -{ - return InputEventType::FocusEvent; -} - -void FocusEvent::SetFocus(const bool focus) noexcept -{ - _focus = focus; -} diff --git a/src/types/IInputEvent.cpp b/src/types/IInputEvent.cpp deleted file mode 100644 index c2285caec66..00000000000 --- a/src/types/IInputEvent.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" -#include "inc/IInputEvent.hpp" - -#include - -std::unique_ptr IInputEvent::Create(const INPUT_RECORD& record) -{ - switch (record.EventType) - { - case KEY_EVENT: - return std::make_unique(record.Event.KeyEvent); - case MOUSE_EVENT: - return std::make_unique(record.Event.MouseEvent); - case WINDOW_BUFFER_SIZE_EVENT: - return std::make_unique(record.Event.WindowBufferSizeEvent); - case MENU_EVENT: - return std::make_unique(record.Event.MenuEvent); - case FOCUS_EVENT: - return std::make_unique(record.Event.FocusEvent); - default: - THROW_HR(E_INVALIDARG); - } -} - -std::deque> IInputEvent::Create(std::span records) -{ - std::deque> outEvents; - - for (const auto& record : records) - { - outEvents.push_back(Create(record)); - } - - return outEvents; -} - -// Routine Description: -// - Converts std::deque to std::deque> -// Arguments: -// - inRecords - records to convert -// Return Value: -// - std::deque of IInputEvents on success. Will throw exception on failure. -std::deque> IInputEvent::Create(const std::deque& records) -{ - std::deque> outEvents; - for (const auto& record : records) - { - auto event = Create(record); - outEvents.push_back(std::move(event)); - } - return outEvents; -} - -std::vector IInputEvent::ToInputRecords(const std::deque>& events) -{ - std::vector records; - records.reserve(events.size()); - - for (auto& evt : events) - { - records.push_back(evt->ToInputRecord()); - } - - return records; -} diff --git a/src/types/IInputEventStreams.cpp b/src/types/IInputEventStreams.cpp deleted file mode 100644 index 376cd3c5872..00000000000 --- a/src/types/IInputEventStreams.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" -#include "inc/IInputEvent.hpp" -#include - -std::wostream& operator<<(std::wostream& stream, const IInputEvent* const pEvent) -{ - if (pEvent == nullptr) - { - return stream << L"nullptr"; - } - - try - { - switch (pEvent->EventType()) - { - case InputEventType::KeyEvent: - return stream << static_cast(pEvent); - case InputEventType::MouseEvent: - return stream << static_cast(pEvent); - case InputEventType::WindowBufferSizeEvent: - return stream << static_cast(pEvent); - case InputEventType::MenuEvent: - return stream << static_cast(pEvent); - case InputEventType::FocusEvent: - return stream << static_cast(pEvent); - default: - return stream << L"IInputEvent()"; - } - } - catch (...) - { - return stream << L"IInputEvent stream error"; - } -} - -std::wostream& operator<<(std::wostream& stream, const KeyEvent* const pKeyEvent) -{ - if (pKeyEvent == nullptr) - { - return stream << L"nullptr"; - } - - std::wstring keyMotion = pKeyEvent->_keyDown ? L"keyDown" : L"keyUp"; - std::wstring charData = { pKeyEvent->_charData }; - if (pKeyEvent->_charData == L'\0') - { - charData = L"null"; - } - - // clang-format off - return stream << L"KeyEvent(" << - keyMotion << L", " << - L"repeat: " << pKeyEvent->_repeatCount << L", " << - L"keyCode: " << pKeyEvent->_virtualKeyCode << L", " << - L"scanCode: " << pKeyEvent->_virtualScanCode << L", " << - L"char: " << charData << L", " << - L"mods: " << pKeyEvent->GetActiveModifierKeys() << L")"; - // clang-format on -} - -std::wostream& operator<<(std::wostream& stream, const MouseEvent* const pMouseEvent) -{ - if (pMouseEvent == nullptr) - { - return stream << L"nullptr"; - } - - // clang-format off - return stream << L"MouseEvent(" << - L"X: " << pMouseEvent->_position.x << L", " << - L"Y: " << pMouseEvent->_position.y << L", " << - L"buttons: " << pMouseEvent->_buttonState << L", " << - L"mods: " << pMouseEvent->_activeModifierKeys << L", " << - L"events: " << pMouseEvent->_eventFlags << L")"; - // clang-format on -} - -std::wostream& operator<<(std::wostream& stream, const WindowBufferSizeEvent* const pEvent) -{ - if (pEvent == nullptr) - { - return stream << L"nullptr"; - } - - // clang-format off - return stream << L"WindowbufferSizeEvent(" << - L"X: " << pEvent->_size.width << L", " << - L"Y: " << pEvent->_size.height << L")"; - // clang-format on -} - -std::wostream& operator<<(std::wostream& stream, const MenuEvent* const pMenuEvent) -{ - if (pMenuEvent == nullptr) - { - return stream << L"nullptr"; - } - - return stream << L"MenuEvent(" << L"CommandId" << pMenuEvent->_commandId << L")"; -} - -std::wostream& operator<<(std::wostream& stream, const FocusEvent* const pFocusEvent) -{ - if (pFocusEvent == nullptr) - { - return stream << L"nullptr"; - } - - return stream << L"FocusEvent(" << L"focus" << pFocusEvent->_focus << L")"; -} diff --git a/src/types/KeyEvent.cpp b/src/types/KeyEvent.cpp deleted file mode 100644 index 475cd837411..00000000000 --- a/src/types/KeyEvent.cpp +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" -#include "inc/IInputEvent.hpp" - -KeyEvent::~KeyEvent() = default; - -INPUT_RECORD KeyEvent::ToInputRecord() const noexcept -{ - INPUT_RECORD record{}; - record.EventType = KEY_EVENT; - record.Event.KeyEvent.bKeyDown = !!_keyDown; - record.Event.KeyEvent.wRepeatCount = _repeatCount; - record.Event.KeyEvent.wVirtualKeyCode = _virtualKeyCode; - record.Event.KeyEvent.wVirtualScanCode = _virtualScanCode; - record.Event.KeyEvent.uChar.UnicodeChar = _charData; - record.Event.KeyEvent.dwControlKeyState = GetActiveModifierKeys(); - return record; -} - -InputEventType KeyEvent::EventType() const noexcept -{ - return InputEventType::KeyEvent; -} - -void KeyEvent::SetKeyDown(const bool keyDown) noexcept -{ - _keyDown = keyDown; -} - -void KeyEvent::SetRepeatCount(const WORD repeatCount) noexcept -{ - _repeatCount = repeatCount; -} - -void KeyEvent::SetVirtualKeyCode(const WORD virtualKeyCode) noexcept -{ - _virtualKeyCode = virtualKeyCode; -} - -void KeyEvent::SetVirtualScanCode(const WORD virtualScanCode) noexcept -{ - _virtualScanCode = virtualScanCode; -} - -void KeyEvent::SetCharData(const char character) noexcept -{ - // With MSVC char is signed by default and the conversion to wchar_t (unsigned) would turn negative - // chars into very large wchar_t values. While this doesn't pose a problem per se (even with such sign - // extension, the lower 8 bit stay the same), it makes debugging and reading key events more difficult. - _charData = til::as_unsigned(character); -} - -void KeyEvent::SetCharData(const wchar_t character) noexcept -{ - _charData = character; -} - -void KeyEvent::SetActiveModifierKeys(const DWORD activeModifierKeys) noexcept -{ - _activeModifierKeys = static_cast(activeModifierKeys); -} - -void KeyEvent::DeactivateModifierKey(const ModifierKeyState modifierKey) noexcept -{ - const auto bitFlag = ToConsoleControlKeyFlag(modifierKey); - auto keys = GetActiveModifierKeys(); - WI_ClearAllFlags(keys, bitFlag); - SetActiveModifierKeys(keys); -} - -void KeyEvent::ActivateModifierKey(const ModifierKeyState modifierKey) noexcept -{ - const auto bitFlag = ToConsoleControlKeyFlag(modifierKey); - auto keys = GetActiveModifierKeys(); - WI_SetAllFlags(keys, bitFlag); - SetActiveModifierKeys(keys); -} - -bool KeyEvent::DoActiveModifierKeysMatch(const std::unordered_set& consoleModifiers) const noexcept -{ - DWORD consoleBits = 0; - for (const auto& mod : consoleModifiers) - { - WI_SetAllFlags(consoleBits, ToConsoleControlKeyFlag(mod)); - } - return consoleBits == GetActiveModifierKeys(); -} - -// Routine Description: -// - checks if this key event is a special key for line editing -// Arguments: -// - none -// Return Value: -// - true if this key has special relevance to line editing, false otherwise -bool KeyEvent::IsCommandLineEditingKey() const noexcept -{ - if (!IsAltPressed() && !IsCtrlPressed()) - { - switch (GetVirtualKeyCode()) - { - case VK_ESCAPE: - case VK_PRIOR: - case VK_NEXT: - case VK_END: - case VK_HOME: - case VK_LEFT: - case VK_UP: - case VK_RIGHT: - case VK_DOWN: - case VK_INSERT: - case VK_DELETE: - case VK_F1: - case VK_F2: - case VK_F3: - case VK_F4: - case VK_F5: - case VK_F6: - case VK_F7: - case VK_F8: - case VK_F9: - return true; - default: - break; - } - } - if (IsCtrlPressed()) - { - switch (GetVirtualKeyCode()) - { - case VK_END: - case VK_HOME: - case VK_LEFT: - case VK_RIGHT: - return true; - default: - break; - } - } - - if (IsAltPressed()) - { - switch (GetVirtualKeyCode()) - { - case VK_F7: - case VK_F10: - return true; - default: - break; - } - } - return false; -} - -// Routine Description: -// - checks if this key event is a special key for popups -// Arguments: -// - None -// Return Value: -// - true if this key has special relevance to popups, false otherwise -bool KeyEvent::IsPopupKey() const noexcept -{ - if (!IsAltPressed() && !IsCtrlPressed()) - { - switch (GetVirtualKeyCode()) - { - case VK_ESCAPE: - case VK_PRIOR: - case VK_NEXT: - case VK_END: - case VK_HOME: - case VK_LEFT: - case VK_UP: - case VK_RIGHT: - case VK_DOWN: - case VK_F2: - case VK_F4: - case VK_F7: - case VK_F9: - case VK_DELETE: - return true; - default: - break; - } - } - - return false; -} diff --git a/src/types/MenuEvent.cpp b/src/types/MenuEvent.cpp deleted file mode 100644 index 445a215f4b8..00000000000 --- a/src/types/MenuEvent.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" -#include "inc/IInputEvent.hpp" - -MenuEvent::~MenuEvent() = default; - -INPUT_RECORD MenuEvent::ToInputRecord() const noexcept -{ - INPUT_RECORD record{ 0 }; - record.EventType = MENU_EVENT; - record.Event.MenuEvent.dwCommandId = _commandId; - return record; -} - -InputEventType MenuEvent::EventType() const noexcept -{ - return InputEventType::MenuEvent; -} - -void MenuEvent::SetCommandId(const UINT commandId) noexcept -{ - _commandId = commandId; -} diff --git a/src/types/ModifierKeyState.cpp b/src/types/ModifierKeyState.cpp deleted file mode 100644 index 603b52c7999..00000000000 --- a/src/types/ModifierKeyState.cpp +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" -#include "inc/IInputEvent.hpp" - -#include - -// Routine Description: -// - checks if flag is present in flags -// Arguments: -// - flags - bit pattern to check for flag -// - flag - bit pattern to search for -// Return Value: -// - true if flag is present in flags -// Note: -// - The wil version of IsFlagSet is only to operate on values that -// are compile time constants. This will work with runtime calculated -// values. -constexpr bool RuntimeIsFlagSet(const DWORD flags, const DWORD flag) noexcept -{ - return !!(flags & flag); -} - -std::unordered_set FromVkKeyScan(const short vkKeyScanFlags) -{ - std::unordered_set keyState; - - switch (vkKeyScanFlags) - { - case VkKeyScanModState::None: - break; - case VkKeyScanModState::ShiftPressed: - keyState.insert(ModifierKeyState::Shift); - break; - case VkKeyScanModState::CtrlPressed: - keyState.insert(ModifierKeyState::LeftCtrl); - keyState.insert(ModifierKeyState::RightCtrl); - break; - case VkKeyScanModState::ShiftAndCtrlPressed: - keyState.insert(ModifierKeyState::Shift); - keyState.insert(ModifierKeyState::LeftCtrl); - keyState.insert(ModifierKeyState::RightCtrl); - break; - case VkKeyScanModState::AltPressed: - keyState.insert(ModifierKeyState::LeftAlt); - keyState.insert(ModifierKeyState::RightAlt); - break; - case VkKeyScanModState::ShiftAndAltPressed: - keyState.insert(ModifierKeyState::Shift); - keyState.insert(ModifierKeyState::LeftAlt); - keyState.insert(ModifierKeyState::RightAlt); - break; - case VkKeyScanModState::CtrlAndAltPressed: - keyState.insert(ModifierKeyState::LeftCtrl); - keyState.insert(ModifierKeyState::RightCtrl); - keyState.insert(ModifierKeyState::LeftAlt); - keyState.insert(ModifierKeyState::RightAlt); - break; - case VkKeyScanModState::ModPressed: - keyState.insert(ModifierKeyState::Shift); - keyState.insert(ModifierKeyState::LeftCtrl); - keyState.insert(ModifierKeyState::RightCtrl); - keyState.insert(ModifierKeyState::LeftAlt); - keyState.insert(ModifierKeyState::RightAlt); - break; - default: - THROW_HR(E_INVALIDARG); - break; - } - - return keyState; -} - -using ModifierKeyStateMapping = std::pair; - -constexpr static ModifierKeyStateMapping ModifierKeyStateTranslationTable[] = { - { ModifierKeyState::RightAlt, RIGHT_ALT_PRESSED }, - { ModifierKeyState::LeftAlt, LEFT_ALT_PRESSED }, - { ModifierKeyState::RightCtrl, RIGHT_CTRL_PRESSED }, - { ModifierKeyState::LeftCtrl, LEFT_CTRL_PRESSED }, - { ModifierKeyState::Shift, SHIFT_PRESSED }, - { ModifierKeyState::NumLock, NUMLOCK_ON }, - { ModifierKeyState::ScrollLock, SCROLLLOCK_ON }, - { ModifierKeyState::CapsLock, CAPSLOCK_ON }, - { ModifierKeyState::EnhancedKey, ENHANCED_KEY }, - { ModifierKeyState::NlsDbcsChar, NLS_DBCSCHAR }, - { ModifierKeyState::NlsAlphanumeric, NLS_ALPHANUMERIC }, - { ModifierKeyState::NlsKatakana, NLS_KATAKANA }, - { ModifierKeyState::NlsHiragana, NLS_HIRAGANA }, - { ModifierKeyState::NlsRoman, NLS_ROMAN }, - { ModifierKeyState::NlsImeConversion, NLS_IME_CONVERSION }, - { ModifierKeyState::AltNumpad, ALTNUMPAD_BIT }, - { ModifierKeyState::NlsImeDisable, NLS_IME_DISABLE } -}; - -static_assert(size(ModifierKeyStateTranslationTable) == static_cast(ModifierKeyState::ENUM_COUNT), - "ModifierKeyStateTranslationTable must have a valid mapping for each modifier value"); - -// Routine Description: -// - Expands legacy control keys bitsets into a stl set -// Arguments: -// - flags - legacy bitset to expand -// Return Value: -// - set of ModifierKeyState values that represent flags -std::unordered_set FromConsoleControlKeyFlags(const DWORD flags) -{ - std::unordered_set keyStates; - - for (const auto& mapping : ModifierKeyStateTranslationTable) - { - if (RuntimeIsFlagSet(flags, mapping.second)) - { - keyStates.insert(mapping.first); - } - } - - return keyStates; -} - -// Routine Description: -// - Converts ModifierKeyState back to the bizarre console bitflag associated with it. -// Arguments: -// - modifierKey - modifier to convert -// Return Value: -// - console bitflag associated with modifierKey -DWORD ToConsoleControlKeyFlag(const ModifierKeyState modifierKey) noexcept -{ - for (const auto& mapping : ModifierKeyStateTranslationTable) - { - if (mapping.first == modifierKey) - { - return mapping.second; - } - } - return 0; -} diff --git a/src/types/MouseEvent.cpp b/src/types/MouseEvent.cpp deleted file mode 100644 index 4179cf40117..00000000000 --- a/src/types/MouseEvent.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" -#include "inc/IInputEvent.hpp" - -MouseEvent::~MouseEvent() = default; - -INPUT_RECORD MouseEvent::ToInputRecord() const noexcept -{ - INPUT_RECORD record{ 0 }; - record.EventType = MOUSE_EVENT; - record.Event.MouseEvent.dwMousePosition.X = ::base::saturated_cast(_position.x); - record.Event.MouseEvent.dwMousePosition.Y = ::base::saturated_cast(_position.y); - record.Event.MouseEvent.dwButtonState = _buttonState; - record.Event.MouseEvent.dwControlKeyState = _activeModifierKeys; - record.Event.MouseEvent.dwEventFlags = _eventFlags; - return record; -} - -InputEventType MouseEvent::EventType() const noexcept -{ - return InputEventType::MouseEvent; -} - -void MouseEvent::SetPosition(const til::point position) noexcept -{ - _position = position; -} - -void MouseEvent::SetButtonState(const DWORD buttonState) noexcept -{ - _buttonState = buttonState; -} - -void MouseEvent::SetActiveModifierKeys(const DWORD activeModifierKeys) noexcept -{ - _activeModifierKeys = activeModifierKeys; -} -void MouseEvent::SetEventFlags(const DWORD eventFlags) noexcept -{ - _eventFlags = eventFlags; -} diff --git a/src/types/WindowBufferSizeEvent.cpp b/src/types/WindowBufferSizeEvent.cpp deleted file mode 100644 index 456d452cae8..00000000000 --- a/src/types/WindowBufferSizeEvent.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" -#include "inc/IInputEvent.hpp" - -WindowBufferSizeEvent::~WindowBufferSizeEvent() = default; - -INPUT_RECORD WindowBufferSizeEvent::ToInputRecord() const noexcept -{ - INPUT_RECORD record{ 0 }; - record.EventType = WINDOW_BUFFER_SIZE_EVENT; - record.Event.WindowBufferSizeEvent.dwSize.X = ::base::saturated_cast(_size.width); - record.Event.WindowBufferSizeEvent.dwSize.Y = ::base::saturated_cast(_size.height); - return record; -} - -InputEventType WindowBufferSizeEvent::EventType() const noexcept -{ - return InputEventType::WindowBufferSizeEvent; -} - -void WindowBufferSizeEvent::SetSize(const til::size size) noexcept -{ - _size = size; -} diff --git a/src/types/inc/IInputEvent.hpp b/src/types/inc/IInputEvent.hpp index 8e5dfb0afd7..4641c17332d 100644 --- a/src/types/inc/IInputEvent.hpp +++ b/src/types/inc/IInputEvent.hpp @@ -1,553 +1,89 @@ -/*++ -Copyright (c) Microsoft Corporation - -Module Name: -- IInputEvent.hpp - -Abstract: -- Internal representation of public INPUT_RECORD struct. - -Author: -- Austin Diviness (AustDi) 18-Aug-2017 ---*/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. #pragma once -#include -#include - -#ifndef ALTNUMPAD_BIT -// from winconp.h -#define ALTNUMPAD_BIT 0x04000000 // AltNumpad OEM char (copied from ntuser/inc/kbd.h) -#endif - -#include - -#include -#include -#include -#include - -enum class InputEventType -{ - KeyEvent, - MouseEvent, - WindowBufferSizeEvent, - MenuEvent, - FocusEvent -}; - -class IInputEvent -{ -public: - static std::unique_ptr Create(const INPUT_RECORD& record); - static std::deque> Create(std::span records); - static std::deque> Create(const std::deque& records); - - static std::vector ToInputRecords(const std::deque>& events); - - virtual ~IInputEvent() = 0; - IInputEvent() = default; - IInputEvent(const IInputEvent&) = default; - IInputEvent(IInputEvent&&) = default; - IInputEvent& operator=(const IInputEvent&) & = default; - IInputEvent& operator=(IInputEvent&&) & = default; - - virtual INPUT_RECORD ToInputRecord() const noexcept = 0; - - virtual InputEventType EventType() const noexcept = 0; - -#ifdef UNIT_TESTING - friend std::wostream& operator<<(std::wostream& stream, const IInputEvent* const pEvent); -#endif -}; - -inline IInputEvent::~IInputEvent() = default; - -using InputEventQueue = std::deque>; - -#ifdef UNIT_TESTING -std::wostream& operator<<(std::wostream& stream, const IInputEvent* pEvent); -#endif +#include #define ALT_PRESSED (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED) #define CTRL_PRESSED (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED) #define MOD_PRESSED (SHIFT_PRESSED | ALT_PRESSED | CTRL_PRESSED) -// Note taken from VkKeyScan docs (https://msdn.microsoft.com/en-us/library/windows/desktop/ms646329(v=vs.85).aspx): -// For keyboard layouts that use the right-hand ALT key as a shift key -// (for example, the French keyboard layout), the shift state is -// represented by the value 6, because the right-hand ALT key is -// converted internally into CTRL+ALT. -struct VkKeyScanModState -{ - static const byte None = 0; - static const byte ShiftPressed = 1; - static const byte CtrlPressed = 2; - static const byte ShiftAndCtrlPressed = ShiftPressed | CtrlPressed; - static const byte AltPressed = 4; - static const byte ShiftAndAltPressed = ShiftPressed | AltPressed; - static const byte CtrlAndAltPressed = CtrlPressed | AltPressed; - static const byte ModPressed = ShiftPressed | CtrlPressed | AltPressed; -}; - -enum class ModifierKeyState -{ - RightAlt, - LeftAlt, - RightCtrl, - LeftCtrl, - Shift, - NumLock, - ScrollLock, - CapsLock, - EnhancedKey, - NlsDbcsChar, - NlsAlphanumeric, - NlsKatakana, - NlsHiragana, - NlsRoman, - NlsImeConversion, - AltNumpad, - NlsImeDisable, - ENUM_COUNT // must be the last element in the enum class -}; +using InputEventQueue = til::small_vector; -std::unordered_set FromVkKeyScan(const short vkKeyScanFlags); -std::unordered_set FromConsoleControlKeyFlags(const DWORD flags); -DWORD ToConsoleControlKeyFlag(const ModifierKeyState modifierKey) noexcept; - -class KeyEvent : public IInputEvent +// The following abstractions exist to hopefully make it easier to migrate +// to a different underlying InputEvent type if we ever choose to do so. +// (As unlikely as that is, given that the main user is the console API which will always use INPUT_RECORD.) +constexpr INPUT_RECORD SynthesizeKeyEvent(bool bKeyDown, uint16_t wRepeatCount, uint16_t wVirtualKeyCode, uint16_t wVirtualScanCode, wchar_t UnicodeChar, uint32_t dwControlKeyState) { -public: - enum class Modifiers : DWORD - { - None = 0, - RightAlt = RIGHT_ALT_PRESSED, - LeftAlt = LEFT_ALT_PRESSED, - RightCtrl = RIGHT_CTRL_PRESSED, - LeftCtrl = LEFT_CTRL_PRESSED, - Shift = SHIFT_PRESSED, - NumLock = NUMLOCK_ON, - ScrollLock = SCROLLLOCK_ON, - CapsLock = CAPSLOCK_ON, - EnhancedKey = ENHANCED_KEY, - DbcsChar = NLS_DBCSCHAR, - Alphanumeric = NLS_ALPHANUMERIC, - Katakana = NLS_KATAKANA, - Hiragana = NLS_HIRAGANA, - Roman = NLS_ROMAN, - ImeConvert = NLS_IME_CONVERSION, - AltNumpad = ALTNUMPAD_BIT, - ImeDisable = NLS_IME_DISABLE - }; - - constexpr KeyEvent(const KEY_EVENT_RECORD& record) : - _keyDown{ !!record.bKeyDown }, - _repeatCount{ record.wRepeatCount }, - _virtualKeyCode{ record.wVirtualKeyCode }, - _virtualScanCode{ record.wVirtualScanCode }, - _charData{ record.uChar.UnicodeChar }, - _activeModifierKeys{ record.dwControlKeyState } - { - } - - constexpr KeyEvent(const bool keyDown, - const WORD repeatCount, - const WORD virtualKeyCode, - const WORD virtualScanCode, - const wchar_t charData, - const DWORD activeModifierKeys) : - _keyDown{ keyDown }, - _repeatCount{ repeatCount }, - _virtualKeyCode{ virtualKeyCode }, - _virtualScanCode{ virtualScanCode }, - _charData{ charData }, - _activeModifierKeys{ activeModifierKeys } - { - } - - constexpr KeyEvent() noexcept : - _keyDown{ 0 }, - _repeatCount{ 0 }, - _virtualKeyCode{ 0 }, - _virtualScanCode{ 0 }, - _charData{ 0 }, - _activeModifierKeys{ 0 } - { - } - - static std::pair MakePair( - const WORD repeatCount, - const WORD virtualKeyCode, - const WORD virtualScanCode, - const wchar_t charData, - const DWORD activeModifierKeys) - { - return std::make_pair( - { true, - repeatCount, - virtualKeyCode, - virtualScanCode, - charData, - activeModifierKeys }, - { false, - repeatCount, - virtualKeyCode, - virtualScanCode, - charData, - activeModifierKeys }); - } - - ~KeyEvent(); - KeyEvent(const KeyEvent&) = default; - KeyEvent(KeyEvent&&) = default; -// For these two operators, there seems to be a bug in the compiler: -// See https://stackoverflow.com/a/60206505/1481137 -// > C.128 applies only to virtual member functions, but operator= is not -// > virtual in your base class and neither does it have the same signature as -// > in the derived class, so there is no reason for it to apply. -#pragma warning(push) -#pragma warning(disable : 26456) - KeyEvent& operator=(const KeyEvent&) & = default; - KeyEvent& operator=(KeyEvent&&) & = default; -#pragma warning(pop) - - INPUT_RECORD ToInputRecord() const noexcept override; - InputEventType EventType() const noexcept override; - - constexpr bool IsShiftPressed() const noexcept - { - return WI_IsFlagSet(GetActiveModifierKeys(), SHIFT_PRESSED); - } - - constexpr bool IsAltPressed() const noexcept - { - return WI_IsAnyFlagSet(GetActiveModifierKeys(), ALT_PRESSED); - } - - constexpr bool IsCtrlPressed() const noexcept - { - return WI_IsAnyFlagSet(GetActiveModifierKeys(), CTRL_PRESSED); - } - - constexpr bool IsAltGrPressed() const noexcept - { - return WI_IsFlagSet(GetActiveModifierKeys(), LEFT_CTRL_PRESSED) && - WI_IsFlagSet(GetActiveModifierKeys(), RIGHT_ALT_PRESSED); - } - - constexpr bool IsModifierPressed() const noexcept - { - return WI_IsAnyFlagSet(GetActiveModifierKeys(), MOD_PRESSED); - } - - constexpr bool IsCursorKey() const noexcept - { - // true iff vk in [End, Home, Left, Up, Right, Down] - return (_virtualKeyCode >= VK_END) && (_virtualKeyCode <= VK_DOWN); - } - - constexpr bool IsAltNumpadSet() const noexcept - { - return WI_IsFlagSet(GetActiveModifierKeys(), ALTNUMPAD_BIT); - } - - constexpr bool IsKeyDown() const noexcept - { - return _keyDown; - } - - constexpr bool IsPauseKey() const noexcept - { - return (_virtualKeyCode == VK_PAUSE); - } - - constexpr WORD GetRepeatCount() const noexcept - { - return _repeatCount; - } - - constexpr WORD GetVirtualKeyCode() const noexcept - { - return _virtualKeyCode; - } - - constexpr WORD GetVirtualScanCode() const noexcept - { - return _virtualScanCode; - } - - constexpr wchar_t GetCharData() const noexcept - { - return _charData; - } - - constexpr DWORD GetActiveModifierKeys() const noexcept - { - return static_cast(_activeModifierKeys); - } - - void SetKeyDown(const bool keyDown) noexcept; - void SetRepeatCount(const WORD repeatCount) noexcept; - void SetVirtualKeyCode(const WORD virtualKeyCode) noexcept; - void SetVirtualScanCode(const WORD virtualScanCode) noexcept; - void SetCharData(const char character) noexcept; - void SetCharData(const wchar_t character) noexcept; - - void SetActiveModifierKeys(const DWORD activeModifierKeys) noexcept; - void DeactivateModifierKey(const ModifierKeyState modifierKey) noexcept; - void ActivateModifierKey(const ModifierKeyState modifierKey) noexcept; - bool DoActiveModifierKeysMatch(const std::unordered_set& consoleModifiers) const noexcept; - bool IsCommandLineEditingKey() const noexcept; - bool IsPopupKey() const noexcept; - - // Function Description: - // - Returns true if the given VKey represents a modifier key - shift, alt, - // control or the Win key. - // Arguments: - // - vkey: the VKEY to check - // Return Value: - // - true iff the key is a modifier key. - constexpr static bool IsModifierKey(const WORD vkey) - { - return (vkey == VK_CONTROL) || - (vkey == VK_LCONTROL) || - (vkey == VK_RCONTROL) || - (vkey == VK_MENU) || - (vkey == VK_LMENU) || - (vkey == VK_RMENU) || - (vkey == VK_SHIFT) || - (vkey == VK_LSHIFT) || - (vkey == VK_RSHIFT) || - // There is no VK_WIN - (vkey == VK_LWIN) || - (vkey == VK_RWIN); + return INPUT_RECORD{ + .EventType = KEY_EVENT, + .Event = { + .KeyEvent = { + .bKeyDown = bKeyDown, + .wRepeatCount = wRepeatCount, + .wVirtualKeyCode = wVirtualKeyCode, + .wVirtualScanCode = wVirtualScanCode, + .uChar = { .UnicodeChar = UnicodeChar }, + .dwControlKeyState = dwControlKeyState, + }, + }, }; - -private: - bool _keyDown; - WORD _repeatCount; - WORD _virtualKeyCode; - WORD _virtualScanCode; - wchar_t _charData; - Modifiers _activeModifierKeys; - - friend constexpr bool operator==(const KeyEvent& a, const KeyEvent& b) noexcept; -#ifdef UNIT_TESTING - friend std::wostream& operator<<(std::wostream& stream, const KeyEvent* const pKeyEvent); -#endif -}; - -constexpr bool operator==(const KeyEvent& a, const KeyEvent& b) noexcept -{ - return (a._keyDown == b._keyDown && - a._repeatCount == b._repeatCount && - a._virtualKeyCode == b._virtualKeyCode && - a._virtualScanCode == b._virtualScanCode && - a._charData == b._charData && - a._activeModifierKeys == b._activeModifierKeys); } -#ifdef UNIT_TESTING -std::wostream& operator<<(std::wostream& stream, const KeyEvent* const pKeyEvent); -#endif - -class MouseEvent : public IInputEvent +constexpr INPUT_RECORD SynthesizeMouseEvent(til::point dwMousePosition, uint32_t dwButtonState, uint32_t dwControlKeyState, uint32_t dwEventFlags) { -public: - constexpr MouseEvent(const MOUSE_EVENT_RECORD& record) : - _position{ til::wrap_coord(record.dwMousePosition) }, - _buttonState{ record.dwButtonState }, - _activeModifierKeys{ record.dwControlKeyState }, - _eventFlags{ record.dwEventFlags } - { - } - - constexpr MouseEvent(const til::point position, - const DWORD buttonState, - const DWORD activeModifierKeys, - const DWORD eventFlags) : - _position{ position }, - _buttonState{ buttonState }, - _activeModifierKeys{ activeModifierKeys }, - _eventFlags{ eventFlags } - { - } - - ~MouseEvent(); - MouseEvent(const MouseEvent&) = default; - MouseEvent(MouseEvent&&) = default; - MouseEvent& operator=(const MouseEvent&) & = default; - MouseEvent& operator=(MouseEvent&&) & = default; - - INPUT_RECORD ToInputRecord() const noexcept override; - InputEventType EventType() const noexcept override; - - constexpr bool IsMouseMoveEvent() const noexcept - { - return _eventFlags == MOUSE_MOVED; - } - - constexpr til::point GetPosition() const noexcept - { - return _position; - } - - constexpr DWORD GetButtonState() const noexcept - { - return _buttonState; - } - - constexpr DWORD GetActiveModifierKeys() const noexcept - { - return _activeModifierKeys; - } - - constexpr DWORD GetEventFlags() const noexcept - { - return _eventFlags; - } - - void SetPosition(const til::point position) noexcept; - void SetButtonState(const DWORD buttonState) noexcept; - void SetActiveModifierKeys(const DWORD activeModifierKeys) noexcept; - void SetEventFlags(const DWORD eventFlags) noexcept; - -private: - til::point _position; - DWORD _buttonState; - DWORD _activeModifierKeys; - DWORD _eventFlags; - -#ifdef UNIT_TESTING - friend std::wostream& operator<<(std::wostream& stream, const MouseEvent* const pMouseEvent); -#endif -}; - -#ifdef UNIT_TESTING -std::wostream& operator<<(std::wostream& stream, const MouseEvent* const pMouseEvent); -#endif + return INPUT_RECORD{ + .EventType = MOUSE_EVENT, + .Event = { + .MouseEvent = { + .dwMousePosition = { + ::base::saturated_cast(dwMousePosition.x), + ::base::saturated_cast(dwMousePosition.y), + }, + .dwButtonState = dwButtonState, + .dwControlKeyState = dwControlKeyState, + .dwEventFlags = dwEventFlags, + }, + }, + }; +} -class WindowBufferSizeEvent : public IInputEvent +constexpr INPUT_RECORD SynthesizeWindowBufferSizeEvent(til::size dwSize) { -public: - constexpr WindowBufferSizeEvent(const WINDOW_BUFFER_SIZE_RECORD& record) : - _size{ til::wrap_coord_size(record.dwSize) } - { - } - - constexpr WindowBufferSizeEvent(const til::size size) : - _size{ size } - { - } - - ~WindowBufferSizeEvent(); - WindowBufferSizeEvent(const WindowBufferSizeEvent&) = default; - WindowBufferSizeEvent(WindowBufferSizeEvent&&) = default; - WindowBufferSizeEvent& operator=(const WindowBufferSizeEvent&) & = default; - WindowBufferSizeEvent& operator=(WindowBufferSizeEvent&&) & = default; - - INPUT_RECORD ToInputRecord() const noexcept override; - InputEventType EventType() const noexcept override; - - constexpr til::size GetSize() const noexcept - { - return _size; - } - - void SetSize(const til::size size) noexcept; - -private: - til::size _size; - -#ifdef UNIT_TESTING - friend std::wostream& operator<<(std::wostream& stream, const WindowBufferSizeEvent* const pEvent); -#endif -}; - -#ifdef UNIT_TESTING -std::wostream& operator<<(std::wostream& stream, const WindowBufferSizeEvent* const pEvent); -#endif + return INPUT_RECORD{ + .EventType = WINDOW_BUFFER_SIZE_EVENT, + .Event = { + .WindowBufferSizeEvent = { + .dwSize = { + ::base::saturated_cast(dwSize.width), + ::base::saturated_cast(dwSize.height), + }, + }, + }, + }; +} -class MenuEvent : public IInputEvent +constexpr INPUT_RECORD SynthesizeMenuEvent(uint32_t dwCommandId) { -public: - constexpr MenuEvent(const MENU_EVENT_RECORD& record) : - _commandId{ record.dwCommandId } - { - } - - constexpr MenuEvent(const UINT commandId) : - _commandId{ commandId } - { - } - - ~MenuEvent(); - MenuEvent(const MenuEvent&) = default; - MenuEvent(MenuEvent&&) = default; - MenuEvent& operator=(const MenuEvent&) & = default; - MenuEvent& operator=(MenuEvent&&) & = default; - - INPUT_RECORD ToInputRecord() const noexcept override; - InputEventType EventType() const noexcept override; - - constexpr UINT GetCommandId() const noexcept - { - return _commandId; - } - - void SetCommandId(const UINT commandId) noexcept; - -private: - UINT _commandId; - -#ifdef UNIT_TESTING - friend std::wostream& operator<<(std::wostream& stream, const MenuEvent* const pMenuEvent); -#endif -}; - -#ifdef UNIT_TESTING -std::wostream& operator<<(std::wostream& stream, const MenuEvent* const pMenuEvent); -#endif + return INPUT_RECORD{ + .EventType = MENU_EVENT, + .Event = { + .MenuEvent = { + .dwCommandId = dwCommandId, + }, + }, + }; +} -class FocusEvent : public IInputEvent +constexpr INPUT_RECORD SynthesizeFocusEvent(bool bSetFocus) { -public: - constexpr FocusEvent(const FOCUS_EVENT_RECORD& record) : - _focus{ !!record.bSetFocus } - { - } - - constexpr FocusEvent(const bool focus) : - _focus{ focus } - { - } - - ~FocusEvent(); - FocusEvent(const FocusEvent&) = default; - FocusEvent(FocusEvent&&) = default; - FocusEvent& operator=(const FocusEvent&) & = default; - FocusEvent& operator=(FocusEvent&&) & = default; - - INPUT_RECORD ToInputRecord() const noexcept override; - InputEventType EventType() const noexcept override; - - constexpr bool GetFocus() const noexcept - { - return _focus; - } - - void SetFocus(const bool focus) noexcept; - -private: - bool _focus; - -#ifdef UNIT_TESTING - friend std::wostream& operator<<(std::wostream& stream, const FocusEvent* const pFocusEvent); -#endif -}; - -#ifdef UNIT_TESTING -std::wostream& operator<<(std::wostream& stream, const FocusEvent* const pFocusEvent); -#endif + return INPUT_RECORD{ + .EventType = FOCUS_EVENT, + .Event = { + .FocusEvent = { + .bSetFocus = bSetFocus, + }, + }, + }; +} diff --git a/src/types/lib/types.vcxproj b/src/types/lib/types.vcxproj index bceb8348e8c..4da14e915bf 100644 --- a/src/types/lib/types.vcxproj +++ b/src/types/lib/types.vcxproj @@ -16,12 +16,6 @@ - - - - - - @@ -30,7 +24,6 @@ - Create @@ -66,4 +59,4 @@ - + \ No newline at end of file diff --git a/src/types/lib/types.vcxproj.filters b/src/types/lib/types.vcxproj.filters index ce73211829c..efb65b2bc42 100644 --- a/src/types/lib/types.vcxproj.filters +++ b/src/types/lib/types.vcxproj.filters @@ -24,30 +24,9 @@ Source Files - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - Source Files - - Source Files - Source Files @@ -69,9 +48,6 @@ Source Files - - Source Files - Source Files @@ -116,24 +92,6 @@ Header Files - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - Header Files @@ -152,9 +110,6 @@ Header Files - - Header Files - Header Files diff --git a/src/types/sources.inc b/src/types/sources.inc index 7df45eae88b..6527fbed578 100644 --- a/src/types/sources.inc +++ b/src/types/sources.inc @@ -30,15 +30,9 @@ PRECOMPILED_INCLUDE = ..\precomp.h SOURCES= \ ..\CodepointWidthDetector.cpp \ ..\ColorFix.cpp \ - ..\IInputEvent.cpp \ - ..\FocusEvent.cpp \ ..\GlyphWidth.cpp \ - ..\KeyEvent.cpp \ - ..\MenuEvent.cpp \ ..\ModifierKeyState.cpp \ - ..\MouseEvent.cpp \ ..\Viewport.cpp \ - ..\WindowBufferSizeEvent.cpp \ ..\convert.cpp \ ..\colorTable.cpp \ ..\utils.cpp \ diff --git a/tools/ConsoleTypes.natvis b/tools/ConsoleTypes.natvis index c7e3342ba0d..7f5f59a4341 100644 --- a/tools/ConsoleTypes.natvis +++ b/tools/ConsoleTypes.natvis @@ -45,9 +45,27 @@ - - {{↓ wch:{_charData} mod:{_activeModifierKeys} repeat:{_repeatCount} vk:{_virtualKeyCode} vsc:{_virtualScanCode}} - {{↑ wch:{_charData} mod:{_activeModifierKeys} repeat:{_repeatCount} vk:{_virtualKeyCode} vsc:{_virtualScanCode}} + + down:{bKeyDown} rep:{wRepeatCount} vk:{wVirtualKeyCode} sc:{wVirtualScanCode} ch:{uChar.UnicodeChar} ctrl:{(unsigned short)dwControlKeyState,x} + + + pos:{dwMousePosition.X},{dwMousePosition.Y} state:{dwButtonState,x} ctrl:{(unsigned short)dwControlKeyState,x} flags:{(unsigned short)dwEventFlags,x} + + + size:{dwSize.X},{dwSize.Y} + + + id:{dwCommandId} + + + focus:{bSetFocus} + + + Key {Event.KeyEvent} + Mouse {Event.MouseEvent} + WindowBufferSize {Event.WindowBufferSizeEvent} + Menu {Event.MenuEvent} + Focus {Event.FocusEvent} From cd37514e00110a3a33cc0b9b88d6aa72e221fafb Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 27 Jul 2023 17:36:55 +0200 Subject: [PATCH 2/8] Address feedback --- tools/ConsoleTypes.natvis | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/ConsoleTypes.natvis b/tools/ConsoleTypes.natvis index 7f5f59a4341..fa1975e3e48 100644 --- a/tools/ConsoleTypes.natvis +++ b/tools/ConsoleTypes.natvis @@ -46,7 +46,8 @@ - down:{bKeyDown} rep:{wRepeatCount} vk:{wVirtualKeyCode} sc:{wVirtualScanCode} ch:{uChar.UnicodeChar} ctrl:{(unsigned short)dwControlKeyState,x} + ↓ rep:{wRepeatCount} vk:{wVirtualKeyCode} sc:{wVirtualScanCode} ch:{uChar.UnicodeChar} ctrl:{(unsigned short)dwControlKeyState,x} + ↑ rep:{wRepeatCount} vk:{wVirtualKeyCode} sc:{wVirtualScanCode} ch:{uChar.UnicodeChar} ctrl:{(unsigned short)dwControlKeyState,x} pos:{dwMousePosition.X},{dwMousePosition.Y} state:{dwButtonState,x} ctrl:{(unsigned short)dwControlKeyState,x} flags:{(unsigned short)dwEventFlags,x} From 77cb0e30058009a8757b8c118a362705196c2581 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 27 Jul 2023 21:21:49 +0200 Subject: [PATCH 3/8] Fix bad rebase --- src/host/ft_host/CJK_DbcsTests.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/host/ft_host/CJK_DbcsTests.cpp b/src/host/ft_host/CJK_DbcsTests.cpp index 6e29b8a815c..68798b700fd 100644 --- a/src/host/ft_host/CJK_DbcsTests.cpp +++ b/src/host/ft_host/CJK_DbcsTests.cpp @@ -1919,11 +1919,11 @@ void DbcsTests::TestMultibyteInputCoalescing() DWORD count; { - const auto record = KeyEvent{ true, 1, 123, 456, 0x82, 789 }.ToInputRecord(); + const auto record = SynthesizeKeyEvent(true, 1, 123, 456, 0x82, 789); VERIFY_WIN32_BOOL_SUCCEEDED(WriteConsoleInputA(in, &record, 1, &count)); } { - const auto record = KeyEvent{ true, 1, 234, 567, 0xA2, 890 }.ToInputRecord(); + const auto record = SynthesizeKeyEvent(true, 1, 234, 567, 0xA2, 890); VERIFY_WIN32_BOOL_SUCCEEDED(WriteConsoleInputA(in, &record, 1, &count)); } @@ -1933,7 +1933,7 @@ void DbcsTests::TestMultibyteInputCoalescing() VERIFY_WIN32_BOOL_SUCCEEDED(ReadConsoleInputW(in, &actual[0], 2, &count)); VERIFY_ARE_EQUAL(1u, count); - const auto expected = KeyEvent{ true, 1, 123, 456, L'い', 789 }.ToInputRecord(); + const auto expected = SynthesizeKeyEvent(true, 1, 123, 456, L'い', 789); VERIFY_ARE_EQUAL(expected, actual[0]); } From 18b13ce4d2042362a3bbacac1f77528d2f8b57d6 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 27 Jul 2023 23:43:00 +0200 Subject: [PATCH 4/8] Fix another bad rebase --- src/terminal/parser/ut_parser/InputEngineTest.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/terminal/parser/ut_parser/InputEngineTest.cpp b/src/terminal/parser/ut_parser/InputEngineTest.cpp index 77b734895a2..2e21c77c74f 100644 --- a/src/terminal/parser/ut_parser/InputEngineTest.cpp +++ b/src/terminal/parser/ut_parser/InputEngineTest.cpp @@ -959,7 +959,10 @@ void InputEngineTest::AltIntermediateTest() auto pfnInputStateMachineCallback = [&](const std::span& inEvents) { for (auto& ev : inEvents) { - terminalInput.HandleKey(ev); + if (const auto str = terminalInput.HandleKey(ev)) + { + translation.append(*str); + } } }; auto dispatch = std::make_unique(pfnInputStateMachineCallback, &testState); From d28fa9c3cba2191cbaca4b21284ec10c5b3d48b6 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 2 Aug 2023 15:52:44 +0200 Subject: [PATCH 5/8] Address feedback --- src/terminal/input/terminalInput.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/terminal/input/terminalInput.cpp b/src/terminal/input/terminalInput.cpp index 7c90ec383ec..855ba74b28a 100644 --- a/src/terminal/input/terminalInput.cpp +++ b/src/terminal/input/terminalInput.cpp @@ -269,45 +269,45 @@ void TerminalInput::ForceDisableWin32InputMode(const bool win32InputMode) noexce _forceDisableWin32InputMode = win32InputMode; } -static const std::span _getKeyMapping(const KEY_EVENT_RECORD& keyEvent, - const bool ansiMode, - const bool cursorApplicationMode, - const bool keypadApplicationMode) noexcept +static std::span _getKeyMapping(const KEY_EVENT_RECORD& keyEvent, const bool ansiMode, const bool cursorApplicationMode, const bool keypadApplicationMode) noexcept { + // Cursor keys: VK_END, VK_HOME, VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN + const auto isCursorKey = keyEvent.wVirtualKeyCode >= VK_END && keyEvent.wVirtualKeyCode <= VK_DOWN; + if (ansiMode) { - if (keyEvent.wVirtualKeyCode >= VK_END && keyEvent.wVirtualKeyCode <= VK_DOWN) + if (isCursorKey) { if (cursorApplicationMode) { - return { s_cursorKeysApplicationMapping.data(), s_cursorKeysApplicationMapping.size() }; + return s_cursorKeysApplicationMapping; } else { - return { s_cursorKeysNormalMapping.data(), s_cursorKeysNormalMapping.size() }; + return s_cursorKeysNormalMapping; } } else { if (keypadApplicationMode) { - return { s_keypadApplicationMapping.data(), s_keypadApplicationMapping.size() }; + return s_keypadApplicationMapping; } else { - return { s_keypadNumericMapping.data(), s_keypadNumericMapping.size() }; + return s_keypadNumericMapping; } } } else { - if (keyEvent.wVirtualKeyCode >= VK_END && keyEvent.wVirtualKeyCode <= VK_DOWN) + if (isCursorKey) { - return { s_cursorKeysVt52Mapping.data(), s_cursorKeysVt52Mapping.size() }; + return s_cursorKeysVt52Mapping; } else { - return { s_keypadVt52Mapping.data(), s_keypadVt52Mapping.size() }; + return s_keypadVt52Mapping; } } } From 2683f093f42d1b58eaea27eb121ed690e1db42f6 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 7 Aug 2023 14:23:39 +0200 Subject: [PATCH 6/8] Move GetChar functions next to GetChar --- src/host/cmdline.cpp | 89 -------------------------------------------- src/host/cmdline.h | 3 -- src/host/stream.cpp | 89 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 92 deletions(-) diff --git a/src/host/cmdline.cpp b/src/host/cmdline.cpp index 3efcec4a92f..1e1587e37fc 100644 --- a/src/host/cmdline.cpp +++ b/src/host/cmdline.cpp @@ -256,95 +256,6 @@ void SetCurrentCommandLine(COOKED_READ_DATA& cookedReadData, _In_ CommandHistory cookedReadData.SetBufferCurrentPtr(cookedReadData.BufferStartPtr() + CharsToWrite); } -bool IsCommandLinePopupKey(const KEY_EVENT_RECORD& event) -{ - if (WI_AreAllFlagsClear(event.dwControlKeyState, RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED | RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) - { - switch (event.wVirtualKeyCode) - { - case VK_ESCAPE: - case VK_PRIOR: - case VK_NEXT: - case VK_END: - case VK_HOME: - case VK_LEFT: - case VK_UP: - case VK_RIGHT: - case VK_DOWN: - case VK_F2: - case VK_F4: - case VK_F7: - case VK_F9: - case VK_DELETE: - return true; - default: - break; - } - } - - return false; -} - -bool IsCommandLineEditingKey(const KEY_EVENT_RECORD& event) -{ - if (WI_AreAllFlagsClear(event.dwControlKeyState, RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED | RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) - { - switch (event.wVirtualKeyCode) - { - case VK_ESCAPE: - case VK_PRIOR: - case VK_NEXT: - case VK_END: - case VK_HOME: - case VK_LEFT: - case VK_UP: - case VK_RIGHT: - case VK_DOWN: - case VK_INSERT: - case VK_DELETE: - case VK_F1: - case VK_F2: - case VK_F3: - case VK_F4: - case VK_F5: - case VK_F6: - case VK_F7: - case VK_F8: - case VK_F9: - return true; - default: - break; - } - } - if (WI_AreAllFlagsClear(event.dwControlKeyState, RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) - { - switch (event.wVirtualKeyCode) - { - case VK_END: - case VK_HOME: - case VK_LEFT: - case VK_RIGHT: - return true; - default: - break; - } - } - - if (WI_AreAllFlagsClear(event.dwControlKeyState, RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) - { - switch (event.wVirtualKeyCode) - { - case VK_F7: - case VK_F10: - return true; - default: - break; - } - } - - return false; -} - // Routine Description: // - This routine handles the command list popup. It puts up the popup, then calls ProcessCommandListInput to get and process input. // Return Value: diff --git a/src/host/cmdline.h b/src/host/cmdline.h index 69c49b4e79e..e3e9511ff29 100644 --- a/src/host/cmdline.h +++ b/src/host/cmdline.h @@ -90,6 +90,3 @@ bool IsWordDelim(const std::wstring_view charData); bool IsValidStringBuffer(_In_ bool Unicode, _In_reads_bytes_(Size) PVOID Buffer, _In_ ULONG Size, _In_ ULONG Count, ...); void SetCurrentCommandLine(COOKED_READ_DATA& cookedReadData, _In_ CommandHistory::Index Index); - -bool IsCommandLinePopupKey(const KEY_EVENT_RECORD& event); -bool IsCommandLineEditingKey(const KEY_EVENT_RECORD& event); diff --git a/src/host/stream.cpp b/src/host/stream.cpp index 3ba139b1826..e845dd9b634 100644 --- a/src/host/stream.cpp +++ b/src/host/stream.cpp @@ -18,6 +18,95 @@ using Microsoft::Console::Interactivity::ServiceLocator; +static bool IsCommandLinePopupKey(const KEY_EVENT_RECORD& event) +{ + if (WI_AreAllFlagsClear(event.dwControlKeyState, RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED | RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) + { + switch (event.wVirtualKeyCode) + { + case VK_ESCAPE: + case VK_PRIOR: + case VK_NEXT: + case VK_END: + case VK_HOME: + case VK_LEFT: + case VK_UP: + case VK_RIGHT: + case VK_DOWN: + case VK_F2: + case VK_F4: + case VK_F7: + case VK_F9: + case VK_DELETE: + return true; + default: + break; + } + } + + return false; +} + +static bool IsCommandLineEditingKey(const KEY_EVENT_RECORD& event) +{ + if (WI_AreAllFlagsClear(event.dwControlKeyState, RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED | RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) + { + switch (event.wVirtualKeyCode) + { + case VK_ESCAPE: + case VK_PRIOR: + case VK_NEXT: + case VK_END: + case VK_HOME: + case VK_LEFT: + case VK_UP: + case VK_RIGHT: + case VK_DOWN: + case VK_INSERT: + case VK_DELETE: + case VK_F1: + case VK_F2: + case VK_F3: + case VK_F4: + case VK_F5: + case VK_F6: + case VK_F7: + case VK_F8: + case VK_F9: + return true; + default: + break; + } + } + if (WI_AreAllFlagsClear(event.dwControlKeyState, RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) + { + switch (event.wVirtualKeyCode) + { + case VK_END: + case VK_HOME: + case VK_LEFT: + case VK_RIGHT: + return true; + default: + break; + } + } + + if (WI_AreAllFlagsClear(event.dwControlKeyState, RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) + { + switch (event.wVirtualKeyCode) + { + case VK_F7: + case VK_F10: + return true; + default: + break; + } + } + + return false; +} + // Routine Description: // - This routine is used in stream input. It gets input and filters it for unicode characters. // Arguments: From 73f545ec4663a66bb2533a3c4da6773c1bdf20f8 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 10 Aug 2023 21:26:06 +0200 Subject: [PATCH 7/8] Address feedback --- src/interactivity/base/EventSynthesis.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/interactivity/base/EventSynthesis.cpp b/src/interactivity/base/EventSynthesis.cpp index 1258468b1cb..b26f8a369c3 100644 --- a/src/interactivity/base/EventSynthesis.cpp +++ b/src/interactivity/base/EventSynthesis.cpp @@ -12,7 +12,7 @@ static constexpr WORD leftShiftScanCode = 0x2A; // Routine Description: // - naively determines the width of a UCS2 encoded wchar (with caveats noted above) #pragma warning(suppress : 4505) // this function will be deleted if numpad events are disabled -static bool GetQuickCharWidthLegacyForNumpadEventSynthesis(const wchar_t wch) noexcept +static bool IsCharFullWidth(const wchar_t wch) noexcept { return (0x1100 <= wch && wch <= 0x115f) || // From Unicode 9.0, Hangul Choseong is wide (0x2e80 <= wch && wch <= 0x303e) || // From Unicode 9.0, this range is wide (assorted languages) @@ -47,7 +47,7 @@ void Microsoft::Console::Interactivity::CharToKeyEvents(const wchar_t wch, const WORD CharType = 0; GetStringTypeW(CT_CTYPE3, &wch, 1, &CharType); - if (WI_IsFlagClear(CharType, C3_ALPHA) && !GetQuickCharWidthLegacyForNumpadEventSynthesis(wch)) + if (WI_IsFlagClear(CharType, C3_ALPHA) && !IsCharFullWidth(wch)) { // It wasn't alphanumeric or determined to be wide by the old algorithm // if VkKeyScanW fails (char is not in kbd layout), we must @@ -75,6 +75,8 @@ void Microsoft::Console::Interactivity::SynthesizeKeyboardEvents(const wchar_t w { const auto vk = LOBYTE(keyState); const auto sc = gsl::narrow(OneCoreSafeMapVirtualKeyW(vk, MAPVK_VK_TO_VSC)); + // The caller provides us with the result of VkKeyScanW() in keyState. + // The magic constants below are the expected (documented) return values from VkKeyScanW(). const auto modifierState = HIBYTE(keyState); const auto shiftSet = WI_IsFlagSet(modifierState, 1); const auto ctrlSet = WI_IsFlagSet(modifierState, 2); From b1e2810303a55bdfabcf97d854fe63cc298e7a50 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 10 Aug 2023 23:21:07 +0200 Subject: [PATCH 8/8] Fix cmdline vkey filter --- src/host/stream.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/host/stream.cpp b/src/host/stream.cpp index e845dd9b634..e8dbe5aa2b0 100644 --- a/src/host/stream.cpp +++ b/src/host/stream.cpp @@ -43,7 +43,6 @@ static bool IsCommandLinePopupKey(const KEY_EVENT_RECORD& event) break; } } - return false; } @@ -78,7 +77,7 @@ static bool IsCommandLineEditingKey(const KEY_EVENT_RECORD& event) break; } } - if (WI_AreAllFlagsClear(event.dwControlKeyState, RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) + if (WI_IsAnyFlagSet(event.dwControlKeyState, RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) { switch (event.wVirtualKeyCode) { @@ -91,8 +90,7 @@ static bool IsCommandLineEditingKey(const KEY_EVENT_RECORD& event) break; } } - - if (WI_AreAllFlagsClear(event.dwControlKeyState, RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) + if (WI_IsAnyFlagSet(event.dwControlKeyState, RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) { switch (event.wVirtualKeyCode) {