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_controlSocket = makeSocket(controlPipeName);
m_dataSocket = makeSocket(dataPipeName); m_dataSocket = makeSocket(dataPipeName);
m_terminal = new Terminal(m_dataSocket); m_terminal = new Terminal(m_dataSocket);
m_consoleInput = new ConsoleInput(m_console); m_consoleInput = new ConsoleInput(m_console, this);
resetConsoleTracking(false); resetConsoleTracking(false);
@ -77,6 +77,15 @@ Agent::~Agent()
delete m_consoleInput; 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 *Agent::makeSocket(LPCWSTR pipeName)
{ {
NamedPipe *pipe = createNamedPipe(); NamedPipe *pipe = createNamedPipe();

View File

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

View File

@ -1,14 +1,12 @@
#include "ConsoleInput.h" #include "ConsoleInput.h"
#include "Win32Console.h" #include "Win32Console.h"
#include "DsrSender.h"
#include "../Shared/DebugClient.h" #include "../Shared/DebugClient.h"
#include <string.h> #include <string.h>
#ifndef MAPVK_VK_TO_VSC #ifndef MAPVK_VK_TO_VSC
#define MAPVK_VK_TO_VSC 0 #define MAPVK_VK_TO_VSC 0
#endif #endif
#ifndef MAPVK_VK_TO_CHAR
#define MAPVK_VK_TO_CHAR 2
#endif
const int kIncompleteEscapeTimeoutMs = 1000; const int kIncompleteEscapeTimeoutMs = 1000;
@ -17,149 +15,103 @@ const int kIncompleteEscapeTimeoutMs = 1000;
ConsoleInput::KeyDescriptor ConsoleInput::keyDescriptorTable[] = { ConsoleInput::KeyDescriptor ConsoleInput::keyDescriptorTable[] = {
// Ctrl-<letter/digit> seems to be handled OK by the default code path. // 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> // Alt-<letter/digit>
{ ESC"a", 'A', LEFT_ALT_PRESSED }, { ESC"O", 'O', 0, LEFT_ALT_PRESSED },
{ ESC"b", 'B', LEFT_ALT_PRESSED }, { ESC"[", '[', 0, 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 },
// Function keys // Function keys
{ ESC"OP", VK_F1, 0, }, // xt gt kon { ESC"OP", VK_F1, 0, 0, }, // xt gt kon
{ ESC"OQ", VK_F2, 0, }, // xt gt kon { ESC"OQ", VK_F2, 0, 0, }, // xt gt kon
{ ESC"OR", VK_F3, 0, }, // xt gt kon { ESC"OR", VK_F3, 0, 0, }, // xt gt kon
{ ESC"OS", VK_F4, 0, }, // xt gt kon { ESC"OS", VK_F4, 0, 0, }, // xt gt kon
{ CSI"11~", VK_F1, 0, }, // rxvt { CSI"11~", VK_F1, 0, 0, }, // rxvt
{ CSI"12~", VK_F2, 0, }, // rxvt { CSI"12~", VK_F2, 0, 0, }, // rxvt
{ CSI"13~", VK_F3, 0, }, // rxvt { CSI"13~", VK_F3, 0, 0, }, // rxvt
{ CSI"14~", VK_F4, 0, }, // rxvt { CSI"14~", VK_F4, 0, 0, }, // rxvt
{ CSI"15~", VK_F5, 0, }, // xt gt kon rxvt { CSI"15~", VK_F5, 0, 0, }, // xt gt kon rxvt
{ CSI"17~", VK_F6, 0, }, // xt gt kon rxvt { CSI"17~", VK_F6, 0, 0, }, // xt gt kon rxvt
{ CSI"18~", VK_F7, 0, }, // xt gt kon rxvt { CSI"18~", VK_F7, 0, 0, }, // xt gt kon rxvt
{ CSI"19~", VK_F8, 0, }, // xt gt kon rxvt { CSI"19~", VK_F8, 0, 0, }, // xt gt kon rxvt
{ CSI"20~", VK_F9, 0, }, // xt gt kon rxvt { CSI"20~", VK_F9, 0, 0, }, // xt gt kon rxvt
{ CSI"21~", VK_F10, 0, }, // xt gt kon rxvt { CSI"21~", VK_F10, 0, 0, }, // xt gt kon rxvt
{ CSI"23~", VK_F11, 0, }, // xt gt kon rxvt { CSI"23~", VK_F11, 0, 0, }, // xt gt kon rxvt
{ CSI"24~", VK_F12, 0, }, // xt gt kon rxvt { CSI"24~", VK_F12, 0, 0, }, // xt gt kon rxvt
{ "\x7F", VK_BACK, 0, }, { "\x7F", VK_BACK, '\x08', 0, },
{ ESC"\x7F", VK_BACK, LEFT_ALT_PRESSED, }, { ESC"\x7F", VK_BACK, '\x08', LEFT_ALT_PRESSED, },
// arrow keys // arrow keys
{ CSI"A", VK_UP, 0, }, // xt gt kon rxvt { CSI"A", VK_UP, 0, 0, }, // xt gt kon rxvt
{ CSI"B", VK_DOWN, 0, }, // xt gt kon rxvt { CSI"B", VK_DOWN, 0, 0, }, // xt gt kon rxvt
{ CSI"C", VK_RIGHT, 0, }, // xt gt kon rxvt { CSI"C", VK_RIGHT, 0, 0, }, // xt gt kon rxvt
{ CSI"D", VK_LEFT, 0, }, // xt gt kon rxvt { CSI"D", VK_LEFT, 0, 0, }, // xt gt kon rxvt
// ctrl-<arrow> // ctrl-<arrow>
{ CSI"1;5A", VK_UP, LEFT_CTRL_PRESSED }, // xt gt kon { CSI"1;5A", VK_UP, 0, LEFT_CTRL_PRESSED }, // xt gt kon
{ CSI"1;5B", VK_DOWN, LEFT_CTRL_PRESSED }, // xt gt kon { CSI"1;5B", VK_DOWN, 0, LEFT_CTRL_PRESSED }, // xt gt kon
{ CSI"1;5C", VK_RIGHT, LEFT_CTRL_PRESSED }, // xt gt kon { CSI"1;5C", VK_RIGHT, 0, LEFT_CTRL_PRESSED }, // xt gt kon
{ CSI"1;5D", VK_LEFT, LEFT_CTRL_PRESSED }, // xt gt kon { CSI"1;5D", VK_LEFT, 0, LEFT_CTRL_PRESSED }, // xt gt kon
{ ESC"Oa", VK_UP, LEFT_CTRL_PRESSED }, // rxvt { ESC"Oa", VK_UP, 0, LEFT_CTRL_PRESSED }, // rxvt
{ ESC"Ob", VK_DOWN, LEFT_CTRL_PRESSED }, // rxvt { ESC"Ob", VK_DOWN, 0, LEFT_CTRL_PRESSED }, // rxvt
{ ESC"Oc", VK_RIGHT, LEFT_CTRL_PRESSED }, // rxvt { ESC"Oc", VK_RIGHT, 0, LEFT_CTRL_PRESSED }, // rxvt
{ ESC"Od", VK_LEFT, LEFT_CTRL_PRESSED }, // rxvt { ESC"Od", VK_LEFT, 0, LEFT_CTRL_PRESSED }, // rxvt
// alt-<arrow> // alt-<arrow>
{ CSI"1;3A", VK_UP, LEFT_ALT_PRESSED }, // xt gt kon { CSI"1;3A", VK_UP, 0, LEFT_ALT_PRESSED }, // xt gt kon
{ CSI"1;3B", VK_DOWN, LEFT_ALT_PRESSED }, // xt gt kon { CSI"1;3B", VK_DOWN, 0, LEFT_ALT_PRESSED }, // xt gt kon
{ CSI"1;3C", VK_RIGHT, LEFT_ALT_PRESSED }, // xt gt kon { CSI"1;3C", VK_RIGHT, 0, LEFT_ALT_PRESSED }, // xt gt kon
{ CSI"1;3D", VK_LEFT, LEFT_ALT_PRESSED }, // xt gt kon { CSI"1;3D", VK_LEFT, 0, LEFT_ALT_PRESSED }, // xt gt kon
{ ESC CSI"A", VK_UP, LEFT_ALT_PRESSED }, // rxvt { ESC CSI"A", VK_UP, 0, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"B", VK_DOWN, LEFT_ALT_PRESSED }, // rxvt { ESC CSI"B", VK_DOWN, 0, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"C", VK_RIGHT, LEFT_ALT_PRESSED }, // rxvt { ESC CSI"C", VK_RIGHT, 0, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"D", VK_LEFT, LEFT_ALT_PRESSED }, // rxvt { ESC CSI"D", VK_LEFT, 0, LEFT_ALT_PRESSED }, // rxvt
// insert,delete,home,end,pgup,pgdn // insert,delete,home,end,pgup,pgdn
{ CSI"2~", VK_INSERT, 0, }, // xt gt kon rxvt { CSI"2~", VK_INSERT, 0, 0, }, // xt gt kon rxvt
{ CSI"3~", VK_DELETE, 0, }, // xt gt kon rxvt { CSI"3~", VK_DELETE, 0, 0, }, // xt gt kon rxvt
{ CSI"5~", VK_PRIOR, 0, }, // xt gt kon rxvt { CSI"5~", VK_PRIOR, 0, 0, }, // xt gt kon rxvt
{ CSI"6~", VK_NEXT, 0, }, // xt gt kon rxvt { CSI"6~", VK_NEXT, 0, 0, }, // xt gt kon rxvt
{ CSI"H", VK_HOME, 0, }, // xt kon { CSI"H", VK_HOME, 0, 0, }, // xt kon
{ CSI"F", VK_END, 0, }, // xt kon { CSI"F", VK_END, 0, 0, }, // xt kon
{ ESC"OH", VK_HOME, 0, }, // gt { ESC"OH", VK_HOME, 0, 0, }, // gt
{ ESC"OF", VK_END, 0, }, // gt { ESC"OF", VK_END, 0, 0, }, // gt
{ CSI"7^", VK_HOME, 0, }, // rxvt { CSI"7^", VK_HOME, 0, 0, }, // rxvt
{ CSI"8^", VK_END, 0, }, // rxvt { CSI"8^", VK_END, 0, 0, }, // rxvt
// ctrl-<key> // ctrl-<key>
{ CSI"2;5~", VK_INSERT, LEFT_CTRL_PRESSED }, // xt { CSI"2;5~", VK_INSERT, 0, LEFT_CTRL_PRESSED }, // xt
{ CSI"3;5~", VK_DELETE, LEFT_CTRL_PRESSED }, // xt gt kon { CSI"3;5~", VK_DELETE, 0, LEFT_CTRL_PRESSED }, // xt gt kon
{ CSI"1;5H", VK_HOME, LEFT_CTRL_PRESSED }, // xt kon { CSI"1;5H", VK_HOME, 0, LEFT_CTRL_PRESSED }, // xt kon
{ CSI"1;5F", VK_END, LEFT_CTRL_PRESSED }, // xt kon { CSI"1;5F", VK_END, 0, LEFT_CTRL_PRESSED }, // xt kon
{ CSI"5;5~", VK_PRIOR, LEFT_CTRL_PRESSED }, // xt gt { CSI"5;5~", VK_PRIOR, 0, LEFT_CTRL_PRESSED }, // xt gt
{ CSI"6;5~", VK_NEXT, LEFT_CTRL_PRESSED }, // xt gt { CSI"6;5~", VK_NEXT, 0, LEFT_CTRL_PRESSED }, // xt gt
{ CSI"2^", VK_INSERT, LEFT_CTRL_PRESSED }, // rxvt { CSI"2^", VK_INSERT, 0, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"3^", VK_DELETE, LEFT_CTRL_PRESSED }, // rxvt { CSI"3^", VK_DELETE, 0, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"7^", VK_HOME, LEFT_CTRL_PRESSED }, // rxvt { CSI"7^", VK_HOME, 0, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"8^", VK_END, LEFT_CTRL_PRESSED }, // rxvt { CSI"8^", VK_END, 0, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"5^", VK_PRIOR, LEFT_CTRL_PRESSED }, // rxvt { CSI"5^", VK_PRIOR, 0, LEFT_CTRL_PRESSED }, // rxvt
{ CSI"6^", VK_NEXT, LEFT_CTRL_PRESSED }, // rxvt { CSI"6^", VK_NEXT, 0, LEFT_CTRL_PRESSED }, // rxvt
// alt-<key> // alt-<key>
{ CSI"2;3~", VK_INSERT, LEFT_ALT_PRESSED }, // xt gt { CSI"2;3~", VK_INSERT, 0, LEFT_ALT_PRESSED }, // xt gt
{ CSI"3;3~", VK_DELETE, LEFT_ALT_PRESSED }, // xt gt { CSI"3;3~", VK_DELETE, 0, LEFT_ALT_PRESSED }, // xt gt
{ CSI"1;3H", VK_HOME, LEFT_ALT_PRESSED }, // xt { CSI"1;3H", VK_HOME, 0, LEFT_ALT_PRESSED }, // xt
{ CSI"1;3F", VK_END, LEFT_ALT_PRESSED }, // xt { CSI"1;3F", VK_END, 0, LEFT_ALT_PRESSED }, // xt
{ CSI"5;3~", VK_PRIOR, LEFT_ALT_PRESSED }, // xt gt { CSI"5;3~", VK_PRIOR, 0, LEFT_ALT_PRESSED }, // xt gt
{ CSI"6;3~", VK_NEXT, LEFT_ALT_PRESSED }, // xt gt { CSI"6;3~", VK_NEXT, 0, LEFT_ALT_PRESSED }, // xt gt
{ ESC CSI"2~", VK_INSERT, LEFT_ALT_PRESSED }, // rxvt { ESC CSI"2~", VK_INSERT, 0, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"3~", VK_DELETE, LEFT_ALT_PRESSED }, // rxvt { ESC CSI"3~", VK_DELETE, 0, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"7~", VK_HOME, LEFT_ALT_PRESSED }, // rxvt { ESC CSI"7~", VK_HOME, 0, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"8~", VK_END, LEFT_ALT_PRESSED }, // rxvt { ESC CSI"8~", VK_END, 0, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"5~", VK_PRIOR, LEFT_ALT_PRESSED }, // rxvt { ESC CSI"5~", VK_PRIOR, 0, LEFT_ALT_PRESSED }, // rxvt
{ ESC CSI"6~", VK_NEXT, 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) { for (size_t i = 0; i < sizeof(keyDescriptorTable) / sizeof(keyDescriptorTable[0]); ++i) {
KeyDescriptor *k = &keyDescriptorTable[i]; KeyDescriptor *k = &keyDescriptorTable[i];
@ -175,6 +127,10 @@ void ConsoleInput::writeInput(const std::string &input)
return; return;
m_byteQueue.append(input); m_byteQueue.append(input);
doWrite(false); doWrite(false);
if (!m_byteQueue.empty() && !m_dsrSent) {
m_dsrSender->sendDsr();
m_dsrSent = true;
}
lastWriteTick = GetTickCount(); lastWriteTick = GetTickCount();
} }
@ -224,7 +180,7 @@ void ConsoleInput::doWrite(bool isEof)
std::vector<INPUT_RECORD> records; std::vector<INPUT_RECORD> records;
size_t idx = 0; size_t idx = 0;
while (idx < m_byteQueue.size()) { 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) if (charSize == -1)
break; break;
idx += charSize; idx += charSize;
@ -233,90 +189,165 @@ void ConsoleInput::doWrite(bool isEof)
m_console->writeInput(records.data(), records.size()); m_console->writeInput(records.data(), records.size());
} }
int ConsoleInput::appendChar(std::vector<INPUT_RECORD> &records, int ConsoleInput::scanKeyPress(std::vector<INPUT_RECORD> &records,
const char *input, const char *input,
int inputSize, int inputSize,
bool isEof) bool isEof)
{ {
if (*input == '\x03' && m_console->processedInputMode()) { // Ctrl-C.
if (input[0] == '\x03' && m_console->processedInputMode()) {
Trace("Ctrl-C"); Trace("Ctrl-C");
BOOL ret = GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); BOOL ret = GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
Trace("GenerateConsoleCtrlEvent: %d", ret); Trace("GenerateConsoleCtrlEvent: %d", ret);
return 1; 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; bool incomplete;
const KeyDescriptor *match = lookupKey(input, isEof, &incomplete); const KeyDescriptor *match = lookupKey(input, isEof, &incomplete);
if (incomplete) { if (incomplete) {
// Incomplete match -- need more characters (or wait for a // Incomplete match -- need more characters (or wait for a
// timeout to signify flushed input). // timeout to signify flushed input).
Trace("Incomplete ESC-keypress match");
return -1; return -1;
} else if (match != NULL) { } else if (match != NULL) {
Trace("keypress: VK=%d (%d bytes)", match->virtualKey, match->encodingLen); appendKeyPress(records,
INPUT_RECORD ir; match->virtualKey,
memset(&ir, 0, sizeof(ir)); match->unicodeChar,
ir.EventType = KEY_EVENT; match->keyState);
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);
return match->encodingLen; 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 // 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; 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> #include <windows.h>
class Win32Console; class Win32Console;
class DsrSender;
class ConsoleInput class ConsoleInput
{ {
public: public:
ConsoleInput(Win32Console *console); ConsoleInput(Win32Console *console, DsrSender *dsrSender);
void writeInput(const std::string &input); void writeInput(const std::string &input);
void flushIncompleteEscapeCode(); void flushIncompleteEscapeCode();
@ -18,6 +19,7 @@ private:
struct KeyDescriptor { struct KeyDescriptor {
const char *encoding; const char *encoding;
int virtualKey; int virtualKey;
int unicodeChar;
int keyState; int keyState;
int encodingLen; int encodingLen;
}; };
@ -36,16 +38,32 @@ private:
}; };
void doWrite(bool isEof); void doWrite(bool isEof);
int appendChar(std::vector<INPUT_RECORD> &records, int scanKeyPress(std::vector<INPUT_RECORD> &records,
const char *input, const char *input,
int inputSize, int inputSize,
bool isEof); 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); static int utf8CharLength(char firstByte);
const KeyDescriptor *lookupKey(const char *encoding, bool isEof, bool *incomplete); const KeyDescriptor *lookupKey(const char *encoding, bool isEof, bool *incomplete);
static int matchDsr(const char *encoding);
private: private:
static KeyDescriptor keyDescriptorTable[]; static KeyDescriptor keyDescriptorTable[];
Win32Console *m_console; Win32Console *m_console;
DsrSender *m_dsrSender;
bool m_dsrSent;
std::string m_byteQueue; std::string m_byteQueue;
KeyLookup m_lookup; KeyLookup m_lookup;
DWORD lastWriteTick; DWORD lastWriteTick;