TUI: kitty keyboard protocol, word movement and deletion keys

Query CSI ? u at startup and push the disambiguate flag on the alternate
screen when the terminal answers; CSI u reports are mapped back to legacy
input (Shift/Ctrl+Enter insert a newline, Ctrl+Backspace deletes a word).
Ctrl/Alt+Left/Right move by word, Ctrl/Alt+Delete delete the next word.
Our define_key codes move above the range ncurses uses for terminfo
extended keys.
This commit is contained in:
WHC
2026-10-06 01:52:48 +08:00
parent 8efd63bb31
commit b28bca2cfc
6 changed files with 251 additions and 14 deletions
+64
View File
@@ -291,6 +291,70 @@ const char *cst_key_name(int code, char *buf, size_t n) {
return buf;
}
/* Shift on a US layout; the kitty report names the unshifted key. */
static int us_shift(int c) {
static const char PLAIN[] = "`1234567890-=[]\\;',./";
static const char SHIFTED[] = "~!@#$%^&*()_+{}|:\"<>?";
if (c >= 'a' && c <= 'z')
return c - 'a' + 'A';
const char *p = c > 0 && c < 0x80 ? strchr(PLAIN, c) : NULL;
return p ? SHIFTED[p - PLAIN] : c;
}
/* The control character xterm sends for ctrl+c, or c when there is none. */
static int ctrl_char(int c) {
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
return c & 0x1f;
switch (c) {
case ' ': case '2': case '@': return 0;
case '3': case '[': return 27;
case '4': case '\\': return 28;
case '5': case ']': return 29;
case '6': case '^': return 30;
case '7': case '-': case '_': case '/': return 31;
case '8': case '?': return 127;
default: return c;
}
}
int cst_key_from_kitty(int cp, int mods, int out[2]) {
int shift = mods & 1, alt = mods & 2, ctrl = mods & 4;
if (mods & ~7)
return 0; /* super, hyper, meta, caps/num lock: not ours */
int c;
switch (cp) {
case 13:
if (!mods)
return out[0] = 13, 1;
out[0] = 27, out[1] = 13; /* newline */
return 2;
case 127:
if (!alt && !ctrl)
return out[0] = 127, 1;
out[0] = 27, out[1] = 127; /* delete word */
return 2;
case 9:
if (shift && !alt && !ctrl)
return out[0] = CST_K_BTAB, 1;
c = 9;
break;
case 27:
c = 27;
break;
default:
if (cp < ' ' || cp > 0x10FFFF)
return 0;
c = shift ? us_shift(cp) : cp;
if (ctrl)
c = ctrl_char(c);
break;
}
if (!alt)
return out[0] = c, 1;
out[0] = 27, out[1] = c;
return 2;
}
/* "ctrl+x n", "<leader> n" or "f2" → keys; returns the count (0 on error). */
static int parse_seq(const cst_keymap_t *km, const char *s, int keys[2]) {
int n = 0;