feat(tui): recognize keypad keys when using kitty keyboard protocol

(cherry picked from commit 33ed85a2f6)
This commit is contained in:
erw7
2021-11-24 02:05:35 +09:00
committed by github-actions[bot]
parent 202666b1b7
commit a4d1ef3128
6 changed files with 276 additions and 19 deletions

View File

@ -1568,12 +1568,16 @@ int vgetc(void)
}
break;
case K_KUP:
case K_XUP:
c = K_UP; break;
case K_KDOWN:
case K_XDOWN:
c = K_DOWN; break;
case K_KLEFT:
case K_XLEFT:
c = K_LEFT; break;
case K_KRIGHT:
case K_XRIGHT:
c = K_RIGHT; break;
}

View File

@ -31,6 +31,8 @@
#define int_eq kh_int_hash_equal
#define handle_T_hash kh_int_hash_func
#define handle_T_eq kh_int_hash_equal
#define KittyKey_hash kh_int_hash_func
#define KittyKey_eq kh_int_hash_equal
#if defined(ARCH_64)
@ -165,6 +167,7 @@ static inline bool ColorKey_eq(ColorKey ae1, ColorKey ae2)
MAP_IMPL(int, int, DEFAULT_INITIALIZER)
MAP_IMPL(int, cstr_t, DEFAULT_INITIALIZER)
MAP_IMPL(cstr_t, ptr_t, DEFAULT_INITIALIZER)
MAP_IMPL(cstr_t, int, DEFAULT_INITIALIZER)
MAP_IMPL(ptr_t, ptr_t, DEFAULT_INITIALIZER)
@ -183,6 +186,8 @@ MAP_IMPL(String, UIClientHandler, NULL)
MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER)
MAP_IMPL(KittyKey, cstr_t, DEFAULT_INITIALIZER)
/// Deletes a key:value pair from a string:pointer map, and frees the
/// storage of both key and value.
///

View File

@ -8,6 +8,7 @@
#include "nvim/extmark_defs.h"
#include "nvim/highlight_defs.h"
#include "nvim/map_defs.h"
#include "nvim/tui/input_defs.h"
#include "nvim/ui_client.h"
#if defined(__NetBSD__)
@ -35,6 +36,7 @@
// NOTE: Keys AND values must be allocated! khash.h does not make a copy.
//
MAP_DECLS(int, int)
MAP_DECLS(int, cstr_t)
MAP_DECLS(cstr_t, ptr_t)
MAP_DECLS(cstr_t, int)
MAP_DECLS(ptr_t, ptr_t)
@ -53,6 +55,8 @@ MAP_DECLS(String, UIClientHandler)
MAP_DECLS(ColorKey, ColorItem)
MAP_DECLS(KittyKey, cstr_t)
#define MAP_INIT { { 0, 0, 0, 0, NULL, NULL, NULL } }
#define map_init(k, v, map) do { *(map) = (Map(k, v)) MAP_INIT; } while (false)

View File

