Pass certain keys to the console using windows messages.

If the terminal is placed in an alternate mode using DECCKM, then we
need to generate different input escape sequences for WSL (and other
programs using ENABLE_VIRTUAL_TERMINAL_INPUT).  AFAIK, there is no way
for winpty to detect whether an alternate mode is enabled, but it only
seems to affect a small number of keys, so send those keys as window
messages.

We don't send *all* keys as window messages, because the console may try
to interpret some of them.  It doesn't interpret the arrow keys and
Home/End, AFAICT, and those seem to be the only keys affected by DECCKM.
(The console's line-input mode *does* care about these navigation keys,
but in that case, WriteConsoleInput and window messages work equally well.)

DECCKM only seems to affect the keys when there is no modifier key.

I believe that by using SendMessage, winpty-agent will block until the
keys are appended to the console input buffer.  I'm not sure how to verify
it.  If this *weren't* the case, there could be a danger of key input
being transposed.  It seems unlikely to be an issue.

Fixes https://github.com/rprichard/winpty/issues/90
This commit is contained in:
Ryan Prichard 2017-01-15 04:20:05 -06:00
parent 27dfaaefb6
commit 40d5b72c43
4 changed files with 41 additions and 3 deletions

View File

@ -1,3 +1,12 @@
# Next Version
Bug fixes:
* winpty generates more correct input escape sequences for WSL programs that
enable an alternate input mode using DECCKM. This bug affected arrow keys
and Home/End in WSL programs such as `vim`, `mc`, and `less`.
[#90](https://github.com/rprichard/winpty/issues/90)
# Version 0.4.1 (2017-01-03)
Bug fixes:

View File

@ -208,7 +208,8 @@ Agent::Agent(LPCWSTR controlPipeName,
m_console.setTitle(m_currentTitle);
const HANDLE conin = GetStdHandle(STD_INPUT_HANDLE);
m_consoleInput.reset(new ConsoleInput(conin, m_mouseMode, *this));
m_consoleInput.reset(
new ConsoleInput(conin, m_mouseMode, *this, m_console));
// Setup Ctrl-C handling. First restore default handling of Ctrl-C. This
// attribute is inherited by child processes. Then register a custom

View File

@ -191,7 +191,9 @@ static int matchMouseRecord(const char *input, int inputSize, MouseRecord &out)
} // anonymous namespace
ConsoleInput::ConsoleInput(HANDLE conin, int mouseMode, DsrSender &dsrSender) :
ConsoleInput::ConsoleInput(HANDLE conin, int mouseMode, DsrSender &dsrSender,
Win32Console &console) :
m_console(console),
m_conin(conin),
m_mouseMode(mouseMode),
m_dsrSender(dsrSender)
@ -594,15 +596,39 @@ void ConsoleInput::appendKeyPress(std::vector<INPUT_RECORD> &records,
const bool ctrl = (keyState & LEFT_CTRL_PRESSED) != 0;
const bool alt = (keyState & LEFT_ALT_PRESSED) != 0;
const bool shift = (keyState & SHIFT_PRESSED) != 0;
bool hasDebugInput = false;
if (isTracingEnabled()) {
static bool debugInput = hasDebugFlag("input");
if (debugInput) {
hasDebugInput = true;
InputMap::Key key = { virtualKey, codePoint, keyState };
trace("keypress: %s", key.toString().c_str());
}
}
if (m_escapeInputEnabled &&
(virtualKey == VK_UP ||
virtualKey == VK_DOWN ||
virtualKey == VK_LEFT ||
virtualKey == VK_RIGHT ||
virtualKey == VK_HOME ||
virtualKey == VK_END) &&
!ctrl && !alt && !shift) {
if (hasDebugInput) {
trace("sending keypress to console HWND");
}
uint32_t scanCode = MapVirtualKey(virtualKey, MAPVK_VK_TO_VSC);
if (scanCode > 255) {
scanCode = 0;
}
SendMessage(m_console.hwnd(), WM_KEYDOWN, virtualKey,
(scanCode << 16) | 1u);
SendMessage(m_console.hwnd(), WM_KEYUP, virtualKey,
(scanCode << 16) | (1u | (1u << 30) | (1u << 31)));
return;
}
uint16_t stepKeyState = 0;
if (ctrl) {
stepKeyState |= LEFT_CTRL_PRESSED;

View File

@ -38,7 +38,8 @@ class DsrSender;
class ConsoleInput
{
public:
ConsoleInput(HANDLE conin, int mouseMode, DsrSender &dsrSender);
ConsoleInput(HANDLE conin, int mouseMode, DsrSender &dsrSender,
Win32Console &console);
void writeInput(const std::string &input);
void flushIncompleteEscapeCode();
void setMouseWindowRect(SmallRect val) { m_mouseWindowRect = val; }
@ -79,6 +80,7 @@ private:
DWORD inputConsoleMode();
private:
Win32Console &m_console;
HANDLE m_conin = nullptr;
int m_mouseMode = 0;
DsrSender &m_dsrSender;