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_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();
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user