From 622ae2f53e77873a114f86f5acaff341ef3098ac Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Jul 2023 15:03:01 +0800 Subject: [PATCH] feat(tui): support Super and Meta modifiers (#24357) --- src/nvim/tui/input.c | 78 +++++++++++++++++++-------- test/functional/terminal/tui_spec.lua | 22 ++++++++ 2 files changed, 79 insertions(+), 21 deletions(-) diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 30a1af68ad..a5d021943b 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -229,6 +229,44 @@ static void tinput_enqueue(TermInput *input, char *buf, size_t size) rbuffer_write(input->key_buffer, buf, size); } +/// Handle TERMKEY_KEYMOD_* modifiers, i.e. Shift, Alt and Ctrl. +/// +/// @return The number of bytes written into "buf", excluding the final NUL. +static size_t handle_termkey_modifiers(TermKeyKey *key, char *buf, size_t buflen) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + size_t len = 0; + if (key->modifiers & TERMKEY_KEYMOD_SHIFT) { // Shift + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "S-"); + } + if (key->modifiers & TERMKEY_KEYMOD_ALT) { // Alt + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "A-"); + } + if (key->modifiers & TERMKEY_KEYMOD_CTRL) { // Ctrl + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "C-"); + } + assert(len < buflen); + return len; +} + +/// Handle modifiers not handled by libtermkey. +/// Currently only Super ("D-") and Meta ("T-") are supported in Nvim. +/// +/// @return The number of bytes written into "buf", excluding the final NUL. +static size_t handle_more_modifiers(TermKeyKey *key, char *buf, size_t buflen) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + size_t len = 0; + if (key->modifiers & 8) { // Super + len += (size_t)snprintf(buf + len, buflen - len, "D-"); + } + if (key->modifiers & 32) { // Meta + len += (size_t)snprintf(buf + len, buflen - len, "T-"); + } + assert(len < buflen); + return len; +} + static void handle_kitty_key_protocol(TermInput *input, TermKeyKey *key) { const char *name = map_get(int, cstr_t)(&kitty_key_map, (int)key->code.codepoint); @@ -236,16 +274,10 @@ static void handle_kitty_key_protocol(TermInput *input, TermKeyKey *key) 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 += handle_termkey_modifiers(key, buf + len, sizeof(buf) - len); + len += handle_more_modifiers(key, buf + len, sizeof(buf) - len); len += (size_t)snprintf(buf + len, sizeof(buf) - len, "%s>", name); + assert(len < sizeof(buf)); tinput_enqueue(input, buf, len); } } @@ -270,6 +302,7 @@ static void forward_simple_utf8(TermInput *input, TermKeyKey *key) ptr++; } + assert(len < sizeof(buf)); tinput_enqueue(input, buf, len); } @@ -297,7 +330,7 @@ static void forward_modified_utf8(TermInput *input, TermKeyKey *key) if ((key->modifiers & TERMKEY_KEYMOD_CTRL) && !(key->modifiers & TERMKEY_KEYMOD_SHIFT) && ASCII_ISUPPER(key->code.codepoint)) { - assert(len <= 62); + assert(len + 2 < sizeof(buf)); // Make room for the S- memmove(buf + 3, buf + 1, len - 1); buf[1] = 'S'; @@ -306,6 +339,16 @@ static void forward_modified_utf8(TermInput *input, TermKeyKey *key) } } + char more_buf[25]; + size_t more_len = handle_more_modifiers(key, more_buf, sizeof(more_buf)); + if (more_len > 0) { + assert(len + more_len < sizeof(buf)); + memmove(buf + 1 + more_len, buf + 1, len - 1); + memcpy(buf + 1, more_buf, more_len); + len += more_len; + } + + assert(len < sizeof(buf)); tinput_enqueue(input, buf, len); } @@ -343,17 +386,9 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key) row--; col--; // Termkey uses 1-based coordinates buf[len++] = '<'; - if (key->modifiers & TERMKEY_KEYMOD_SHIFT) { - len += (size_t)snprintf(buf + len, sizeof(buf) - len, "S-"); - } - - if (key->modifiers & TERMKEY_KEYMOD_CTRL) { - len += (size_t)snprintf(buf + len, sizeof(buf) - len, "C-"); - } - - if (key->modifiers & TERMKEY_KEYMOD_ALT) { - len += (size_t)snprintf(buf + len, sizeof(buf) - len, "A-"); - } + len += handle_termkey_modifiers(key, buf + len, sizeof(buf) - len); + // Doesn't actually work because there are only 3 bits (0x1c) for modifiers. + // len += handle_more_modifiers(key, buf + len, sizeof(buf) - len); if (button == 1) { len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Left"); @@ -390,6 +425,7 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key) } len += (size_t)snprintf(buf + len, sizeof(buf) - len, "><%d,%d>", col, row); + assert(len < sizeof(buf)); tinput_enqueue(input, buf, len); } diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 44e06a0baf..f48d0e073c 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -697,6 +697,28 @@ describe('TUI', function() ]]) end) + it('supports Super and Meta modifiers', function() + feed_data('i') + feed_data('\022\027[106;9u') -- Super + j + feed_data('\022\027[107;33u') -- Meta + k + feed_data('\022\027[13;41u') -- Super + Meta + Enter + feed_data('\022\027[127;48u') -- Shift + Alt + Ctrl + Super + Meta + Backspace + feed('\n') + feed_data('\022\027[57376;9u') -- Super + F13 + feed_data('\022\027[57377;33u') -- Meta + F14 + feed_data('\022\027[57378;41u') -- Super + Meta + F15 + feed_data('\022\027[57379;48u') -- Shift + Alt + Ctrl + Super + Meta + F16 + screen:expect([[ + | + {1: } | + {4:~ }| + {4:~ }| + {5:[No Name] [+] }| + {3:-- INSERT --} | + {3:-- TERMINAL --} | + ]]) + end) + it('mouse events work with right-click menu', function() child_session:request('nvim_exec', [[ call setline(1, 'popup menu test')