From f93ecd2760f5859fd5eeec28c7c2196ece98e9a1 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Sun, 21 Jul 2024 21:47:37 -0500 Subject: feat(tui): parse CSI subparams in termkey (#29805) libtermkey does not know how to parse CSI subparameters (parameters separated by ':', ASCII 0x3A) and currently just ignores them. However, many important CSI sequences sent by the terminal make use of subparameters, most notably key events when using the kitty keyboard protocol [1]. Enabling subparameters is a prerequisite for expanding kitty keyboard protocol support in Neovim. Concretely, we do this by returning pointers into the internal termkey buffer for each CSI parameter rather than parsing them into integers directly. When a caller wants to actually use the parameter as an integer, they must call termkey_interpret_csi_param, which parses the full parameter string into an integer parameter and zero or more subparameters. The pointers into the internal buffer will become invalidated when new input arrives from the terminal so it is important that the individual params are used and parsed right away. All of our code (and libtermkey's code) does this, so this is fine for now, but is something to keep in mind moving forward. [1]: https://sw.kovidgoyal.net/kitty/keyboard-protocol/ --- src/termkey/driver-csi.c | 245 ++++++++++++++++++++++++++++++++++++----------- src/termkey/termkey.h | 9 +- 2 files changed, 196 insertions(+), 58 deletions(-) (limited to 'src/termkey') diff --git a/src/termkey/driver-csi.c b/src/termkey/driver-csi.c index 4cd5bbafe4..f9c39757b7 100644 --- a/src/termkey/driver-csi.c +++ b/src/termkey/driver-csi.c @@ -1,6 +1,7 @@ #include "termkey.h" #include "termkey-internal.h" +#include #include #include @@ -15,7 +16,7 @@ typedef struct { char *saved_string; } TermKeyCsi; -typedef TermKeyResult CsiHandler(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args); +typedef TermKeyResult CsiHandler(TermKey *tk, TermKeyKey *key, int cmd, TermKeyCsiParam *params, int nparams); static CsiHandler *csi_handlers[64]; /* @@ -24,12 +25,21 @@ static CsiHandler *csi_handlers[64]; static struct keyinfo csi_ss3s[64]; -static TermKeyResult handle_csi_ss3_full(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) +static TermKeyResult handle_csi_ss3_full(TermKey *tk, TermKeyKey *key, int cmd, TermKeyCsiParam *params, int nparams) { - if(args > 1 && arg[1] != -1) - key->modifiers = arg[1] - 1; - else + TermKeyResult result = TERMKEY_RES_KEY; + + if(nparams > 1 && params[1].param != NULL) { + long 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; @@ -37,9 +47,9 @@ static TermKeyResult handle_csi_ss3_full(TermKey *tk, TermKeyKey *key, int cmd, key->modifiers |= csi_ss3s[cmd - 0x40].modifier_set; if(key->code.sym == TERMKEY_SYM_UNKNOWN) - return TERMKEY_RES_NONE; + result = TERMKEY_RES_NONE; - return TERMKEY_RES_KEY; + return result; } static void register_csi_ss3_full(TermKeyType type, TermKeySym sym, int modifier_set, int modifier_mask, unsigned char cmd) @@ -85,25 +95,48 @@ static void register_ss3kpalt(TermKeyType type, TermKeySym sym, unsigned char cm static struct keyinfo csifuncs[35]; /* This value must be increased if more CSI function keys are added */ #define NCSIFUNCS (sizeof(csifuncs)/sizeof(csifuncs[0])) -static TermKeyResult handle_csifunc(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) +static TermKeyResult handle_csifunc(TermKey *tk, TermKeyKey *key, int cmd, TermKeyCsiParam *params, int nparams) { - if(args > 1 && arg[1] != -1) - key->modifiers = arg[1] - 1; - else + if (nparams == 0) { + return TERMKEY_RES_NONE; + } + + TermKeyResult result = TERMKEY_RES_KEY; + long 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; - if(arg[0] == 27) { + 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, arg[2], key); + (*tk->method.emit_codepoint)(tk, args[2], key); key->modifiers |= mod; } - else if(arg[0] >= 0 && arg[0] < NCSIFUNCS) { - key->type = csifuncs[arg[0]].type; - key->code.sym = csifuncs[arg[0]].sym; - key->modifiers &= ~(csifuncs[arg[0]].modifier_mask); - key->modifiers |= csifuncs[arg[0]].modifier_set; + 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; @@ -112,10 +145,10 @@ static TermKeyResult handle_csifunc(TermKey *tk, TermKeyKey *key, int cmd, long #ifdef DEBUG fprintf(stderr, "CSI: Unknown function key %ld\n", arg[0]); #endif - return TERMKEY_RES_NONE; + result = TERMKEY_RES_NONE; } - return TERMKEY_RES_KEY; + return result; } static void register_csifunc(TermKeyType type, TermKeySym sym, int number) @@ -136,18 +169,35 @@ static void register_csifunc(TermKeyType type, TermKeySym sym, int number) * Handler for CSI u extended Unicode keys */ -static TermKeyResult handle_csi_u(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) +static TermKeyResult handle_csi_u(TermKey *tk, TermKeyKey *key, int cmd, TermKeyCsiParam *params, int nparams) { switch(cmd) { case 'u': { - if(args > 1 && arg[1] != -1) - key->modifiers = arg[1] - 1; - else + long args[2]; + if(nparams > 1 && params[1].param != NULL) { + long 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, arg[0], key); + (*tk->method.emit_codepoint)(tk, args[0], key); key->modifiers |= mod; return TERMKEY_RES_KEY; @@ -162,7 +212,7 @@ static TermKeyResult handle_csi_u(TermKey *tk, TermKeyKey *key, int cmd, long *a * Note: This does not handle X10 encoding */ -static TermKeyResult handle_csi_m(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) +static TermKeyResult handle_csi_m(TermKey *tk, TermKeyKey *key, int cmd, TermKeyCsiParam *params, int nparams) { int initial = cmd >> 8; cmd &= 0xff; @@ -175,26 +225,37 @@ static TermKeyResult handle_csi_m(TermKey *tk, TermKeyKey *key, int cmd, long *a return TERMKEY_RES_NONE; } - if(!initial && args >= 3) { // rxvt protocol + if (nparams < 3) { + return TERMKEY_RES_NONE; + } + + long 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] = arg[0]; + key->code.mouse[0] = args[0]; key->modifiers = (key->code.mouse[0] & 0x1c) >> 2; key->code.mouse[0] &= ~0x1c; - termkey_key_set_linecol(key, arg[1], arg[2]); + termkey_key_set_linecol(key, args[1], args[2]); return TERMKEY_RES_KEY; } - if(initial == '<' && args >= 3) { // SGR protocol + if(initial == '<') { // SGR protocol key->type = TERMKEY_TYPE_MOUSE; - key->code.mouse[0] = arg[0]; + key->code.mouse[0] = args[0]; key->modifiers = (key->code.mouse[0] & 0x1c) >> 2; key->code.mouse[0] &= ~0x1c; - termkey_key_set_linecol(key, arg[1], arg[2]); + termkey_key_set_linecol(key, args[1], args[2]); if(cmd == 'm') // release key->code.mouse[3] |= 0x80; @@ -265,19 +326,28 @@ TermKeyResult termkey_interpret_mouse(TermKey *tk, const TermKeyKey *key, TermKe * A plain CSI R with no arguments is probably actually */ -static TermKeyResult handle_csi_R(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) +static TermKeyResult handle_csi_R(TermKey *tk, TermKeyKey *key, int cmd, TermKeyCsiParam *params, int nparams) { switch(cmd) { case 'R'|'?'<<8: - if(args < 2) + if(nparams < 2) return TERMKEY_RES_NONE; + long 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, arg[1], arg[0]); + termkey_key_set_linecol(key, args[1], args[0]); return TERMKEY_RES_KEY; default: - return handle_csi_ss3_full(tk, key, cmd, arg, args); + return handle_csi_ss3_full(tk, key, cmd, params, nparams); } } @@ -295,19 +365,28 @@ TermKeyResult termkey_interpret_position(TermKey *tk, const TermKeyKey *key, int * Handler for CSI $y mode status reports */ -static TermKeyResult handle_csi_y(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args) +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(args < 2) + if(nparams < 2) return TERMKEY_RES_NONE; + long 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] = (cmd >> 8); - key->code.mouse[1] = arg[0] >> 8; - key->code.mouse[2] = arg[0] & 0xff; - key->code.mouse[3] = arg[1]; + key->code.mouse[1] = args[0] >> 8; + key->code.mouse[2] = args[0] & 0xff; + key->code.mouse[3] = args[1]; return TERMKEY_RES_KEY; default: @@ -334,7 +413,7 @@ TermKeyResult termkey_interpret_modereport(TermKey *tk, const TermKeyKey *key, i #define CHARAT(i) (tk->buffer[tk->buffstart + (i)]) -static TermKeyResult parse_csi(TermKey *tk, size_t introlen, size_t *csi_len, long args[], size_t *nargs, unsigned long *commandp) +static TermKeyResult parse_csi(TermKey *tk, size_t introlen, size_t *csi_len, TermKeyCsiParam params[], size_t *nargs, unsigned long *commandp) { size_t csi_end = introlen; @@ -365,18 +444,19 @@ static TermKeyResult parse_csi(TermKey *tk, size_t introlen, size_t *csi_len, lo while(p < csi_end) { unsigned char c = CHARAT(p); - if(c >= '0' && c <= '9') { + if(c >= '0' && c < ';') { if(!present) { - args[argi] = c - '0'; + params[argi].param = &CHARAT(p); present = 1; } - else { - args[argi] = (args[argi] * 10) + c - '0'; - } } else if(c == ';') { - if(!present) - args[argi] = -1; + if(!present) { + params[argi].param = NULL; + params[argi].length = 0; + } else { + params[argi].length = &CHARAT(p) - params[argi].param; + } present = 0; argi++; @@ -391,8 +471,10 @@ static TermKeyResult parse_csi(TermKey *tk, size_t introlen, size_t *csi_len, lo p++; } - if(present) + if(present) { + params[argi].length = &CHARAT(p) - params[argi].param; argi++; + } *nargs = argi; *csi_len = csi_end + 1; @@ -400,7 +482,7 @@ static TermKeyResult parse_csi(TermKey *tk, size_t introlen, size_t *csi_len, lo return TERMKEY_RES_KEY; } -TermKeyResult termkey_interpret_csi(TermKey *tk, const TermKeyKey *key, long args[], size_t *nargs, unsigned long *cmd) +TermKeyResult termkey_interpret_csi(TermKey *tk, const TermKeyKey *key, TermKeyCsiParam params[], size_t *nparams, unsigned long *cmd) { size_t dummy; @@ -409,7 +491,56 @@ TermKeyResult termkey_interpret_csi(TermKey *tk, const TermKeyKey *key, long arg if(key->type != TERMKEY_TYPE_UNKNOWN_CSI) return TERMKEY_RES_NONE; - return parse_csi(tk, 0, &dummy, args, nargs, cmd); + return parse_csi(tk, 0, &dummy, params, nparams, cmd); +} + +TermKeyResult termkey_interpret_csi_param(TermKeyCsiParam param, long *paramp, long 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; + } + + long 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) @@ -531,11 +662,11 @@ static void free_driver(void *info) static TermKeyResult peekkey_csi(TermKey *tk, TermKeyCsi *csi, size_t introlen, TermKeyKey *key, int force, size_t *nbytep) { size_t csi_len; - size_t args = 16; - long arg[16]; + size_t nparams = 16; + TermKeyCsiParam params[16]; unsigned long cmd; - TermKeyResult ret = parse_csi(tk, introlen, &csi_len, arg, &args, &cmd); + TermKeyResult ret = parse_csi(tk, introlen, &csi_len, params, &nparams, &cmd); if(ret == TERMKEY_RES_AGAIN) { if(!force) @@ -547,7 +678,7 @@ static TermKeyResult peekkey_csi(TermKey *tk, TermKeyCsi *csi, size_t introlen, return TERMKEY_RES_KEY; } - if(cmd == 'M' && args < 3) { // Mouse in X10 encoding consumes the next 3 bytes also + if(cmd == 'M' && nparams < 3) { // Mouse in X10 encoding consumes the next 3 bytes also tk->buffstart += csi_len; tk->buffcount -= csi_len; @@ -566,7 +697,7 @@ static TermKeyResult peekkey_csi(TermKey *tk, TermKeyCsi *csi, size_t introlen, // 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, cmd, arg, args); + result = (*csi_handlers[(cmd & 0xff) - 0x40])(tk, key, cmd, params, nparams); if(result == TERMKEY_RES_NONE) { #ifdef DEBUG diff --git a/src/termkey/termkey.h b/src/termkey/termkey.h index 8e10fcff0c..94405f6516 100644 --- a/src/termkey/termkey.h +++ b/src/termkey/termkey.h @@ -143,6 +143,11 @@ typedef struct { char utf8[7]; } TermKeyKey; +typedef struct { + const unsigned char *param; + size_t length; +} TermKeyCsiParam; + typedef struct TermKey TermKey; enum { @@ -215,7 +220,9 @@ TermKeyResult termkey_interpret_position(TermKey *tk, const TermKeyKey *key, int TermKeyResult termkey_interpret_modereport(TermKey *tk, const TermKeyKey *key, int *initial, int *mode, int *value); -TermKeyResult termkey_interpret_csi(TermKey *tk, const TermKeyKey *key, long args[], size_t *nargs, unsigned long *cmd); +TermKeyResult termkey_interpret_csi(TermKey *tk, const TermKeyKey *key, TermKeyCsiParam params[], size_t *nparams, unsigned long *cmd); + +TermKeyResult termkey_interpret_csi_param(TermKeyCsiParam param, long *paramp, long subparams[], size_t *nsubparams); TermKeyResult termkey_interpret_string(TermKey *tk, const TermKeyKey *key, const char **strp); -- cgit From f9108378b7a7e08b48685f0a3ff4f7a3a14b56d6 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Wed, 14 Aug 2024 15:52:51 +0200 Subject: refactor: adopt termkey and eliminate duplicate code Termkey is abandoned and it's now our code, so there's no reason not to treat it as such. An alternative approach could be to have a proper repo that we maintain such as with unibilium, although with this approach we can make a few assumptions that will allow us to remove more code. Also eliminate duplicate code from both termkey and libvterm. --- src/termkey/driver-csi.c | 903 ---------------------- src/termkey/driver-ti.c | 654 ---------------- src/termkey/termkey-internal.h | 112 --- src/termkey/termkey.c | 1606 ---------------------------------------- src/termkey/termkey.h | 256 ------- 5 files changed, 3531 deletions(-) delete mode 100644 src/termkey/driver-csi.c delete mode 100644 src/termkey/driver-ti.c delete mode 100644 src/termkey/termkey-internal.h delete mode 100644 src/termkey/termkey.c delete mode 100644 src/termkey/termkey.h (limited to 'src/termkey') diff --git a/src/termkey/driver-csi.c b/src/termkey/driver-csi.c deleted file mode 100644 index f9c39757b7..0000000000 --- a/src/termkey/driver-csi.c +++ /dev/null @@ -1,903 +0,0 @@ -#include "termkey.h" -#include "termkey-internal.h" - -#include -#include -#include - -// There are 64 codes 0x40 - 0x7F -static int keyinfo_initialised = 0; -static struct keyinfo ss3s[64]; -static char ss3_kpalts[64]; - -typedef struct { - TermKey *tk; - int saved_string_id; - char *saved_string; -} TermKeyCsi; - -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) { - long 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 - */ - -static struct keyinfo csifuncs[35]; /* This value must be increased if more CSI function keys are added */ -#define NCSIFUNCS (sizeof(csifuncs)/sizeof(csifuncs[0])) - -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; - long 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': { - long args[2]; - if(nparams > 1 && params[1].param != NULL) { - long 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; - } - - long 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] = 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] = 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 = 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 - */ - -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; - - long 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; - - long 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] = (cmd >> 8); - key->code.mouse[1] = args[0] >> 8; - key->code.mouse[2] = args[0] & 0xff; - key->code.mouse[3] = 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 = key->code.mouse[0]; - - if(mode) - *mode = ((uint8_t)key->code.mouse[1] << 8) | (uint8_t)key->code.mouse[2]; - - if(value) - *value = 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 long *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 |= (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 = &CHARAT(p) - params[argi].param; - } - present = 0; - argi++; - - if(argi > 16) - break; - } - else if(c >= 0x20 && c <= 0x2f) { - *commandp |= c << 16; - break; - } - - p++; - } - - if(present) { - params[argi].length = &CHARAT(p) - params[argi].param; - argi++; - } - - *nargs = 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 long *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, long *paramp, long 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; - } - - long 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; -} - -static void *new_driver(TermKey *tk, const char *term) -{ - if(!keyinfo_initialised) - if(!register_keys()) - return NULL; - - TermKeyCsi *csi = malloc(sizeof *csi); - if(!csi) - return NULL; - - csi->tk = tk; - csi->saved_string_id = 0; - csi->saved_string = NULL; - - return csi; -} - -static void free_driver(void *info) -{ - TermKeyCsi *csi = info; - - if(csi->saved_string) - free(csi->saved_string); - - free(csi); -} - -static TermKeyResult peekkey_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 long 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, cmd, params, 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 = 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 = ss3_kpalts[cmd - 0x40]; - key->modifiers = 0; - - key->utf8[0] = 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) - free(csi->saved_string); - - size_t len = str_end - introlen; - - csi->saved_string_id++; - csi->saved_string = malloc(len + 1); - - strncpy(csi->saved_string, (char *)tk->buffer + tk->buffstart + introlen, len); - 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; -} - -static TermKeyResult peekkey(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(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(tk, csi, 1, key, force, nbytep); - } - - return TERMKEY_RES_NONE; -} - -struct TermKeyDriver termkey_driver_csi = { - .name = "CSI", - - .new_driver = new_driver, - .free_driver = free_driver, - - .peekkey = peekkey, -}; - -TermKeyResult termkey_interpret_string(TermKey *tk, const TermKeyKey *key, const char **strp) -{ - struct TermKeyDriverNode *p; - for(p = tk->drivers; p; p = p->next) - if(p->driver == &termkey_driver_csi) - break; - - if(!p) - return TERMKEY_RES_NONE; - - if(key->type != TERMKEY_TYPE_DCS && - key->type != TERMKEY_TYPE_OSC) - return TERMKEY_RES_NONE; - - TermKeyCsi *csi = p->info; - - if(csi->saved_string_id != key->code.number) - return TERMKEY_RES_NONE; - - *strp = csi->saved_string; - - return TERMKEY_RES_KEY; -} diff --git a/src/termkey/driver-ti.c b/src/termkey/driver-ti.c deleted file mode 100644 index 1f8ee10808..0000000000 --- a/src/termkey/driver-ti.c +++ /dev/null @@ -1,654 +0,0 @@ -// we want strdup() -#define _XOPEN_SOURCE 600 - -#include "termkey.h" -#include "termkey-internal.h" - -#ifdef HAVE_UNIBILIUM -# include -#else -# include -# include - -/* curses.h has just polluted our namespace. We want this back */ -# undef buttons -#endif - -#include -#include -#include -#include -#include -#ifndef _WIN32 -# include -#else -# include -#endif -#include -#include - -#define streq(a,b) (!strcmp(a,b)) - -#define MAX_FUNCNAME 9 - -static struct { - const char *funcname; - TermKeyType type; - TermKeySym sym; - int mods; -} funcs[] = -{ - /* THIS LIST MUST REMAIN SORTED! */ - { "backspace", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BACKSPACE, 0 }, - { "begin", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BEGIN, 0 }, - { "beg", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BEGIN, 0 }, - { "btab", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_TAB, TERMKEY_KEYMOD_SHIFT }, - { "cancel", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_CANCEL, 0 }, - { "clear", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_CLEAR, 0 }, - { "close", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_CLOSE, 0 }, - { "command", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_COMMAND, 0 }, - { "copy", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_COPY, 0 }, - { "dc", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DELETE, 0 }, - { "down", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DOWN, 0 }, - { "end", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_END, 0 }, - { "enter", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_ENTER, 0 }, - { "exit", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_EXIT, 0 }, - { "find", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_FIND, 0 }, - { "help", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HELP, 0 }, - { "home", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HOME, 0 }, - { "ic", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_INSERT, 0 }, - { "left", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_LEFT, 0 }, - { "mark", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_MARK, 0 }, - { "message", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_MESSAGE, 0 }, - { "move", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_MOVE, 0 }, - { "next", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEDOWN, 0 }, // Not quite, but it's the best we can do - { "npage", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEDOWN, 0 }, - { "open", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_OPEN, 0 }, - { "options", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_OPTIONS, 0 }, - { "ppage", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEUP, 0 }, - { "previous", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEUP, 0 }, // Not quite, but it's the best we can do - { "print", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PRINT, 0 }, - { "redo", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_REDO, 0 }, - { "reference", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_REFERENCE, 0 }, - { "refresh", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_REFRESH, 0 }, - { "replace", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_REPLACE, 0 }, - { "restart", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RESTART, 0 }, - { "resume", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RESUME, 0 }, - { "right", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RIGHT, 0 }, - { "save", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SAVE, 0 }, - { "select", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SELECT, 0 }, - { "suspend", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SUSPEND, 0 }, - { "undo", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_UNDO, 0 }, - { "up", TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_UP, 0 }, - { NULL }, -}; - -#ifdef HAVE_UNIBILIUM -static enum unibi_string unibi_lookup_str(const char *name) -{ - for(enum unibi_string ret = unibi_string_begin_+1; ret < unibi_string_end_; ret++) - if(streq(unibi_name_str(ret), name)) - return ret; - - return -1; -} - -static const char *unibi_get_str_by_name(const unibi_term *ut, const char *name) -{ - enum unibi_string idx = unibi_lookup_str(name); - if(idx == (enum unibi_string)-1) - return NULL; - - return unibi_get_str(ut, idx); -} -#endif - -/* To be efficient at lookups, we store the byte sequence => keyinfo mapping - * in a trie. This avoids a slow linear search through a flat list of - * sequences. Because it is likely most nodes will be very sparse, we optimise - * vector to store an extent map after the database is loaded. - */ - -typedef enum { - TYPE_KEY, - TYPE_ARR, -} trie_nodetype; - -struct trie_node { - trie_nodetype type; -}; - -struct trie_node_key { - trie_nodetype type; - struct keyinfo key; -}; - -struct trie_node_arr { - trie_nodetype type; - unsigned char min, max; /* INCLUSIVE endpoints of the extent range */ - struct trie_node *arr[]; /* dynamic size at allocation time */ -}; - -typedef struct { - TermKey *tk; - -#ifdef HAVE_UNIBILIUM - unibi_term *unibi; /* only valid until first 'start' call */ -#else - char *term; /* only valid until first 'start' call */ -#endif - - struct trie_node *root; - - char *start_string; - char *stop_string; -} TermKeyTI; - -static int insert_seq(TermKeyTI *ti, const char *seq, struct trie_node *node); - -static struct trie_node *new_node_key(TermKeyType type, TermKeySym sym, int modmask, int modset) -{ - struct trie_node_key *n = malloc(sizeof(*n)); - if(!n) - return NULL; - - n->type = TYPE_KEY; - - n->key.type = type; - n->key.sym = sym; - n->key.modifier_mask = modmask; - n->key.modifier_set = modset; - - return (struct trie_node*)n; -} - -static struct trie_node *new_node_arr(unsigned char min, unsigned char max) -{ - struct trie_node_arr *n = malloc(sizeof(*n) + ((int)max-min+1) * sizeof(n->arr[0])); - if(!n) - return NULL; - - n->type = TYPE_ARR; - n->min = min; n->max = max; - - int i; - for(i = min; i <= max; i++) - n->arr[i-min] = NULL; - - return (struct trie_node*)n; -} - -static struct trie_node *lookup_next(struct trie_node *n, unsigned char b) -{ - switch(n->type) { - case TYPE_KEY: - fprintf(stderr, "ABORT: lookup_next within a TYPE_KEY node\n"); - abort(); - case TYPE_ARR: - { - struct trie_node_arr *nar = (struct trie_node_arr*)n; - if(b < nar->min || b > nar->max) - return NULL; - return nar->arr[b - nar->min]; - } - } - - return NULL; // Never reached but keeps compiler happy -} - -static void free_trie(struct trie_node *n) -{ - switch(n->type) { - case TYPE_KEY: - break; - case TYPE_ARR: - { - struct trie_node_arr *nar = (struct trie_node_arr*)n; - int i; - for(i = nar->min; i <= nar->max; i++) - if(nar->arr[i - nar->min]) - free_trie(nar->arr[i - nar->min]); - break; - } - } - - free(n); -} - -static struct trie_node *compress_trie(struct trie_node *n) -{ - if(!n) - return NULL; - - switch(n->type) { - case TYPE_KEY: - return n; - case TYPE_ARR: - { - struct trie_node_arr *nar = (struct trie_node_arr*)n; - unsigned char min, max; - // Find the real bounds - for(min = 0; !nar->arr[min]; min++) - if(min == 255 && !nar->arr[min]) { - free(nar); - return new_node_arr(1, 0); - } - - for(max = 0xff; !nar->arr[max]; max--) - ; - - struct trie_node_arr *new = (struct trie_node_arr*)new_node_arr(min, max); - int i; - for(i = min; i <= max; i++) - new->arr[i - min] = compress_trie(nar->arr[i]); - - free(nar); - return (struct trie_node*)new; - } - } - - return n; -} - -static bool try_load_terminfo_key(TermKeyTI *ti, const char *name, struct keyinfo *info) -{ - const char *value = NULL; - -#ifdef HAVE_UNIBILIUM - if(ti->unibi) - value = unibi_get_str_by_name(ti->unibi, name); -#else - if(ti->term) - value = tigetstr(name); -#endif - - if(ti->tk->ti_getstr_hook) - value = (ti->tk->ti_getstr_hook)(name, value, ti->tk->ti_getstr_hook_data); - - if(!value || value == (char*)-1 || !value[0]) - return false; - - struct trie_node *node = new_node_key(info->type, info->sym, info->modifier_mask, info->modifier_set); - insert_seq(ti, value, node); - - return true; -} - -static int load_terminfo(TermKeyTI *ti) -{ - int i; - -#ifdef HAVE_UNIBILIUM - unibi_term *unibi = ti->unibi; -#else - { - int err; - - /* Have to cast away the const. But it's OK - we know terminfo won't really - * modify term */ - if(setupterm((char*)ti->term, 1, &err) != OK) - return 0; - } -#endif - - ti->root = new_node_arr(0, 0xff); - if(!ti->root) - return 0; - - /* First the regular key strings - */ - for(i = 0; funcs[i].funcname; i++) { - char name[MAX_FUNCNAME + 5 + 1]; - - sprintf(name, "key_%s", funcs[i].funcname); - if(!try_load_terminfo_key(ti, name, &(struct keyinfo){ - .type = funcs[i].type, - .sym = funcs[i].sym, - .modifier_mask = funcs[i].mods, - .modifier_set = funcs[i].mods, - })) - continue; - - /* Maybe it has a shifted version */ - sprintf(name, "key_s%s", funcs[i].funcname); - try_load_terminfo_key(ti, name, &(struct keyinfo){ - .type = funcs[i].type, - .sym = funcs[i].sym, - .modifier_mask = funcs[i].mods | TERMKEY_KEYMOD_SHIFT, - .modifier_set = funcs[i].mods | TERMKEY_KEYMOD_SHIFT, - }); - } - - /* Now the F keys - */ - for(i = 1; i < 255; i++) { - char name[9]; - sprintf(name, "key_f%d", i); - if(!try_load_terminfo_key(ti, name, &(struct keyinfo){ - .type = TERMKEY_TYPE_FUNCTION, - .sym = i, - .modifier_mask = 0, - .modifier_set = 0, - })) - break; - } - - /* Finally mouse mode */ - { - const char *value = NULL; - -#ifdef HAVE_UNIBILIUM - if(ti->unibi) - value = unibi_get_str_by_name(ti->unibi, "key_mouse"); -#else - if(ti->term) - value = tigetstr("key_mouse"); -#endif - - if(ti->tk->ti_getstr_hook) - value = (ti->tk->ti_getstr_hook)("key_mouse", value, ti->tk->ti_getstr_hook_data); - - /* Some terminfos (e.g. xterm-1006) claim a different key_mouse that won't - * give X10 encoding. We'll only accept this if it's exactly "\e[M" - */ - if(value && streq(value, "\x1b[M")) { - struct trie_node *node = new_node_key(TERMKEY_TYPE_MOUSE, 0, 0, 0); - insert_seq(ti, value, node); - } - } - - /* Take copies of these terminfo strings, in case we build multiple termkey - * instances for multiple different termtypes, and it's different by the - * time we want to use it - */ -#ifdef HAVE_UNIBILIUM - const char *keypad_xmit = unibi ? - unibi_get_str(unibi, unibi_keypad_xmit) : - NULL; -#endif - - if(keypad_xmit) - ti->start_string = strdup(keypad_xmit); - else - ti->start_string = NULL; - -#ifdef HAVE_UNIBILIUM - const char *keypad_local = unibi ? - unibi_get_str(unibi, unibi_keypad_local) : - NULL; -#endif - - if(keypad_local) - ti->stop_string = strdup(keypad_local); - else - ti->stop_string = NULL; - -#ifdef HAVE_UNIBILIUM - if(unibi) - unibi_destroy(unibi); - - ti->unibi = NULL; -#else - if(ti->term) - free(ti->term); - - ti->term = NULL; -#endif - - ti->root = compress_trie(ti->root); - - return 1; -} - -static void *new_driver(TermKey *tk, const char *term) -{ - TermKeyTI *ti = malloc(sizeof *ti); - if(!ti) - return NULL; - - ti->tk = tk; - ti->root = NULL; - ti->start_string = NULL; - ti->stop_string = NULL; - -#ifdef HAVE_UNIBILIUM - ti->unibi = unibi_from_term(term); - int saved_errno = errno; - if(!ti->unibi && saved_errno != ENOENT) { - free(ti); - return NULL; - } - /* ti->unibi may be NULL if errno == ENOENT. That means the terminal wasn't - * known. Lets keep going because if we get getstr hook that might invent - * new strings for us - */ -#else - { - int err; - - ti->term = NULL; - - /* Have to cast away the const. But it's OK - we know terminfo won't really - * modify term */ - if(setupterm((char*)term, 1, &err) == OK) - ti->term = strdup(term); - } -#endif - - return ti; -} - -static int start_driver(TermKey *tk, void *info) -{ - TermKeyTI *ti = info; - struct stat statbuf; - char *start_string; - size_t len; - - if(!ti->root) - load_terminfo(ti); - - start_string = ti->start_string; - - if(tk->fd == -1 || !start_string) - return 1; - - /* The terminfo database will contain keys in application cursor key mode. - * We may need to enable that mode - */ - - /* There's no point trying to write() to a pipe */ - if(fstat(tk->fd, &statbuf) == -1) - return 0; - -#ifndef _WIN32 - if(S_ISFIFO(statbuf.st_mode)) - return 1; -#endif - - // Can't call putp or tputs because they suck and don't give us fd control - len = strlen(start_string); - while(len) { - size_t written = write(tk->fd, start_string, len); - if(written == -1) - return 0; - start_string += written; - len -= written; - } - return 1; -} - -static int stop_driver(TermKey *tk, void *info) -{ - TermKeyTI *ti = info; - struct stat statbuf; - char *stop_string = ti->stop_string; - size_t len; - - if(tk->fd == -1 || !stop_string) - return 1; - - /* There's no point trying to write() to a pipe */ - if(fstat(tk->fd, &statbuf) == -1) - return 0; - -#ifndef _WIN32 - if(S_ISFIFO(statbuf.st_mode)) - return 1; -#endif - - /* The terminfo database will contain keys in application cursor key mode. - * We may need to enable that mode - */ - - // Can't call putp or tputs because they suck and don't give us fd control - len = strlen(stop_string); - while(len) { - size_t written = write(tk->fd, stop_string, len); - if(written == -1) - return 0; - stop_string += written; - len -= written; - } - return 1; -} - -static void free_driver(void *info) -{ - TermKeyTI *ti = info; - - free_trie(ti->root); - - if(ti->start_string) - free(ti->start_string); - - if(ti->stop_string) - free(ti->stop_string); - -#ifdef HAVE_UNIBILIUM - if(ti->unibi) - unibi_destroy(ti->unibi); -#else - if(ti->term) - free(ti->term); -#endif - - free(ti); -} - -#define CHARAT(i) (tk->buffer[tk->buffstart + (i)]) - -static TermKeyResult peekkey(TermKey *tk, void *info, TermKeyKey *key, int force, size_t *nbytep) -{ - TermKeyTI *ti = info; - - if(tk->buffcount == 0) - return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE; - - struct trie_node *p = ti->root; - - unsigned int pos = 0; - while(pos < tk->buffcount) { - p = lookup_next(p, CHARAT(pos)); - if(!p) - break; - - pos++; - - if(p->type != TYPE_KEY) - continue; - - struct trie_node_key *nk = (struct trie_node_key*)p; - if(nk->key.type == TERMKEY_TYPE_MOUSE) { - tk->buffstart += pos; - tk->buffcount -= pos; - - TermKeyResult mouse_result = (*tk->method.peekkey_mouse)(tk, key, nbytep); - - tk->buffstart -= pos; - tk->buffcount += pos; - - if(mouse_result == TERMKEY_RES_KEY) - *nbytep += pos; - - return mouse_result; - } - - key->type = nk->key.type; - key->code.sym = nk->key.sym; - key->modifiers = nk->key.modifier_set; - *nbytep = pos; - return TERMKEY_RES_KEY; - } - - // If p is not NULL then we hadn't walked off the end yet, so we have a - // partial match - if(p && !force) - return TERMKEY_RES_AGAIN; - - return TERMKEY_RES_NONE; -} - -static int insert_seq(TermKeyTI *ti, const char *seq, struct trie_node *node) -{ - int pos = 0; - struct trie_node *p = ti->root; - - // Unsigned because we'll be using it as an array subscript - unsigned char b; - - while((b = seq[pos])) { - struct trie_node *next = lookup_next(p, b); - if(!next) - break; - p = next; - pos++; - } - - while((b = seq[pos])) { - struct trie_node *next; - if(seq[pos+1]) - // Intermediate node - next = new_node_arr(0, 0xff); - else - // Final key node - next = node; - - if(!next) - return 0; - - switch(p->type) { - case TYPE_ARR: - { - struct trie_node_arr *nar = (struct trie_node_arr*)p; - if(b < nar->min || b > nar->max) { - fprintf(stderr, "ASSERT FAIL: Trie insert at 0x%02x is outside of extent bounds (0x%02x..0x%02x)\n", - b, nar->min, nar->max); - abort(); - } - nar->arr[b - nar->min] = next; - p = next; - break; - } - case TYPE_KEY: - fprintf(stderr, "ASSERT FAIL: Tried to insert child node in TYPE_KEY\n"); - abort(); - } - - pos++; - } - - return 1; -} - -struct TermKeyDriver termkey_driver_ti = { - .name = "terminfo", - - .new_driver = new_driver, - .free_driver = free_driver, - - .start_driver = start_driver, - .stop_driver = stop_driver, - - .peekkey = peekkey, -}; diff --git a/src/termkey/termkey-internal.h b/src/termkey/termkey-internal.h deleted file mode 100644 index c300b02616..0000000000 --- a/src/termkey/termkey-internal.h +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef GUARD_TERMKEY_INTERNAL_H_ -#define GUARD_TERMKEY_INTERNAL_H_ - -#define HAVE_TERMIOS - -#ifdef _WIN32 -# undef HAVE_TERMIOS -#endif - -#include "termkey.h" - -#include -#ifdef HAVE_TERMIOS -# include -#endif - -#ifdef _MSC_VER -#include -typedef SSIZE_T ssize_t; -#endif - -struct TermKeyDriver -{ - const char *name; - void *(*new_driver)(TermKey *tk, const char *term); - void (*free_driver)(void *info); - int (*start_driver)(TermKey *tk, void *info); - int (*stop_driver)(TermKey *tk, void *info); - TermKeyResult (*peekkey)(TermKey *tk, void *info, TermKeyKey *key, int force, size_t *nbytes); -}; - -struct keyinfo { - TermKeyType type; - TermKeySym sym; - int modifier_mask; - int modifier_set; -}; - -struct TermKeyDriverNode; -struct TermKeyDriverNode { - struct TermKeyDriver *driver; - void *info; - struct TermKeyDriverNode *next; -}; - -struct TermKey { - int fd; - int flags; - int canonflags; - unsigned char *buffer; - size_t buffstart; // First offset in buffer - size_t buffcount; // NUMBER of entires valid in buffer - size_t buffsize; // Total malloc'ed size - size_t hightide; /* Position beyond buffstart at which peekkey() should next start - * normally 0, but see also termkey_interpret_csi */ - -#ifdef HAVE_TERMIOS - struct termios restore_termios; - char restore_termios_valid; -#endif - - TermKey_Terminfo_Getstr_Hook *ti_getstr_hook; - void *ti_getstr_hook_data; - - int waittime; // msec - - char is_closed; - char is_started; - - int nkeynames; - const char **keynames; - - // There are 32 C0 codes - struct keyinfo c0[32]; - - struct TermKeyDriverNode *drivers; - - // Now some "protected" methods for the driver to call but which we don't - // want exported as real symbols in the library - struct { - void (*emit_codepoint)(TermKey *tk, long codepoint, TermKeyKey *key); - TermKeyResult (*peekkey_simple)(TermKey *tk, TermKeyKey *key, int force, size_t *nbytes); - TermKeyResult (*peekkey_mouse)(TermKey *tk, TermKeyKey *key, size_t *nbytes); - } method; -}; - -static inline void termkey_key_get_linecol(const TermKeyKey *key, int *line, int *col) -{ - if(col) - *col = (unsigned char)key->code.mouse[1] | ((unsigned char)key->code.mouse[3] & 0x0f) << 8; - - if(line) - *line = (unsigned char)key->code.mouse[2] | ((unsigned char)key->code.mouse[3] & 0x70) << 4; -} - -static inline void termkey_key_set_linecol(TermKeyKey *key, int line, int col) -{ - if(line > 0xfff) - line = 0xfff; - - if(col > 0x7ff) - col = 0x7ff; - - key->code.mouse[1] = (line & 0x0ff); - key->code.mouse[2] = (col & 0x0ff); - key->code.mouse[3] = (line & 0xf00) >> 8 | (col & 0x300) >> 4; -} - -extern struct TermKeyDriver termkey_driver_csi; -extern struct TermKeyDriver termkey_driver_ti; - -#endif diff --git a/src/termkey/termkey.c b/src/termkey/termkey.c deleted file mode 100644 index 832e5a9a9e..0000000000 --- a/src/termkey/termkey.c +++ /dev/null @@ -1,1606 +0,0 @@ -#include "termkey.h" -#include "termkey-internal.h" - -#include -#include -#ifndef _WIN32 -# include -# include -# include -#else -# include -#endif -#include - -#include - -#ifdef _MSC_VER -# define strcaseeq(a,b) (_stricmp(a,b) == 0) -#else -# define strcaseeq(a,b) (strcasecmp(a,b) == 0) -#endif - -void termkey_check_version(int major, int minor) -{ - if(major != TERMKEY_VERSION_MAJOR) { - fprintf(stderr, "libtermkey major version mismatch; %d (wants) != %d (library)\n", - major, TERMKEY_VERSION_MAJOR); - exit(1); - } - - if(minor > TERMKEY_VERSION_MINOR) { - fprintf(stderr, "libtermkey minor version mismatch; %d (wants) > %d (library)\n", - minor, TERMKEY_VERSION_MINOR); - exit(1); - } - - // Happy -} - -static struct TermKeyDriver *drivers[] = { - &termkey_driver_ti, - &termkey_driver_csi, - NULL, -}; - -// Forwards for the "protected" methods -// static void eat_bytes(TermKey *tk, size_t count); -static void emit_codepoint(TermKey *tk, long codepoint, TermKeyKey *key); -static TermKeyResult peekkey_simple(TermKey *tk, TermKeyKey *key, int force, size_t *nbytes); -static TermKeyResult peekkey_mouse(TermKey *tk, TermKeyKey *key, size_t *nbytes); - -static TermKeySym register_c0(TermKey *tk, TermKeySym sym, unsigned char ctrl, const char *name); -static TermKeySym register_c0_full(TermKey *tk, TermKeySym sym, int modifier_set, int modifier_mask, unsigned char ctrl, const char *name); - -static struct { - TermKeySym sym; - const char *name; -} keynames[] = { - { TERMKEY_SYM_NONE, "NONE" }, - { TERMKEY_SYM_BACKSPACE, "Backspace" }, - { TERMKEY_SYM_TAB, "Tab" }, - { TERMKEY_SYM_ENTER, "Enter" }, - { TERMKEY_SYM_ESCAPE, "Escape" }, - { TERMKEY_SYM_SPACE, "Space" }, - { TERMKEY_SYM_DEL, "DEL" }, - { TERMKEY_SYM_UP, "Up" }, - { TERMKEY_SYM_DOWN, "Down" }, - { TERMKEY_SYM_LEFT, "Left" }, - { TERMKEY_SYM_RIGHT, "Right" }, - { TERMKEY_SYM_BEGIN, "Begin" }, - { TERMKEY_SYM_FIND, "Find" }, - { TERMKEY_SYM_INSERT, "Insert" }, - { TERMKEY_SYM_DELETE, "Delete" }, - { TERMKEY_SYM_SELECT, "Select" }, - { TERMKEY_SYM_PAGEUP, "PageUp" }, - { TERMKEY_SYM_PAGEDOWN, "PageDown" }, - { TERMKEY_SYM_HOME, "Home" }, - { TERMKEY_SYM_END, "End" }, - { TERMKEY_SYM_CANCEL, "Cancel" }, - { TERMKEY_SYM_CLEAR, "Clear" }, - { TERMKEY_SYM_CLOSE, "Close" }, - { TERMKEY_SYM_COMMAND, "Command" }, - { TERMKEY_SYM_COPY, "Copy" }, - { TERMKEY_SYM_EXIT, "Exit" }, - { TERMKEY_SYM_HELP, "Help" }, - { TERMKEY_SYM_MARK, "Mark" }, - { TERMKEY_SYM_MESSAGE, "Message" }, - { TERMKEY_SYM_MOVE, "Move" }, - { TERMKEY_SYM_OPEN, "Open" }, - { TERMKEY_SYM_OPTIONS, "Options" }, - { TERMKEY_SYM_PRINT, "Print" }, - { TERMKEY_SYM_REDO, "Redo" }, - { TERMKEY_SYM_REFERENCE, "Reference" }, - { TERMKEY_SYM_REFRESH, "Refresh" }, - { TERMKEY_SYM_REPLACE, "Replace" }, - { TERMKEY_SYM_RESTART, "Restart" }, - { TERMKEY_SYM_RESUME, "Resume" }, - { TERMKEY_SYM_SAVE, "Save" }, - { TERMKEY_SYM_SUSPEND, "Suspend" }, - { TERMKEY_SYM_UNDO, "Undo" }, - { TERMKEY_SYM_KP0, "KP0" }, - { TERMKEY_SYM_KP1, "KP1" }, - { TERMKEY_SYM_KP2, "KP2" }, - { TERMKEY_SYM_KP3, "KP3" }, - { TERMKEY_SYM_KP4, "KP4" }, - { TERMKEY_SYM_KP5, "KP5" }, - { TERMKEY_SYM_KP6, "KP6" }, - { TERMKEY_SYM_KP7, "KP7" }, - { TERMKEY_SYM_KP8, "KP8" }, - { TERMKEY_SYM_KP9, "KP9" }, - { TERMKEY_SYM_KPENTER, "KPEnter" }, - { TERMKEY_SYM_KPPLUS, "KPPlus" }, - { TERMKEY_SYM_KPMINUS, "KPMinus" }, - { TERMKEY_SYM_KPMULT, "KPMult" }, - { TERMKEY_SYM_KPDIV, "KPDiv" }, - { TERMKEY_SYM_KPCOMMA, "KPComma" }, - { TERMKEY_SYM_KPPERIOD, "KPPeriod" }, - { TERMKEY_SYM_KPEQUALS, "KPEquals" }, - { 0, NULL }, -}; - -// Mouse event names -static const char *evnames[] = { "Unknown", "Press", "Drag", "Release" }; - -#define CHARAT(i) (tk->buffer[tk->buffstart + (i)]) - -#ifdef DEBUG -/* Some internal debugging functions */ - -static void print_buffer(TermKey *tk) -{ - int i; - for(i = 0; i < tk->buffcount && i < 20; i++) - fprintf(stderr, "%02x ", CHARAT(i)); - if(tk->buffcount > 20) - fprintf(stderr, "..."); -} - -static void print_key(TermKey *tk, TermKeyKey *key) -{ - switch(key->type) { - case TERMKEY_TYPE_UNICODE: - fprintf(stderr, "Unicode codepoint=U+%04lx utf8='%s'", key->code.codepoint, key->utf8); - break; - case TERMKEY_TYPE_FUNCTION: - fprintf(stderr, "Function F%d", key->code.number); - break; - case TERMKEY_TYPE_KEYSYM: - fprintf(stderr, "Keysym sym=%d(%s)", key->code.sym, termkey_get_keyname(tk, key->code.sym)); - break; - case TERMKEY_TYPE_MOUSE: - { - TermKeyMouseEvent ev; - int button, line, col; - termkey_interpret_mouse(tk, key, &ev, &button, &line, &col); - fprintf(stderr, "Mouse ev=%d button=%d pos=(%d,%d)\n", ev, button, line, col); - } - break; - case TERMKEY_TYPE_POSITION: - { - int line, col; - termkey_interpret_position(tk, key, &line, &col); - fprintf(stderr, "Position report pos=(%d,%d)\n", line, col); - } - break; - case TERMKEY_TYPE_MODEREPORT: - { - int initial, mode, value; - termkey_interpret_modereport(tk, key, &initial, &mode, &value); - fprintf(stderr, "Mode report mode=%s %d val=%d\n", initial == '?' ? "DEC" : "ANSI", mode, value); - } - break; - case TERMKEY_TYPE_DCS: - fprintf(stderr, "Device Control String"); - break; - case TERMKEY_TYPE_OSC: - fprintf(stderr, "Operating System Control"); - break; - case TERMKEY_TYPE_UNKNOWN_CSI: - fprintf(stderr, "unknown CSI\n"); - break; - } - - int m = key->modifiers; - fprintf(stderr, " mod=%s%s%s+%02x", - (m & TERMKEY_KEYMOD_CTRL ? "C" : ""), - (m & TERMKEY_KEYMOD_ALT ? "A" : ""), - (m & TERMKEY_KEYMOD_SHIFT ? "S" : ""), - m & ~(TERMKEY_KEYMOD_CTRL|TERMKEY_KEYMOD_ALT|TERMKEY_KEYMOD_SHIFT)); -} - -static const char *res2str(TermKeyResult res) -{ - static char errorbuffer[256]; - - switch(res) { - case TERMKEY_RES_KEY: - return "TERMKEY_RES_KEY"; - case TERMKEY_RES_EOF: - return "TERMKEY_RES_EOF"; - case TERMKEY_RES_AGAIN: - return "TERMKEY_RES_AGAIN"; - case TERMKEY_RES_NONE: - return "TERMKEY_RES_NONE"; - case TERMKEY_RES_ERROR: - snprintf(errorbuffer, sizeof errorbuffer, "TERMKEY_RES_ERROR(errno=%d)\n", errno); - return (const char*)errorbuffer; - } - - return "unknown"; -} -#endif - -/* Similar to snprintf(str, size, "%s", src) except it turns CamelCase into - * space separated values - */ -static int snprint_cameltospaces(char *str, size_t size, const char *src) -{ - int prev_lower = 0; - size_t l = 0; - while(*src && l < size - 1) { - if(isupper(*src) && prev_lower) { - if(str) - str[l++] = ' '; - if(l >= size - 1) - break; - } - prev_lower = islower(*src); - str[l++] = tolower(*src++); - } - str[l] = 0; - /* For consistency with snprintf, return the number of bytes that would have - * been written, excluding '\0' */ - while(*src) { - if(isupper(*src) && prev_lower) { - l++; - } - prev_lower = islower(*src); - src++; l++; - } - return l; -} - -/* Similar to strcmp(str, strcamel, n) except that: - * it compares CamelCase in strcamel with space separated values in str; - * it takes char**s and updates them - * n counts bytes of strcamel, not str - */ -static int strpncmp_camel(const char **strp, const char **strcamelp, size_t n) -{ - const char *str = *strp, *strcamel = *strcamelp; - int prev_lower = 0; - - for( ; (*str || *strcamel) && n; n--) { - char b = tolower(*strcamel); - if(isupper(*strcamel) && prev_lower) { - if(*str != ' ') - break; - str++; - if(*str != b) - break; - } - else - if(*str != b) - break; - - prev_lower = islower(*strcamel); - - str++; - strcamel++; - } - - *strp = str; - *strcamelp = strcamel; - return *str - *strcamel; -} - -static TermKey *termkey_alloc(void) -{ - TermKey *tk = malloc(sizeof(TermKey)); - if(!tk) - return NULL; - - /* Default all the object fields but don't allocate anything */ - - tk->fd = -1; - tk->flags = 0; - tk->canonflags = 0; - - tk->buffer = NULL; - tk->buffstart = 0; - tk->buffcount = 0; - tk->buffsize = 256; /* bytes */ - tk->hightide = 0; - -#ifdef HAVE_TERMIOS - tk->restore_termios_valid = 0; -#endif - - tk->ti_getstr_hook = NULL; - tk->ti_getstr_hook_data = NULL; - - tk->waittime = 50; /* msec */ - - tk->is_closed = 0; - tk->is_started = 0; - - tk->nkeynames = 64; - tk->keynames = NULL; - - for(int i = 0; i < 32; i++) - tk->c0[i].sym = TERMKEY_SYM_NONE; - - tk->drivers = NULL; - - tk->method.emit_codepoint = &emit_codepoint; - tk->method.peekkey_simple = &peekkey_simple; - tk->method.peekkey_mouse = &peekkey_mouse; - - return tk; -} - -static int termkey_init(TermKey *tk, const char *term) -{ - tk->buffer = malloc(tk->buffsize); - if(!tk->buffer) - return 0; - - tk->keynames = malloc(sizeof(tk->keynames[0]) * tk->nkeynames); - if(!tk->keynames) - goto abort_free_buffer; - - int i; - for(i = 0; i < tk->nkeynames; i++) - tk->keynames[i] = NULL; - - for(i = 0; keynames[i].name; i++) - if(termkey_register_keyname(tk, keynames[i].sym, keynames[i].name) == -1) - goto abort_free_keynames; - - register_c0(tk, TERMKEY_SYM_TAB, 0x09, NULL); - register_c0(tk, TERMKEY_SYM_ENTER, 0x0d, NULL); - register_c0(tk, TERMKEY_SYM_ESCAPE, 0x1b, NULL); - - struct TermKeyDriverNode *tail = NULL; - - for(i = 0; drivers[i]; i++) { - void *info = (*drivers[i]->new_driver)(tk, term); - if(!info) - continue; - -#ifdef DEBUG - fprintf(stderr, "Loading the %s driver...\n", drivers[i]->name); -#endif - - struct TermKeyDriverNode *thisdrv = malloc(sizeof(*thisdrv)); - if(!thisdrv) - goto abort_free_drivers; - - thisdrv->driver = drivers[i]; - thisdrv->info = info; - thisdrv->next = NULL; - - if(!tail) - tk->drivers = thisdrv; - else - tail->next = thisdrv; - - tail = thisdrv; - -#ifdef DEBUG - fprintf(stderr, "Loaded %s driver\n", drivers[i]->name); -#endif - } - - if(!tk->drivers) { - errno = ENOENT; - goto abort_free_keynames; - } - - return 1; - -abort_free_drivers: - for(struct TermKeyDriverNode *p = tk->drivers; p; ) { - (*p->driver->free_driver)(p->info); - struct TermKeyDriverNode *next = p->next; - free(p); - p = next; - } - -abort_free_keynames: - free(tk->keynames); - -abort_free_buffer: - free(tk->buffer); - - return 0; -} - -TermKey *termkey_new(int fd, int flags) -{ - TermKey *tk = termkey_alloc(); - if(!tk) - return NULL; - - tk->fd = fd; - - if(!(flags & (TERMKEY_FLAG_RAW|TERMKEY_FLAG_UTF8))) { - char *e; - - /* Most OSes will set .UTF-8. Some will set .utf8. Try to be fairly - * generous in parsing these - */ - if(((e = getenv("LANG")) || (e = getenv("LC_MESSAGES")) || (e = getenv("LC_ALL"))) && - (e = strchr(e, '.')) && e++ && - (strcaseeq(e, "UTF-8") || strcaseeq(e, "UTF8"))) - flags |= TERMKEY_FLAG_UTF8; - else - flags |= TERMKEY_FLAG_RAW; - } - - termkey_set_flags(tk, flags); - - const char *term = getenv("TERM"); - - if(!termkey_init(tk, term)) - goto abort; - - if(!(flags & TERMKEY_FLAG_NOSTART) && !termkey_start(tk)) - goto abort; - - return tk; - -abort: - free(tk); - return NULL; -} - -TermKey *termkey_new_abstract(const char *term, int flags) -{ - TermKey *tk = termkey_alloc(); - if(!tk) - return NULL; - - tk->fd = -1; - - termkey_set_flags(tk, flags); - - if(!termkey_init(tk, term)) { - free(tk); - return NULL; - } - - if(!(flags & TERMKEY_FLAG_NOSTART) && !termkey_start(tk)) - goto abort; - - return tk; - -abort: - free(tk); - return NULL; -} - -void termkey_free(TermKey *tk) -{ - free(tk->buffer); tk->buffer = NULL; - free(tk->keynames); tk->keynames = NULL; - - struct TermKeyDriverNode *p; - for(p = tk->drivers; p; ) { - (*p->driver->free_driver)(p->info); - struct TermKeyDriverNode *next = p->next; - free(p); - p = next; - } - - free(tk); -} - -void termkey_destroy(TermKey *tk) -{ - if(tk->is_started) - termkey_stop(tk); - - termkey_free(tk); -} - -void termkey_hook_terminfo_getstr(TermKey *tk, TermKey_Terminfo_Getstr_Hook *hookfn, void *data) -{ - tk->ti_getstr_hook = hookfn; - tk->ti_getstr_hook_data = data; -} - -int termkey_start(TermKey *tk) -{ - if(tk->is_started) - return 1; - -#ifdef HAVE_TERMIOS - if(tk->fd != -1 && !(tk->flags & TERMKEY_FLAG_NOTERMIOS)) { - struct termios termios; - if(tcgetattr(tk->fd, &termios) == 0) { - tk->restore_termios = termios; - tk->restore_termios_valid = 1; - - termios.c_iflag &= ~(IXON|INLCR|ICRNL); - termios.c_lflag &= ~(ICANON|ECHO -#ifdef IEXTEN - | IEXTEN -#endif - ); - termios.c_cc[VMIN] = 1; - termios.c_cc[VTIME] = 0; - - if(tk->flags & TERMKEY_FLAG_CTRLC) - /* want no signal keys at all, so just disable ISIG */ - termios.c_lflag &= ~ISIG; - else { - /* Disable Ctrl-\==VQUIT and Ctrl-D==VSUSP but leave Ctrl-C as SIGINT */ - termios.c_cc[VQUIT] = _POSIX_VDISABLE; - termios.c_cc[VSUSP] = _POSIX_VDISABLE; - /* Some OSes have Ctrl-Y==VDSUSP */ -#ifdef VDSUSP - termios.c_cc[VDSUSP] = _POSIX_VDISABLE; -#endif - } - -#ifdef DEBUG - fprintf(stderr, "Setting termios(3) flags\n"); -#endif - tcsetattr(tk->fd, TCSANOW, &termios); - } - } -#endif - - struct TermKeyDriverNode *p; - for(p = tk->drivers; p; p = p->next) - if(p->driver->start_driver) - if(!(*p->driver->start_driver)(tk, p->info)) - return 0; - -#ifdef DEBUG - fprintf(stderr, "Drivers started; termkey instance %p is ready\n", tk); -#endif - - tk->is_started = 1; - return 1; -} - -int termkey_stop(TermKey *tk) -{ - if(!tk->is_started) - return 1; - - struct TermKeyDriverNode *p; - for(p = tk->drivers; p; p = p->next) - if(p->driver->stop_driver) - (*p->driver->stop_driver)(tk, p->info); - -#ifdef HAVE_TERMIOS - if(tk->restore_termios_valid) - tcsetattr(tk->fd, TCSANOW, &tk->restore_termios); -#endif - - tk->is_started = 0; - - return 1; -} - -int termkey_is_started(TermKey *tk) -{ - return tk->is_started; -} - -int termkey_get_fd(TermKey *tk) -{ - return tk->fd; -} - -int termkey_get_flags(TermKey *tk) -{ - return tk->flags; -} - -void termkey_set_flags(TermKey *tk, int newflags) -{ - tk->flags = newflags; - - if(tk->flags & TERMKEY_FLAG_SPACESYMBOL) - tk->canonflags |= TERMKEY_CANON_SPACESYMBOL; - else - tk->canonflags &= ~TERMKEY_CANON_SPACESYMBOL; -} - -void termkey_set_waittime(TermKey *tk, int msec) -{ - tk->waittime = msec; -} - -int termkey_get_waittime(TermKey *tk) -{ - return tk->waittime; -} - -int termkey_get_canonflags(TermKey *tk) -{ - return tk->canonflags; -} - -void termkey_set_canonflags(TermKey *tk, int flags) -{ - tk->canonflags = flags; - - if(tk->canonflags & TERMKEY_CANON_SPACESYMBOL) - tk->flags |= TERMKEY_FLAG_SPACESYMBOL; - else - tk->flags &= ~TERMKEY_FLAG_SPACESYMBOL; -} - -size_t termkey_get_buffer_size(TermKey *tk) -{ - return tk->buffsize; -} - -int termkey_set_buffer_size(TermKey *tk, size_t size) -{ - unsigned char *buffer = realloc(tk->buffer, size); - if(!buffer) - return 0; - - tk->buffer = buffer; - tk->buffsize = size; - - return 1; -} - -size_t termkey_get_buffer_remaining(TermKey *tk) -{ - /* Return the total number of free bytes in the buffer, because that's what - * is available to the user. */ - return tk->buffsize - tk->buffcount; -} - -static void eat_bytes(TermKey *tk, size_t count) -{ - if(count >= tk->buffcount) { - tk->buffstart = 0; - tk->buffcount = 0; - return; - } - - tk->buffstart += count; - tk->buffcount -= count; -} - -static inline unsigned int utf8_seqlen(long codepoint) -{ - if(codepoint < 0x0000080) return 1; - if(codepoint < 0x0000800) return 2; - if(codepoint < 0x0010000) return 3; - if(codepoint < 0x0200000) return 4; - if(codepoint < 0x4000000) return 5; - return 6; -} - -static void fill_utf8(TermKeyKey *key) -{ - long codepoint = key->code.codepoint; - int nbytes = utf8_seqlen(codepoint); - - key->utf8[nbytes] = 0; - - // This is easier done backwards - int b = nbytes; - while(b > 1) { - b--; - key->utf8[b] = 0x80 | (codepoint & 0x3f); - codepoint >>= 6; - } - - switch(nbytes) { - case 1: key->utf8[0] = (codepoint & 0x7f); break; - case 2: key->utf8[0] = 0xc0 | (codepoint & 0x1f); break; - case 3: key->utf8[0] = 0xe0 | (codepoint & 0x0f); break; - case 4: key->utf8[0] = 0xf0 | (codepoint & 0x07); break; - case 5: key->utf8[0] = 0xf8 | (codepoint & 0x03); break; - case 6: key->utf8[0] = 0xfc | (codepoint & 0x01); break; - } -} - -#define UTF8_INVALID 0xFFFD -static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, long *cp, size_t *nbytep) -{ - unsigned int nbytes; - - unsigned char b0 = bytes[0]; - - if(b0 < 0x80) { - // Single byte ASCII - *cp = b0; - *nbytep = 1; - return TERMKEY_RES_KEY; - } - else if(b0 < 0xc0) { - // Starts with a continuation byte - that's not right - *cp = UTF8_INVALID; - *nbytep = 1; - return TERMKEY_RES_KEY; - } - else if(b0 < 0xe0) { - nbytes = 2; - *cp = b0 & 0x1f; - } - else if(b0 < 0xf0) { - nbytes = 3; - *cp = b0 & 0x0f; - } - else if(b0 < 0xf8) { - nbytes = 4; - *cp = b0 & 0x07; - } - else if(b0 < 0xfc) { - nbytes = 5; - *cp = b0 & 0x03; - } - else if(b0 < 0xfe) { - nbytes = 6; - *cp = b0 & 0x01; - } - else { - *cp = UTF8_INVALID; - *nbytep = 1; - return TERMKEY_RES_KEY; - } - - for(unsigned int b = 1; b < nbytes; b++) { - unsigned char cb; - - if(b >= len) - return TERMKEY_RES_AGAIN; - - cb = bytes[b]; - if(cb < 0x80 || cb >= 0xc0) { - *cp = UTF8_INVALID; - *nbytep = b; - return TERMKEY_RES_KEY; - } - - *cp <<= 6; - *cp |= cb & 0x3f; - } - - // Check for overlong sequences - if(nbytes > utf8_seqlen(*cp)) - *cp = UTF8_INVALID; - - // Check for UTF-16 surrogates or invalid *cps - if((*cp >= 0xD800 && *cp <= 0xDFFF) || - *cp == 0xFFFE || - *cp == 0xFFFF) - *cp = UTF8_INVALID; - - *nbytep = nbytes; - return TERMKEY_RES_KEY; -} - -static void emit_codepoint(TermKey *tk, long codepoint, TermKeyKey *key) -{ - if(codepoint == 0) { - // ASCII NUL = Ctrl-Space - key->type = TERMKEY_TYPE_KEYSYM; - key->code.sym = TERMKEY_SYM_SPACE; - key->modifiers = TERMKEY_KEYMOD_CTRL; - } - else if(codepoint < 0x20) { - // C0 range - key->code.codepoint = 0; - key->modifiers = 0; - - if(!(tk->flags & TERMKEY_FLAG_NOINTERPRET) && tk->c0[codepoint].sym != TERMKEY_SYM_UNKNOWN) { - key->code.sym = tk->c0[codepoint].sym; - key->modifiers |= tk->c0[codepoint].modifier_set; - } - - if(!key->code.sym) { - key->type = TERMKEY_TYPE_UNICODE; - /* Generically modified Unicode ought not report the SHIFT state, or else - * we get into complications trying to report Shift-; vs : and so on... - * In order to be able to represent Ctrl-Shift-A as CTRL modified - * unicode A, we need to call Ctrl-A simply 'a', lowercase - */ - if(codepoint+0x40 >= 'A' && codepoint+0x40 <= 'Z') - // it's a letter - use lowercase instead - key->code.codepoint = codepoint + 0x60; - else - key->code.codepoint = codepoint + 0x40; - key->modifiers = TERMKEY_KEYMOD_CTRL; - } - else { - key->type = TERMKEY_TYPE_KEYSYM; - } - } - else if(codepoint == 0x7f && !(tk->flags & TERMKEY_FLAG_NOINTERPRET)) { - // ASCII DEL - key->type = TERMKEY_TYPE_KEYSYM; - key->code.sym = TERMKEY_SYM_DEL; - key->modifiers = 0; - } - else if(codepoint >= 0x20 && codepoint < 0x80) { - // ASCII lowbyte range - key->type = TERMKEY_TYPE_UNICODE; - key->code.codepoint = codepoint; - key->modifiers = 0; - } - else if(codepoint >= 0x80 && codepoint < 0xa0) { - // UTF-8 never starts with a C1 byte. So we can be sure of these - key->type = TERMKEY_TYPE_UNICODE; - key->code.codepoint = codepoint - 0x40; - key->modifiers = TERMKEY_KEYMOD_CTRL|TERMKEY_KEYMOD_ALT; - } - else { - // UTF-8 codepoint - key->type = TERMKEY_TYPE_UNICODE; - key->code.codepoint = codepoint; - key->modifiers = 0; - } - - termkey_canonicalise(tk, key); - - if(key->type == TERMKEY_TYPE_UNICODE) - fill_utf8(key); -} - -void termkey_canonicalise(TermKey *tk, TermKeyKey *key) -{ - int flags = tk->canonflags; - - if(flags & TERMKEY_CANON_SPACESYMBOL) { - if(key->type == TERMKEY_TYPE_UNICODE && key->code.codepoint == 0x20) { - key->type = TERMKEY_TYPE_KEYSYM; - key->code.sym = TERMKEY_SYM_SPACE; - } - } - else { - if(key->type == TERMKEY_TYPE_KEYSYM && key->code.sym == TERMKEY_SYM_SPACE) { - key->type = TERMKEY_TYPE_UNICODE; - key->code.codepoint = 0x20; - fill_utf8(key); - } - } - - if(flags & TERMKEY_CANON_DELBS) { - if(key->type == TERMKEY_TYPE_KEYSYM && key->code.sym == TERMKEY_SYM_DEL) { - key->code.sym = TERMKEY_SYM_BACKSPACE; - } - } -} - -static TermKeyResult peekkey(TermKey *tk, TermKeyKey *key, int force, size_t *nbytep) -{ - int again = 0; - - if(!tk->is_started) { - errno = EINVAL; - return TERMKEY_RES_ERROR; - } - -#ifdef DEBUG - fprintf(stderr, "getkey(force=%d): buffer ", force); - print_buffer(tk); - fprintf(stderr, "\n"); -#endif - - if(tk->hightide) { - tk->buffstart += tk->hightide; - tk->buffcount -= tk->hightide; - tk->hightide = 0; - } - - TermKeyResult ret; - struct TermKeyDriverNode *p; - for(p = tk->drivers; p; p = p->next) { - ret = (p->driver->peekkey)(tk, p->info, key, force, nbytep); - -#ifdef DEBUG - fprintf(stderr, "Driver %s yields %s\n", p->driver->name, res2str(ret)); -#endif - - switch(ret) { - case TERMKEY_RES_KEY: -#ifdef DEBUG - print_key(tk, key); fprintf(stderr, "\n"); -#endif - // Slide the data down to stop it running away - { - size_t halfsize = tk->buffsize / 2; - - if(tk->buffstart > halfsize) { - memcpy(tk->buffer, tk->buffer + halfsize, halfsize); - tk->buffstart -= halfsize; - } - } - - /* fallthrough */ - case TERMKEY_RES_EOF: - case TERMKEY_RES_ERROR: - return ret; - - case TERMKEY_RES_AGAIN: - if(!force) - again = 1; - - /* fallthrough */ - case TERMKEY_RES_NONE: - break; - } - } - - if(again) - return TERMKEY_RES_AGAIN; - - ret = peekkey_simple(tk, key, force, nbytep); - -#ifdef DEBUG - fprintf(stderr, "getkey_simple(force=%d) yields %s\n", force, res2str(ret)); - if(ret == TERMKEY_RES_KEY) { - print_key(tk, key); fprintf(stderr, "\n"); - } -#endif - - return ret; -} - -static TermKeyResult peekkey_simple(TermKey *tk, TermKeyKey *key, int force, size_t *nbytep) -{ - if(tk->buffcount == 0) - return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE; - - unsigned char b0 = CHARAT(0); - - if(b0 == 0x1b) { - // Escape-prefixed value? Might therefore be Alt+key - if(tk->buffcount == 1) { - // This might be an press, or it may want to be part of a longer - // sequence - if(!force) - return TERMKEY_RES_AGAIN; - - (*tk->method.emit_codepoint)(tk, b0, key); - *nbytep = 1; - return TERMKEY_RES_KEY; - } - - // Try another key there - tk->buffstart++; - tk->buffcount--; - - // Run the full driver - TermKeyResult metakey_result = peekkey(tk, key, force, nbytep); - - tk->buffstart--; - tk->buffcount++; - - switch(metakey_result) { - case TERMKEY_RES_KEY: - key->modifiers |= TERMKEY_KEYMOD_ALT; - (*nbytep)++; - break; - - case TERMKEY_RES_NONE: - case TERMKEY_RES_EOF: - case TERMKEY_RES_AGAIN: - case TERMKEY_RES_ERROR: - break; - } - - return metakey_result; - } - else if(b0 < 0xa0) { - // Single byte C0, G0 or C1 - C1 is never UTF-8 initial byte - (*tk->method.emit_codepoint)(tk, b0, key); - *nbytep = 1; - return TERMKEY_RES_KEY; - } - else if(tk->flags & TERMKEY_FLAG_UTF8) { - // Some UTF-8 - long codepoint; - TermKeyResult res = parse_utf8(tk->buffer + tk->buffstart, tk->buffcount, &codepoint, nbytep); - - if(res == TERMKEY_RES_AGAIN && force) { - /* There weren't enough bytes for a complete UTF-8 sequence but caller - * demands an answer. About the best thing we can do here is eat as many - * bytes as we have, and emit a UTF8_INVALID. If the remaining bytes - * arrive later, they'll be invalid too. - */ - codepoint = UTF8_INVALID; - *nbytep = tk->buffcount; - res = TERMKEY_RES_KEY; - } - - key->type = TERMKEY_TYPE_UNICODE; - key->modifiers = 0; - (*tk->method.emit_codepoint)(tk, codepoint, key); - return res; - } - else { - // Non UTF-8 case - just report the raw byte - key->type = TERMKEY_TYPE_UNICODE; - key->code.codepoint = b0; - key->modifiers = 0; - - key->utf8[0] = key->code.codepoint; - key->utf8[1] = 0; - - *nbytep = 1; - - return TERMKEY_RES_KEY; - } -} - -static TermKeyResult peekkey_mouse(TermKey *tk, TermKeyKey *key, size_t *nbytep) -{ - if(tk->buffcount < 3) - return TERMKEY_RES_AGAIN; - - key->type = TERMKEY_TYPE_MOUSE; - key->code.mouse[0] = CHARAT(0) - 0x20; - key->code.mouse[1] = CHARAT(1) - 0x20; - key->code.mouse[2] = CHARAT(2) - 0x20; - key->code.mouse[3] = 0; - - key->modifiers = (key->code.mouse[0] & 0x1c) >> 2; - key->code.mouse[0] &= ~0x1c; - - *nbytep = 3; - return TERMKEY_RES_KEY; -} - -TermKeyResult termkey_getkey(TermKey *tk, TermKeyKey *key) -{ - size_t nbytes = 0; - TermKeyResult ret = peekkey(tk, key, 0, &nbytes); - - if(ret == TERMKEY_RES_KEY) - eat_bytes(tk, nbytes); - - if(ret == TERMKEY_RES_AGAIN) - /* Call peekkey() again in force mode to obtain whatever it can */ - (void)peekkey(tk, key, 1, &nbytes); - /* Don't eat it yet though */ - - return ret; -} - -TermKeyResult termkey_getkey_force(TermKey *tk, TermKeyKey *key) -{ - size_t nbytes = 0; - TermKeyResult ret = peekkey(tk, key, 1, &nbytes); - - if(ret == TERMKEY_RES_KEY) - eat_bytes(tk, nbytes); - - return ret; -} - -#ifndef _WIN32 -TermKeyResult termkey_waitkey(TermKey *tk, TermKeyKey *key) -{ - if(tk->fd == -1) { - errno = EBADF; - return TERMKEY_RES_ERROR; - } - - while(1) { - TermKeyResult ret = termkey_getkey(tk, key); - - switch(ret) { - case TERMKEY_RES_KEY: - case TERMKEY_RES_EOF: - case TERMKEY_RES_ERROR: - return ret; - - case TERMKEY_RES_NONE: - ret = termkey_advisereadable(tk); - if(ret == TERMKEY_RES_ERROR) - return ret; - break; - - case TERMKEY_RES_AGAIN: - { - if(tk->is_closed) - // We're closed now. Never going to get more bytes so just go with - // what we have - return termkey_getkey_force(tk, key); - - struct pollfd fd; - -retry: - fd.fd = tk->fd; - fd.events = POLLIN; - - int pollret = poll(&fd, 1, tk->waittime); - if(pollret == -1) { - if(errno == EINTR && !(tk->flags & TERMKEY_FLAG_EINTR)) - goto retry; - - return TERMKEY_RES_ERROR; - } - - if(fd.revents & (POLLIN|POLLHUP|POLLERR)) - ret = termkey_advisereadable(tk); - else - ret = TERMKEY_RES_NONE; - - if(ret == TERMKEY_RES_ERROR) - return ret; - if(ret == TERMKEY_RES_NONE) - return termkey_getkey_force(tk, key); - } - break; - } - } - - /* UNREACHABLE */ -} -#endif - -TermKeyResult termkey_advisereadable(TermKey *tk) -{ - ssize_t len; - - if(tk->fd == -1) { - errno = EBADF; - return TERMKEY_RES_ERROR; - } - - if(tk->buffstart) { - memmove(tk->buffer, tk->buffer + tk->buffstart, tk->buffcount); - tk->buffstart = 0; - } - - /* Not expecting it ever to be greater but doesn't hurt to handle that */ - if(tk->buffcount >= tk->buffsize) { - errno = ENOMEM; - return TERMKEY_RES_ERROR; - } - -retry: - len = read(tk->fd, tk->buffer + tk->buffcount, tk->buffsize - tk->buffcount); - - if(len == -1) { - if(errno == EAGAIN) - return TERMKEY_RES_NONE; - else if(errno == EINTR && !(tk->flags & TERMKEY_FLAG_EINTR)) - goto retry; - else - return TERMKEY_RES_ERROR; - } - else if(len < 1) { - tk->is_closed = 1; - return TERMKEY_RES_NONE; - } - else { - tk->buffcount += len; - return TERMKEY_RES_AGAIN; - } -} - -size_t termkey_push_bytes(TermKey *tk, const char *bytes, size_t len) -{ - if(tk->buffstart) { - memmove(tk->buffer, tk->buffer + tk->buffstart, tk->buffcount); - tk->buffstart = 0; - } - - /* Not expecting it ever to be greater but doesn't hurt to handle that */ - if(tk->buffcount >= tk->buffsize) { - errno = ENOMEM; - return (size_t)-1; - } - - if(len > tk->buffsize - tk->buffcount) - len = tk->buffsize - tk->buffcount; - - // memcpy(), not strncpy() in case of null bytes in input - memcpy(tk->buffer + tk->buffcount, bytes, len); - tk->buffcount += len; - - return len; -} - -TermKeySym termkey_register_keyname(TermKey *tk, TermKeySym sym, const char *name) -{ - if(!sym) - sym = tk->nkeynames; - - if(sym >= tk->nkeynames) { - const char **new_keynames = realloc(tk->keynames, sizeof(new_keynames[0]) * (sym + 1)); - if(!new_keynames) - return -1; - - tk->keynames = new_keynames; - - // Fill in the hole - for(int i = tk->nkeynames; i < sym; i++) - tk->keynames[i] = NULL; - - tk->nkeynames = sym + 1; - } - - tk->keynames[sym] = name; - - return sym; -} - -const char *termkey_get_keyname(TermKey *tk, TermKeySym sym) -{ - if(sym == TERMKEY_SYM_UNKNOWN) - return "UNKNOWN"; - - if(sym < tk->nkeynames) - return tk->keynames[sym]; - - return "UNKNOWN"; -} - -static const char *termkey_lookup_keyname_format(TermKey *tk, const char *str, TermKeySym *sym, TermKeyFormat format) -{ - /* We store an array, so we can't do better than a linear search. Doesn't - * matter because user won't be calling this too often */ - - for(*sym = 0; *sym < tk->nkeynames; (*sym)++) { - const char *thiskey = tk->keynames[*sym]; - if(!thiskey) - continue; - size_t len = strlen(thiskey); - if(format & TERMKEY_FORMAT_LOWERSPACE) { - const char *thisstr = str; - if(strpncmp_camel(&thisstr, &thiskey, len) == 0) - return thisstr; - } - else { - if(strncmp(str, thiskey, len) == 0) - return (char *)str + len; - } - } - - return NULL; -} - -const char *termkey_lookup_keyname(TermKey *tk, const char *str, TermKeySym *sym) -{ - return termkey_lookup_keyname_format(tk, str, sym, 0); -} - -TermKeySym termkey_keyname2sym(TermKey *tk, const char *keyname) -{ - TermKeySym sym; - const char *endp = termkey_lookup_keyname(tk, keyname, &sym); - if(!endp || endp[0]) - return TERMKEY_SYM_UNKNOWN; - return sym; -} - -static TermKeySym register_c0(TermKey *tk, TermKeySym sym, unsigned char ctrl, const char *name) -{ - return register_c0_full(tk, sym, 0, 0, ctrl, name); -} - -static TermKeySym register_c0_full(TermKey *tk, TermKeySym sym, int modifier_set, int modifier_mask, unsigned char ctrl, const char *name) -{ - if(ctrl >= 0x20) { - errno = EINVAL; - return -1; - } - - if(name) - sym = termkey_register_keyname(tk, sym, name); - - tk->c0[ctrl].sym = sym; - tk->c0[ctrl].modifier_set = modifier_set; - tk->c0[ctrl].modifier_mask = modifier_mask; - - return sym; -} - -/* Previous name for this function - * No longer declared in termkey.h but it remains in the compiled library for - * backward-compatibility reasons. - */ -size_t termkey_snprint_key(TermKey *tk, char *buffer, size_t len, TermKeyKey *key, TermKeyFormat format) -{ - return termkey_strfkey(tk, buffer, len, key, format); -} - -static struct modnames { - const char *shift, *alt, *ctrl; -} -modnames[] = { - { "S", "A", "C" }, // 0 - { "Shift", "Alt", "Ctrl" }, // LONGMOD - { "S", "M", "C" }, // ALTISMETA - { "Shift", "Meta", "Ctrl" }, // ALTISMETA+LONGMOD - { "s", "a", "c" }, // LOWERMOD - { "shift", "alt", "ctrl" }, // LOWERMOD+LONGMOD - { "s", "m", "c" }, // LOWERMOD+ALTISMETA - { "shift", "meta", "ctrl" }, // LOWERMOD+ALTISMETA+LONGMOD -}; - -size_t termkey_strfkey(TermKey *tk, char *buffer, size_t len, TermKeyKey *key, TermKeyFormat format) -{ - size_t pos = 0; - size_t l = 0; - - struct modnames *mods = &modnames[!!(format & TERMKEY_FORMAT_LONGMOD) + - !!(format & TERMKEY_FORMAT_ALTISMETA) * 2 + - !!(format & TERMKEY_FORMAT_LOWERMOD) * 4]; - - int wrapbracket = (format & TERMKEY_FORMAT_WRAPBRACKET) && - (key->type != TERMKEY_TYPE_UNICODE || key->modifiers != 0); - - char sep = (format & TERMKEY_FORMAT_SPACEMOD) ? ' ' : '-'; - - if(format & TERMKEY_FORMAT_CARETCTRL && - key->type == TERMKEY_TYPE_UNICODE && - key->modifiers == TERMKEY_KEYMOD_CTRL) { - long codepoint = key->code.codepoint; - - // Handle some of the special cases first - if(codepoint >= 'a' && codepoint <= 'z') { - l = snprintf(buffer + pos, len - pos, wrapbracket ? "<^%c>" : "^%c", (char)codepoint - 0x20); - if(l <= 0) return pos; - pos += l; - return pos; - } - else if((codepoint >= '@' && codepoint < 'A') || - (codepoint > 'Z' && codepoint <= '_')) { - l = snprintf(buffer + pos, len - pos, wrapbracket ? "<^%c>" : "^%c", (char)codepoint); - if(l <= 0) return pos; - pos += l; - return pos; - } - } - - if(wrapbracket) { - l = snprintf(buffer + pos, len - pos, "<"); - if(l <= 0) return pos; - pos += l; - } - - if(key->modifiers & TERMKEY_KEYMOD_ALT) { - l = snprintf(buffer + pos, len - pos, "%s%c", mods->alt, sep); - if(l <= 0) return pos; - pos += l; - } - - if(key->modifiers & TERMKEY_KEYMOD_CTRL) { - l = snprintf(buffer + pos, len - pos, "%s%c", mods->ctrl, sep); - if(l <= 0) return pos; - pos += l; - } - - if(key->modifiers & TERMKEY_KEYMOD_SHIFT) { - l = snprintf(buffer + pos, len - pos, "%s%c", mods->shift, sep); - if(l <= 0) return pos; - pos += l; - } - - switch(key->type) { - case TERMKEY_TYPE_UNICODE: - if(!key->utf8[0]) // In case of user-supplied key structures - fill_utf8(key); - l = snprintf(buffer + pos, len - pos, "%s", key->utf8); - break; - case TERMKEY_TYPE_KEYSYM: - { - const char *name = termkey_get_keyname(tk, key->code.sym); - if(format & TERMKEY_FORMAT_LOWERSPACE) - l = snprint_cameltospaces(buffer + pos, len - pos, name); - else - l = snprintf(buffer + pos, len - pos, "%s", name); - } - break; - case TERMKEY_TYPE_FUNCTION: - l = snprintf(buffer + pos, len - pos, "%c%d", - (format & TERMKEY_FORMAT_LOWERSPACE ? 'f' : 'F'), key->code.number); - break; - case TERMKEY_TYPE_MOUSE: - { - TermKeyMouseEvent ev; - int button; - int line, col; - termkey_interpret_mouse(tk, key, &ev, &button, &line, &col); - - l = snprintf(buffer + pos, len - pos, "Mouse%s(%d)", - evnames[ev], button); - - if(format & TERMKEY_FORMAT_MOUSE_POS) { - if(l <= 0) return pos; - pos += l; - - l = snprintf(buffer + pos, len - pos, " @ (%u,%u)", col, line); - } - } - break; - case TERMKEY_TYPE_POSITION: - l = snprintf(buffer + pos, len - pos, "Position"); - break; - case TERMKEY_TYPE_MODEREPORT: - { - int initial, mode, value; - termkey_interpret_modereport(tk, key, &initial, &mode, &value); - if(initial) - l = snprintf(buffer + pos, len - pos, "Mode(%c%d=%d)", initial, mode, value); - else - l = snprintf(buffer + pos, len - pos, "Mode(%d=%d)", mode, value); - } - case TERMKEY_TYPE_DCS: - l = snprintf(buffer + pos, len - pos, "DCS"); - break; - case TERMKEY_TYPE_OSC: - l = snprintf(buffer + pos, len - pos, "OSC"); - break; - case TERMKEY_TYPE_UNKNOWN_CSI: - l = snprintf(buffer + pos, len - pos, "CSI %c", key->code.number & 0xff); - break; - } - - if(l <= 0) return pos; - pos += l; - - if(wrapbracket) { - l = snprintf(buffer + pos, len - pos, ">"); - if(l <= 0) return pos; - pos += l; - } - - return pos; -} - -const char *termkey_strpkey(TermKey *tk, const char *str, TermKeyKey *key, TermKeyFormat format) -{ - struct modnames *mods = &modnames[!!(format & TERMKEY_FORMAT_LONGMOD) + - !!(format & TERMKEY_FORMAT_ALTISMETA) * 2 + - !!(format & TERMKEY_FORMAT_LOWERMOD) * 4]; - - key->modifiers = 0; - - if((format & TERMKEY_FORMAT_CARETCTRL) && str[0] == '^' && str[1]) { - str = termkey_strpkey(tk, str+1, key, format & ~TERMKEY_FORMAT_CARETCTRL); - - if(!str || - key->type != TERMKEY_TYPE_UNICODE || - key->code.codepoint < '@' || key->code.codepoint > '_' || - key->modifiers != 0) - return NULL; - - if(key->code.codepoint >= 'A' && key->code.codepoint <= 'Z') - key->code.codepoint += 0x20; - key->modifiers = TERMKEY_KEYMOD_CTRL; - fill_utf8(key); - return (char *)str; - } - - const char *sep_at; - - while((sep_at = strchr(str, (format & TERMKEY_FORMAT_SPACEMOD) ? ' ' : '-'))) { - size_t n = sep_at - str; - - if(n == strlen(mods->alt) && strncmp(mods->alt, str, n) == 0) - key->modifiers |= TERMKEY_KEYMOD_ALT; - else if(n == strlen(mods->ctrl) && strncmp(mods->ctrl, str, n) == 0) - key->modifiers |= TERMKEY_KEYMOD_CTRL; - else if(n == strlen(mods->shift) && strncmp(mods->shift, str, n) == 0) - key->modifiers |= TERMKEY_KEYMOD_SHIFT; - - else - break; - - str = sep_at + 1; - } - - size_t nbytes; - ssize_t snbytes; - const char *endstr; - int button; - char event_name[32]; - - if((endstr = termkey_lookup_keyname_format(tk, str, &key->code.sym, format))) { - key->type = TERMKEY_TYPE_KEYSYM; - str = endstr; - } - else if(sscanf(str, "F%d%zn", &key->code.number, &snbytes) == 1) { - key->type = TERMKEY_TYPE_FUNCTION; - str += snbytes; - } - else if(sscanf(str, "Mouse%31[^(](%d)%zn", event_name, &button, &snbytes) == 2) { - str += snbytes; - key->type = TERMKEY_TYPE_MOUSE; - - TermKeyMouseEvent ev = TERMKEY_MOUSE_UNKNOWN; - for(size_t i = 0; i < sizeof(evnames)/sizeof(evnames[0]); i++) { - if(strcmp(evnames[i], event_name) == 0) { - ev = TERMKEY_MOUSE_UNKNOWN + i; - break; - } - } - - int code; - switch(ev) { - case TERMKEY_MOUSE_PRESS: - case TERMKEY_MOUSE_DRAG: - code = button - 1; - if(ev == TERMKEY_MOUSE_DRAG) { - code |= 0x20; - } - break; - case TERMKEY_MOUSE_RELEASE: - code = 3; - break; - default: - code = 128; - break; - } - key->code.mouse[0] = code; - - unsigned int line = 0, col = 0; - if((format & TERMKEY_FORMAT_MOUSE_POS) && sscanf(str, " @ (%u,%u)%zn", &col, &line, &snbytes) == 2) { - str += snbytes; - } - termkey_key_set_linecol(key, col, line); - } - // Unicode must be last - else if(parse_utf8((unsigned const char *)str, strlen(str), &key->code.codepoint, &nbytes) == TERMKEY_RES_KEY) { - key->type = TERMKEY_TYPE_UNICODE; - fill_utf8(key); - str += nbytes; - } - else - return NULL; - - termkey_canonicalise(tk, key); - - return (char *)str; -} - -int termkey_keycmp(TermKey *tk, const TermKeyKey *key1p, const TermKeyKey *key2p) -{ - /* Copy the key structs since we'll be modifying them */ - TermKeyKey key1 = *key1p, key2 = *key2p; - - termkey_canonicalise(tk, &key1); - termkey_canonicalise(tk, &key2); - - if(key1.type != key2.type) - return key1.type - key2.type; - - switch(key1.type) { - case TERMKEY_TYPE_UNICODE: - if(key1.code.codepoint != key2.code.codepoint) - return key1.code.codepoint - key2.code.codepoint; - break; - case TERMKEY_TYPE_KEYSYM: - if(key1.code.sym != key2.code.sym) - return key1.code.sym - key2.code.sym; - break; - case TERMKEY_TYPE_FUNCTION: - case TERMKEY_TYPE_UNKNOWN_CSI: - if(key1.code.number != key2.code.number) - return key1.code.number - key2.code.number; - break; - case TERMKEY_TYPE_MOUSE: - { - int cmp = strncmp(key1.code.mouse, key2.code.mouse, 4); - if(cmp != 0) - return cmp; - } - break; - case TERMKEY_TYPE_POSITION: - { - int line1, col1, line2, col2; - termkey_interpret_position(tk, &key1, &line1, &col1); - termkey_interpret_position(tk, &key2, &line2, &col2); - if(line1 != line2) - return line1 - line2; - return col1 - col2; - } - break; - case TERMKEY_TYPE_DCS: - case TERMKEY_TYPE_OSC: - return key1p - key2p; - case TERMKEY_TYPE_MODEREPORT: - { - int initial1, initial2, mode1, mode2, value1, value2; - termkey_interpret_modereport(tk, &key1, &initial1, &mode1, &value1); - termkey_interpret_modereport(tk, &key2, &initial2, &mode2, &value2); - if(initial1 != initial2) - return initial1 - initial2; - if(mode1 != mode2) - return mode1 - mode2; - return value1 - value2; - } - } - - return key1.modifiers - key2.modifiers; -} diff --git a/src/termkey/termkey.h b/src/termkey/termkey.h deleted file mode 100644 index 94405f6516..0000000000 --- a/src/termkey/termkey.h +++ /dev/null @@ -1,256 +0,0 @@ -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef GUARD_TERMKEY_H_ -#define GUARD_TERMKEY_H_ - -#include -#include - -#define TERMKEY_VERSION_MAJOR 0 -#define TERMKEY_VERSION_MINOR 22 - -#define TERMKEY_CHECK_VERSION \ - termkey_check_version(TERMKEY_VERSION_MAJOR, TERMKEY_VERSION_MINOR) - -typedef enum { - TERMKEY_SYM_UNKNOWN = -1, - TERMKEY_SYM_NONE = 0, - - /* Special names in C0 */ - TERMKEY_SYM_BACKSPACE, - TERMKEY_SYM_TAB, - TERMKEY_SYM_ENTER, - TERMKEY_SYM_ESCAPE, - - /* Special names in G0 */ - TERMKEY_SYM_SPACE, - TERMKEY_SYM_DEL, - - /* Special keys */ - TERMKEY_SYM_UP, - TERMKEY_SYM_DOWN, - TERMKEY_SYM_LEFT, - TERMKEY_SYM_RIGHT, - TERMKEY_SYM_BEGIN, - TERMKEY_SYM_FIND, - TERMKEY_SYM_INSERT, - TERMKEY_SYM_DELETE, - TERMKEY_SYM_SELECT, - TERMKEY_SYM_PAGEUP, - TERMKEY_SYM_PAGEDOWN, - TERMKEY_SYM_HOME, - TERMKEY_SYM_END, - - /* Special keys from terminfo */ - TERMKEY_SYM_CANCEL, - TERMKEY_SYM_CLEAR, - TERMKEY_SYM_CLOSE, - TERMKEY_SYM_COMMAND, - TERMKEY_SYM_COPY, - TERMKEY_SYM_EXIT, - TERMKEY_SYM_HELP, - TERMKEY_SYM_MARK, - TERMKEY_SYM_MESSAGE, - TERMKEY_SYM_MOVE, - TERMKEY_SYM_OPEN, - TERMKEY_SYM_OPTIONS, - TERMKEY_SYM_PRINT, - TERMKEY_SYM_REDO, - TERMKEY_SYM_REFERENCE, - TERMKEY_SYM_REFRESH, - TERMKEY_SYM_REPLACE, - TERMKEY_SYM_RESTART, - TERMKEY_SYM_RESUME, - TERMKEY_SYM_SAVE, - TERMKEY_SYM_SUSPEND, - TERMKEY_SYM_UNDO, - - /* Numeric keypad special keys */ - TERMKEY_SYM_KP0, - TERMKEY_SYM_KP1, - TERMKEY_SYM_KP2, - TERMKEY_SYM_KP3, - TERMKEY_SYM_KP4, - TERMKEY_SYM_KP5, - TERMKEY_SYM_KP6, - TERMKEY_SYM_KP7, - TERMKEY_SYM_KP8, - TERMKEY_SYM_KP9, - TERMKEY_SYM_KPENTER, - TERMKEY_SYM_KPPLUS, - TERMKEY_SYM_KPMINUS, - TERMKEY_SYM_KPMULT, - TERMKEY_SYM_KPDIV, - TERMKEY_SYM_KPCOMMA, - TERMKEY_SYM_KPPERIOD, - TERMKEY_SYM_KPEQUALS, - - /* et cetera ad nauseum */ - TERMKEY_N_SYMS -} TermKeySym; - -typedef enum { - TERMKEY_TYPE_UNICODE, - TERMKEY_TYPE_FUNCTION, - TERMKEY_TYPE_KEYSYM, - TERMKEY_TYPE_MOUSE, - TERMKEY_TYPE_POSITION, - TERMKEY_TYPE_MODEREPORT, - TERMKEY_TYPE_DCS, - TERMKEY_TYPE_OSC, - /* add other recognised types here */ - - TERMKEY_TYPE_UNKNOWN_CSI = -1 -} TermKeyType; - -typedef enum { - TERMKEY_RES_NONE, - TERMKEY_RES_KEY, - TERMKEY_RES_EOF, - TERMKEY_RES_AGAIN, - TERMKEY_RES_ERROR -} TermKeyResult; - -typedef enum { - TERMKEY_MOUSE_UNKNOWN, - TERMKEY_MOUSE_PRESS, - TERMKEY_MOUSE_DRAG, - TERMKEY_MOUSE_RELEASE -} TermKeyMouseEvent; - -enum { - TERMKEY_KEYMOD_SHIFT = 1 << 0, - TERMKEY_KEYMOD_ALT = 1 << 1, - TERMKEY_KEYMOD_CTRL = 1 << 2 -}; - -typedef struct { - TermKeyType type; - union { - long codepoint; /* TERMKEY_TYPE_UNICODE */ - int number; /* TERMKEY_TYPE_FUNCTION */ - TermKeySym sym; /* TERMKEY_TYPE_KEYSYM */ - char mouse[4]; /* TERMKEY_TYPE_MOUSE */ - /* opaque. see termkey_interpret_mouse */ - } code; - - int modifiers; - - /* Any Unicode character can be UTF-8 encoded in no more than 6 bytes, plus - * terminating NUL */ - char utf8[7]; -} TermKeyKey; - -typedef struct { - const unsigned char *param; - size_t length; -} TermKeyCsiParam; - -typedef struct TermKey TermKey; - -enum { - TERMKEY_FLAG_NOINTERPRET = 1 << 0, /* Do not interpret C0//DEL codes if possible */ - TERMKEY_FLAG_CONVERTKP = 1 << 1, /* Convert KP codes to regular keypresses */ - TERMKEY_FLAG_RAW = 1 << 2, /* Input is raw bytes, not UTF-8 */ - TERMKEY_FLAG_UTF8 = 1 << 3, /* Input is definitely UTF-8 */ - TERMKEY_FLAG_NOTERMIOS = 1 << 4, /* Do not make initial termios calls on construction */ - TERMKEY_FLAG_SPACESYMBOL = 1 << 5, /* Sets TERMKEY_CANON_SPACESYMBOL */ - TERMKEY_FLAG_CTRLC = 1 << 6, /* Allow Ctrl-C to be read as normal, disabling SIGINT */ - TERMKEY_FLAG_EINTR = 1 << 7, /* Return ERROR on signal (EINTR) rather than retry */ - TERMKEY_FLAG_NOSTART = 1 << 8 /* Do not call termkey_start() in constructor */ -}; - -enum { - TERMKEY_CANON_SPACESYMBOL = 1 << 0, /* Space is symbolic rather than Unicode */ - TERMKEY_CANON_DELBS = 1 << 1 /* Del is converted to Backspace */ -}; - -void termkey_check_version(int major, int minor); - -TermKey *termkey_new(int fd, int flags); -TermKey *termkey_new_abstract(const char *term, int flags); -void termkey_free(TermKey *tk); -void termkey_destroy(TermKey *tk); - -/* Mostly-undocumented hooks for doing evil evil things */ -typedef const char *TermKey_Terminfo_Getstr_Hook(const char *name, const char *value, void *data); -void termkey_hook_terminfo_getstr(TermKey *tk, TermKey_Terminfo_Getstr_Hook *hookfn, void *data); - -int termkey_start(TermKey *tk); -int termkey_stop(TermKey *tk); -int termkey_is_started(TermKey *tk); - -int termkey_get_fd(TermKey *tk); - -int termkey_get_flags(TermKey *tk); -void termkey_set_flags(TermKey *tk, int newflags); - -int termkey_get_waittime(TermKey *tk); -void termkey_set_waittime(TermKey *tk, int msec); - -int termkey_get_canonflags(TermKey *tk); -void termkey_set_canonflags(TermKey *tk, int); - -size_t termkey_get_buffer_size(TermKey *tk); -int termkey_set_buffer_size(TermKey *tk, size_t size); - -size_t termkey_get_buffer_remaining(TermKey *tk); - -void termkey_canonicalise(TermKey *tk, TermKeyKey *key); - -TermKeyResult termkey_getkey(TermKey *tk, TermKeyKey *key); -TermKeyResult termkey_getkey_force(TermKey *tk, TermKeyKey *key); -TermKeyResult termkey_waitkey(TermKey *tk, TermKeyKey *key); - -TermKeyResult termkey_advisereadable(TermKey *tk); - -size_t termkey_push_bytes(TermKey *tk, const char *bytes, size_t len); - -TermKeySym termkey_register_keyname(TermKey *tk, TermKeySym sym, const char *name); -const char *termkey_get_keyname(TermKey *tk, TermKeySym sym); -const char *termkey_lookup_keyname(TermKey *tk, const char *str, TermKeySym *sym); - -TermKeySym termkey_keyname2sym(TermKey *tk, const char *keyname); - -TermKeyResult termkey_interpret_mouse(TermKey *tk, const TermKeyKey *key, TermKeyMouseEvent *event, int *button, int *line, int *col); - -TermKeyResult termkey_interpret_position(TermKey *tk, const TermKeyKey *key, int *line, int *col); - -TermKeyResult termkey_interpret_modereport(TermKey *tk, const TermKeyKey *key, int *initial, int *mode, int *value); - -TermKeyResult termkey_interpret_csi(TermKey *tk, const TermKeyKey *key, TermKeyCsiParam params[], size_t *nparams, unsigned long *cmd); - -TermKeyResult termkey_interpret_csi_param(TermKeyCsiParam param, long *paramp, long subparams[], size_t *nsubparams); - -TermKeyResult termkey_interpret_string(TermKey *tk, const TermKeyKey *key, const char **strp); - -typedef enum { - TERMKEY_FORMAT_LONGMOD = 1 << 0, /* Shift-... instead of S-... */ - TERMKEY_FORMAT_CARETCTRL = 1 << 1, /* ^X instead of C-X */ - TERMKEY_FORMAT_ALTISMETA = 1 << 2, /* Meta- or M- instead of Alt- or A- */ - TERMKEY_FORMAT_WRAPBRACKET = 1 << 3, /* Wrap special keys in brackets like */ - TERMKEY_FORMAT_SPACEMOD = 1 << 4, /* M Foo instead of M-Foo */ - TERMKEY_FORMAT_LOWERMOD = 1 << 5, /* meta or m instead of Meta or M */ - TERMKEY_FORMAT_LOWERSPACE = 1 << 6, /* page down instead of PageDown */ - - TERMKEY_FORMAT_MOUSE_POS = 1 << 8 /* Include mouse position if relevant; @ col,line */ -} TermKeyFormat; - -/* Some useful combinations */ - -#define TERMKEY_FORMAT_VIM (TermKeyFormat)(TERMKEY_FORMAT_ALTISMETA|TERMKEY_FORMAT_WRAPBRACKET) -#define TERMKEY_FORMAT_URWID (TermKeyFormat)(TERMKEY_FORMAT_LONGMOD|TERMKEY_FORMAT_ALTISMETA| \ - TERMKEY_FORMAT_LOWERMOD|TERMKEY_FORMAT_SPACEMOD|TERMKEY_FORMAT_LOWERSPACE) - -size_t termkey_strfkey(TermKey *tk, char *buffer, size_t len, TermKeyKey *key, TermKeyFormat format); -const char *termkey_strpkey(TermKey *tk, const char *str, TermKeyKey *key, TermKeyFormat format); - -int termkey_keycmp(TermKey *tk, const TermKeyKey *key1, const TermKeyKey *key2); - -#endif - -#ifdef __cplusplus -} -#endif -- cgit