keyboard.c (8945B)
1 #include <stdio.h> 2 3 #include "nvim/ascii_defs.h" 4 #include "nvim/tui/termkey/termkey.h" 5 #include "nvim/vterm/keyboard.h" 6 #include "nvim/vterm/vterm.h" 7 #include "nvim/vterm/vterm_internal_defs.h" 8 9 #include "vterm/keyboard.c.generated.h" 10 11 static VTermKeyEncodingFlags vterm_state_get_key_encoding_flags(const VTermState *state) 12 { 13 int screen = state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY; 14 const struct VTermKeyEncodingStack *stack = &state->key_encoding_stacks[screen]; 15 assert(stack->size > 0); 16 return stack->items[stack->size - 1]; 17 } 18 19 void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod) 20 { 21 bool passthru = false; 22 if (c == ' ') { 23 // Space is passed through only when there are no modifiers (including shift) 24 passthru = mod == VTERM_MOD_NONE; 25 } else { 26 // Otherwise pass through when there are no modifiers (ignoring shift) 27 passthru = (mod & (unsigned)~VTERM_MOD_SHIFT) == 0; 28 } 29 30 if (passthru) { 31 char str[6]; 32 int seqlen = fill_utf8((int)c, str); 33 vterm_push_output_bytes(vt, str, (size_t)seqlen); 34 return; 35 } 36 37 VTermKeyEncodingFlags flags = vterm_state_get_key_encoding_flags(vt->state); 38 if (flags.disambiguate) { 39 // Always use unshifted codepoint 40 if (c >= 'A' && c <= 'Z') { 41 c += 'a' - 'A'; 42 mod |= VTERM_MOD_SHIFT; 43 } 44 45 vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", c, mod + 1); 46 return; 47 } 48 49 if (mod & VTERM_MOD_CTRL) { 50 // Handle special cases. These are taken from kitty, but seem mostly 51 // consistent across terminals. 52 switch (c) { 53 case '2': 54 case ' ': 55 // Ctrl+2 is NUL to match Ctrl+@ (which is Shift+2 on US keyboards) 56 // Ctrl+Space is also NUL for some reason 57 c = 0x00; 58 break; 59 case '3': 60 case '4': 61 case '5': 62 case '6': 63 case '7': 64 // Ctrl+3 through Ctrl+7 are sequential starting from 0x1b. Importantly, 65 // this means that Ctrl+6 emits 0x1e (the same as Ctrl+^ on US keyboards) 66 c = 0x1b + c - '3'; 67 break; 68 case '8': 69 // Ctrl+8 is DEL 70 c = 0x7f; 71 break; 72 case '/': 73 // Ctrl+/ is equivalent to Ctrl+_ for historic reasons 74 c = 0x1f; 75 break; 76 default: 77 if (c >= '@' && c <= 0x7f) { 78 c &= 0x1f; 79 } 80 break; 81 } 82 } 83 84 vterm_push_output_sprintf(vt, "%s%c", mod & VTERM_MOD_ALT ? ESC_S : "", c); 85 } 86 87 typedef struct { 88 enum { 89 KEYCODE_NONE, 90 KEYCODE_LITERAL, 91 KEYCODE_TAB, 92 KEYCODE_ENTER, 93 KEYCODE_SS3, 94 KEYCODE_CSI, 95 KEYCODE_CSI_CURSOR, 96 KEYCODE_CSINUM, 97 KEYCODE_KEYPAD, 98 } type; 99 int literal; 100 int csinum; 101 } keycodes_s; 102 103 static keycodes_s keycodes[] = { 104 { KEYCODE_NONE, NUL, 0 }, // NONE 105 106 { KEYCODE_ENTER, '\r', 0 }, // ENTER 107 { KEYCODE_TAB, '\t', 0 }, // TAB 108 { KEYCODE_LITERAL, '\x7f', 0 }, // BACKSPACE == ASCII DEL 109 { KEYCODE_LITERAL, '\x1b', 0 }, // ESCAPE 110 111 { KEYCODE_CSI_CURSOR, 'A', 0 }, // UP 112 { KEYCODE_CSI_CURSOR, 'B', 0 }, // DOWN 113 { KEYCODE_CSI_CURSOR, 'D', 0 }, // LEFT 114 { KEYCODE_CSI_CURSOR, 'C', 0 }, // RIGHT 115 116 { KEYCODE_CSINUM, '~', 2 }, // INS 117 { KEYCODE_CSINUM, '~', 3 }, // DEL 118 { KEYCODE_CSI_CURSOR, 'H', 0 }, // HOME 119 { KEYCODE_CSI_CURSOR, 'F', 0 }, // END 120 { KEYCODE_CSINUM, '~', 5 }, // PAGEUP 121 { KEYCODE_CSINUM, '~', 6 }, // PAGEDOWN 122 }; 123 124 static keycodes_s keycodes_fn[] = { 125 { KEYCODE_NONE, NUL, 0 }, // F0 - shouldn't happen 126 { KEYCODE_SS3, 'P', 0 }, // F1 127 { KEYCODE_SS3, 'Q', 0 }, // F2 128 { KEYCODE_SS3, 'R', 0 }, // F3 129 { KEYCODE_SS3, 'S', 0 }, // F4 130 { KEYCODE_CSINUM, '~', 15 }, // F5 131 { KEYCODE_CSINUM, '~', 17 }, // F6 132 { KEYCODE_CSINUM, '~', 18 }, // F7 133 { KEYCODE_CSINUM, '~', 19 }, // F8 134 { KEYCODE_CSINUM, '~', 20 }, // F9 135 { KEYCODE_CSINUM, '~', 21 }, // F10 136 { KEYCODE_CSINUM, '~', 23 }, // F11 137 { KEYCODE_CSINUM, '~', 24 }, // F12 138 }; 139 140 static keycodes_s keycodes_kp[] = { 141 { KEYCODE_KEYPAD, '0', 'p' }, // KP_0 142 { KEYCODE_KEYPAD, '1', 'q' }, // KP_1 143 { KEYCODE_KEYPAD, '2', 'r' }, // KP_2 144 { KEYCODE_KEYPAD, '3', 's' }, // KP_3 145 { KEYCODE_KEYPAD, '4', 't' }, // KP_4 146 { KEYCODE_KEYPAD, '5', 'u' }, // KP_5 147 { KEYCODE_KEYPAD, '6', 'v' }, // KP_6 148 { KEYCODE_KEYPAD, '7', 'w' }, // KP_7 149 { KEYCODE_KEYPAD, '8', 'x' }, // KP_8 150 { KEYCODE_KEYPAD, '9', 'y' }, // KP_9 151 { KEYCODE_KEYPAD, '*', 'j' }, // KP_MULT 152 { KEYCODE_KEYPAD, '+', 'k' }, // KP_PLUS 153 { KEYCODE_KEYPAD, ',', 'l' }, // KP_COMMA 154 { KEYCODE_KEYPAD, '-', 'm' }, // KP_MINUS 155 { KEYCODE_KEYPAD, '.', 'n' }, // KP_PERIOD 156 { KEYCODE_KEYPAD, '/', 'o' }, // KP_DIVIDE 157 { KEYCODE_KEYPAD, '\n', 'M' }, // KP_ENTER 158 { KEYCODE_KEYPAD, '=', 'X' }, // KP_EQUAL 159 }; 160 161 static keycodes_s keycodes_kp_csiu[] = { 162 { KEYCODE_KEYPAD, 57399, 'p' }, // KP_0 163 { KEYCODE_KEYPAD, 57400, 'q' }, // KP_1 164 { KEYCODE_KEYPAD, 57401, 'r' }, // KP_2 165 { KEYCODE_KEYPAD, 57402, 's' }, // KP_3 166 { KEYCODE_KEYPAD, 57403, 't' }, // KP_4 167 { KEYCODE_KEYPAD, 57404, 'u' }, // KP_5 168 { KEYCODE_KEYPAD, 57405, 'v' }, // KP_6 169 { KEYCODE_KEYPAD, 57406, 'w' }, // KP_7 170 { KEYCODE_KEYPAD, 57407, 'x' }, // KP_8 171 { KEYCODE_KEYPAD, 57408, 'y' }, // KP_9 172 { KEYCODE_KEYPAD, 57411, 'j' }, // KP_MULT 173 { KEYCODE_KEYPAD, 57413, 'k' }, // KP_PLUS 174 { KEYCODE_KEYPAD, 57416, 'l' }, // KP_COMMA 175 { KEYCODE_KEYPAD, 57412, 'm' }, // KP_MINUS 176 { KEYCODE_KEYPAD, 57409, 'n' }, // KP_PERIOD 177 { KEYCODE_KEYPAD, 57410, 'o' }, // KP_DIVIDE 178 { KEYCODE_KEYPAD, 57414, 'M' }, // KP_ENTER 179 { KEYCODE_KEYPAD, 57415, 'X' }, // KP_EQUAL 180 }; 181 182 void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod) 183 { 184 if (key == VTERM_KEY_NONE) { 185 return; 186 } 187 188 VTermKeyEncodingFlags flags = vterm_state_get_key_encoding_flags(vt->state); 189 190 keycodes_s k; 191 if (key < VTERM_KEY_FUNCTION_0) { 192 if (key >= sizeof(keycodes)/sizeof(keycodes[0])) { 193 return; 194 } 195 k = keycodes[key]; 196 } else if (key >= VTERM_KEY_FUNCTION_0 && key <= VTERM_KEY_FUNCTION_MAX) { 197 if ((key - VTERM_KEY_FUNCTION_0) >= sizeof(keycodes_fn)/sizeof(keycodes_fn[0])) { 198 return; 199 } 200 k = keycodes_fn[key - VTERM_KEY_FUNCTION_0]; 201 } else if (key >= VTERM_KEY_KP_0) { 202 if ((key - VTERM_KEY_KP_0) >= sizeof(keycodes_kp)/sizeof(keycodes_kp[0])) { 203 return; 204 } 205 206 if (flags.disambiguate) { 207 k = keycodes_kp_csiu[key - VTERM_KEY_KP_0]; 208 } else { 209 k = keycodes_kp[key - VTERM_KEY_KP_0]; 210 } 211 } 212 213 switch (k.type) { 214 case KEYCODE_NONE: 215 break; 216 217 case KEYCODE_TAB: 218 // Shift-Tab is CSI Z but plain Tab is 0x09 219 if (flags.disambiguate) { 220 goto case_LITERAL; 221 } else if (mod == VTERM_MOD_SHIFT) { 222 vterm_push_output_sprintf_ctrl(vt, C1_CSI, "Z"); 223 } else if (mod & VTERM_MOD_SHIFT) { 224 vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%dZ", mod + 1); 225 } else { 226 goto case_LITERAL; 227 } 228 break; 229 230 case KEYCODE_ENTER: 231 // Enter is CRLF in newline mode, but just LF in linefeed 232 if (vt->state->mode.newline) { 233 vterm_push_output_sprintf(vt, "\r\n"); 234 } else { 235 goto case_LITERAL; 236 } 237 break; 238 239 case KEYCODE_LITERAL: 240 case_LITERAL: 241 if (flags.disambiguate) { 242 switch (key) { 243 case VTERM_KEY_TAB: 244 case VTERM_KEY_ENTER: 245 case VTERM_KEY_BACKSPACE: 246 // If there are no mods then leave these as-is 247 flags.disambiguate = mod != VTERM_MOD_NONE; 248 break; 249 default: 250 break; 251 } 252 } 253 254 if (flags.disambiguate) { 255 vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", k.literal, mod + 1); 256 } else { 257 vterm_push_output_sprintf(vt, mod & VTERM_MOD_ALT ? ESC_S "%c" : "%c", k.literal); 258 } 259 break; 260 261 case KEYCODE_SS3: 262 case_SS3: 263 if (mod == 0) { 264 vterm_push_output_sprintf_ctrl(vt, C1_SS3, "%c", k.literal); 265 } else { 266 goto case_CSI; 267 } 268 break; 269 270 case KEYCODE_CSI: 271 case_CSI: 272 if (mod == 0) { 273 vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%c", k.literal); 274 } else { 275 vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%d%c", mod + 1, k.literal); 276 } 277 break; 278 279 case KEYCODE_CSINUM: 280 if (mod == 0) { 281 vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d%c", k.csinum, k.literal); 282 } else { 283 vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%d%c", k.csinum, mod + 1, k.literal); 284 } 285 break; 286 287 case KEYCODE_CSI_CURSOR: 288 if (vt->state->mode.cursor) { 289 goto case_SS3; 290 } else { 291 goto case_CSI; 292 } 293 294 case KEYCODE_KEYPAD: 295 if (vt->state->mode.keypad) { 296 k.literal = k.csinum; 297 goto case_SS3; 298 } else { 299 goto case_LITERAL; 300 } 301 } 302 } 303 304 void vterm_keyboard_start_paste(VTerm *vt) 305 { 306 if (vt->state->mode.bracketpaste) { 307 vterm_push_output_sprintf_ctrl(vt, C1_CSI, "200~"); 308 } 309 } 310 311 void vterm_keyboard_end_paste(VTerm *vt) 312 { 313 if (vt->state->mode.bracketpaste) { 314 vterm_push_output_sprintf_ctrl(vt, C1_CSI, "201~"); 315 } 316 }