@ -24,6 +24,91 @@
#define KEY_BUFFER_SIZE 0xfff
static const struct kitty_key_map_entry {
KittyKey key;
const char *name;
} kitty_key_map_entry[] = {
{ KITTY_KEY_ESCAPE, "Esc" },
{ KITTY_KEY_ENTER, "CR" },
{ KITTY_KEY_TAB, "Tab" },
{ KITTY_KEY_BACKSPACE, "BS" },
{ KITTY_KEY_INSERT, "Insert" },
{ KITTY_KEY_DELETE, "Del" },
{ KITTY_KEY_LEFT, "Left" },
{ KITTY_KEY_RIGHT, "Right" },
{ KITTY_KEY_UP, "Up" },
{ KITTY_KEY_DOWN, "Down" },
{ KITTY_KEY_PAGE_UP, "PageUp" },
{ KITTY_KEY_PAGE_DOWN, "PageDown" },
{ KITTY_KEY_HOME, "Home" },
{ KITTY_KEY_END, "End" },
{ KITTY_KEY_F1, "F1" },
{ KITTY_KEY_F2, "F2" },
{ KITTY_KEY_F3, "F3" },
{ KITTY_KEY_F4, "F4" },
{ KITTY_KEY_F5, "F5" },
{ KITTY_KEY_F6, "F6" },
{ KITTY_KEY_F7, "F7" },
{ KITTY_KEY_F8, "F8" },
{ KITTY_KEY_F9, "F9" },
{ KITTY_KEY_F10, "F10" },
{ KITTY_KEY_F11, "F11" },
{ KITTY_KEY_F12, "F12" },
{ KITTY_KEY_F13, "F13" },
{ KITTY_KEY_F14, "F14" },
{ KITTY_KEY_F15, "F15" },
{ KITTY_KEY_F16, "F16" },
{ KITTY_KEY_F17, "F17" },
{ KITTY_KEY_F18, "F18" },
{ KITTY_KEY_F19, "F19" },
{ KITTY_KEY_F20, "F20" },
{ KITTY_KEY_F21, "F21" },
{ KITTY_KEY_F22, "F22" },
{ KITTY_KEY_F23, "F23" },
{ KITTY_KEY_F24, "F24" },
{ KITTY_KEY_F25, "F25" },
{ KITTY_KEY_F26, "F26" },
{ KITTY_KEY_F27, "F27" },
{ KITTY_KEY_F28, "F28" },
{ KITTY_KEY_F29, "F29" },
{ KITTY_KEY_F30, "F30" },
{ KITTY_KEY_F31, "F31" },
{ KITTY_KEY_F32, "F32" },
{ KITTY_KEY_F33, "F33" },
{ KITTY_KEY_F34, "F34" },
{ KITTY_KEY_F35, "F35" },
{ KITTY_KEY_KP_0, "k0" },
{ KITTY_KEY_KP_1, "k1" },
{ KITTY_KEY_KP_2, "k2" },
{ KITTY_KEY_KP_3, "k3" },
{ KITTY_KEY_KP_4, "k4" },
{ KITTY_KEY_KP_5, "k5" },
{ KITTY_KEY_KP_6, "k6" },
{ KITTY_KEY_KP_7, "k7" },
{ KITTY_KEY_KP_8, "k8" },
{ KITTY_KEY_KP_9, "k9" },
{ KITTY_KEY_KP_DECIMAL, "kPoint" },
{ KITTY_KEY_KP_DIVIDE, "kDivide" },
{ KITTY_KEY_KP_MULTIPLY, "kMultiply" },
{ KITTY_KEY_KP_SUBTRACT, "kMinus" },
{ KITTY_KEY_KP_ADD, "kPlus" },
{ KITTY_KEY_KP_ENTER, "kEnter" },
{ KITTY_KEY_KP_EQUAL, "kEqual" },
{ KITTY_KEY_KP_LEFT, "kLeft" },
{ KITTY_KEY_KP_RIGHT, "kRight" },
{ KITTY_KEY_KP_UP, "kUp" },
{ KITTY_KEY_KP_DOWN, "kDown" },
{ KITTY_KEY_KP_PAGE_UP, "kPageUp" },
{ KITTY_KEY_KP_PAGE_DOWN, "kPageDown" },
{ KITTY_KEY_KP_HOME, "kHome" },
{ KITTY_KEY_KP_END, "kEnd" },
{ KITTY_KEY_KP_INSERT, "kInsert" },
{ KITTY_KEY_KP_DELETE, "kDel" },
{ KITTY_KEY_KP_BEGIN, "kOrigin" },
};
static Map(KittyKey, cstr_t) kitty_key_map = MAP_INIT;
#ifndef UNIT_TESTING
typedef enum {
kIncomplete = -1,
@ -51,6 +136,11 @@ void tinput_init(TermInput *input, Loop *loop)
uv_mutex_init(&input->key_buffer_mutex);
uv_cond_init(&input->key_buffer_cond);
for (size_t i = 0; i < ARRAY_SIZE(kitty_key_map_entry); i++) {
map_put(KittyKey, cstr_t)(&kitty_key_map, kitty_key_map_entry[i].key,
kitty_key_map_entry[i].name);
}
// If stdin is not a pty, switch to stderr. For cases like:
// echo q | nvim -es
// ls *.md | xargs nvim
@ -90,6 +180,7 @@ void tinput_init(TermInput *input, Loop *loop)
void tinput_destroy(TermInput *input)
{
map_destroy(KittyKey, cstr_t)(&kitty_key_map);
rbuffer_free(input->key_buffer);
uv_mutex_destroy(&input->key_buffer_mutex);
uv_cond_destroy(&input->key_buffer_cond);
@ -205,19 +296,46 @@ static void tinput_enqueue(TermInput *input, char *buf, size_t size)
rbuffer_write(input->key_buffer, buf, size);
}
static void handle_kitty_key_protocol(TermInput *input, TermKeyKey *key)
{
const char *name = map_get(KittyKey, cstr_t)(&kitty_key_map, (KittyKey)key->code.codepoint);
if (name) {
char buf[64];
size_t len = 0;
buf[len++] = '<';
if (key->modifiers & TERMKEY_KEYMOD_SHIFT) {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "S-");
}
if (key->modifiers & TERMKEY_KEYMOD_ALT) {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "A-");
}
if (key->modifiers & TERMKEY_KEYMOD_CTRL) {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "C-");
}
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "%s>", name);
tinput_enqueue(input, buf, len);
}
}
static void forward_simple_utf8(TermInput *input, TermKeyKey *key)
{
size_t len = 0;
char buf[64];
char *ptr = key->utf8;
while (*ptr) {
if (*ptr == '<') {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "<lt>");
} else {
buf[len++] = *ptr;
if (key->code.codepoint >= 0xE000 && key->code.codepoint <= 0xF8FF
&& map_has(KittyKey, cstr_t)(&kitty_key_map, (KittyKey)key->code.codepoint)) {
handle_kitty_key_protocol(input, key);
return;
} else {
while (*ptr) {
if (*ptr == '<') {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "<lt>");
} else {
buf[len++] = *ptr;
}
ptr++;
}
ptr++;
}
tinput_enqueue(input, buf, len);
@ -235,19 +353,26 @@ static void forward_modified_utf8(TermInput *input, TermKeyKey *key)
len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
} else {
assert(key->modifiers);
// Termkey doesn't include the S- modifier for ASCII characters (e.g.,
// ctrl-shift-l is <C-L> instead of <C-S-L>. Vim, on the other hand,
// treats <C-L> and <C-l> the same, requiring the S- modifier.
len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
if ((key->modifiers & TERMKEY_KEYMOD_CTRL)
&& !(key->modifiers & TERMKEY_KEYMOD_SHIFT)
&& ASCII_ISUPPER(key->code.codepoint)) {
assert(len <= 62);
// Make room for the S-
memmove(buf + 3, buf + 1, len - 1);
buf[1] = 'S';
buf[2] = '-';
len += 2;
if (key->code.codepoint >= 0xE000 && key->code.codepoint <= 0xF8FF
&& map_has(KittyKey, cstr_t)(&kitty_key_map,
(KittyKey)key->code.codepoint)) {
handle_kitty_key_protocol(input, key);
return;
} else {
// Termkey doesn't include the S- modifier for ASCII characters (e.g.,
// ctrl-shift-l is <C-L> instead of <C-S-L>. Vim, on the other hand,
// treats <C-L> and <C-l> the same, requiring the S- modifier.
len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
if ((key->modifiers & TERMKEY_KEYMOD_CTRL)
&& !(key->modifiers & TERMKEY_KEYMOD_SHIFT)
&& ASCII_ISUPPER(key->code.codepoint)) {
assert(len <= 62);
// Make room for the S-
memmove(buf + 3, buf + 1, len - 1);
buf[1] = 'S';
buf[2] = '-';
len += 2;
}
}
}

View File

@ -6,6 +6,7 @@
#include "nvim/event/stream.h"
#include "nvim/event/time.h"
#include "nvim/tui/input_defs.h"
#include "nvim/tui/tui.h"
typedef enum {

118
src/nvim/tui/input_defs.h Normal file
View File

@ -0,0 +1,118 @@
#ifndef NVIM_TUI_INPUT_DEFS_H
#define NVIM_TUI_INPUT_DEFS_H
typedef enum {
KITTY_KEY_ESCAPE = 57344,
KITTY_KEY_ENTER = 57345,
KITTY_KEY_TAB = 57346,
KITTY_KEY_BACKSPACE = 57347,
KITTY_KEY_INSERT = 57348,
KITTY_KEY_DELETE = 57349,
KITTY_KEY_LEFT = 57350,
KITTY_KEY_RIGHT = 57351,
KITTY_KEY_UP = 57352,
KITTY_KEY_DOWN = 57353,
KITTY_KEY_PAGE_UP = 57354,
KITTY_KEY_PAGE_DOWN = 57355,
KITTY_KEY_HOME = 57356,
KITTY_KEY_END = 57357,
KITTY_KEY_CAPS_LOCK = 57358,
KITTY_KEY_SCROLL_LOCK = 57359,
KITTY_KEY_NUM_LOCK = 57360,
KITTY_KEY_PRINT_SCREEN = 57361,
KITTY_KEY_PAUSE = 57362,
KITTY_KEY_MENU = 57363,
KITTY_KEY_F1 = 57364,
KITTY_KEY_F2 = 57365,
KITTY_KEY_F3 = 57366,
KITTY_KEY_F4 = 57367,
KITTY_KEY_F5 = 57368,
KITTY_KEY_F6 = 57369,
KITTY_KEY_F7 = 57370,
KITTY_KEY_F8 = 57371,
KITTY_KEY_F9 = 57372,
KITTY_KEY_F10 = 57373,
KITTY_KEY_F11 = 57374,
KITTY_KEY_F12 = 57375,
KITTY_KEY_F13 = 57376,
KITTY_KEY_F14 = 57377,
KITTY_KEY_F15 = 57378,
KITTY_KEY_F16 = 57379,
KITTY_KEY_F17 = 57380,
KITTY_KEY_F18 = 57381,
KITTY_KEY_F19 = 57382,
KITTY_KEY_F20 = 57383,
KITTY_KEY_F21 = 57384,
KITTY_KEY_F22 = 57385,
KITTY_KEY_F23 = 57386,
KITTY_KEY_F24 = 57387,
KITTY_KEY_F25 = 57388,
KITTY_KEY_F26 = 57389,
KITTY_KEY_F27 = 57390,
KITTY_KEY_F28 = 57391,
KITTY_KEY_F29 = 57392,
KITTY_KEY_F30 = 57393,
KITTY_KEY_F31 = 57394,
KITTY_KEY_F32 = 57395,
KITTY_KEY_F33 = 57396,
KITTY_KEY_F34 = 57397,
KITTY_KEY_F35 = 57398,
KITTY_KEY_KP_0 = 57399,
KITTY_KEY_KP_1 = 57400,
KITTY_KEY_KP_2 = 57401,
KITTY_KEY_KP_3 = 57402,
KITTY_KEY_KP_4 = 57403,
KITTY_KEY_KP_5 = 57404,
KITTY_KEY_KP_6 = 57405,
KITTY_KEY_KP_7 = 57406,
KITTY_KEY_KP_8 = 57407,
KITTY_KEY_KP_9 = 57408,
KITTY_KEY_KP_DECIMAL = 57409,
KITTY_KEY_KP_DIVIDE = 57410,
KITTY_KEY_KP_MULTIPLY = 57411,
KITTY_KEY_KP_SUBTRACT = 57412,
KITTY_KEY_KP_ADD = 57413,
KITTY_KEY_KP_ENTER = 57414,
KITTY_KEY_KP_EQUAL = 57415,
KITTY_KEY_KP_SEPARATOR = 57416,
KITTY_KEY_KP_LEFT = 57417,
KITTY_KEY_KP_RIGHT = 57418,
KITTY_KEY_KP_UP = 57419,
KITTY_KEY_KP_DOWN = 57420,
KITTY_KEY_KP_PAGE_UP = 57421,
KITTY_KEY_KP_PAGE_DOWN = 57422,
KITTY_KEY_KP_HOME = 57423,
KITTY_KEY_KP_END = 57424,
KITTY_KEY_KP_INSERT = 57425,
KITTY_KEY_KP_DELETE = 57426,
KITTY_KEY_KP_BEGIN = 57427,
KITTY_KEY_MEDIA_PLAY = 57428,
KITTY_KEY_MEDIA_PAUSE = 57429,
KITTY_KEY_MEDIA_PLAY_PAUSE = 57430,
KITTY_KEY_MEDIA_REVERSE = 57431,
KITTY_KEY_MEDIA_STOP = 57432,
KITTY_KEY_MEDIA_FAST_FORWARD = 57433,
KITTY_KEY_MEDIA_REWIND = 57434,
KITTY_KEY_MEDIA_TRACK_NEXT = 57435,
KITTY_KEY_MEDIA_TRACK_PREVIOUS = 57436,
KITTY_KEY_MEDIA_RECORD = 57437,
KITTY_KEY_LOWER_VOLUME = 57438,
KITTY_KEY_RAISE_VOLUME = 57439,
KITTY_KEY_MUTE_VOLUME = 57440,
KITTY_KEY_LEFT_SHIFT = 57441,
KITTY_KEY_LEFT_CONTROL = 57442,
KITTY_KEY_LEFT_ALT = 57443,
KITTY_KEY_LEFT_SUPER = 57444,
KITTY_KEY_LEFT_HYPER = 57445,
KITTY_KEY_LEFT_META = 57446,
KITTY_KEY_RIGHT_SHIFT = 57447,
KITTY_KEY_RIGHT_CONTROL = 57448,
KITTY_KEY_RIGHT_ALT = 57449,
KITTY_KEY_RIGHT_SUPER = 57450,
KITTY_KEY_RIGHT_HYPER = 57451,
KITTY_KEY_RIGHT_META = 57452,
KITTY_KEY_ISO_LEVEL3_SHIFT = 57453,
KITTY_KEY_ISO_LEVEL5_SHIFT = 57454,
} KittyKey;
#endif // NVIM_TUI_INPUT_DEFS_H