aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/vterm/keyboard.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/vterm/keyboard.c')
-rw-r--r--src/nvim/vterm/keyboard.c140
1 files changed, 103 insertions, 37 deletions
diff --git a/src/nvim/vterm/keyboard.c b/src/nvim/vterm/keyboard.c
index 696b09157e..dd088ac40e 100644
--- a/src/nvim/vterm/keyboard.c
+++ b/src/nvim/vterm/keyboard.c
@@ -10,54 +10,77 @@
# include "vterm/keyboard.c.generated.h"
#endif
+static VTermKeyEncodingFlags vterm_state_get_key_encoding_flags(const VTermState *state)
+{
+ int screen = state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY;
+ const struct VTermKeyEncodingStack *stack = &state->key_encoding_stacks[screen];
+ assert(stack->size > 0);
+ return stack->items[stack->size - 1];
+}
+
void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod)
{
- // The shift modifier is never important for Unicode characters apart from Space
- if (c != ' ') {
- mod &= (unsigned)~VTERM_MOD_SHIFT;
+ bool passthru = false;
+ if (c == ' ') {
+ // Space is passed through only when there are no modifiers (including shift)
+ passthru = mod == VTERM_MOD_NONE;
+ } else {
+ // Otherwise pass through when there are no modifiers (ignoring shift)
+ passthru = (mod & (unsigned)~VTERM_MOD_SHIFT) == 0;
}
- if (mod == 0) {
- // Normal text - ignore just shift
+ if (passthru) {
char str[6];
int seqlen = fill_utf8((int)c, str);
vterm_push_output_bytes(vt, str, (size_t)seqlen);
return;
}
- int needs_CSIu;
- switch (c) {
- // Special Ctrl- letters that can't be represented elsewise
- case 'i':
- case 'j':
- case 'm':
- case '[':
- needs_CSIu = 1;
- break;
- // Ctrl-\ ] ^ _ don't need CSUu
- case '\\':
- case ']':
- case '^':
- case '_':
- needs_CSIu = 0;
- break;
- // Shift-space needs CSIu
- case ' ':
- needs_CSIu = !!(mod & VTERM_MOD_SHIFT);
- break;
- // All other characters needs CSIu except for letters a-z
- default:
- needs_CSIu = (c < 'a' || c > 'z');
- }
+ VTermKeyEncodingFlags flags = vterm_state_get_key_encoding_flags(vt->state);
+ if (flags.disambiguate) {
+ // Always use unshifted codepoint
+ if (c >= 'A' && c <= 'Z') {
+ c += 'a' - 'A';
+ mod |= VTERM_MOD_SHIFT;
+ }
- // ALT we can just prefix with ESC; anything else requires CSI u
- if (needs_CSIu && (mod & (unsigned)~VTERM_MOD_ALT)) {
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", c, mod + 1);
return;
}
if (mod & VTERM_MOD_CTRL) {
- c &= 0x1f;
+ // Handle special cases. These are taken from kitty, but seem mostly
+ // consistent across terminals.
+ switch (c) {
+ case '2':
+ case ' ':
+ // Ctrl+2 is NUL to match Ctrl+@ (which is Shift+2 on US keyboards)
+ // Ctrl+Space is also NUL for some reason
+ c = 0x00;
+ break;
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ // Ctrl+3 through Ctrl+7 are sequential starting from 0x1b. Importantly,
+ // this means that Ctrl+6 emits 0x1e (the same as Ctrl+^ on US keyboards)
+ c = 0x1b + c - '3';
+ break;
+ case '8':
+ // Ctrl+8 is DEL
+ c = 0x7f;
+ break;
+ case '/':
+ // Ctrl+/ is equivalent to Ctrl+_ for historic reasons
+ c = 0x1f;
+ break;
+ default:
+ if (c >= '@' && c <= 0x7f) {
+ c &= 0x1f;
+ }
+ break;
+ }
}
vterm_push_output_sprintf(vt, "%s%c", mod & VTERM_MOD_ALT ? ESC_S : "", c);
@@ -75,7 +98,7 @@ typedef struct {
KEYCODE_CSINUM,
KEYCODE_KEYPAD,
} type;
- char literal;
+ int literal;
int csinum;
} keycodes_s;
@@ -137,12 +160,35 @@ static keycodes_s keycodes_kp[] = {
{ KEYCODE_KEYPAD, '=', 'X' }, // KP_EQUAL
};
+static keycodes_s keycodes_kp_csiu[] = {
+ { KEYCODE_KEYPAD, 57399, 'p' }, // KP_0
+ { KEYCODE_KEYPAD, 57400, 'q' }, // KP_1
+ { KEYCODE_KEYPAD, 57401, 'r' }, // KP_2
+ { KEYCODE_KEYPAD, 57402, 's' }, // KP_3
+ { KEYCODE_KEYPAD, 57403, 't' }, // KP_4
+ { KEYCODE_KEYPAD, 57404, 'u' }, // KP_5
+ { KEYCODE_KEYPAD, 57405, 'v' }, // KP_6
+ { KEYCODE_KEYPAD, 57406, 'w' }, // KP_7
+ { KEYCODE_KEYPAD, 57407, 'x' }, // KP_8
+ { KEYCODE_KEYPAD, 57408, 'y' }, // KP_9
+ { KEYCODE_KEYPAD, 57411, 'j' }, // KP_MULT
+ { KEYCODE_KEYPAD, 57413, 'k' }, // KP_PLUS
+ { KEYCODE_KEYPAD, 57416, 'l' }, // KP_COMMA
+ { KEYCODE_KEYPAD, 57412, 'm' }, // KP_MINUS
+ { KEYCODE_KEYPAD, 57409, 'n' }, // KP_PERIOD
+ { KEYCODE_KEYPAD, 57410, 'o' }, // KP_DIVIDE
+ { KEYCODE_KEYPAD, 57414, 'M' }, // KP_ENTER
+ { KEYCODE_KEYPAD, 57415, 'X' }, // KP_EQUAL
+};
+
void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod)
{
if (key == VTERM_KEY_NONE) {
return;
}
+ VTermKeyEncodingFlags flags = vterm_state_get_key_encoding_flags(vt->state);
+
keycodes_s k;
if (key < VTERM_KEY_FUNCTION_0) {
if (key >= sizeof(keycodes)/sizeof(keycodes[0])) {
@@ -158,7 +204,12 @@ void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod)
if ((key - VTERM_KEY_KP_0) >= sizeof(keycodes_kp)/sizeof(keycodes_kp[0])) {
return;
}
- k = keycodes_kp[key - VTERM_KEY_KP_0];
+
+ if (flags.disambiguate) {
+ k = keycodes_kp_csiu[key - VTERM_KEY_KP_0];
+ } else {
+ k = keycodes_kp[key - VTERM_KEY_KP_0];
+ }
}
switch (k.type) {
@@ -167,7 +218,9 @@ void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod)
case KEYCODE_TAB:
// Shift-Tab is CSI Z but plain Tab is 0x09
- if (mod == VTERM_MOD_SHIFT) {
+ if (flags.disambiguate) {
+ goto case_LITERAL;
+ } else if (mod == VTERM_MOD_SHIFT) {
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "Z");
} else if (mod & VTERM_MOD_SHIFT) {
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%dZ", mod + 1);
@@ -187,7 +240,20 @@ void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod)
case KEYCODE_LITERAL:
case_LITERAL:
- if (mod & (VTERM_MOD_SHIFT|VTERM_MOD_CTRL)) {
+ if (flags.disambiguate) {
+ switch (key) {
+ case VTERM_KEY_TAB:
+ case VTERM_KEY_ENTER:
+ case VTERM_KEY_BACKSPACE:
+ // If there are no mods then leave these as-is
+ flags.disambiguate = mod != VTERM_MOD_NONE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (flags.disambiguate) {
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", k.literal, mod + 1);
} else {
vterm_push_output_sprintf(vt, mod & VTERM_MOD_ALT ? ESC_S "%c" : "%c", k.literal);
@@ -229,7 +295,7 @@ void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod)
case KEYCODE_KEYPAD:
if (vt->state->mode.keypad) {
- k.literal = (char)k.csinum;
+ k.literal = k.csinum;
goto case_SS3;
} else {
goto case_LITERAL;