|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "ConsoleInput.h" |
|
|
|
#include <stdio.h> |
|
#include <string.h> |
|
|
|
#include <algorithm> |
|
#include <string> |
|
|
|
#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" |
|
|
|
|
|
#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) |
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
} |
|
|
|
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(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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() |
|
{ |
|
|
|
if (m_mouseMode == WINPTY_MOUSE_MODE_AUTO) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
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<INPUT_RECORD> 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<INPUT_RECORD> &records) |
|
{ |
|
if (records.size() == 0) { |
|
return; |
|
} |
|
DWORD actual = 0; |
|
if (!WriteConsoleInputW(m_conin, records.data(), records.size(), &actual)) { |
|
trace("WriteConsoleInputW failed"); |
|
} |
|
records.clear(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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<INPUT_RECORD> &records, |
|
const char *input, |
|
int inputSize, |
|
bool isEof) |
|
{ |
|
ASSERT(inputSize >= 1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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') { |
|
|
|
int dsrLen = matchDsr(input, inputSize); |
|
if (dsrLen > 0) { |
|
trace("Received a DSR reply"); |
|
m_dsrSent = false; |
|
return dsrLen; |
|
} else if (!isEof && dsrLen == -1) { |
|
|
|
trace("Incomplete DSR match"); |
|
return -1; |
|
} |
|
|
|
int mouseLen = scanMouseInput(records, input, inputSize); |
|
if (mouseLen > 0 || (!isEof && mouseLen == -1)) { |
|
return mouseLen; |
|
} |
|
} |
|
|
|
|
|
InputMap::Key match; |
|
bool incomplete; |
|
int matchLen = m_inputMap.lookupKey(input, inputSize, match, incomplete); |
|
if (!isEof && incomplete) { |
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (input[0] == '\x1B' && inputSize >= 2 && input[1] != '\x1B') { |
|
const int len = utf8CharLength(input[1]); |
|
if (len > 0) { |
|
if (1 + len > inputSize) { |
|
|
|
trace("Incomplete UTF-8 character in Alt-<Char>"); |
|
return -1; |
|
} |
|
appendUtf8Char(records, &input[1], len, true); |
|
return 1 + len; |
|
} |
|
} |
|
|
|
|
|
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<unsigned char>(input[0])); |
|
} |
|
return 1; |
|
} |
|
if (len > inputSize) { |
|
|
|
trace("Incomplete UTF-8 character"); |
|
return -1; |
|
} |
|
appendUtf8Char(records, &input[0], len, false); |
|
return len; |
|
} |
|
|
|
int ConsoleInput::scanMouseInput(std::vector<INPUT_RECORD> &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<int>(record.coord.X, |
|
m_mouseWindowRect.width() - 1)); |
|
|
|
mer.dwMousePosition.Y = |
|
m_mouseWindowRect.Top + |
|
std::max(0, std::min<int>(record.coord.Y, |
|
m_mouseWindowRect.height() - 1)); |
|
|
|
|
|
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) { |
|
|
|
mer.dwEventFlags |= MOUSE_WHEELED; |
|
if (button == 0) { |
|
|
|
mer.dwButtonState |= 0x00780000; |
|
} else if (button == 1) { |
|
|
|
mer.dwButtonState |= 0xff880000; |
|
} else { |
|
|
|
return len; |
|
} |
|
} else { |
|
|
|
if (record.flags & 0x20) { mer.dwEventFlags |= MOUSE_MOVED; } |
|
if (button == 3) { |
|
m_mouseButtonState = 0; |
|
|
|
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) { |
|
|
|
m_doubleClick.released = true; |
|
} else { |
|
|
|
m_doubleClick = DoubleClickDetection(); |
|
} |
|
} else if ((m_mouseButtonState & relevantFlag) == 0) { |
|
|
|
m_mouseButtonState |= relevantFlag; |
|
|
|
|
|
|
|
|
|
if (m_doubleClick.button == relevantFlag && |
|
m_doubleClick.pos == record.coord && |
|
(GetTickCount() - m_doubleClick.tick < |
|
GetDoubleClickTime())) { |
|
|
|
mer.dwEventFlags |= DOUBLE_CLICK; |
|
m_doubleClick = DoubleClickDetection(); |
|
} else { |
|
|
|
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<INPUT_RECORD> &records, |
|
const char *charBuffer, |
|
const int charLen, |
|
const bool terminalAltEscape) |
|
{ |
|
const uint32_t codePoint = decodeUtf8(charBuffer); |
|
if (codePoint == static_cast<uint32_t>(-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<true, uint8_t>(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)) { |
|
|
|
|
|
|
|
|
|
|
|
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<INPUT_RECORD> &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<INPUT_RECORD> &records, |
|
BOOL keyDown, |
|
uint16_t virtualKey, |
|
uint32_t codePoint, |
|
uint16_t keyState) |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 { |
|
|
|
|
|
trace("INTERNAL ERROR: appendInputRecordCP: invalid codePoint: " |
|
"U+%04X", codePoint); |
|
} |
|
} |
|
|
|
void ConsoleInput::appendInputRecord(std::vector<INPUT_RECORD> &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; |
|
} |
|
|