X-Git-Url: https://git.josue.xyz/?p=VSoRC%2F.git;a=blobdiff_plain;f=node_modules%2Fnode-pty%2Fdeps%2Fwinpty%2Fsrc%2Fagent%2FConsoleInput.cc;fp=node_modules%2Fnode-pty%2Fdeps%2Fwinpty%2Fsrc%2Fagent%2FConsoleInput.cc;h=0000000000000000000000000000000000000000;hp=192cac2a298f3325d56b52b94414caad4691c572;hb=5e96dd57ddd883604e87f62bdddcb111c63a6e1a;hpb=acb5f682a2b75b972710cabd81658f63071324b0 diff --git a/node_modules/node-pty/deps/winpty/src/agent/ConsoleInput.cc b/node_modules/node-pty/deps/winpty/src/agent/ConsoleInput.cc deleted file mode 100644 index 192cac2..0000000 --- a/node_modules/node-pty/deps/winpty/src/agent/ConsoleInput.cc +++ /dev/null @@ -1,852 +0,0 @@ -// Copyright (c) 2011-2015 Ryan Prichard -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -#include "ConsoleInput.h" - -#include -#include - -#include -#include - -#include "../include/winpty_constants.h" - -#include "../shared/DebugClient.h" -#include "../shared/StringBuilder.h" -#include "../shared/UnixCtrlChars.h" - -#include "ConsoleInputReencoding.h" -#include "DebugShowInput.h" -#include "DefaultInputMap.h" -#include "DsrSender.h" -#include "UnicodeEncoding.h" -#include "Win32Console.h" - -// MAPVK_VK_TO_VSC isn't defined by the old MinGW. -#ifndef MAPVK_VK_TO_VSC -#define MAPVK_VK_TO_VSC 0 -#endif - -namespace { - -struct MouseRecord { - bool release; - int flags; - COORD coord; - - std::string toString() const; -}; - -std::string MouseRecord::toString() const { - StringBuilder sb(40); - sb << "pos=" << coord.X << ',' << coord.Y - << " flags=0x" << hexOfInt(flags); - if (release) { - sb << " release"; - } - return sb.str_moved(); -} - -const unsigned int kIncompleteEscapeTimeoutMs = 1000u; - -#define CHECK(cond) \ - do { \ - if (!(cond)) { return 0; } \ - } while(0) - -#define ADVANCE() \ - do { \ - pch++; \ - if (pch == stop) { return -1; } \ - } while(0) - -#define SCAN_INT(out, maxLen) \ - do { \ - (out) = 0; \ - CHECK(isdigit(*pch)); \ - const char *begin = pch; \ - do { \ - CHECK(pch - begin + 1 < maxLen); \ - (out) = (out) * 10 + *pch - '0'; \ - ADVANCE(); \ - } while (isdigit(*pch)); \ - } while(0) - -#define SCAN_SIGNED_INT(out, maxLen) \ - do { \ - bool negative = false; \ - if (*pch == '-') { \ - negative = true; \ - ADVANCE(); \ - } \ - SCAN_INT(out, maxLen); \ - if (negative) { \ - (out) = -(out); \ - } \ - } while(0) - -// Match the Device Status Report console input: ESC [ nn ; mm R -// Returns: -// 0 no match -// >0 match, returns length of match -// -1 incomplete match -static int matchDsr(const char *input, int inputSize) -{ - int32_t dummy = 0; - const char *pch = input; - const char *stop = input + inputSize; - CHECK(*pch == '\x1B'); ADVANCE(); - CHECK(*pch == '['); ADVANCE(); - SCAN_INT(dummy, 8); - CHECK(*pch == ';'); ADVANCE(); - SCAN_INT(dummy, 8); - CHECK(*pch == 'R'); - return pch - input + 1; -} - -static int matchMouseDefault(const char *input, int inputSize, - MouseRecord &out) -{ - const char *pch = input; - const char *stop = input + inputSize; - CHECK(*pch == '\x1B'); ADVANCE(); - CHECK(*pch == '['); ADVANCE(); - CHECK(*pch == 'M'); ADVANCE(); - out.flags = (*pch - 32) & 0xFF; ADVANCE(); - out.coord.X = (*pch - '!') & 0xFF; - ADVANCE(); - out.coord.Y = (*pch - '!') & 0xFF; - out.release = false; - return pch - input + 1; -} - -static int matchMouse1006(const char *input, int inputSize, MouseRecord &out) -{ - const char *pch = input; - const char *stop = input + inputSize; - int32_t temp; - CHECK(*pch == '\x1B'); ADVANCE(); - CHECK(*pch == '['); ADVANCE(); - CHECK(*pch == '<'); ADVANCE(); - SCAN_INT(out.flags, 8); - CHECK(*pch == ';'); ADVANCE(); - SCAN_SIGNED_INT(temp, 8); out.coord.X = temp - 1; - CHECK(*pch == ';'); ADVANCE(); - SCAN_SIGNED_INT(temp, 8); out.coord.Y = temp - 1; - CHECK(*pch == 'M' || *pch == 'm'); - out.release = (*pch == 'm'); - return pch - input + 1; -} - -static int matchMouse1015(const char *input, int inputSize, MouseRecord &out) -{ - const char *pch = input; - const char *stop = input + inputSize; - int32_t temp; - CHECK(*pch == '\x1B'); ADVANCE(); - CHECK(*pch == '['); ADVANCE(); - SCAN_INT(out.flags, 8); out.flags -= 32; - CHECK(*pch == ';'); ADVANCE(); - SCAN_SIGNED_INT(temp, 8); out.coord.X = temp - 1; - CHECK(*pch == ';'); ADVANCE(); - SCAN_SIGNED_INT(temp, 8); out.coord.Y = temp - 1; - CHECK(*pch == 'M'); - out.release = false; - return pch - input + 1; -} - -// Match a mouse input escape sequence of any kind. -// 0 no match -// >0 match, returns length of match -// -1 incomplete match -static int matchMouseRecord(const char *input, int inputSize, MouseRecord &out) -{ - memset(&out, 0, sizeof(out)); - int ret; - if ((ret = matchMouse1006(input, inputSize, out)) != 0) { return ret; } - if ((ret = matchMouse1015(input, inputSize, out)) != 0) { return ret; } - if ((ret = matchMouseDefault(input, inputSize, out)) != 0) { return ret; } - return 0; -} - -#undef CHECK -#undef ADVANCE -#undef SCAN_INT - -} // anonymous namespace - -ConsoleInput::ConsoleInput(HANDLE conin, int mouseMode, DsrSender &dsrSender, - Win32Console &console) : - m_console(console), - m_conin(conin), - m_mouseMode(mouseMode), - m_dsrSender(dsrSender) -{ - addDefaultEntriesToInputMap(m_inputMap); - if (hasDebugFlag("dump_input_map")) { - m_inputMap.dumpInputMap(); - } - - // Configure Quick Edit mode according to the mouse mode. Enable - // InsertMode for two reasons: - // - If it's OFF, it's difficult for the user to turn it ON. The - // properties dialog is inaccesible. winpty still faithfully handles - // the Insert key, which toggles between the insertion and overwrite - // modes. - // - When we modify the QuickEdit setting, if ExtendedFlags is OFF, - // then we must choose the InsertMode setting. I don't *think* this - // case happens, though, because a new console always has ExtendedFlags - // ON. - // See misc/EnableExtendedFlags.txt. - DWORD mode = 0; - if (!GetConsoleMode(conin, &mode)) { - trace("Agent startup: GetConsoleMode failed"); - } else { - mode |= ENABLE_EXTENDED_FLAGS; - mode |= ENABLE_INSERT_MODE; - if (m_mouseMode == WINPTY_MOUSE_MODE_AUTO) { - mode |= ENABLE_QUICK_EDIT_MODE; - } else { - mode &= ~ENABLE_QUICK_EDIT_MODE; - } - if (!SetConsoleMode(conin, mode)) { - trace("Agent startup: SetConsoleMode failed"); - } - } - - updateInputFlags(true); -} - -void ConsoleInput::writeInput(const std::string &input) -{ - if (input.size() == 0) { - return; - } - - if (isTracingEnabled()) { - static bool debugInput = hasDebugFlag("input"); - if (debugInput) { - std::string dumpString; - for (size_t i = 0; i < input.size(); ++i) { - const char ch = input[i]; - const char ctrl = decodeUnixCtrlChar(ch); - if (ctrl != '\0') { - dumpString += '^'; - dumpString += ctrl; - } else { - dumpString += ch; - } - } - dumpString += " ("; - for (size_t i = 0; i < input.size(); ++i) { - if (i > 0) { - dumpString += ' '; - } - const unsigned char uch = input[i]; - char buf[32]; - winpty_snprintf(buf, "%02X", uch); - dumpString += buf; - } - dumpString += ')'; - trace("input chars: %s", dumpString.c_str()); - } - } - - m_byteQueue.append(input); - doWrite(false); - if (!m_byteQueue.empty() && !m_dsrSent) { - trace("send DSR"); - m_dsrSender.sendDsr(); - m_dsrSent = true; - } - m_lastWriteTick = GetTickCount(); -} - -void ConsoleInput::flushIncompleteEscapeCode() -{ - if (!m_byteQueue.empty() && - (GetTickCount() - m_lastWriteTick) > kIncompleteEscapeTimeoutMs) { - doWrite(true); - m_byteQueue.clear(); - } -} - -void ConsoleInput::updateInputFlags(bool forceTrace) -{ - const DWORD mode = inputConsoleMode(); - const bool newFlagEE = (mode & ENABLE_EXTENDED_FLAGS) != 0; - const bool newFlagMI = (mode & ENABLE_MOUSE_INPUT) != 0; - const bool newFlagQE = (mode & ENABLE_QUICK_EDIT_MODE) != 0; - const bool newFlagEI = (mode & 0x200) != 0; - if (forceTrace || - newFlagEE != m_enableExtendedEnabled || - newFlagMI != m_mouseInputEnabled || - newFlagQE != m_quickEditEnabled || - newFlagEI != m_escapeInputEnabled) { - trace("CONIN modes: Extended=%s, MouseInput=%s QuickEdit=%s EscapeInput=%s", - newFlagEE ? "on" : "off", - newFlagMI ? "on" : "off", - newFlagQE ? "on" : "off", - newFlagEI ? "on" : "off"); - } - m_enableExtendedEnabled = newFlagEE; - m_mouseInputEnabled = newFlagMI; - m_quickEditEnabled = newFlagQE; - m_escapeInputEnabled = newFlagEI; -} - -bool ConsoleInput::shouldActivateTerminalMouse() -{ - // Return whether the agent should activate the terminal's mouse mode. - if (m_mouseMode == WINPTY_MOUSE_MODE_AUTO) { - // Some programs (e.g. Cygwin command-line programs like bash.exe and - // python2.7.exe) turn off ENABLE_EXTENDED_FLAGS and turn on - // ENABLE_MOUSE_INPUT, but do not turn off QuickEdit mode and do not - // actually care about mouse input. Only enable the terminal mouse - // mode if ENABLE_EXTENDED_FLAGS is on. See - // misc/EnableExtendedFlags.txt. - return m_mouseInputEnabled && !m_quickEditEnabled && - m_enableExtendedEnabled; - } else if (m_mouseMode == WINPTY_MOUSE_MODE_FORCE) { - return true; - } else { - return false; - } -} - -void ConsoleInput::doWrite(bool isEof) -{ - const char *data = m_byteQueue.c_str(); - std::vector records; - size_t idx = 0; - while (idx < m_byteQueue.size()) { - int charSize = scanInput(records, &data[idx], m_byteQueue.size() - idx, isEof); - if (charSize == -1) - break; - idx += charSize; - } - m_byteQueue.erase(0, idx); - flushInputRecords(records); -} - -void ConsoleInput::flushInputRecords(std::vector &records) -{ - if (records.size() == 0) { - return; - } - DWORD actual = 0; - if (!WriteConsoleInputW(m_conin, records.data(), records.size(), &actual)) { - trace("WriteConsoleInputW failed"); - } - records.clear(); -} - -// This behavior isn't strictly correct, because the keypresses (probably?) -// adopt the keyboard state (e.g. Ctrl/Alt/Shift modifiers) of the current -// window station's keyboard, which has no necessary relationship to the winpty -// instance. It's unlikely to be an issue in practice, but it's conceivable. -// (Imagine a foreground SSH server, where the local user holds down Ctrl, -// while the remote user tries to use WSL navigation keys.) I suspect using -// the BackgroundDesktop mechanism in winpty would fix the problem. -// -// https://github.com/rprichard/winpty/issues/116 -static void sendKeyMessage(HWND hwnd, bool isKeyDown, uint16_t virtualKey) -{ - uint32_t scanCode = MapVirtualKey(virtualKey, MAPVK_VK_TO_VSC); - if (scanCode > 255) { - scanCode = 0; - } - SendMessage(hwnd, isKeyDown ? WM_KEYDOWN : WM_KEYUP, virtualKey, - (scanCode << 16) | 1u | (isKeyDown ? 0u : 0xc0000000u)); -} - -int ConsoleInput::scanInput(std::vector &records, - const char *input, - int inputSize, - bool isEof) -{ - ASSERT(inputSize >= 1); - - // Ctrl-C. - // - // In processed mode, use GenerateConsoleCtrlEvent so that Ctrl-C handlers - // are called. GenerateConsoleCtrlEvent unfortunately doesn't interrupt - // ReadConsole calls[1]. Using WM_KEYDOWN/UP fixes the ReadConsole - // problem, but breaks in background window stations/desktops. - // - // In unprocessed mode, there's an entry for Ctrl-C in the SimpleEncoding - // table in DefaultInputMap. - // - // [1] https://github.com/rprichard/winpty/issues/116 - if (input[0] == '\x03' && (inputConsoleMode() & ENABLE_PROCESSED_INPUT)) { - flushInputRecords(records); - trace("Ctrl-C"); - const BOOL ret = GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); - trace("GenerateConsoleCtrlEvent: %d", ret); - return 1; - } - - if (input[0] == '\x1B') { - // Attempt to match the Device Status Report (DSR) reply. - int dsrLen = matchDsr(input, inputSize); - if (dsrLen > 0) { - trace("Received a DSR reply"); - m_dsrSent = false; - return dsrLen; - } else if (!isEof && dsrLen == -1) { - // Incomplete DSR match. - trace("Incomplete DSR match"); - return -1; - } - - int mouseLen = scanMouseInput(records, input, inputSize); - if (mouseLen > 0 || (!isEof && mouseLen == -1)) { - return mouseLen; - } - } - - // Search the input map. - InputMap::Key match; - bool incomplete; - int matchLen = m_inputMap.lookupKey(input, inputSize, match, incomplete); - if (!isEof && incomplete) { - // Incomplete match -- need more characters (or wait for a - // timeout to signify flushed input). - trace("Incomplete escape sequence"); - return -1; - } else if (matchLen > 0) { - uint32_t winCodePointDn = match.unicodeChar; - if ((match.keyState & LEFT_CTRL_PRESSED) && (match.keyState & LEFT_ALT_PRESSED)) { - winCodePointDn = '\0'; - } - uint32_t winCodePointUp = winCodePointDn; - if (match.keyState & LEFT_ALT_PRESSED) { - winCodePointUp = '\0'; - } - appendKeyPress(records, match.virtualKey, - winCodePointDn, winCodePointUp, match.keyState, - match.unicodeChar, match.keyState); - return matchLen; - } - - // Recognize Alt-. - // - // This code doesn't match Alt-ESC, which is encoded as `ESC ESC`, but - // maybe it should. I was concerned that pressing ESC rapidly enough could - // accidentally trigger Alt-ESC. (e.g. The user would have to be faster - // than the DSR flushing mechanism or use a decrepit terminal. The user - // might be on a slow network connection.) - if (input[0] == '\x1B' && inputSize >= 2 && input[1] != '\x1B') { - const int len = utf8CharLength(input[1]); - if (len > 0) { - if (1 + len > inputSize) { - // Incomplete character. - trace("Incomplete UTF-8 character in Alt-"); - return -1; - } - appendUtf8Char(records, &input[1], len, true); - return 1 + len; - } - } - - // A UTF-8 character. - const int len = utf8CharLength(input[0]); - if (len == 0) { - static bool debugInput = isTracingEnabled() && hasDebugFlag("input"); - if (debugInput) { - trace("Discarding invalid input byte: %02X", - static_cast(input[0])); - } - return 1; - } - if (len > inputSize) { - // Incomplete character. - trace("Incomplete UTF-8 character"); - return -1; - } - appendUtf8Char(records, &input[0], len, false); - return len; -} - -int ConsoleInput::scanMouseInput(std::vector &records, - const char *input, - int inputSize) -{ - MouseRecord record; - const int len = matchMouseRecord(input, inputSize, record); - if (len <= 0) { - return len; - } - - if (isTracingEnabled()) { - static bool debugInput = hasDebugFlag("input"); - if (debugInput) { - trace("mouse input: %s", record.toString().c_str()); - } - } - - const int button = record.flags & 0x03; - INPUT_RECORD newRecord = {0}; - newRecord.EventType = MOUSE_EVENT; - MOUSE_EVENT_RECORD &mer = newRecord.Event.MouseEvent; - - mer.dwMousePosition.X = - m_mouseWindowRect.Left + - std::max(0, std::min(record.coord.X, - m_mouseWindowRect.width() - 1)); - - mer.dwMousePosition.Y = - m_mouseWindowRect.Top + - std::max(0, std::min(record.coord.Y, - m_mouseWindowRect.height() - 1)); - - // The modifier state is neatly independent of everything else. - if (record.flags & 0x04) { mer.dwControlKeyState |= SHIFT_PRESSED; } - if (record.flags & 0x08) { mer.dwControlKeyState |= LEFT_ALT_PRESSED; } - if (record.flags & 0x10) { mer.dwControlKeyState |= LEFT_CTRL_PRESSED; } - - if (record.flags & 0x40) { - // Mouse wheel - mer.dwEventFlags |= MOUSE_WHEELED; - if (button == 0) { - // up - mer.dwButtonState |= 0x00780000; - } else if (button == 1) { - // down - mer.dwButtonState |= 0xff880000; - } else { - // Invalid -- do nothing - return len; - } - } else { - // Ordinary mouse event - if (record.flags & 0x20) { mer.dwEventFlags |= MOUSE_MOVED; } - if (button == 3) { - m_mouseButtonState = 0; - // Potentially advance double-click detection. - m_doubleClick.released = true; - } else { - const DWORD relevantFlag = - (button == 0) ? FROM_LEFT_1ST_BUTTON_PRESSED : - (button == 1) ? FROM_LEFT_2ND_BUTTON_PRESSED : - (button == 2) ? RIGHTMOST_BUTTON_PRESSED : - 0; - ASSERT(relevantFlag != 0); - if (record.release) { - m_mouseButtonState &= ~relevantFlag; - if (relevantFlag == m_doubleClick.button) { - // Potentially advance double-click detection. - m_doubleClick.released = true; - } else { - // End double-click detection. - m_doubleClick = DoubleClickDetection(); - } - } else if ((m_mouseButtonState & relevantFlag) == 0) { - // The button has been newly pressed. - m_mouseButtonState |= relevantFlag; - // Detect a double-click. This code looks for an exact - // coordinate match, which is stricter than what Windows does, - // but Windows has pixel coordinates, and we only have terminal - // coordinates. - if (m_doubleClick.button == relevantFlag && - m_doubleClick.pos == record.coord && - (GetTickCount() - m_doubleClick.tick < - GetDoubleClickTime())) { - // Record a double-click and end double-click detection. - mer.dwEventFlags |= DOUBLE_CLICK; - m_doubleClick = DoubleClickDetection(); - } else { - // Begin double-click detection. - m_doubleClick.button = relevantFlag; - m_doubleClick.pos = record.coord; - m_doubleClick.tick = GetTickCount(); - } - } - } - } - - mer.dwButtonState |= m_mouseButtonState; - - if (m_mouseInputEnabled && !m_quickEditEnabled) { - if (isTracingEnabled()) { - static bool debugInput = hasDebugFlag("input"); - if (debugInput) { - trace("mouse event: %s", mouseEventToString(mer).c_str()); - } - } - - records.push_back(newRecord); - } - - return len; -} - -void ConsoleInput::appendUtf8Char(std::vector &records, - const char *charBuffer, - const int charLen, - const bool terminalAltEscape) -{ - const uint32_t codePoint = decodeUtf8(charBuffer); - if (codePoint == static_cast(-1)) { - static bool debugInput = isTracingEnabled() && hasDebugFlag("input"); - if (debugInput) { - StringBuilder error(64); - error << "Discarding invalid UTF-8 sequence:"; - for (int i = 0; i < charLen; ++i) { - error << ' '; - error << hexOfInt(charBuffer[i]); - } - trace("%s", error.c_str()); - } - return; - } - - const short charScan = codePoint > 0xFFFF ? -1 : VkKeyScan(codePoint); - uint16_t virtualKey = 0; - uint16_t winKeyState = 0; - uint32_t winCodePointDn = codePoint; - uint32_t winCodePointUp = codePoint; - uint16_t vtKeyState = 0; - - if (charScan != -1) { - virtualKey = charScan & 0xFF; - if (charScan & 0x100) { - winKeyState |= SHIFT_PRESSED; - } - if (charScan & 0x200) { - winKeyState |= LEFT_CTRL_PRESSED; - } - if (charScan & 0x400) { - winKeyState |= RIGHT_ALT_PRESSED; - } - if (terminalAltEscape && (winKeyState & LEFT_CTRL_PRESSED)) { - // If the terminal escapes a Ctrl- with Alt, then set the - // codepoint to 0. On the other hand, if a character requires - // AltGr (like U+00B2 on a German layout), then VkKeyScan will - // report both Ctrl and Alt pressed, and we should keep the - // codepoint. See https://github.com/rprichard/winpty/issues/109. - winCodePointDn = 0; - winCodePointUp = 0; - } - } - if (terminalAltEscape) { - winCodePointUp = 0; - winKeyState |= LEFT_ALT_PRESSED; - vtKeyState |= LEFT_ALT_PRESSED; - } - - appendKeyPress(records, virtualKey, - winCodePointDn, winCodePointUp, winKeyState, - codePoint, vtKeyState); -} - -void ConsoleInput::appendKeyPress(std::vector &records, - const uint16_t virtualKey, - const uint32_t winCodePointDn, - const uint32_t winCodePointUp, - const uint16_t winKeyState, - const uint32_t vtCodePoint, - const uint16_t vtKeyState) -{ - const bool ctrl = (winKeyState & LEFT_CTRL_PRESSED) != 0; - const bool leftAlt = (winKeyState & LEFT_ALT_PRESSED) != 0; - const bool rightAlt = (winKeyState & RIGHT_ALT_PRESSED) != 0; - const bool shift = (winKeyState & SHIFT_PRESSED) != 0; - const bool enhanced = (winKeyState & ENHANCED_KEY) != 0; - bool hasDebugInput = false; - - if (isTracingEnabled()) { - static bool debugInput = hasDebugFlag("input"); - if (debugInput) { - hasDebugInput = true; - InputMap::Key key = { virtualKey, winCodePointDn, winKeyState }; - trace("keypress: %s", key.toString().c_str()); - } - } - - if (m_escapeInputEnabled && - (virtualKey == VK_UP || - virtualKey == VK_DOWN || - virtualKey == VK_LEFT || - virtualKey == VK_RIGHT || - virtualKey == VK_HOME || - virtualKey == VK_END) && - !ctrl && !leftAlt && !rightAlt && !shift) { - flushInputRecords(records); - if (hasDebugInput) { - trace("sending keypress to console HWND"); - } - sendKeyMessage(m_console.hwnd(), true, virtualKey); - sendKeyMessage(m_console.hwnd(), false, virtualKey); - return; - } - - uint16_t stepKeyState = 0; - if (ctrl) { - stepKeyState |= LEFT_CTRL_PRESSED; - appendInputRecord(records, TRUE, VK_CONTROL, 0, stepKeyState); - } - if (leftAlt) { - stepKeyState |= LEFT_ALT_PRESSED; - appendInputRecord(records, TRUE, VK_MENU, 0, stepKeyState); - } - if (rightAlt) { - stepKeyState |= RIGHT_ALT_PRESSED; - appendInputRecord(records, TRUE, VK_MENU, 0, stepKeyState | ENHANCED_KEY); - } - if (shift) { - stepKeyState |= SHIFT_PRESSED; - appendInputRecord(records, TRUE, VK_SHIFT, 0, stepKeyState); - } - if (enhanced) { - stepKeyState |= ENHANCED_KEY; - } - if (m_escapeInputEnabled) { - reencodeEscapedKeyPress(records, virtualKey, vtCodePoint, vtKeyState); - } else { - appendCPInputRecords(records, TRUE, virtualKey, winCodePointDn, stepKeyState); - } - appendCPInputRecords(records, FALSE, virtualKey, winCodePointUp, stepKeyState); - if (enhanced) { - stepKeyState &= ~ENHANCED_KEY; - } - if (shift) { - stepKeyState &= ~SHIFT_PRESSED; - appendInputRecord(records, FALSE, VK_SHIFT, 0, stepKeyState); - } - if (rightAlt) { - stepKeyState &= ~RIGHT_ALT_PRESSED; - appendInputRecord(records, FALSE, VK_MENU, 0, stepKeyState | ENHANCED_KEY); - } - if (leftAlt) { - stepKeyState &= ~LEFT_ALT_PRESSED; - appendInputRecord(records, FALSE, VK_MENU, 0, stepKeyState); - } - if (ctrl) { - stepKeyState &= ~LEFT_CTRL_PRESSED; - appendInputRecord(records, FALSE, VK_CONTROL, 0, stepKeyState); - } -} - -void ConsoleInput::appendCPInputRecords(std::vector &records, - BOOL keyDown, - uint16_t virtualKey, - uint32_t codePoint, - uint16_t keyState) -{ - // This behavior really doesn't match that of the Windows console (in - // normal, non-escape-mode). Judging by the copy-and-paste behavior, - // Windows apparently handles everything outside of the keyboard layout by - // first sending a sequence of Alt+KeyPad events, then finally a key-up - // event whose UnicodeChar has the appropriate value. For U+00A2 (CENT - // SIGN): - // - // key: dn rpt=1 scn=56 LAlt-MENU ch=0 - // key: dn rpt=1 scn=79 LAlt-NUMPAD1 ch=0 - // key: up rpt=1 scn=79 LAlt-NUMPAD1 ch=0 - // key: dn rpt=1 scn=76 LAlt-NUMPAD5 ch=0 - // key: up rpt=1 scn=76 LAlt-NUMPAD5 ch=0 - // key: dn rpt=1 scn=76 LAlt-NUMPAD5 ch=0 - // key: up rpt=1 scn=76 LAlt-NUMPAD5 ch=0 - // key: up rpt=1 scn=56 MENU ch=0xa2 - // - // The Alt+155 value matches the encoding of U+00A2 in CP-437. Curiously, - // if I use "chcp 1252" to change the encoding, then copy-and-pasting - // produces Alt+162 instead. (U+00A2 is 162 in CP-1252.) However, typing - // Alt+155 or Alt+162 produce the same characters regardless of console - // code page. (That is, they use CP-437 and yield U+00A2 and U+00F3.) - // - // For characters outside the BMP, Windows repeats the process for both - // UTF-16 code units, e.g, for U+1F300 (CYCLONE): - // - // key: dn rpt=1 scn=56 LAlt-MENU ch=0 - // key: dn rpt=1 scn=77 LAlt-NUMPAD6 ch=0 - // key: up rpt=1 scn=77 LAlt-NUMPAD6 ch=0 - // key: dn rpt=1 scn=81 LAlt-NUMPAD3 ch=0 - // key: up rpt=1 scn=81 LAlt-NUMPAD3 ch=0 - // key: up rpt=1 scn=56 MENU ch=0xd83c - // key: dn rpt=1 scn=56 LAlt-MENU ch=0 - // key: dn rpt=1 scn=77 LAlt-NUMPAD6 ch=0 - // key: up rpt=1 scn=77 LAlt-NUMPAD6 ch=0 - // key: dn rpt=1 scn=81 LAlt-NUMPAD3 ch=0 - // key: up rpt=1 scn=81 LAlt-NUMPAD3 ch=0 - // key: up rpt=1 scn=56 MENU ch=0xdf00 - // - // In this case, it sends Alt+63 twice, which signifies '?'. Apparently - // CMD and Cygwin bash are both able to decode this. - // - // Also note that typing Alt+NNN still works if NumLock is off, e.g.: - // - // key: dn rpt=1 scn=56 LAlt-MENU ch=0 - // key: dn rpt=1 scn=79 LAlt-END ch=0 - // key: up rpt=1 scn=79 LAlt-END ch=0 - // key: dn rpt=1 scn=76 LAlt-CLEAR ch=0 - // key: up rpt=1 scn=76 LAlt-CLEAR ch=0 - // key: dn rpt=1 scn=76 LAlt-CLEAR ch=0 - // key: up rpt=1 scn=76 LAlt-CLEAR ch=0 - // key: up rpt=1 scn=56 MENU ch=0xa2 - // - // Evidently, the Alt+NNN key events are not intended to be decoded to a - // character. Maybe programs are looking for a key-up ALT/MENU event with - // a non-zero character? - - wchar_t ws[2]; - const int wslen = encodeUtf16(ws, codePoint); - - if (wslen == 1) { - appendInputRecord(records, keyDown, virtualKey, ws[0], keyState); - } else if (wslen == 2) { - appendInputRecord(records, keyDown, virtualKey, ws[0], keyState); - appendInputRecord(records, keyDown, virtualKey, ws[1], keyState); - } else { - // This situation isn't that bad, but it should never happen, - // because invalid codepoints shouldn't reach this point. - trace("INTERNAL ERROR: appendInputRecordCP: invalid codePoint: " - "U+%04X", codePoint); - } -} - -void ConsoleInput::appendInputRecord(std::vector &records, - BOOL keyDown, - uint16_t virtualKey, - wchar_t utf16Char, - uint16_t keyState) -{ - INPUT_RECORD ir = {}; - ir.EventType = KEY_EVENT; - ir.Event.KeyEvent.bKeyDown = keyDown; - ir.Event.KeyEvent.wRepeatCount = 1; - ir.Event.KeyEvent.wVirtualKeyCode = virtualKey; - ir.Event.KeyEvent.wVirtualScanCode = - MapVirtualKey(virtualKey, MAPVK_VK_TO_VSC); - ir.Event.KeyEvent.uChar.UnicodeChar = utf16Char; - ir.Event.KeyEvent.dwControlKeyState = keyState; - records.push_back(ir); -} - -DWORD ConsoleInput::inputConsoleMode() -{ - DWORD mode = 0; - if (!GetConsoleMode(m_conin, &mode)) { - trace("GetConsoleMode failed"); - return 0; - } - return mode; -}