aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/tui/termkey/driver-csi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/tui/termkey/driver-csi.c')
-rw-r--r--src/nvim/tui/termkey/driver-csi.c902
1 files changed, 902 insertions, 0 deletions
diff --git a/src/nvim/tui/termkey/driver-csi.c b/src/nvim/tui/termkey/driver-csi.c
new file mode 100644
index 0000000000..28c7eaccfd
--- /dev/null
+++ b/src/nvim/tui/termkey/driver-csi.c
@@ -0,0 +1,902 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "nvim/memory.h"
+#include "nvim/tui/termkey/driver-csi.h"
+#include "nvim/tui/termkey/termkey-internal.h"
+#include "nvim/tui/termkey/termkey.h"
+#include "nvim/tui/termkey/termkey_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "tui/termkey/driver-csi.c.generated.h"
+#endif
+
+// There are 64 codes 0x40 - 0x7F
+static int keyinfo_initialised = 0;
+static struct keyinfo ss3s[64];
+static char ss3_kpalts[64];
+
+typedef TermKeyResult CsiHandler(TermKey *tk, TermKeyKey *key, int cmd, TermKeyCsiParam *params,
+ int nparams);
+static CsiHandler *csi_handlers[64];
+
+// Handler for CSI/SS3 cmd keys
+
+static struct keyinfo csi_ss3s[64];
+
+static TermKeyResult handle_csi_ss3_full(TermKey *tk, TermKeyKey *key, int cmd,
+ TermKeyCsiParam *params, int nparams)
+{
+ TermKeyResult result = TERMKEY_RES_KEY;
+
+ if (nparams > 1 && params[1].param != NULL) {
+ int arg = 0;
+ result = termkey_interpret_csi_param(params[1], &arg, NULL, NULL);
+ if (result != TERMKEY_RES_KEY) {
+ return result;
+ }
+
+ key->modifiers = arg - 1;
+ } else {
+ key->modifiers = 0;
+ }
+
+ key->type = csi_ss3s[cmd - 0x40].type;
+ key->code.sym = csi_ss3s[cmd - 0x40].sym;
+ key->modifiers &= ~(csi_ss3s[cmd - 0x40].modifier_mask);
+ key->modifiers |= csi_ss3s[cmd - 0x40].modifier_set;
+
+ if (key->code.sym == TERMKEY_SYM_UNKNOWN) {
+ result = TERMKEY_RES_NONE;
+ }
+
+ return result;
+}
+
+static void register_csi_ss3_full(TermKeyType type, TermKeySym sym, int modifier_set,
+ int modifier_mask, unsigned char cmd)
+{
+ if (cmd < 0x40 || cmd >= 0x80) {
+ return;
+ }
+
+ csi_ss3s[cmd - 0x40].type = type;
+ csi_ss3s[cmd - 0x40].sym = sym;
+ csi_ss3s[cmd - 0x40].modifier_set = modifier_set;
+ csi_ss3s[cmd - 0x40].modifier_mask = modifier_mask;
+
+ csi_handlers[cmd - 0x40] = &handle_csi_ss3_full;
+}
+
+static void register_csi_ss3(TermKeyType type, TermKeySym sym, unsigned char cmd)
+{
+ register_csi_ss3_full(type, sym, 0, 0, cmd);
+}
+
+/// Handler for SS3 keys with kpad alternate representations
+static void register_ss3kpalt(TermKeyType type, TermKeySym sym, unsigned char cmd, char kpalt)
+{
+ if (cmd < 0x40 || cmd >= 0x80) {
+ return;
+ }
+
+ ss3s[cmd - 0x40].type = type;
+ ss3s[cmd - 0x40].sym = sym;
+ ss3s[cmd - 0x40].modifier_set = 0;
+ ss3s[cmd - 0x40].modifier_mask = 0;
+ ss3_kpalts[cmd - 0x40] = kpalt;
+}
+
+// Handler for CSI number ~ function keys
+
+#define NCSIFUNCS 35 // This value must be increased if more CSI function keys are added
+static struct keyinfo csifuncs[NCSIFUNCS];
+
+static TermKeyResult handle_csifunc(TermKey *tk, TermKeyKey *key, int cmd, TermKeyCsiParam *params,
+ int nparams)
+{
+ if (nparams == 0) {
+ return TERMKEY_RES_NONE;
+ }
+
+ TermKeyResult result = TERMKEY_RES_KEY;
+ int args[3];
+
+ if (nparams > 1 && params[1].param != NULL) {
+ result = termkey_interpret_csi_param(params[1], &args[1], NULL, NULL);
+ if (result != TERMKEY_RES_KEY) {
+ return result;
+ }
+
+ key->modifiers = args[1] - 1;
+ } else {
+ key->modifiers = 0;
+ }
+
+ key->type = TERMKEY_TYPE_KEYSYM;
+
+ result = termkey_interpret_csi_param(params[0], &args[0], NULL, NULL);
+ if (result != TERMKEY_RES_KEY) {
+ return result;
+ }
+
+ if (args[0] == 27 && nparams > 2 && params[2].param != NULL) {
+ result = termkey_interpret_csi_param(params[2], &args[2], NULL, NULL);
+ if (result != TERMKEY_RES_KEY) {
+ return result;
+ }
+
+ int mod = key->modifiers;
+ (*tk->method.emit_codepoint)(tk, args[2], key);
+ key->modifiers |= mod;
+ } else if (args[0] >= 0 && args[0] < NCSIFUNCS) {
+ key->type = csifuncs[args[0]].type;
+ key->code.sym = csifuncs[args[0]].sym;
+ key->modifiers &= ~(csifuncs[args[0]].modifier_mask);
+ key->modifiers |= csifuncs[args[0]].modifier_set;
+ } else {
+ key->code.sym = TERMKEY_SYM_UNKNOWN;
+ }
+
+ if (key->code.sym == TERMKEY_SYM_UNKNOWN) {
+#ifdef DEBUG
+ fprintf(stderr, "CSI: Unknown function key %ld\n", arg[0]);
+#endif
+ result = TERMKEY_RES_NONE;
+ }
+
+ return result;
+}
+
+static void register_csifunc(TermKeyType type, TermKeySym sym, int number)
+{
+ if (number >= NCSIFUNCS) {
+ return;
+ }
+
+ csifuncs[number].type = type;
+ csifuncs[number].sym = sym;
+ csifuncs[number].modifier_set = 0;
+ csifuncs[number].modifier_mask = 0;
+
+ csi_handlers['~' - 0x40] = &handle_csifunc;
+}
+
+/// Handler for CSI u extended Unicode keys
+static TermKeyResult handle_csi_u(TermKey *tk, TermKeyKey *key, int cmd, TermKeyCsiParam *params,
+ int nparams)
+{
+ switch (cmd) {
+ case 'u': {
+ int args[2];
+ if (nparams > 1 && params[1].param != NULL) {
+ int subparam = 0;
+ size_t nsubparams = 1;
+ if (termkey_interpret_csi_param(params[1], &args[1], &subparam,
+ &nsubparams) != TERMKEY_RES_KEY) {
+ return TERMKEY_RES_ERROR;
+ }
+
+ if (nsubparams > 0 && subparam != 1) {
+ // Not a press event. Ignore for now
+ return TERMKEY_RES_NONE;
+ }
+
+ key->modifiers = args[1] - 1;
+ } else {
+ key->modifiers = 0;
+ }
+
+ if (termkey_interpret_csi_param(params[0], &args[0], NULL, NULL) != TERMKEY_RES_KEY) {
+ return TERMKEY_RES_ERROR;
+ }
+
+ int mod = key->modifiers;
+ key->type = TERMKEY_TYPE_KEYSYM;
+ (*tk->method.emit_codepoint)(tk, args[0], key);
+ key->modifiers |= mod;
+
+ return TERMKEY_RES_KEY;
+ }
+ default:
+ return TERMKEY_RES_NONE;
+ }
+}
+
+/// Handler for CSI M / CSI m mouse events in SGR and rxvt encodings
+/// Note: This does not handle X10 encoding
+static TermKeyResult handle_csi_m(TermKey *tk, TermKeyKey *key, int cmd, TermKeyCsiParam *params,
+ int nparams)
+{
+ int initial = cmd >> 8;
+ cmd &= 0xff;
+
+ switch (cmd) {
+ case 'M':
+ case 'm':
+ break;
+ default:
+ return TERMKEY_RES_NONE;
+ }
+
+ if (nparams < 3) {
+ return TERMKEY_RES_NONE;
+ }
+
+ int args[3];
+ for (size_t i = 0; i < 3; i++) {
+ if (termkey_interpret_csi_param(params[i], &args[i], NULL, NULL) != TERMKEY_RES_KEY) {
+ return TERMKEY_RES_ERROR;
+ }
+ }
+
+ if (!initial) { // rxvt protocol
+ key->type = TERMKEY_TYPE_MOUSE;
+ key->code.mouse[0] = (char)args[0];
+
+ key->modifiers = (key->code.mouse[0] & 0x1c) >> 2;
+ key->code.mouse[0] &= ~0x1c;
+
+ termkey_key_set_linecol(key, args[1], args[2]);
+
+ return TERMKEY_RES_KEY;
+ }
+
+ if (initial == '<') { // SGR protocol
+ key->type = TERMKEY_TYPE_MOUSE;
+ key->code.mouse[0] = (char)args[0];
+
+ key->modifiers = (key->code.mouse[0] & 0x1c) >> 2;
+ key->code.mouse[0] &= ~0x1c;
+
+ termkey_key_set_linecol(key, args[1], args[2]);
+
+ if (cmd == 'm') { // release
+ key->code.mouse[3] |= 0x80;
+ }
+
+ return TERMKEY_RES_KEY;
+ }
+
+ return TERMKEY_RES_NONE;
+}
+
+TermKeyResult termkey_interpret_mouse(TermKey *tk, const TermKeyKey *key, TermKeyMouseEvent *event,
+ int *button, int *line, int *col)
+{
+ if (key->type != TERMKEY_TYPE_MOUSE) {
+ return TERMKEY_RES_NONE;
+ }
+
+ if (button) {
+ *button = 0;
+ }
+
+ termkey_key_get_linecol(key, line, col);
+
+ if (!event) {
+ return TERMKEY_RES_KEY;
+ }
+
+ int btn = 0;
+
+ int code = (unsigned char)key->code.mouse[0];
+
+ int drag = code & 0x20;
+
+ code &= ~0x3c;
+
+ switch (code) {
+ case 0:
+ case 1:
+ case 2:
+ *event = drag ? TERMKEY_MOUSE_DRAG : TERMKEY_MOUSE_PRESS;
+ btn = code + 1;
+ break;
+
+ case 3:
+ *event = TERMKEY_MOUSE_RELEASE;
+ // no button hint
+ break;
+
+ case 64:
+ case 65:
+ case 66:
+ case 67:
+ *event = drag ? TERMKEY_MOUSE_DRAG : TERMKEY_MOUSE_PRESS;
+ btn = code + 4 - 64;
+ break;
+
+ default:
+ *event = TERMKEY_MOUSE_UNKNOWN;
+ }
+
+ if (button) {
+ *button = btn;
+ }
+
+ if (key->code.mouse[3] & 0x80) {
+ *event = TERMKEY_MOUSE_RELEASE;
+ }
+
+ return TERMKEY_RES_KEY;
+}
+
+/// Handler for CSI ? R position reports
+/// A plain CSI R with no arguments is probably actually <F3>
+static TermKeyResult handle_csi_R(TermKey *tk, TermKeyKey *key, int cmd, TermKeyCsiParam *params,
+ int nparams)
+{
+ switch (cmd) {
+ case 'R'|'?' << 8:
+ if (nparams < 2) {
+ return TERMKEY_RES_NONE;
+ }
+
+ int args[2];
+ if (termkey_interpret_csi_param(params[0], &args[0], NULL, NULL) != TERMKEY_RES_KEY) {
+ return TERMKEY_RES_ERROR;
+ }
+
+ if (termkey_interpret_csi_param(params[1], &args[1], NULL, NULL) != TERMKEY_RES_KEY) {
+ return TERMKEY_RES_ERROR;
+ }
+
+ key->type = TERMKEY_TYPE_POSITION;
+ termkey_key_set_linecol(key, args[1], args[0]);
+ return TERMKEY_RES_KEY;
+
+ default:
+ return handle_csi_ss3_full(tk, key, cmd, params, nparams);
+ }
+}
+
+TermKeyResult termkey_interpret_position(TermKey *tk, const TermKeyKey *key, int *line, int *col)
+{
+ if (key->type != TERMKEY_TYPE_POSITION) {
+ return TERMKEY_RES_NONE;
+ }
+
+ termkey_key_get_linecol(key, line, col);
+
+ return TERMKEY_RES_KEY;
+}
+
+/// Handler for CSI $y mode status reports
+static TermKeyResult handle_csi_y(TermKey *tk, TermKeyKey *key, int cmd, TermKeyCsiParam *params,
+ int nparams)
+{
+ switch (cmd) {
+ case 'y'|'$' << 16:
+ case 'y'|'$' << 16 | '?' << 8:
+ if (nparams < 2) {
+ return TERMKEY_RES_NONE;
+ }
+
+ int args[2];
+ if (termkey_interpret_csi_param(params[0], &args[0], NULL, NULL) != TERMKEY_RES_KEY) {
+ return TERMKEY_RES_ERROR;
+ }
+
+ if (termkey_interpret_csi_param(params[1], &args[1], NULL, NULL) != TERMKEY_RES_KEY) {
+ return TERMKEY_RES_ERROR;
+ }
+
+ key->type = TERMKEY_TYPE_MODEREPORT;
+ key->code.mouse[0] = (char)(cmd >> 8);
+ key->code.mouse[1] = (char)(args[0] >> 8);
+ key->code.mouse[2] = (char)(args[0] & 0xff);
+ key->code.mouse[3] = (char)args[1];
+ return TERMKEY_RES_KEY;
+
+ default:
+ return TERMKEY_RES_NONE;
+ }
+}
+
+TermKeyResult termkey_interpret_modereport(TermKey *tk, const TermKeyKey *key, int *initial,
+ int *mode, int *value)
+{
+ if (key->type != TERMKEY_TYPE_MODEREPORT) {
+ return TERMKEY_RES_NONE;
+ }
+
+ if (initial) {
+ *initial = (unsigned char)key->code.mouse[0];
+ }
+
+ if (mode) {
+ *mode = ((uint8_t)key->code.mouse[1] << 8) | (uint8_t)key->code.mouse[2];
+ }
+
+ if (value) {
+ *value = (unsigned char)key->code.mouse[3];
+ }
+
+ return TERMKEY_RES_KEY;
+}
+
+#define CHARAT(i) (tk->buffer[tk->buffstart + (i)])
+
+static TermKeyResult parse_csi(TermKey *tk, size_t introlen, size_t *csi_len,
+ TermKeyCsiParam params[], size_t *nargs, unsigned *commandp)
+{
+ size_t csi_end = introlen;
+
+ while (csi_end < tk->buffcount) {
+ if (CHARAT(csi_end) >= 0x40 && CHARAT(csi_end) < 0x80) {
+ break;
+ }
+ csi_end++;
+ }
+
+ if (csi_end >= tk->buffcount) {
+ return TERMKEY_RES_AGAIN;
+ }
+
+ unsigned char cmd = CHARAT(csi_end);
+ *commandp = cmd;
+
+ char present = 0;
+ int argi = 0;
+
+ size_t p = introlen;
+
+ // See if there is an initial byte
+ if (CHARAT(p) >= '<' && CHARAT(p) <= '?') {
+ *commandp |= (unsigned)(CHARAT(p) << 8);
+ p++;
+ }
+
+ // Now attempt to parse out up number;number;... separated values
+ while (p < csi_end) {
+ unsigned char c = CHARAT(p);
+
+ if (c >= '0' && c < ';') {
+ if (!present) {
+ params[argi].param = &CHARAT(p);
+ present = 1;
+ }
+ } else if (c == ';') {
+ if (!present) {
+ params[argi].param = NULL;
+ params[argi].length = 0;
+ } else {
+ params[argi].length = (size_t)(&CHARAT(p) - params[argi].param);
+ }
+ present = 0;
+ argi++;
+
+ if (argi > 16) {
+ break;
+ }
+ } else if (c >= 0x20 && c <= 0x2f) {
+ *commandp |= (unsigned)(c << 16);
+ break;
+ }
+
+ p++;
+ }
+
+ if (present) {
+ params[argi].length = (size_t)(&CHARAT(p) - params[argi].param);
+ argi++;
+ }
+
+ *nargs = (size_t)argi;
+ *csi_len = csi_end + 1;
+
+ return TERMKEY_RES_KEY;
+}
+
+TermKeyResult termkey_interpret_csi(TermKey *tk, const TermKeyKey *key, TermKeyCsiParam params[],
+ size_t *nparams, unsigned *cmd)
+{
+ size_t dummy;
+
+ if (tk->hightide == 0) {
+ return TERMKEY_RES_NONE;
+ }
+ if (key->type != TERMKEY_TYPE_UNKNOWN_CSI) {
+ return TERMKEY_RES_NONE;
+ }
+
+ return parse_csi(tk, 0, &dummy, params, nparams, cmd);
+}
+
+TermKeyResult termkey_interpret_csi_param(TermKeyCsiParam param, int *paramp, int subparams[],
+ size_t *nsubparams)
+{
+ if (paramp == NULL) {
+ return TERMKEY_RES_ERROR;
+ }
+
+ if (param.param == NULL) {
+ *paramp = -1;
+ if (nsubparams) {
+ *nsubparams = 0;
+ }
+ return TERMKEY_RES_KEY;
+ }
+
+ int arg = 0;
+ size_t i = 0;
+ size_t capacity = nsubparams ? *nsubparams : 0;
+ size_t length = 0;
+ for (; i < param.length && length <= capacity; i++) {
+ unsigned char c = param.param[i];
+ if (c == ':') {
+ if (length == 0) {
+ *paramp = arg;
+ } else {
+ subparams[length - 1] = arg;
+ }
+
+ arg = 0;
+ length++;
+ continue;
+ }
+
+ assert(c >= '0' && c <= '9');
+ arg = (10 * arg) + (c - '0');
+ }
+
+ if (length == 0) {
+ *paramp = arg;
+ } else {
+ subparams[length - 1] = arg;
+ }
+
+ if (nsubparams) {
+ *nsubparams = length;
+ }
+
+ return TERMKEY_RES_KEY;
+}
+
+static int register_keys(void)
+{
+ int i;
+
+ for (i = 0; i < 64; i++) {
+ csi_ss3s[i].sym = TERMKEY_SYM_UNKNOWN;
+ ss3s[i].sym = TERMKEY_SYM_UNKNOWN;
+ ss3_kpalts[i] = 0;
+ }
+
+ for (i = 0; i < NCSIFUNCS; i++) {
+ csifuncs[i].sym = TERMKEY_SYM_UNKNOWN;
+ }
+
+ register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_UP, 'A');
+ register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DOWN, 'B');
+ register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RIGHT, 'C');
+ register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_LEFT, 'D');
+ register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BEGIN, 'E');
+ register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_END, 'F');
+ register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HOME, 'H');
+ register_csi_ss3(TERMKEY_TYPE_FUNCTION, 1, 'P');
+ register_csi_ss3(TERMKEY_TYPE_FUNCTION, 2, 'Q');
+ register_csi_ss3(TERMKEY_TYPE_FUNCTION, 3, 'R');
+ register_csi_ss3(TERMKEY_TYPE_FUNCTION, 4, 'S');
+
+ register_csi_ss3_full(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_TAB, TERMKEY_KEYMOD_SHIFT,
+ TERMKEY_KEYMOD_SHIFT, 'Z');
+
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPENTER, 'M', 0);
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPEQUALS, 'X', '=');
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPMULT, 'j', '*');
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPPLUS, 'k', '+');
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPCOMMA, 'l', ',');
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPMINUS, 'm', '-');
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPPERIOD, 'n', '.');
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPDIV, 'o', '/');
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP0, 'p', '0');
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP1, 'q', '1');
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP2, 'r', '2');
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP3, 's', '3');
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP4, 't', '4');
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP5, 'u', '5');
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP6, 'v', '6');
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP7, 'w', '7');
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP8, 'x', '8');
+ register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP9, 'y', '9');
+
+ register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_FIND, 1);
+ register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_INSERT, 2);
+ register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DELETE, 3);
+ register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SELECT, 4);
+ register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEUP, 5);
+ register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEDOWN, 6);
+ register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HOME, 7);
+ register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_END, 8);
+
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 1, 11);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 2, 12);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 3, 13);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 4, 14);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 5, 15);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 6, 17);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 7, 18);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 8, 19);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 9, 20);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 10, 21);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 11, 23);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 12, 24);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 13, 25);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 14, 26);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 15, 28);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 16, 29);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 17, 31);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 18, 32);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 19, 33);
+ register_csifunc(TERMKEY_TYPE_FUNCTION, 20, 34);
+
+ csi_handlers['u' - 0x40] = &handle_csi_u;
+
+ csi_handlers['M' - 0x40] = &handle_csi_m;
+ csi_handlers['m' - 0x40] = &handle_csi_m;
+
+ csi_handlers['R' - 0x40] = &handle_csi_R;
+
+ csi_handlers['y' - 0x40] = &handle_csi_y;
+
+ keyinfo_initialised = 1;
+ return 1;
+}
+
+void *new_driver_csi(TermKey *tk, const char *term)
+{
+ if (!keyinfo_initialised) {
+ if (!register_keys()) {
+ return NULL;
+ }
+ }
+
+ TermKeyCsi *csi = xmalloc(sizeof *csi);
+
+ csi->tk = tk;
+ csi->saved_string_id = 0;
+ csi->saved_string = NULL;
+
+ return csi;
+}
+
+void free_driver_csi(void *info)
+{
+ TermKeyCsi *csi = info;
+
+ if (csi->saved_string) {
+ xfree(csi->saved_string);
+ }
+
+ xfree(csi);
+}
+
+static TermKeyResult peekkey_csi_csi(TermKey *tk, TermKeyCsi *csi, size_t introlen, TermKeyKey *key,
+ int force, size_t *nbytep)
+{
+ size_t csi_len;
+ size_t nparams = 16;
+ TermKeyCsiParam params[16];
+ unsigned cmd;
+
+ TermKeyResult ret = parse_csi(tk, introlen, &csi_len, params, &nparams, &cmd);
+
+ if (ret == TERMKEY_RES_AGAIN) {
+ if (!force) {
+ return TERMKEY_RES_AGAIN;
+ }
+
+ (*tk->method.emit_codepoint)(tk, '[', key);
+ key->modifiers |= TERMKEY_KEYMOD_ALT;
+ *nbytep = introlen;
+ return TERMKEY_RES_KEY;
+ }
+
+ if (cmd == 'M' && nparams < 3) { // Mouse in X10 encoding consumes the next 3 bytes also
+ tk->buffstart += csi_len;
+ tk->buffcount -= csi_len;
+
+ TermKeyResult mouse_result = (*tk->method.peekkey_mouse)(tk, key, nbytep);
+
+ tk->buffstart -= csi_len;
+ tk->buffcount += csi_len;
+
+ if (mouse_result == TERMKEY_RES_KEY) {
+ *nbytep += csi_len;
+ }
+
+ return mouse_result;
+ }
+
+ TermKeyResult result = TERMKEY_RES_NONE;
+
+ // We know from the logic above that cmd must be >= 0x40 and < 0x80
+ if (csi_handlers[(cmd & 0xff) - 0x40]) {
+ result = (*csi_handlers[(cmd & 0xff) - 0x40])(tk, key, (int)cmd, params, (int)nparams);
+ }
+
+ if (result == TERMKEY_RES_NONE) {
+#ifdef DEBUG
+ switch (args) {
+ case 0:
+ fprintf(stderr, "CSI: Unknown cmd=%c\n", (char)cmd);
+ break;
+ case 1:
+ fprintf(stderr, "CSI: Unknown arg1=%ld cmd=%c\n", arg[0], (char)cmd);
+ break;
+ case 2:
+ fprintf(stderr, "CSI: Unknown arg1=%ld arg2=%ld cmd=%c\n", arg[0], arg[1], (char)cmd);
+ break;
+ case 3:
+ fprintf(stderr, "CSI: Unknown arg1=%ld arg2=%ld arg3=%ld cmd=%c\n", arg[0], arg[1], arg[2],
+ (char)cmd);
+ break;
+ default:
+ fprintf(stderr, "CSI: Unknown arg1=%ld arg2=%ld arg3=%ld ... args=%d cmd=%c\n", arg[0],
+ arg[1], arg[2], args, (char)cmd);
+ break;
+ }
+#endif
+ key->type = TERMKEY_TYPE_UNKNOWN_CSI;
+ key->code.number = (int)cmd;
+ key->modifiers = 0;
+
+ tk->hightide = csi_len - introlen;
+ *nbytep = introlen; // Do not yet eat the data bytes
+ return TERMKEY_RES_KEY;
+ }
+
+ *nbytep = csi_len;
+ return result;
+}
+
+static TermKeyResult peekkey_ss3(TermKey *tk, TermKeyCsi *csi, size_t introlen, TermKeyKey *key,
+ int force, size_t *nbytep)
+{
+ if (tk->buffcount < introlen + 1) {
+ if (!force) {
+ return TERMKEY_RES_AGAIN;
+ }
+
+ (*tk->method.emit_codepoint)(tk, 'O', key);
+ key->modifiers |= TERMKEY_KEYMOD_ALT;
+ *nbytep = tk->buffcount;
+ return TERMKEY_RES_KEY;
+ }
+
+ unsigned char cmd = CHARAT(introlen);
+
+ if (cmd < 0x40 || cmd >= 0x80) {
+ return TERMKEY_RES_NONE;
+ }
+
+ key->type = csi_ss3s[cmd - 0x40].type;
+ key->code.sym = csi_ss3s[cmd - 0x40].sym;
+ key->modifiers = csi_ss3s[cmd - 0x40].modifier_set;
+
+ if (key->code.sym == TERMKEY_SYM_UNKNOWN) {
+ if (tk->flags & TERMKEY_FLAG_CONVERTKP && ss3_kpalts[cmd - 0x40]) {
+ key->type = TERMKEY_TYPE_UNICODE;
+ key->code.codepoint = (unsigned char)ss3_kpalts[cmd - 0x40];
+ key->modifiers = 0;
+
+ key->utf8[0] = (char)key->code.codepoint;
+ key->utf8[1] = 0;
+ } else {
+ key->type = ss3s[cmd - 0x40].type;
+ key->code.sym = ss3s[cmd - 0x40].sym;
+ key->modifiers = ss3s[cmd - 0x40].modifier_set;
+ }
+ }
+
+ if (key->code.sym == TERMKEY_SYM_UNKNOWN) {
+#ifdef DEBUG
+ fprintf(stderr, "CSI: Unknown SS3 %c (0x%02x)\n", (char)cmd, cmd);
+#endif
+ return TERMKEY_RES_NONE;
+ }
+
+ *nbytep = introlen + 1;
+
+ return TERMKEY_RES_KEY;
+}
+
+static TermKeyResult peekkey_ctrlstring(TermKey *tk, TermKeyCsi *csi, size_t introlen,
+ TermKeyKey *key, int force, size_t *nbytep)
+{
+ size_t str_end = introlen;
+
+ while (str_end < tk->buffcount) {
+ if (CHARAT(str_end) == 0x07) { // BEL
+ break;
+ }
+ if (CHARAT(str_end) == 0x9c) { // ST
+ break;
+ }
+ if (CHARAT(str_end) == 0x1b
+ && (str_end + 1) < tk->buffcount
+ && CHARAT(str_end + 1) == 0x5c) { // ESC-prefixed ST
+ break;
+ }
+
+ str_end++;
+ }
+
+ if (str_end >= tk->buffcount) {
+ return TERMKEY_RES_AGAIN;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "Found a control string: %*s",
+ str_end - introlen, tk->buffer + tk->buffstart + introlen);
+#endif
+
+ *nbytep = str_end + 1;
+ if (CHARAT(str_end) == 0x1b) {
+ (*nbytep)++;
+ }
+
+ if (csi->saved_string) {
+ xfree(csi->saved_string);
+ }
+
+ size_t len = str_end - introlen;
+
+ csi->saved_string_id++;
+ csi->saved_string = xmalloc(len + 1);
+
+ strncpy(csi->saved_string, (char *)tk->buffer + tk->buffstart + introlen, len); // NOLINT(runtime/printf)
+ csi->saved_string[len] = 0;
+
+ key->type = (CHARAT(introlen - 1) & 0x1f) == 0x10
+ ? TERMKEY_TYPE_DCS : TERMKEY_TYPE_OSC;
+ key->code.number = csi->saved_string_id;
+ key->modifiers = 0;
+
+ return TERMKEY_RES_KEY;
+}
+
+TermKeyResult peekkey_csi(TermKey *tk, void *info, TermKeyKey *key, int force, size_t *nbytep)
+{
+ if (tk->buffcount == 0) {
+ return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE;
+ }
+
+ TermKeyCsi *csi = info;
+
+ switch (CHARAT(0)) {
+ case 0x1b:
+ if (tk->buffcount < 2) {
+ return TERMKEY_RES_NONE;
+ }
+
+ switch (CHARAT(1)) {
+ case 0x4f: // ESC-prefixed SS3
+ return peekkey_ss3(tk, csi, 2, key, force, nbytep);
+
+ case 0x50: // ESC-prefixed DCS
+ case 0x5d: // ESC-prefixed OSC
+ return peekkey_ctrlstring(tk, csi, 2, key, force, nbytep);
+
+ case 0x5b: // ESC-prefixed CSI
+ return peekkey_csi_csi(tk, csi, 2, key, force, nbytep);
+ }
+
+ return TERMKEY_RES_NONE;
+
+ case 0x8f: // SS3
+ return peekkey_ss3(tk, csi, 1, key, force, nbytep);
+
+ case 0x90: // DCS
+ case 0x9d: // OSC
+ return peekkey_ctrlstring(tk, csi, 1, key, force, nbytep);
+
+ case 0x9b: // CSI
+ return peekkey_csi_csi(tk, csi, 1, key, force, nbytep);
+ }
+
+ return TERMKEY_RES_NONE;
+}