Recognize terminal mouse input and generate MOUSE_EVENT records.
Tracked by https://github.com/rprichard/winpty/issues/57
This commit is contained in:
parent
d4b4cb6bcc
commit
48ddc93ed9
@ -371,6 +371,7 @@ void Agent::updateMouseInputFlag(bool forceTrace)
|
|||||||
newFlag ? "enabled" : "disabled");
|
newFlag ? "enabled" : "disabled");
|
||||||
}
|
}
|
||||||
m_consoleMouseInputFlag = newFlag;
|
m_consoleMouseInputFlag = newFlag;
|
||||||
|
m_consoleInput->setMouseInputEnabled(newFlag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Agent::onPollTimeout()
|
void Agent::onPollTimeout()
|
||||||
@ -584,6 +585,7 @@ void Agent::syncConsoleContentAndSize(bool forceResize)
|
|||||||
syncConsoleTitle();
|
syncConsoleTitle();
|
||||||
|
|
||||||
const ConsoleScreenBufferInfo info = m_console->bufferInfo();
|
const ConsoleScreenBufferInfo info = m_console->bufferInfo();
|
||||||
|
m_consoleInput->setMouseWindowRect(info.windowRect());
|
||||||
|
|
||||||
// If an app resizes the buffer height, then we enter "direct mode", where
|
// If an app resizes the buffer height, then we enter "direct mode", where
|
||||||
// we stop trying to track incremental console changes.
|
// we stop trying to track incremental console changes.
|
||||||
|
@ -23,8 +23,11 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "DebugShowInput.h"
|
||||||
#include "DefaultInputMap.h"
|
#include "DefaultInputMap.h"
|
||||||
#include "DsrSender.h"
|
#include "DsrSender.h"
|
||||||
#include "Win32Console.h"
|
#include "Win32Console.h"
|
||||||
@ -35,13 +38,181 @@
|
|||||||
#define MAPVK_VK_TO_VSC 0
|
#define MAPVK_VK_TO_VSC 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const int kIncompleteEscapeTimeoutMs = 1000;
|
namespace {
|
||||||
|
|
||||||
|
struct MouseRecord {
|
||||||
|
bool release;
|
||||||
|
int flags;
|
||||||
|
COORD coord;
|
||||||
|
|
||||||
|
std::string toString() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string MouseRecord::toString() const {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "pos=" << std::dec << coord.X << "," << coord.Y
|
||||||
|
<< " flags=0x"
|
||||||
|
<< std::hex << flags;
|
||||||
|
if (release) {
|
||||||
|
ss << " release";
|
||||||
|
}
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
const int kIncompleteEscapeTimeoutMs = 1000u;
|
||||||
|
|
||||||
|
#define CHECK(cond) \
|
||||||
|
do { \
|
||||||
|
if (!(cond)) { return 0; } \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define ADVANCE() \
|
||||||
|
do { \
|
||||||
|
pch++; \
|
||||||
|
if (pch == stop) { return -1; } \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define SCAN_INT(out, maxLen) \
|
||||||
|
do { \
|
||||||
|
(out) = 0; \
|
||||||
|
CHECK(isdigit(*pch)); \
|
||||||
|
const char *begin = pch; \
|
||||||
|
do { \
|
||||||
|
CHECK(pch - begin + 1 < maxLen); \
|
||||||
|
(out) = (out) * 10 + *pch - '0'; \
|
||||||
|
ADVANCE(); \
|
||||||
|
} while (isdigit(*pch)); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define SCAN_SIGNED_INT(out, maxLen) \
|
||||||
|
do { \
|
||||||
|
bool negative = false; \
|
||||||
|
if (*pch == '-') { \
|
||||||
|
negative = true; \
|
||||||
|
ADVANCE(); \
|
||||||
|
} \
|
||||||
|
SCAN_INT(out, maxLen); \
|
||||||
|
if (negative) { \
|
||||||
|
(out) = -(out); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
// Match the Device Status Report console input: ESC [ nn ; mm R
|
||||||
|
// Returns:
|
||||||
|
// 0 no match
|
||||||
|
// >0 match, returns length of match
|
||||||
|
// -1 incomplete match
|
||||||
|
static int matchDsr(const char *input, int inputSize)
|
||||||
|
{
|
||||||
|
int32_t dummy = 0;
|
||||||
|
const char *pch = input;
|
||||||
|
const char *stop = input + inputSize;
|
||||||
|
CHECK(*pch == '\x1B'); ADVANCE();
|
||||||
|
CHECK(*pch == '['); ADVANCE();
|
||||||
|
SCAN_INT(dummy, 8);
|
||||||
|
CHECK(*pch == ';'); ADVANCE();
|
||||||
|
SCAN_INT(dummy, 8);
|
||||||
|
CHECK(*pch == 'R');
|
||||||
|
return pch - input + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int matchMouseDefault(const char *input, int inputSize,
|
||||||
|
MouseRecord &out)
|
||||||
|
{
|
||||||
|
const char *pch = input;
|
||||||
|
const char *stop = input + inputSize;
|
||||||
|
CHECK(*pch == '\x1B'); ADVANCE();
|
||||||
|
CHECK(*pch == '['); ADVANCE();
|
||||||
|
CHECK(*pch == 'M'); ADVANCE();
|
||||||
|
out.flags = (*pch - 32) & 0xFF; ADVANCE();
|
||||||
|
out.coord.X = (*pch - '!') & 0xFF;
|
||||||
|
ADVANCE();
|
||||||
|
out.coord.Y = (*pch - '!') & 0xFF;
|
||||||
|
out.release = false;
|
||||||
|
return pch - input + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int matchMouse1006(const char *input, int inputSize, MouseRecord &out)
|
||||||
|
{
|
||||||
|
const char *pch = input;
|
||||||
|
const char *stop = input + inputSize;
|
||||||
|
int32_t temp;
|
||||||
|
CHECK(*pch == '\x1B'); ADVANCE();
|
||||||
|
CHECK(*pch == '['); ADVANCE();
|
||||||
|
CHECK(*pch == '<'); ADVANCE();
|
||||||
|
SCAN_INT(out.flags, 8);
|
||||||
|
CHECK(*pch == ';'); ADVANCE();
|
||||||
|
SCAN_SIGNED_INT(temp, 8); out.coord.X = temp - 1;
|
||||||
|
CHECK(*pch == ';'); ADVANCE();
|
||||||
|
SCAN_SIGNED_INT(temp, 8); out.coord.Y = temp - 1;
|
||||||
|
CHECK(*pch == 'M' || *pch == 'm');
|
||||||
|
out.release = (*pch == 'm');
|
||||||
|
return pch - input + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int matchMouse1015(const char *input, int inputSize, MouseRecord &out)
|
||||||
|
{
|
||||||
|
const char *pch = input;
|
||||||
|
const char *stop = input + inputSize;
|
||||||
|
int32_t temp;
|
||||||
|
CHECK(*pch == '\x1B'); ADVANCE();
|
||||||
|
CHECK(*pch == '['); ADVANCE();
|
||||||
|
SCAN_INT(out.flags, 8); out.flags -= 32;
|
||||||
|
CHECK(*pch == ';'); ADVANCE();
|
||||||
|
SCAN_SIGNED_INT(temp, 8); out.coord.X = temp - 1;
|
||||||
|
CHECK(*pch == ';'); ADVANCE();
|
||||||
|
SCAN_SIGNED_INT(temp, 8); out.coord.Y = temp - 1;
|
||||||
|
CHECK(*pch == 'M');
|
||||||
|
out.release = false;
|
||||||
|
return pch - input + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int matchMouseRecord(const char *input, int inputSize, MouseRecord &out)
|
||||||
|
{
|
||||||
|
memset(&out, 0, sizeof(out));
|
||||||
|
int ret;
|
||||||
|
if ((ret = matchMouse1006(input, inputSize, out)) != 0) { return ret; }
|
||||||
|
if ((ret = matchMouse1015(input, inputSize, out)) != 0) { return ret; }
|
||||||
|
if ((ret = matchMouseDefault(input, inputSize, out)) != 0) { return ret; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef CHECK
|
||||||
|
#undef ADVANCE
|
||||||
|
#undef SCAN_INT
|
||||||
|
|
||||||
|
// Return the byte size of a UTF-8 character using the value of the first
|
||||||
|
// byte.
|
||||||
|
static int utf8CharLength(char firstByte)
|
||||||
|
{
|
||||||
|
// This code would probably be faster if it used __builtin_clz.
|
||||||
|
if ((firstByte & 0x80) == 0) {
|
||||||
|
return 1;
|
||||||
|
} else if ((firstByte & 0xE0) == 0xC0) {
|
||||||
|
return 2;
|
||||||
|
} else if ((firstByte & 0xF0) == 0xE0) {
|
||||||
|
return 3;
|
||||||
|
} else if ((firstByte & 0xF8) == 0xF0) {
|
||||||
|
return 4;
|
||||||
|
} else if ((firstByte & 0xFC) == 0xF8) {
|
||||||
|
return 5;
|
||||||
|
} else if ((firstByte & 0xFE) == 0xFC) {
|
||||||
|
return 6;
|
||||||
|
} else {
|
||||||
|
// Malformed UTF-8.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
ConsoleInput::ConsoleInput(DsrSender *dsrSender) :
|
ConsoleInput::ConsoleInput(DsrSender *dsrSender) :
|
||||||
m_console(new Win32Console),
|
m_console(new Win32Console),
|
||||||
m_dsrSender(dsrSender),
|
m_dsrSender(dsrSender),
|
||||||
m_dsrSent(false),
|
m_dsrSent(false),
|
||||||
m_lastWriteTick(0)
|
m_lastWriteTick(0),
|
||||||
|
m_mouseButtonState(0),
|
||||||
|
m_mouseInputEnabled(false)
|
||||||
{
|
{
|
||||||
addDefaultEntriesToInputMap(m_inputMap);
|
addDefaultEntriesToInputMap(m_inputMap);
|
||||||
if (hasDebugFlag("dump_input_map")) {
|
if (hasDebugFlag("dump_input_map")) {
|
||||||
@ -102,7 +273,7 @@ void ConsoleInput::writeInput(const std::string &input)
|
|||||||
void ConsoleInput::flushIncompleteEscapeCode()
|
void ConsoleInput::flushIncompleteEscapeCode()
|
||||||
{
|
{
|
||||||
if (!m_byteQueue.empty() &&
|
if (!m_byteQueue.empty() &&
|
||||||
(int)(GetTickCount() - m_lastWriteTick) > kIncompleteEscapeTimeoutMs) {
|
(GetTickCount() - m_lastWriteTick) > kIncompleteEscapeTimeoutMs) {
|
||||||
doWrite(true);
|
doWrite(true);
|
||||||
m_byteQueue.clear();
|
m_byteQueue.clear();
|
||||||
}
|
}
|
||||||
@ -114,7 +285,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 = scanKeyPress(records, &data[idx], m_byteQueue.size() - idx, isEof);
|
int charSize = scanInput(records, &data[idx], m_byteQueue.size() - idx, isEof);
|
||||||
if (charSize == -1)
|
if (charSize == -1)
|
||||||
break;
|
break;
|
||||||
idx += charSize;
|
idx += charSize;
|
||||||
@ -123,12 +294,12 @@ void ConsoleInput::doWrite(bool isEof)
|
|||||||
m_console->writeInput(records.data(), records.size());
|
m_console->writeInput(records.data(), records.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
int ConsoleInput::scanKeyPress(std::vector<INPUT_RECORD> &records,
|
int ConsoleInput::scanInput(std::vector<INPUT_RECORD> &records,
|
||||||
const char *input,
|
const char *input,
|
||||||
int inputSize,
|
int inputSize,
|
||||||
bool isEof)
|
bool isEof)
|
||||||
{
|
{
|
||||||
//trace("scanKeyPress: %d bytes", inputSize);
|
ASSERT(inputSize >= 1);
|
||||||
|
|
||||||
// Ctrl-C.
|
// Ctrl-C.
|
||||||
if (input[0] == '\x03' && m_console->processedInputMode()) {
|
if (input[0] == '\x03' && m_console->processedInputMode()) {
|
||||||
@ -138,16 +309,23 @@ int ConsoleInput::scanKeyPress(std::vector<INPUT_RECORD> &records,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to match the Device Status Report (DSR) reply.
|
if (input[0] == '\x1B') {
|
||||||
int dsrLen = matchDsr(input, inputSize);
|
// Attempt to match the Device Status Report (DSR) reply.
|
||||||
if (dsrLen > 0) {
|
int dsrLen = matchDsr(input, inputSize);
|
||||||
trace("Received a DSR reply");
|
if (dsrLen > 0) {
|
||||||
m_dsrSent = false;
|
trace("Received a DSR reply");
|
||||||
return dsrLen;
|
m_dsrSent = false;
|
||||||
} else if (!isEof && dsrLen == -1) {
|
return dsrLen;
|
||||||
// Incomplete DSR match.
|
} else if (!isEof && dsrLen == -1) {
|
||||||
trace("Incomplete DSR match");
|
// Incomplete DSR match.
|
||||||
return -1;
|
trace("Incomplete DSR match");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mouseLen = scanMouseInput(records, input, inputSize);
|
||||||
|
if (mouseLen > 0 || (!isEof && mouseLen == -1)) {
|
||||||
|
return mouseLen;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search the input map.
|
// Search the input map.
|
||||||
@ -196,6 +374,119 @@ int ConsoleInput::scanKeyPress(std::vector<INPUT_RECORD> &records,
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ConsoleInput::scanMouseInput(std::vector<INPUT_RECORD> &records,
|
||||||
|
const char *input,
|
||||||
|
int inputSize)
|
||||||
|
{
|
||||||
|
MouseRecord record;
|
||||||
|
int len = matchMouseRecord(input, inputSize, record);
|
||||||
|
if (len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTracingEnabled()) {
|
||||||
|
static bool debugInput = hasDebugFlag("input");
|
||||||
|
if (debugInput) {
|
||||||
|
trace("mouse input: %s", record.toString().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int button = record.flags & 0x03;
|
||||||
|
INPUT_RECORD newRecord = {0};
|
||||||
|
newRecord.EventType = MOUSE_EVENT;
|
||||||
|
MOUSE_EVENT_RECORD &mer = newRecord.Event.MouseEvent;
|
||||||
|
|
||||||
|
mer.dwMousePosition.X =
|
||||||
|
m_mouseWindowRect.Left +
|
||||||
|
std::max(0, std::min<int>(record.coord.X,
|
||||||
|
m_mouseWindowRect.width() - 1));
|
||||||
|
|
||||||
|
mer.dwMousePosition.Y =
|
||||||
|
m_mouseWindowRect.Top +
|
||||||
|
std::max(0, std::min<int>(record.coord.Y,
|
||||||
|
m_mouseWindowRect.height() - 1));
|
||||||
|
|
||||||
|
// The modifier state is neatly independent of everything else.
|
||||||
|
if (record.flags & 0x04) { mer.dwControlKeyState |= SHIFT_PRESSED; }
|
||||||
|
if (record.flags & 0x08) { mer.dwControlKeyState |= LEFT_ALT_PRESSED; }
|
||||||
|
if (record.flags & 0x10) { mer.dwControlKeyState |= LEFT_CTRL_PRESSED; }
|
||||||
|
|
||||||
|
if (record.flags & 0x40) {
|
||||||
|
// Mouse wheel
|
||||||
|
mer.dwEventFlags |= MOUSE_WHEELED;
|
||||||
|
if (button == 0) {
|
||||||
|
// up
|
||||||
|
mer.dwButtonState |= 0x00780000;
|
||||||
|
} else if (button == 1) {
|
||||||
|
// down
|
||||||
|
mer.dwButtonState |= 0xff880000;
|
||||||
|
} else {
|
||||||
|
// Invalid -- do nothing
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Ordinary mouse event
|
||||||
|
if (record.flags & 0x20) { mer.dwEventFlags |= MOUSE_MOVED; }
|
||||||
|
if (button == 3) {
|
||||||
|
m_mouseButtonState = 0;
|
||||||
|
// Potentially advance double-click detection.
|
||||||
|
m_doubleClick.released = true;
|
||||||
|
} else {
|
||||||
|
const DWORD relevantFlag =
|
||||||
|
(button == 0) ? FROM_LEFT_1ST_BUTTON_PRESSED :
|
||||||
|
(button == 1) ? FROM_LEFT_2ND_BUTTON_PRESSED :
|
||||||
|
(button == 2) ? RIGHTMOST_BUTTON_PRESSED :
|
||||||
|
0;
|
||||||
|
ASSERT(relevantFlag != 0);
|
||||||
|
if (record.release) {
|
||||||
|
m_mouseButtonState &= ~relevantFlag;
|
||||||
|
if (relevantFlag == m_doubleClick.button) {
|
||||||
|
// Potentially advance double-click detection.
|
||||||
|
m_doubleClick.released = true;
|
||||||
|
} else {
|
||||||
|
// End double-click detection.
|
||||||
|
m_doubleClick = DoubleClickDetection();
|
||||||
|
}
|
||||||
|
} else if ((m_mouseButtonState & relevantFlag) == 0) {
|
||||||
|
// The button has been newly pressed.
|
||||||
|
m_mouseButtonState |= relevantFlag;
|
||||||
|
// Detect a double-click. This code looks for an exact
|
||||||
|
// coordinate match, which is stricter than what Windows does,
|
||||||
|
// but Windows has pixel coordinates, and we only have terminal
|
||||||
|
// coordinates.
|
||||||
|
if (m_doubleClick.button == relevantFlag &&
|
||||||
|
m_doubleClick.pos == record.coord &&
|
||||||
|
(GetTickCount() - m_doubleClick.tick <
|
||||||
|
GetDoubleClickTime())) {
|
||||||
|
// Record a double-click and end double-click detection.
|
||||||
|
mer.dwEventFlags |= DOUBLE_CLICK;
|
||||||
|
m_doubleClick = DoubleClickDetection();
|
||||||
|
} else {
|
||||||
|
// Begin double-click detection.
|
||||||
|
m_doubleClick.button = relevantFlag;
|
||||||
|
m_doubleClick.pos = record.coord;
|
||||||
|
m_doubleClick.tick = GetTickCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mer.dwButtonState |= m_mouseButtonState;
|
||||||
|
|
||||||
|
if (m_mouseInputEnabled) {
|
||||||
|
if (isTracingEnabled()) {
|
||||||
|
static bool debugInput = hasDebugFlag("input");
|
||||||
|
if (debugInput) {
|
||||||
|
trace("mouse event: %s", mouseEventToString(mer).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
records.push_back(newRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
void ConsoleInput::appendUtf8Char(std::vector<INPUT_RECORD> &records,
|
void ConsoleInput::appendUtf8Char(std::vector<INPUT_RECORD> &records,
|
||||||
const char *charBuffer,
|
const char *charBuffer,
|
||||||
const int charLen,
|
const int charLen,
|
||||||
@ -299,66 +590,3 @@ void ConsoleInput::appendInputRecord(std::vector<INPUT_RECORD> &records,
|
|||||||
ir.Event.KeyEvent.dwControlKeyState = keyState;
|
ir.Event.KeyEvent.dwControlKeyState = keyState;
|
||||||
records.push_back(ir);
|
records.push_back(ir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the byte size of a UTF-8 character using the value of the first
|
|
||||||
// byte.
|
|
||||||
int ConsoleInput::utf8CharLength(char firstByte)
|
|
||||||
{
|
|
||||||
// This code would probably be faster if it used __builtin_clz.
|
|
||||||
if ((firstByte & 0x80) == 0) {
|
|
||||||
return 1;
|
|
||||||
} else if ((firstByte & 0xE0) == 0xC0) {
|
|
||||||
return 2;
|
|
||||||
} else if ((firstByte & 0xF0) == 0xE0) {
|
|
||||||
return 3;
|
|
||||||
} else if ((firstByte & 0xF8) == 0xF0) {
|
|
||||||
return 4;
|
|
||||||
} else if ((firstByte & 0xFC) == 0xF8) {
|
|
||||||
return 5;
|
|
||||||
} else if ((firstByte & 0xFE) == 0xFC) {
|
|
||||||
return 6;
|
|
||||||
} else {
|
|
||||||
// Malformed UTF-8.
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 *input, int inputSize)
|
|
||||||
{
|
|
||||||
const char *pch = input;
|
|
||||||
const char *stop = input + inputSize;
|
|
||||||
|
|
||||||
if (pch == stop) { return -1; }
|
|
||||||
|
|
||||||
#define CHECK(cond) \
|
|
||||||
do { \
|
|
||||||
if (!(cond)) { return 0; } \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define ADVANCE() \
|
|
||||||
do { \
|
|
||||||
pch++; \
|
|
||||||
if (pch == stop) { return -1; } \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
CHECK(*pch == '\x1B'); ADVANCE();
|
|
||||||
CHECK(*pch == '['); ADVANCE();
|
|
||||||
CHECK(isdigit(*pch)); ADVANCE();
|
|
||||||
while (isdigit(*pch)) {
|
|
||||||
ADVANCE();
|
|
||||||
}
|
|
||||||
CHECK(*pch == ';'); ADVANCE();
|
|
||||||
CHECK(isdigit(*pch)); ADVANCE();
|
|
||||||
while (isdigit(*pch)) {
|
|
||||||
ADVANCE();
|
|
||||||
}
|
|
||||||
CHECK(*pch == 'R');
|
|
||||||
return pch - input + 1;
|
|
||||||
#undef CHECK
|
|
||||||
#undef ADVANCE
|
|
||||||
}
|
|
||||||
|
@ -27,7 +27,9 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Coord.h"
|
||||||
#include "InputMap.h"
|
#include "InputMap.h"
|
||||||
|
#include "SmallRect.h"
|
||||||
|
|
||||||
class Win32Console;
|
class Win32Console;
|
||||||
class DsrSender;
|
class DsrSender;
|
||||||
@ -39,13 +41,18 @@ public:
|
|||||||
~ConsoleInput();
|
~ConsoleInput();
|
||||||
void writeInput(const std::string &input);
|
void writeInput(const std::string &input);
|
||||||
void flushIncompleteEscapeCode();
|
void flushIncompleteEscapeCode();
|
||||||
|
void setMouseInputEnabled(bool val) { m_mouseInputEnabled = val; }
|
||||||
|
void setMouseWindowRect(SmallRect val) { m_mouseWindowRect = val; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void doWrite(bool isEof);
|
void doWrite(bool isEof);
|
||||||
int scanKeyPress(std::vector<INPUT_RECORD> &records,
|
int scanInput(std::vector<INPUT_RECORD> &records,
|
||||||
const char *input,
|
const char *input,
|
||||||
int inputSize,
|
int inputSize,
|
||||||
bool isEof);
|
bool isEof);
|
||||||
|
int scanMouseInput(std::vector<INPUT_RECORD> &records,
|
||||||
|
const char *input,
|
||||||
|
int inputSize);
|
||||||
void appendUtf8Char(std::vector<INPUT_RECORD> &records,
|
void appendUtf8Char(std::vector<INPUT_RECORD> &records,
|
||||||
const char *charBuffer,
|
const char *charBuffer,
|
||||||
int charLen,
|
int charLen,
|
||||||
@ -59,8 +66,6 @@ private:
|
|||||||
uint16_t virtualKey,
|
uint16_t virtualKey,
|
||||||
uint16_t unicodeChar,
|
uint16_t unicodeChar,
|
||||||
uint16_t keyState);
|
uint16_t keyState);
|
||||||
static int utf8CharLength(char firstByte);
|
|
||||||
static int matchDsr(const char *input, int inputSize);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Win32Console *m_console;
|
Win32Console *m_console;
|
||||||
@ -69,6 +74,16 @@ private:
|
|||||||
std::string m_byteQueue;
|
std::string m_byteQueue;
|
||||||
InputMap m_inputMap;
|
InputMap m_inputMap;
|
||||||
DWORD m_lastWriteTick;
|
DWORD m_lastWriteTick;
|
||||||
|
DWORD m_mouseButtonState;
|
||||||
|
struct DoubleClickDetection {
|
||||||
|
DoubleClickDetection() : button(0), tick(0), released(0) {}
|
||||||
|
DWORD button;
|
||||||
|
Coord pos;
|
||||||
|
DWORD tick;
|
||||||
|
bool released;
|
||||||
|
} m_doubleClick;
|
||||||
|
bool m_mouseInputEnabled;
|
||||||
|
SmallRect m_mouseWindowRect;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONSOLEINPUT_H
|
#endif // CONSOLEINPUT_H
|
||||||
|
Loading…
Reference in New Issue
Block a user