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:
parent
d32e36032d
commit
1fdda9e1d4
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user