Work on console input handling.

* If bytes are still queued after processing as many keypresses as
   possible, send a DSR to the console.  The console should respond with
   a DSR reply, which will flush out the input buffer without having to
   wait for a timeout.

 * Add ah hoc code for ALT-<character> rather than listing every character
   in the table.

 * When keypresses have Alt/Ctrl/Shift modifiers, generate extra
   INPUT_RECORDS to press and release each modifier.  EDIT.COM seemed to
   require these for some Ctrl-<key> keypresses I tried.
This commit is contained in:
Ryan Prichard 2012-03-20 02:04:46 -07:00
parent d32e36032d
commit 1fdda9e1d4
4 changed files with 296 additions and 208 deletions

View File

@ -49,7 +49,7 @@ Agent::Agent(LPCWSTR controlPipeName,
m_controlSocket = makeSocket(controlPipeName);
m_dataSocket = makeSocket(dataPipeName);
m_terminal = new Terminal(m_dataSocket);
m_consoleInput = new ConsoleInput(m_console);
m_consoleInput = new ConsoleInput(m_console, this);
resetConsoleTracking(false);
@ -77,6 +77,15 @@ Agent::~Agent()
delete m_consoleInput;
}
// Write a "Device Status Report" command to the terminal. The terminal will
// reply with a row+col escape sequence. Presumably, the DSR reply will not
// split a keypress escape sequence, so it should be safe to assume that the
// bytes before it are complete keypresses.
void Agent::sendDsr()
{
m_dataSocket->write("\x1B[6n");
}
NamedPipe *Agent::makeSocket(LPCWSTR pipeName)
{
NamedPipe *pipe = createNamedPipe();

View File

@ -3,6 +3,7 @@
#include <windows.h>
#include "EventLoop.h"
#include "DsrSender.h"
class Win32Console;
class ConsoleInput;
@ -13,14 +14,15 @@ class NamedPipe;
const int BUFFER_LINE_COUNT = 3000; // TODO: Use something like 9000.
const int MAX_CONSOLE_WIDTH = 500;
class Agent : public EventLoop
class Agent : public EventLoop, public DsrSender
{
public:
Agent(LPCWSTR controlPipeName,
LPCWSTR dataPipeName,
int initialCols,
int initialRows);
~Agent();
virtual ~Agent();
void sendDsr();
private:
NamedPipe *makeSocket(LPCWSTR pipeName);

View File

@ -1,14 +1,12 @@
#include "ConsoleInput.h"
#include "Win32Console.h"
#include "DsrSender.h"
#include "../Shared/DebugClient.h"
#include <string.h>
#ifndef MAPVK_VK_TO_VSC
#define MAPVK_VK_TO_VSC 0
#endif
#ifndef MAPVK_VK_TO_CHAR
#define MAPVK_VK_TO_CHAR 2
#endif
const int kIncompleteEscapeTimeoutMs = 1000;
@ -17,149 +15,103 @@ const int kIncompleteEscapeTimeoutMs = 1000;
ConsoleInput::KeyDescriptor ConsoleInput::keyDescriptorTable[] = {
// Ctrl-<letter/digit> seems to be handled OK by the default code path.
// TODO: Alt-ESC is encoded as ESC ESC. Can it be handled?
{ ESC, VK_ESCAPE, '\x1B', 0, },
// Alt-<letter/digit>
{ ESC"a", 'A', LEFT_ALT_PRESSED },
{ ESC"b", 'B', LEFT_ALT_PRESSED },
{ ESC"c", 'C', LEFT_ALT_PRESSED },
{ ESC"d", 'D', LEFT_ALT_PRESSED },
{ ESC"e", 'E', LEFT_ALT_PRESSED },
{ ESC"f", 'F', LEFT_ALT_PRESSED },
{ ESC"g", 'G', LEFT_ALT_PRESSED },
{ ESC"h", 'H', LEFT_ALT_PRESSED },
{ ESC"i", 'I', LEFT_ALT_PRESSED },
{ ESC"j", 'J', LEFT_ALT_PRESSED },
{ ESC"k", 'K', LEFT_ALT_PRESSED },
{ ESC"l", 'L', LEFT_ALT_PRESSED },
{ ESC"m", 'M', LEFT_ALT_PRESSED },
{ ESC"n", 'N', LEFT_ALT_PRESSED },
{ ESC"o", 'O', LEFT_ALT_PRESSED },
{ ESC"p", 'P', LEFT_ALT_PRESSED },
{ ESC"q", 'Q', LEFT_ALT_PRESSED },
{ ESC"r", 'R', LEFT_ALT_PRESSED },
{ ESC"s", 'S', LEFT_ALT_PRESSED },
{ ESC"t", 'T', LEFT_ALT_PRESSED },
{ ESC"u", 'U', LEFT_ALT_PRESSED },
{ ESC"v", 'V', LEFT_ALT_PRESSED },
{ ESC"w", 'W', LEFT_ALT_PRESSED },
{ ESC"x", 'X', LEFT_ALT_PRESSED },
{ ESC"y", 'Y', LEFT_ALT_PRESSED },
{ ESC"z", 'Z', LEFT_ALT_PRESSED },
{ ESC"A", 'A', LEFT_ALT_PRESSED },
{ ESC"B", 'B', LEFT_ALT_PRESSED },
{ ESC"C", 'C', LEFT_ALT_PRESSED },
{ ESC"D", 'D', LEFT_ALT_PRESSED },
{ ESC"E", 'E', LEFT_ALT_PRESSED },
{ ESC"F", 'F', LEFT_ALT_PRESSED },
{ ESC"G", 'G', LEFT_ALT_PRESSED },
{ ESC"H", 'H', LEFT_ALT_PRESSED },
{ ESC"I", 'I', LEFT_ALT_PRESSED },
{ ESC"J", 'J', LEFT_ALT_PRESSED },
{ ESC"K", 'K', LEFT_ALT_PRESSED },
{ ESC"L", 'L', LEFT_ALT_PRESSED },
{ ESC"M", 'M', LEFT_ALT_PRESSED },
{ ESC"N", 'N', LEFT_ALT_PRESSED },
{ ESC"O", 'O', LEFT_ALT_PRESSED },
{ ESC"P", 'P', LEFT_ALT_PRESSED },
{ ESC"Q", 'Q', LEFT_ALT_PRESSED },
{ ESC"R", 'R', LEFT_ALT_PRESSED },
{ ESC"S", 'S', LEFT_ALT_PRESSED },
{ ESC"T", 'T', LEFT_ALT_PRESSED },
{ ESC"U", 'U', LEFT_ALT_PRESSED },
{ ESC"V", 'V', LEFT_ALT_PRESSED },
{ ESC"W", 'W', LEFT_ALT_PRESSED },
{ ESC"X", 'X', LEFT_ALT_PRESSED },
{ ESC"Y", 'Y', LEFT_ALT_PRESSED },
{ ESC"Z", 'Z', LEFT_ALT_PRESSED },
{ ESC, VK_ESCAPE, 0, },
{ ESC"[", '[', LEFT_ALT_PRESSED },
{ ESC"O", 'O', 0, LEFT_ALT_PRESSED },
{ ESC"[", '[', 0, LEFT_ALT_PRESSED },
// Function keys
{ ESC"OP", VK_F1, 0, }, // xt gt kon
{ ESC"OQ", VK_F2, 0, }, // xt gt kon
{ ESC"OR", VK_F3, 0, }, // xt gt kon
{ ESC"OS", VK_F4, 0, }, // xt gt kon
{ CSI"11~", VK_F1, 0, }, // rxvt
{ CSI"12~", VK_F2, 0, }, // rxvt
{ CSI"13~", VK_F3, 0, }, // rxvt
{ CSI"14~", VK_F4, 0, }, // rxvt
{ CSI"15~", VK_F5, 0, }, // xt gt kon rxvt
{ CSI"17~", VK_F6, 0, }, // xt gt kon rxvt
{ CSI"18~", VK_F7, 0, }, // xt gt kon rxvt
{ CSI"19~", VK_F8, 0, }, // xt gt kon rxvt
{ CSI"20~", VK_F9, 0, }, // xt gt kon rxvt
{ CSI"21~", VK_F10, 0, }, // xt gt kon rxvt
{ CSI"23~", VK_F11, 0, }, // xt gt kon rxvt
{ CSI"24~", VK_F12, 0, }, // xt gt kon rxvt
{ ESC"OP", VK_F1, 0, 0, }, // xt gt kon
{ ESC"OQ", VK_F2, 0, 0, }, // xt gt kon
{ ESC"OR", VK_F3, 0, 0, }, // xt gt kon
{ ESC"OS", VK_F4, 0, 0, }, // xt gt kon
{ CSI"11~", VK_F1, 0, 0, }, // rxvt
{ CSI"12~", VK_F2, 0, 0, }, // rxvt
{ CSI"13~", VK_F3, 0, 0, }, // rxvt
{ CSI"14~", VK_F4, 0, 0, }, // rxvt
{ CSI"15~", VK_F5, 0, 0, }, // xt gt kon rxvt
{ CSI"17~", VK_F6, 0, 0, }, // xt gt kon rxvt
{ CSI"18~", VK_F7, 0, 0, }, // xt gt kon rxvt
{ CSI"19~", VK_F8, 0, 0, }, // xt gt kon rxvt
{ CSI"20~", VK_F9, 0, 0, }, // xt gt kon rxvt
{ CSI"21~", VK_F10, 0, 0, }, // xt gt kon rxvt
{ CSI"23~", VK_F11, 0, 0, }, // xt gt kon rxvt
{ CSI"24~", VK_F12, 0, 0, }, // xt gt kon rxvt
{ "\x7F", VK_BACK, 0, },
{ ESC"\x7F", VK_BACK, LEFT_ALT_PRESSED, },
{ "\x7F", VK_BACK, '\x08', 0, },
{ ESC"\x7F", VK_BACK, '\x08', LEFT_ALT_PRESSED, },
// arrow keys
{ CSI"A", VK_UP, 0, }, // xt gt kon rxvt
{ CSI"B", VK_DOWN, 0, }, // xt gt kon rxvt
{ CSI"C", VK_RIGHT, 0, }, // xt gt kon rxvt
{ CSI"D", VK_LEFT, 0, }, // xt gt kon rxvt
{ CSI"A", VK_UP, 0, 0, }, // xt gt kon rxvt
{ CSI"B", VK_DOWN, 0, 0, }, // xt gt kon rxvt
{ CSI"C", VK_RIGHT, 0, 0, }, // xt gt kon rxvt
{ CSI"D", VK_LEFT, 0, 0, }, // xt gt kon rxvt
// ctrl-<arrow>
{ CSI"1;5A", VK_UP, LEFT_CTRL_PRESSED }, // xt gt kon
{ CSI"1;5B", VK_DOWN, LEFT_CTRL_PRESSED }, // xt gt kon
{ CSI"1;5C", VK_RIGHT, LEFT_CTRL_PRESSED }, // xt gt kon
{ CSI"1;5D", VK_LEFT, LEFT_CTRL_PRESSED }, // xt gt kon
{ ESC"Oa", VK_UP, LEFT_CTRL_PRESSED }, // rxvt
{ ESC"Ob", VK_DOWN, LEFT_CTRL_PRESSED }, // rxvt
{ ESC"Oc", VK_RIGHT, LEFT_CTRL_PRESSED }, // rxvt
{ ESC"Od", VK_LEFT, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"1;5A", VK_UP, 0, LEFT_CTRL_PRESSED }, // xt gt kon
{ CSI"1;5B", VK_DOWN, 0, LEFT_CTRL_PRESSED }, // xt gt kon
{ CSI"1;5C", VK_RIGHT, 0, LEFT_CTRL_PRESSED }, // xt gt kon
{ CSI"1;5D", VK_LEFT, 0, LEFT_CTRL_PRESSED }, // xt gt kon
{ ESC"Oa", VK_UP, 0, LEFT_CTRL_PRESSED }, // rxvt
{ ESC"Ob", VK_DOWN, 0, LEFT_CTRL_PRESSED }, // rxvt
{ ESC"Oc", VK_RIGHT, 0, LEFT_CTRL_PRESSED }, // rxvt
{ ESC"Od", VK_LEFT, 0, LEFT_CTRL_PRESSED }, // rxvt
// alt-<arrow>
{ CSI"1;3A", VK_UP, LEFT_ALT_PRESSED }, // xt gt kon
{ CSI"1;3B", VK_DOWN, LEFT_ALT_PRESSED }, // xt gt kon
{ CSI"1;3C", VK_RIGHT, LEFT_ALT_PRESSED }, // xt gt kon
{ CSI"1;3D", VK_LEFT, LEFT_ALT_PRESSED }, // xt gt kon
{ ESC CSI"A", VK_UP, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"B", VK_DOWN, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"C", VK_RIGHT, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"D", VK_LEFT, LEFT_ALT_PRESSED }, // rxvt
{ CSI"1;3A", VK_UP, 0, LEFT_ALT_PRESSED }, // xt gt kon
{ CSI"1;3B", VK_DOWN, 0, LEFT_ALT_PRESSED }, // xt gt kon
{ CSI"1;3C", VK_RIGHT, 0, LEFT_ALT_PRESSED }, // xt gt kon
{ CSI"1;3D", VK_LEFT, 0, LEFT_ALT_PRESSED }, // xt gt kon
{ ESC CSI"A", VK_UP, 0, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"B", VK_DOWN, 0, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"C", VK_RIGHT, 0, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"D", VK_LEFT, 0, LEFT_ALT_PRESSED }, // rxvt
// insert,delete,home,end,pgup,pgdn
{ CSI"2~", VK_INSERT, 0, }, // xt gt kon rxvt
{ CSI"3~", VK_DELETE, 0, }, // xt gt kon rxvt
{ CSI"5~", VK_PRIOR, 0, }, // xt gt kon rxvt
{ CSI"6~", VK_NEXT, 0, }, // xt gt kon rxvt
{ CSI"H", VK_HOME, 0, }, // xt kon
{ CSI"F", VK_END, 0, }, // xt kon
{ ESC"OH", VK_HOME, 0, }, // gt
{ ESC"OF", VK_END, 0, }, // gt
{ CSI"7^", VK_HOME, 0, }, // rxvt
{ CSI"8^", VK_END, 0, }, // rxvt
{ CSI"2~", VK_INSERT, 0, 0, }, // xt gt kon rxvt
{ CSI"3~", VK_DELETE, 0, 0, }, // xt gt kon rxvt
{ CSI"5~", VK_PRIOR, 0, 0, }, // xt gt kon rxvt
{ CSI"6~", VK_NEXT, 0, 0, }, // xt gt kon rxvt
{ CSI"H", VK_HOME, 0, 0, }, // xt kon
{ CSI"F", VK_END, 0, 0, }, // xt kon
{ ESC"OH", VK_HOME, 0, 0, }, // gt
{ ESC"OF", VK_END, 0, 0, }, // gt
{ CSI"7^", VK_HOME, 0, 0, }, // rxvt
{ CSI"8^", VK_END, 0, 0, }, // rxvt
// ctrl-<key>
{ CSI"2;5~", VK_INSERT, LEFT_CTRL_PRESSED }, // xt
{ CSI"3;5~", VK_DELETE, LEFT_CTRL_PRESSED }, // xt gt kon
{ CSI"1;5H", VK_HOME, LEFT_CTRL_PRESSED }, // xt kon
{ CSI"1;5F", VK_END, LEFT_CTRL_PRESSED }, // xt kon
{ CSI"5;5~", VK_PRIOR, LEFT_CTRL_PRESSED }, // xt gt
{ CSI"6;5~", VK_NEXT, LEFT_CTRL_PRESSED }, // xt gt
{ CSI"2^", VK_INSERT, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"3^", VK_DELETE, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"7^", VK_HOME, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"8^", VK_END, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"5^", VK_PRIOR, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"6^", VK_NEXT, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"2;5~", VK_INSERT, 0, LEFT_CTRL_PRESSED }, // xt
{ CSI"3;5~", VK_DELETE, 0, LEFT_CTRL_PRESSED }, // xt gt kon
{ CSI"1;5H", VK_HOME, 0, LEFT_CTRL_PRESSED }, // xt kon
{ CSI"1;5F", VK_END, 0, LEFT_CTRL_PRESSED }, // xt kon
{ CSI"5;5~", VK_PRIOR, 0, LEFT_CTRL_PRESSED }, // xt gt
{ CSI"6;5~", VK_NEXT, 0, LEFT_CTRL_PRESSED }, // xt gt
{ CSI"2^", VK_INSERT, 0, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"3^", VK_DELETE, 0, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"7^", VK_HOME, 0, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"8^", VK_END, 0, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"5^", VK_PRIOR, 0, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"6^", VK_NEXT, 0, LEFT_CTRL_PRESSED }, // rxvt
// alt-<key>
{ CSI"2;3~", VK_INSERT, LEFT_ALT_PRESSED }, // xt gt
{ CSI"3;3~", VK_DELETE, LEFT_ALT_PRESSED }, // xt gt
{ CSI"1;3H", VK_HOME, LEFT_ALT_PRESSED }, // xt
{ CSI"1;3F", VK_END, LEFT_ALT_PRESSED }, // xt
{ CSI"5;3~", VK_PRIOR, LEFT_ALT_PRESSED }, // xt gt
{ CSI"6;3~", VK_NEXT, LEFT_ALT_PRESSED }, // xt gt
{ ESC CSI"2~", VK_INSERT, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"3~", VK_DELETE, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"7~", VK_HOME, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"8~", VK_END, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"5~", VK_PRIOR, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"6~", VK_NEXT, LEFT_ALT_PRESSED }, // rxvt
{ CSI"2;3~", VK_INSERT, 0, LEFT_ALT_PRESSED }, // xt gt
{ CSI"3;3~", VK_DELETE, 0, LEFT_ALT_PRESSED }, // xt gt
{ CSI"1;3H", VK_HOME, 0, LEFT_ALT_PRESSED }, // xt
{ CSI"1;3F", VK_END, 0, LEFT_ALT_PRESSED }, // xt
{ CSI"5;3~", VK_PRIOR, 0, LEFT_ALT_PRESSED }, // xt gt
{ CSI"6;3~", VK_NEXT, 0, LEFT_ALT_PRESSED }, // xt gt
{ ESC CSI"2~", VK_INSERT, 0, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"3~", VK_DELETE, 0, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"7~", VK_HOME, 0, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"8~", VK_END, 0, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"5~", VK_PRIOR, 0, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"6~", VK_NEXT, 0, LEFT_ALT_PRESSED }, // rxvt
};
ConsoleInput::ConsoleInput(Win32Console *console) : m_console(console), lastWriteTick(0)
ConsoleInput::ConsoleInput(Win32Console *console, DsrSender *dsrSender) :
m_console(console),
m_dsrSender(dsrSender),
m_dsrSent(false),
lastWriteTick(0)
{
for (size_t i = 0; i < sizeof(keyDescriptorTable) / sizeof(keyDescriptorTable[0]); ++i) {
KeyDescriptor *k = &keyDescriptorTable[i];
@ -175,6 +127,10 @@ void ConsoleInput::writeInput(const std::string &input)
return;
m_byteQueue.append(input);
doWrite(false);
if (!m_byteQueue.empty() && !m_dsrSent) {
m_dsrSender->sendDsr();
m_dsrSent = true;
}
lastWriteTick = GetTickCount();
}
@ -224,7 +180,7 @@ void ConsoleInput::doWrite(bool isEof)
std::vector<INPUT_RECORD> records;
size_t idx = 0;
while (idx < m_byteQueue.size()) {
int charSize = appendChar(records, &data[idx], m_byteQueue.size() - idx, isEof);
int charSize = scanKeyPress(records, &data[idx], m_byteQueue.size() - idx, isEof);
if (charSize == -1)
break;
idx += charSize;
@ -233,90 +189,165 @@ void ConsoleInput::doWrite(bool isEof)
m_console->writeInput(records.data(), records.size());
}
int ConsoleInput::appendChar(std::vector<INPUT_RECORD> &records,
const char *input,
int inputSize,
bool isEof)
int ConsoleInput::scanKeyPress(std::vector<INPUT_RECORD> &records,
const char *input,
int inputSize,
bool isEof)
{
if (*input == '\x03' && m_console->processedInputMode()) {
// Ctrl-C.
if (input[0] == '\x03' && m_console->processedInputMode()) {
Trace("Ctrl-C");
BOOL ret = GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
Trace("GenerateConsoleCtrlEvent: %d", ret);
return 1;
}
// Attempt to match the Device Status Report (DSR) reply.
int dsrLen = matchDsr(input);
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;
}
// Recognize Alt-<character>.
if (input[0] == '\x1B' && input[1] != '\0' &&
m_lookup.getChild('\x1B')->getChild(input[1]) == NULL) {
int len = utf8CharLength(input[1]);
if (1 + len > inputSize) {
// Incomplete character.
Trace("Incomplete Alt-char match");
return -1;
}
appendUtf8Char(records, &input[1], len, LEFT_ALT_PRESSED);
return 1 + len;
}
// Recognize an ESC-encoded keypress.
bool incomplete;
const KeyDescriptor *match = lookupKey(input, isEof, &incomplete);
if (incomplete) {
// Incomplete match -- need more characters (or wait for a
// timeout to signify flushed input).
Trace("Incomplete ESC-keypress match");
return -1;
} else if (match != NULL) {
Trace("keypress: VK=%d (%d bytes)", match->virtualKey, match->encodingLen);
INPUT_RECORD ir;
memset(&ir, 0, sizeof(ir));
ir.EventType = KEY_EVENT;
ir.Event.KeyEvent.bKeyDown = TRUE;
ir.Event.KeyEvent.wRepeatCount = 1;
ir.Event.KeyEvent.wVirtualKeyCode = match->virtualKey;
ir.Event.KeyEvent.wVirtualScanCode =
MapVirtualKey(match->virtualKey, MAPVK_VK_TO_VSC);
ir.Event.KeyEvent.uChar.UnicodeChar =
MapVirtualKey(match->virtualKey, MAPVK_VK_TO_CHAR);
ir.Event.KeyEvent.dwControlKeyState = match->keyState;
records.push_back(ir);
ir.Event.KeyEvent.bKeyDown = FALSE;
records.push_back(ir);
appendKeyPress(records,
match->virtualKey,
match->unicodeChar,
match->keyState);
return match->encodingLen;
} else {
// A UTF-8 character.
int len = utf8CharLength(input[0]);
if (len > inputSize) {
// Incomplete character.
return -1;
}
Trace("UTF-8 char (%d bytes)", len);
WCHAR wideInput[2];
int wideLen = MultiByteToWideChar(CP_UTF8,
0,
input,
len,
wideInput,
sizeof(wideInput) / sizeof(wideInput[0]));
// TODO: Characters outside the BMP.
if (wideLen == 1) {
short vk = VkKeyScan(wideInput[0]);
INPUT_RECORD ir;
memset(&ir, 0, sizeof(ir));
ir.EventType = KEY_EVENT;
ir.Event.KeyEvent.bKeyDown = TRUE;
ir.Event.KeyEvent.wRepeatCount = 1;
if (vk != -1) {
ir.Event.KeyEvent.wVirtualKeyCode = vk & 0xFF;
int keyState = 0;
if (vk & 0x100)
keyState |= SHIFT_PRESSED;
else if (vk & 0x200)
keyState |= LEFT_CTRL_PRESSED;
else if (vk & 0x400)
keyState |= LEFT_ALT_PRESSED;
ir.Event.KeyEvent.dwControlKeyState = keyState;
}
ir.Event.KeyEvent.wVirtualScanCode =
MapVirtualKey(ir.Event.KeyEvent.wVirtualKeyCode,
MAPVK_VK_TO_VSC);
ir.Event.KeyEvent.uChar.UnicodeChar = wideInput[0];
Trace("vk:%d sc:%d wc:%d cks:%d",
ir.Event.KeyEvent.wVirtualKeyCode,
ir.Event.KeyEvent.wVirtualScanCode,
ir.Event.KeyEvent.uChar.UnicodeChar,
ir.Event.KeyEvent.dwControlKeyState);
records.push_back(ir);
ir.Event.KeyEvent.bKeyDown = FALSE;
records.push_back(ir);
}
return len;
}
// A UTF-8 character.
int len = utf8CharLength(input[0]);
if (len > inputSize) {
// Incomplete character.
Trace("Incomplete UTF-8 character");
return -1;
}
appendUtf8Char(records, &input[0], len, 0);
return len;
}
void ConsoleInput::appendUtf8Char(std::vector<INPUT_RECORD> &records,
const char *charBuffer,
int charLen,
int keyState)
{
WCHAR wideInput[2];
int wideLen = MultiByteToWideChar(CP_UTF8,
0,
charBuffer,
charLen,
wideInput,
sizeof(wideInput) / sizeof(wideInput[0]));
// TODO: Characters outside the BMP.
if (wideLen != 1)
return;
short charScan = VkKeyScan(wideInput[0]);
int virtualKey = 0;
if (charScan != -1) {
virtualKey = charScan & 0xFF;
if (charScan & 0x100)
keyState |= SHIFT_PRESSED;
else if (charScan & 0x200)
keyState |= LEFT_CTRL_PRESSED;
else if (charScan & 0x400)
keyState |= LEFT_ALT_PRESSED;
}
appendKeyPress(records, virtualKey, wideInput[0], keyState);
}
void ConsoleInput::appendKeyPress(std::vector<INPUT_RECORD> &records,
int virtualKey,
int unicodeChar,
int keyState)
{
bool ctrl = keyState & LEFT_CTRL_PRESSED;
bool alt = keyState & LEFT_ALT_PRESSED;
bool shift = keyState & SHIFT_PRESSED;
int stepKeyState = 0;
if (ctrl) {
stepKeyState |= LEFT_CTRL_PRESSED;
appendInputRecord(records, TRUE, VK_CONTROL, 0, stepKeyState);
}
if (alt) {
stepKeyState |= LEFT_ALT_PRESSED;
appendInputRecord(records, TRUE, VK_MENU, 0, stepKeyState);
}
if (shift) {
stepKeyState |= SHIFT_PRESSED;
appendInputRecord(records, TRUE, VK_SHIFT, 0, stepKeyState);
}
if (ctrl && alt) {
// This behavior seems arbitrary, but it's what I see in the Windows 7
// console.
unicodeChar = 0;
}
appendInputRecord(records, TRUE, virtualKey, unicodeChar, stepKeyState);
if (alt) {
// This behavior seems arbitrary, but it's what I see in the Windows 7
// console.
unicodeChar = 0;
}
appendInputRecord(records, FALSE, virtualKey, unicodeChar, stepKeyState);
if (shift) {
stepKeyState &= ~SHIFT_PRESSED;
appendInputRecord(records, FALSE, VK_SHIFT, 0, stepKeyState);
}
if (alt) {
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::appendInputRecord(std::vector<INPUT_RECORD> &records,
BOOL keyDown,
int virtualKey,
int unicodeChar,
int keyState)
{
INPUT_RECORD ir;
memset(&ir, 0, sizeof(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 = unicodeChar;
ir.Event.KeyEvent.dwControlKeyState = keyState;
records.push_back(ir);
}
// Return the byte size of a UTF-8 character using the value of the first
@ -372,3 +403,31 @@ ConsoleInput::lookupKey(const char *encoding, bool isEof, bool *incomplete)
return longestMatch;
}
}
// Match the Device Status Report console input: ESC [ nn ; mm R
// Returns:
// 0 no match
// >0 match, returns length of match
// -1 incomplete match
int ConsoleInput::matchDsr(const char *encoding)
{
const char *pch = encoding;
#define CHECK(cond) \
do { \
if (cond) { pch++; } \
else if (*pch == '\0') { return -1; } \
else { return 0; } \
} while(0)
CHECK(*pch == '\x1B');
CHECK(*pch == '[');
CHECK(isdigit(*pch));
while (isdigit(*pch))
pch++;
CHECK(*pch == ';');
CHECK(isdigit(*pch));
while (isdigit(*pch))
pch++;
CHECK(*pch == 'R');
return pch - encoding;
#undef CHECK
}

View File

@ -6,11 +6,12 @@
#include <windows.h>
class Win32Console;
class DsrSender;
class ConsoleInput
{
public:
ConsoleInput(Win32Console *console);
ConsoleInput(Win32Console *console, DsrSender *dsrSender);
void writeInput(const std::string &input);
void flushIncompleteEscapeCode();
@ -18,6 +19,7 @@ private:
struct KeyDescriptor {
const char *encoding;
int virtualKey;
int unicodeChar;
int keyState;
int encodingLen;
};
@ -36,16 +38,32 @@ private:
};
void doWrite(bool isEof);
int appendChar(std::vector<INPUT_RECORD> &records,
const char *input,
int inputSize,
bool isEof);
int scanKeyPress(std::vector<INPUT_RECORD> &records,
const char *input,
int inputSize,
bool isEof);
void appendUtf8Char(std::vector<INPUT_RECORD> &records,
const char *charBuffer,
int charLen,
int keyState);
void appendKeyPress(std::vector<INPUT_RECORD> &records,
int virtualKey,
int unicodeChar,
int keyState);
void appendInputRecord(std::vector<INPUT_RECORD> &records,
BOOL keyDown,
int virtualKey,
int unicodeChar,
int keyState);
static int utf8CharLength(char firstByte);
const KeyDescriptor *lookupKey(const char *encoding, bool isEof, bool *incomplete);
static int matchDsr(const char *encoding);
private:
static KeyDescriptor keyDescriptorTable[];
Win32Console *m_console;
DsrSender *m_dsrSender;
bool m_dsrSent;
std::string m_byteQueue;
KeyLookup m_lookup;
DWORD lastWriteTick;