Handle many more keypress escape sequences, particularly for putty/rxvt
* Factor out the tables from ConsoleInput.cc to DefaultInputMap.cc. Fixes https://github.com/rprichard/winpty/issues/40
This commit is contained in:
parent
b416a4a7c1
commit
8d8506d489
@ -25,8 +25,9 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Win32Console.h"
|
||||
#include "DefaultInputMap.h"
|
||||
#include "DsrSender.h"
|
||||
#include "Win32Console.h"
|
||||
#include "../shared/DebugClient.h"
|
||||
#include "../shared/UnixCtrlChars.h"
|
||||
|
||||
@ -36,131 +37,13 @@
|
||||
|
||||
const int kIncompleteEscapeTimeoutMs = 1000;
|
||||
|
||||
#define ESC "\x1B"
|
||||
#define CSI ESC"["
|
||||
#define DIM(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
ConsoleInput::ConsoleInput(DsrSender *dsrSender) :
|
||||
m_console(new Win32Console),
|
||||
m_dsrSender(dsrSender),
|
||||
m_dsrSent(false),
|
||||
lastWriteTick(0)
|
||||
{
|
||||
struct KeyDescriptor {
|
||||
const char *encoding;
|
||||
InputMap::Key key;
|
||||
};
|
||||
const int vkLB = VkKeyScan('[') & 0xFF;
|
||||
static const KeyDescriptor 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"O", { 'O', 'O', LEFT_ALT_PRESSED | SHIFT_PRESSED } },
|
||||
{ ESC"[", { vkLB, '[', LEFT_ALT_PRESSED } },
|
||||
|
||||
// F1-F4 function keys. F5-F12 seem to be handled more consistently among
|
||||
// various TERM=xterm terminals (gnome-terminal, konsole, xterm, mintty),
|
||||
// using a CSI-prefix with an optional extra modifier digit. (putty is
|
||||
// also TERM=xterm, though, and has completely different modified F5-F12
|
||||
// encodings.)
|
||||
{ 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
|
||||
|
||||
{ "\x7F", { VK_BACK, '\x08', 0, } },
|
||||
{ ESC"\x7F", { VK_BACK, '\x08', LEFT_ALT_PRESSED, } },
|
||||
{ ESC"OH", { VK_HOME, '\0', 0, } }, // gt
|
||||
{ ESC"OF", { VK_END, '\0', 0, } }, // gt
|
||||
{ ESC"[Z", { VK_TAB, '\t', SHIFT_PRESSED } },
|
||||
};
|
||||
|
||||
struct CsiEncoding {
|
||||
int id;
|
||||
char letter;
|
||||
int virtualKey;
|
||||
};
|
||||
static const CsiEncoding csiEncodings[] = {
|
||||
{ 0, 'A', VK_UP },
|
||||
{ 0, 'B', VK_DOWN },
|
||||
{ 0, 'C', VK_RIGHT },
|
||||
{ 0, 'D', VK_LEFT },
|
||||
{ 0, 'E', VK_NUMPAD5 },
|
||||
{ 0, 'F', VK_END },
|
||||
{ 0, 'H', VK_HOME },
|
||||
{ 0, 'P', VK_F1 }, // mod+F1 for xterm and mintty
|
||||
{ 0, 'Q', VK_F2 }, // mod+F2 for xterm and mintty
|
||||
{ 0, 'R', VK_F3 }, // mod+F3 for xterm and mintty
|
||||
{ 0, 'S', VK_F4 }, // mod+F4 for xterm and mintty
|
||||
{ 1, '~', VK_HOME },
|
||||
{ 2, '~', VK_INSERT },
|
||||
{ 3, '~', VK_DELETE },
|
||||
{ 4, '~', VK_END }, // gnome-terminal keypad home/end
|
||||
{ 5, '~', VK_PRIOR },
|
||||
{ 6, '~', VK_NEXT },
|
||||
{ 7, '~', VK_HOME },
|
||||
{ 8, '~', VK_END },
|
||||
{ 15, '~', VK_F5 },
|
||||
{ 17, '~', VK_F6 },
|
||||
{ 18, '~', VK_F7 },
|
||||
{ 19, '~', VK_F8 },
|
||||
{ 20, '~', VK_F9 },
|
||||
{ 21, '~', VK_F10 },
|
||||
{ 23, '~', VK_F11 },
|
||||
{ 24, '~', VK_F12 },
|
||||
};
|
||||
|
||||
const int kCsiShiftModifier = 1;
|
||||
const int kCsiAltModifier = 2;
|
||||
const int kCsiCtrlModifier = 4;
|
||||
char encoding[32];
|
||||
for (size_t i = 0; i < DIM(csiEncodings); ++i) {
|
||||
const CsiEncoding *e = &csiEncodings[i];
|
||||
if (e->id == 0)
|
||||
sprintf(encoding, CSI"%c", e->letter);
|
||||
else
|
||||
sprintf(encoding, CSI"%d%c", e->id, e->letter);
|
||||
InputMap::Key k = { csiEncodings[i].virtualKey, 0, 0 };
|
||||
m_inputMap.set(encoding, k);
|
||||
int id = !e->id ? 1 : e->id;
|
||||
for (int mod = 2; mod <= 8; ++mod) {
|
||||
sprintf(encoding, CSI"%d;%d%c", id, mod, e->letter);
|
||||
k.keyState = 0;
|
||||
if ((mod - 1) & kCsiShiftModifier) k.keyState |= SHIFT_PRESSED;
|
||||
if ((mod - 1) & kCsiAltModifier) k.keyState |= LEFT_ALT_PRESSED;
|
||||
if ((mod - 1) & kCsiCtrlModifier) k.keyState |= LEFT_CTRL_PRESSED;
|
||||
m_inputMap.set(encoding, k);
|
||||
}
|
||||
}
|
||||
|
||||
// Modified F1-F4 on gnome-terminal and konsole.
|
||||
for (int mod = 2; mod <= 8; ++mod) {
|
||||
for (int fn = 0; fn < 4; ++fn) {
|
||||
for (int fmt = 0; fmt < 1; ++fmt) {
|
||||
if (fmt == 0) {
|
||||
// gnome-terminal
|
||||
sprintf(encoding, ESC"O1;%d%c", mod, 'P' + fn);
|
||||
} else {
|
||||
// konsole
|
||||
sprintf(encoding, ESC"O%d%c", mod, 'P' + fn);
|
||||
}
|
||||
InputMap::Key k = { VK_F1 + fn, 0, 0 };
|
||||
if ((mod - 1) & kCsiShiftModifier) k.keyState |= SHIFT_PRESSED;
|
||||
if ((mod - 1) & kCsiAltModifier) k.keyState |= LEFT_ALT_PRESSED;
|
||||
if ((mod - 1) & kCsiCtrlModifier) k.keyState |= LEFT_CTRL_PRESSED;
|
||||
m_inputMap.set(encoding, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Static key encodings.
|
||||
for (size_t i = 0; i < DIM(keyDescriptorTable); ++i) {
|
||||
m_inputMap.set(keyDescriptorTable[i].encoding,
|
||||
keyDescriptorTable[i].key);
|
||||
}
|
||||
addDefaultEntriesToInputMap(m_inputMap);
|
||||
}
|
||||
|
||||
ConsoleInput::~ConsoleInput()
|
||||
|
322
src/agent/DefaultInputMap.cc
Executable file
322
src/agent/DefaultInputMap.cc
Executable file
@ -0,0 +1,322 @@
|
||||
// Copyright (c) 2015 Ryan Prichard
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
#include "DefaultInputMap.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "InputMap.h"
|
||||
|
||||
#define ESC "\x1B"
|
||||
#define DIM(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
namespace {
|
||||
|
||||
struct EscapeEncoding {
|
||||
bool alt_prefix_allowed;
|
||||
char prefix;
|
||||
char id;
|
||||
int modifiers;
|
||||
InputMap::Key key;
|
||||
};
|
||||
|
||||
// Modifiers. A "modifier" is an integer from 2 to 8 that conveys the status
|
||||
// of Shift(1), Alt(2), and Ctrl(4). The value is constructed by OR'ing the
|
||||
// appropriate value for each active modifier, then adding 1.
|
||||
//
|
||||
// Details:
|
||||
// - kBare: expands to: ESC <prefix> <suffix>
|
||||
// - kSemiMod: expands to: ESC <prefix> <numid> ; <mod> <suffix>
|
||||
// - kBareMod: expands to: ESC <prefix> <mod> <suffix>
|
||||
// (kBareMod is used only for Konsole's modified F1-F4)
|
||||
const int kBare = 0x01;
|
||||
const int kSemiMod = 0x02;
|
||||
const int kBareMod = 0x04;
|
||||
|
||||
// Numeric escape sequences suffixes:
|
||||
// - with no flag: accept: ~
|
||||
// - kSuffixCtrl: accept: ~ ^
|
||||
// - kSuffixShift: accept: ~ $
|
||||
// - kSuffixBoth: accept: ~ ^ $ @
|
||||
const int kSuffixCtrl = 0x08;
|
||||
const int kSuffixShift = 0x10;
|
||||
const int kSuffixBoth = kSuffixCtrl | kSuffixShift;
|
||||
|
||||
static const EscapeEncoding escapeLetterEncodings[] = {
|
||||
// Conventional arrow keys
|
||||
{ true, '[', 'A', kBare | kSemiMod, { VK_UP, '\0', 0 } },
|
||||
{ true, '[', 'B', kBare | kSemiMod, { VK_DOWN, '\0', 0 } },
|
||||
{ true, '[', 'C', kBare | kSemiMod, { VK_RIGHT, '\0', 0 } },
|
||||
{ true, '[', 'D', kBare | kSemiMod, { VK_LEFT, '\0', 0 } },
|
||||
|
||||
// putty. putty uses this sequence for Ctrl-Arrow, Shift-Arrow, and
|
||||
// Ctrl-Shift-Arrow, but I can only decode to one choice, so I'm just
|
||||
// leaving the modifier off altogether.
|
||||
{ true, 'O', 'A', kBare, { VK_UP, '\0', 0 } },
|
||||
{ true, 'O', 'B', kBare, { VK_DOWN, '\0', 0 } },
|
||||
{ true, 'O', 'C', kBare, { VK_RIGHT, '\0', 0 } },
|
||||
{ true, 'O', 'D', kBare, { VK_LEFT, '\0', 0 } },
|
||||
|
||||
// rxvt, rxvt-unicode
|
||||
// Shift-Ctrl-Arrow can't be identified. It's the same as Shift-Arrow.
|
||||
{ true, '[', 'a', kBare, { VK_UP, '\0', SHIFT_PRESSED } },
|
||||
{ true, '[', 'b', kBare, { VK_DOWN, '\0', SHIFT_PRESSED } },
|
||||
{ true, '[', 'c', kBare, { VK_RIGHT, '\0', SHIFT_PRESSED } },
|
||||
{ true, '[', 'd', kBare, { VK_LEFT, '\0', SHIFT_PRESSED } },
|
||||
{ true, 'O', 'a', kBare, { VK_UP, '\0', LEFT_CTRL_PRESSED } },
|
||||
{ true, 'O', 'b', kBare, { VK_DOWN, '\0', LEFT_CTRL_PRESSED } },
|
||||
{ true, 'O', 'c', kBare, { VK_RIGHT, '\0', LEFT_CTRL_PRESSED } },
|
||||
{ true, 'O', 'd', kBare, { VK_LEFT, '\0', LEFT_CTRL_PRESSED } },
|
||||
|
||||
// Numpad 5 with NumLock off
|
||||
// * xterm, mintty, and gnome-terminal use `ESC [ E`.
|
||||
// * putty, TERM=cygwin, TERM=linux all use `ESC [ G` for 5
|
||||
// * putty uses `ESC O G` for Ctrl-5 and Shift-5. Omit the modifier
|
||||
// as with putty's arrow keys.
|
||||
// * I never saw modifiers inserted into these escapes, but I think
|
||||
// it should be completely OK with the CSI escapes.
|
||||
{ true, '[', 'E', kBare | kSemiMod, { VK_NUMPAD5, '\0', 0 } },
|
||||
{ true, '[', 'G', kBare | kSemiMod, { VK_NUMPAD5, '\0', 0 } },
|
||||
{ true, 'O', 'G', kBare, { VK_NUMPAD5, '\0', 0 } },
|
||||
|
||||
// Home/End, letter version
|
||||
// * gnome-terminal uses `ESC O [HF]`. I never saw it modified.
|
||||
{ true, '[', 'H', kBare | kSemiMod, { VK_HOME, '\0', 0 } },
|
||||
{ true, '[', 'F', kBare | kSemiMod, { VK_END, '\0', 0 } },
|
||||
{ true, 'O', 'H', kBare, { VK_HOME, '\0', 0 } },
|
||||
{ true, 'O', 'F', kBare, { VK_END, '\0', 0 } },
|
||||
|
||||
// F1-F4, letter version (xterm, VTE, konsole)
|
||||
{ true, '[', 'P', kSemiMod, { VK_F1, '\0', 0 } },
|
||||
{ true, '[', 'Q', kSemiMod, { VK_F2, '\0', 0 } },
|
||||
{ true, '[', 'R', kSemiMod, { VK_F3, '\0', 0 } },
|
||||
{ true, '[', 'S', kSemiMod, { VK_F4, '\0', 0 } },
|
||||
|
||||
// GNOME VTE and Konsole have special encodings for modified F1-F4:
|
||||
// * [VTE] ESC O 1 ; n [PQRS]
|
||||
// * [Konsole] ESC O n [PQRS]
|
||||
{ false, 'O', 'P', kBare | kBareMod | kSemiMod, { VK_F1, '\0', 0 } },
|
||||
{ false, 'O', 'Q', kBare | kBareMod | kSemiMod, { VK_F2, '\0', 0 } },
|
||||
{ false, 'O', 'R', kBare | kBareMod | kSemiMod, { VK_F3, '\0', 0 } },
|
||||
{ false, 'O', 'S', kBare | kBareMod | kSemiMod, { VK_F4, '\0', 0 } },
|
||||
|
||||
{ false, '[', 'Z', kBare, { VK_TAB, '\t', SHIFT_PRESSED } },
|
||||
};
|
||||
|
||||
static const EscapeEncoding escapeNumericEncodings[] = {
|
||||
{ true, '[', 1, kBare | kSemiMod | kSuffixBoth, { VK_HOME, '\0', 0 } },
|
||||
{ true, '[', 2, kBare | kSemiMod | kSuffixBoth, { VK_INSERT, '\0', 0 } },
|
||||
{ true, '[', 3, kBare | kSemiMod | kSuffixBoth, { VK_DELETE, '\0', 0 } },
|
||||
{ true, '[', 4, kBare | kSemiMod | kSuffixBoth, { VK_END, '\0', 0 } },
|
||||
{ true, '[', 5, kBare | kSemiMod | kSuffixBoth, { VK_PRIOR, '\0', 0 } },
|
||||
{ true, '[', 6, kBare | kSemiMod | kSuffixBoth, { VK_NEXT, '\0', 0 } },
|
||||
{ true, '[', 7, kBare | kSemiMod | kSuffixBoth, { VK_HOME, '\0', 0 } },
|
||||
{ true, '[', 8, kBare | kSemiMod | kSuffixBoth, { VK_END, '\0', 0 } },
|
||||
{ true, '[', 11, kBare | kSemiMod | kSuffixBoth, { VK_F1, '\0', 0 } },
|
||||
{ true, '[', 12, kBare | kSemiMod | kSuffixBoth, { VK_F2, '\0', 0 } },
|
||||
{ true, '[', 13, kBare | kSemiMod | kSuffixBoth, { VK_F3, '\0', 0 } },
|
||||
{ true, '[', 14, kBare | kSemiMod | kSuffixBoth, { VK_F4, '\0', 0 } },
|
||||
{ true, '[', 15, kBare | kSemiMod | kSuffixBoth, { VK_F5, '\0', 0 } },
|
||||
{ true, '[', 17, kBare | kSemiMod | kSuffixBoth, { VK_F6, '\0', 0 } },
|
||||
{ true, '[', 18, kBare | kSemiMod | kSuffixBoth, { VK_F7, '\0', 0 } },
|
||||
{ true, '[', 19, kBare | kSemiMod | kSuffixBoth, { VK_F8, '\0', 0 } },
|
||||
{ true, '[', 20, kBare | kSemiMod | kSuffixBoth, { VK_F9, '\0', 0 } },
|
||||
{ true, '[', 21, kBare | kSemiMod | kSuffixBoth, { VK_F10, '\0', 0 } },
|
||||
{ true, '[', 23, kBare | kSemiMod | kSuffixBoth, { VK_F11, '\0', 0 } },
|
||||
{ true, '[', 24, kBare | kSemiMod | kSuffixBoth, { VK_F12, '\0', 0 } },
|
||||
{ true, '[', 25, kBare | kSemiMod | kSuffixBoth, { VK_F3, '\0', SHIFT_PRESSED } },
|
||||
{ true, '[', 26, kBare | kSemiMod | kSuffixBoth, { VK_F4, '\0', SHIFT_PRESSED } },
|
||||
{ true, '[', 28, kBare | kSemiMod | kSuffixBoth, { VK_F5, '\0', SHIFT_PRESSED } },
|
||||
{ true, '[', 29, kBare | kSemiMod | kSuffixBoth, { VK_F6, '\0', SHIFT_PRESSED } },
|
||||
{ true, '[', 31, kBare | kSemiMod | kSuffixBoth, { VK_F7, '\0', SHIFT_PRESSED } },
|
||||
{ true, '[', 32, kBare | kSemiMod | kSuffixBoth, { VK_F8, '\0', SHIFT_PRESSED } },
|
||||
{ true, '[', 33, kBare | kSemiMod | kSuffixBoth, { VK_F9, '\0', SHIFT_PRESSED } },
|
||||
{ true, '[', 34, kBare | kSemiMod | kSuffixBoth, { VK_F10, '\0', SHIFT_PRESSED } },
|
||||
};
|
||||
|
||||
const int kCsiShiftModifier = 1;
|
||||
const int kCsiAltModifier = 2;
|
||||
const int kCsiCtrlModifier = 4;
|
||||
|
||||
static void addSimpleEntries(InputMap &inputMap) {
|
||||
struct SimpleEncoding {
|
||||
const char *encoding;
|
||||
InputMap::Key key;
|
||||
};
|
||||
|
||||
const int vkLB = VkKeyScan('[') & 0xFF;
|
||||
|
||||
static const SimpleEncoding simpleEncodings[] = {
|
||||
// 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"O", { 'O', 'O', LEFT_ALT_PRESSED | SHIFT_PRESSED } },
|
||||
{ ESC"[", { vkLB, '[', LEFT_ALT_PRESSED } },
|
||||
|
||||
{ "\x7F", { VK_BACK, '\x08', 0, } },
|
||||
{ ESC"\x7F", { VK_BACK, '\x08', LEFT_ALT_PRESSED, } },
|
||||
|
||||
// Handle special F1-F5 for TERM=linux and TERM=cygwin.
|
||||
{ ESC"[[A", { VK_F1, '\0', 0 } },
|
||||
{ ESC"[[B", { VK_F2, '\0', 0 } },
|
||||
{ ESC"[[C", { VK_F3, '\0', 0 } },
|
||||
{ ESC"[[D", { VK_F4, '\0', 0 } },
|
||||
{ ESC"[[E", { VK_F5, '\0', 0 } },
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < DIM(simpleEncodings); ++i) {
|
||||
inputMap.set(simpleEncodings[i].encoding,
|
||||
simpleEncodings[i].key);
|
||||
}
|
||||
}
|
||||
|
||||
struct ExpandContext {
|
||||
InputMap &inputMap;
|
||||
const EscapeEncoding &e;
|
||||
char *buffer;
|
||||
};
|
||||
|
||||
static inline void setEncoding(const ExpandContext &ctx, int extraKeyState) {
|
||||
InputMap::Key k = ctx.e.key;
|
||||
k.keyState |= extraKeyState;
|
||||
ctx.inputMap.set(ctx.buffer, k);
|
||||
}
|
||||
|
||||
static inline int keyStateForMod(int mod) {
|
||||
int ret = 0;
|
||||
if ((mod - 1) & kCsiShiftModifier) ret |= SHIFT_PRESSED;
|
||||
if ((mod - 1) & kCsiAltModifier) ret |= LEFT_ALT_PRESSED;
|
||||
if ((mod - 1) & kCsiCtrlModifier) ret |= LEFT_CTRL_PRESSED;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void expandNumericEncodingSuffix(const ExpandContext &ctx, char *p,
|
||||
int extraKeyState) {
|
||||
{
|
||||
char *q = p;
|
||||
*q++ = '~';
|
||||
*q++ = '\0';
|
||||
setEncoding(ctx, extraKeyState);
|
||||
}
|
||||
if (ctx.e.modifiers & kSuffixShift) {
|
||||
char *q = p;
|
||||
*q++ = '$';
|
||||
*q++ = '\0';
|
||||
setEncoding(ctx, extraKeyState | SHIFT_PRESSED);
|
||||
}
|
||||
if (ctx.e.modifiers & kSuffixCtrl) {
|
||||
char *q = p;
|
||||
*q++ = '^';
|
||||
*q++ = '\0';
|
||||
setEncoding(ctx, extraKeyState | LEFT_CTRL_PRESSED);
|
||||
}
|
||||
if (ctx.e.modifiers & (kSuffixCtrl | kSuffixShift)) {
|
||||
char *q = p;
|
||||
*q++ = '@';
|
||||
*q++ = '\0';
|
||||
setEncoding(ctx, extraKeyState | SHIFT_PRESSED | LEFT_CTRL_PRESSED);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool is_numeric>
|
||||
static inline void expandEncodingAfterAltPrefix(
|
||||
const ExpandContext &ctx, char *p, int extraKeyState) {
|
||||
*p++ = '\x1b';
|
||||
*p++ = ctx.e.prefix;
|
||||
if (ctx.e.modifiers & kBare) {
|
||||
char *q = p;
|
||||
if (is_numeric) {
|
||||
q += sprintf(q, "%d", ctx.e.id);
|
||||
expandNumericEncodingSuffix(ctx, q, extraKeyState);
|
||||
} else {
|
||||
*q++ = ctx.e.id;
|
||||
*q++ = '\0';
|
||||
setEncoding(ctx, extraKeyState);
|
||||
}
|
||||
}
|
||||
if (ctx.e.modifiers & kBareMod) {
|
||||
ASSERT(!is_numeric && "kBareMod is invalid with numeric sequences");
|
||||
for (int mod = 2; mod <= 8; ++mod) {
|
||||
char *q = p;
|
||||
*q++ = '0' + mod;
|
||||
*q++ = ctx.e.id;
|
||||
*q++ = '\0';
|
||||
setEncoding(ctx, extraKeyState | keyStateForMod(mod));
|
||||
}
|
||||
}
|
||||
if (ctx.e.modifiers & kSemiMod) {
|
||||
for (int mod = 2; mod <= 8; ++mod) {
|
||||
char *q = p;
|
||||
if (is_numeric) {
|
||||
q += sprintf(q, "%d", ctx.e.id);
|
||||
*q++ = ';';
|
||||
*q++ = '0' + mod;
|
||||
expandNumericEncodingSuffix(
|
||||
ctx, q, extraKeyState | keyStateForMod(mod));
|
||||
} else {
|
||||
*q++ = '1';
|
||||
*q++ = ';';
|
||||
*q++ = '0' + mod;
|
||||
*q++ = ctx.e.id;
|
||||
*q++ = '\0';
|
||||
setEncoding(ctx, extraKeyState | keyStateForMod(mod));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <bool is_numeric>
|
||||
static inline void expandEncoding(const ExpandContext &ctx) {
|
||||
if (ctx.e.alt_prefix_allowed) {
|
||||
// For better or for worse, this code expands all of:
|
||||
// * ESC [ <key> -- <key>
|
||||
// * ESC ESC [ <key> -- Alt-<key>
|
||||
// * ESC [ 1 ; 3 <key> -- Alt-<key>
|
||||
// * ESC ESC [ 1 ; 3 <key> -- Alt-<key> specified twice
|
||||
// I suspect no terminal actually emits the last one (i.e. specifying
|
||||
// the Alt modifier using both methods), but I have seen a terminal
|
||||
// that emitted a prefix ESC for Alt and a non-Alt modifier.
|
||||
char *p = ctx.buffer;
|
||||
*p++ = '\x1b';
|
||||
expandEncodingAfterAltPrefix<is_numeric>(ctx, p, LEFT_ALT_PRESSED);
|
||||
}
|
||||
expandEncodingAfterAltPrefix<is_numeric>(ctx, ctx.buffer, 0);
|
||||
}
|
||||
|
||||
template <bool is_numeric, size_t N>
|
||||
static void addEscapes(InputMap &inputMap, const EscapeEncoding (&encodings)[N]) {
|
||||
char buffer[32];
|
||||
for (size_t i = 0; i < DIM(encodings); ++i) {
|
||||
ExpandContext ctx = { inputMap, encodings[i], buffer };
|
||||
expandEncoding<is_numeric>(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void addDefaultEntriesToInputMap(InputMap &inputMap) {
|
||||
addEscapes<false>(inputMap, escapeLetterEncodings);
|
||||
addEscapes<true>(inputMap, escapeNumericEncodings);
|
||||
addSimpleEntries(inputMap);
|
||||
}
|
28
src/agent/DefaultInputMap.h
Executable file
28
src/agent/DefaultInputMap.h
Executable file
@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2015 Ryan Prichard
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
#ifndef DEFAULT_INPUT_MAP_H
|
||||
#define DEFAULT_INPUT_MAP_H
|
||||
|
||||
class InputMap;
|
||||
|
||||
void addDefaultEntriesToInputMap(InputMap &inputMap);
|
||||
|
||||
#endif // DEFAULT_INPUT_MAP_H
|
@ -26,6 +26,7 @@ AGENT_OBJECTS = \
|
||||
build/mingw/agent/ConsoleInput.o \
|
||||
build/mingw/agent/ConsoleLine.o \
|
||||
build/mingw/agent/Coord.o \
|
||||
build/mingw/agent/DefaultInputMap.o \
|
||||
build/mingw/agent/EventLoop.o \
|
||||
build/mingw/agent/InputMap.o \
|
||||
build/mingw/agent/LargeConsoleRead.o \
|
||||
|
@ -38,6 +38,8 @@
|
||||
'agent/ConsoleLine.h',
|
||||
'agent/Coord.h',
|
||||
'agent/Coord.cc',
|
||||
'agent/DefaultInputMap.h',
|
||||
'agent/DefaultInputMap.cc',
|
||||
'agent/DsrSender.h',
|
||||
'agent/EventLoop.h',
|
||||
'agent/EventLoop.cc',
|
||||
|
Loading…
Reference in New Issue
Block a user