diff options
author | Mattias Andrée <m@maandree.se> | 2025-02-16 14:23:16 +0100 |
---|---|---|
committer | Mattias Andrée <m@maandree.se> | 2025-02-16 14:23:16 +0100 |
commit | 16e00dd5f26ce342e9562bec08f529d98c23c01c (patch) | |
tree | a8389795475d59653930322e10e9f07025ff1e14 /libterminput.c | |
parent | interactive-test: add TEST_LIBTERMINPUT_PRINT_STATE (diff) | |
download | libterminput-16e00dd5f26ce342e9562bec08f529d98c23c01c.tar.gz libterminput-16e00dd5f26ce342e9562bec08f529d98c23c01c.tar.bz2 libterminput-16e00dd5f26ce342e9562bec08f529d98c23c01c.tar.xz |
Improve code organisation, documentation, and m code improvement
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to 'libterminput.c')
-rw-r--r-- | libterminput.c | 922 |
1 files changed, 0 insertions, 922 deletions
diff --git a/libterminput.c b/libterminput.c deleted file mode 100644 index 86666c5..0000000 --- a/libterminput.c +++ /dev/null @@ -1,922 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "libterminput.h" - -#include <alloca.h> -#include <ctype.h> -#include <limits.h> -#include <string.h> -#include <unistd.h> - - -struct input { - enum libterminput_mod mods; - char symbol[7]; -}; - - -static int -read_input(int fd, struct input *input, struct libterminput_state *ctx) -{ - unsigned char c, tc; - ssize_t r; - - /* Get next byte from input */ - if (ctx->stored_head != ctx->stored_tail) { - c = ((unsigned char *)ctx->stored)[ctx->stored_tail++]; - if (ctx->stored_tail == ctx->stored_head) - ctx->stored_tail = ctx->stored_head = 0; - } else { - r = read(fd, ctx->stored, sizeof(ctx->stored)); - if (r <= 0) - return (int)r; - c = (unsigned char)ctx->stored[0]; - if (r > 1) { - ctx->stored_tail = 1; - ctx->stored_head = (size_t)r; - } - } - -again: - if (ctx->n) { - /* Continuation of multibyte-character */ - if ((c & 0xC0) != 0x80) { - /* Short multibyte-character: return short and store read byte from next input */ - input->mods = ctx->mods; - ctx->partial[(unsigned char)ctx->npartial] = '\0'; - ctx->n = 0; - ctx->npartial = 0; - ctx->mods = 0; - ctx->stored[ctx->stored_head++] = (char)c; - strcpy(input->symbol, ctx->partial); - return 1; - } else { - /* Store byte, and if done, return */ - ctx->partial[(unsigned char)ctx->npartial++] = (char)c; - if (ctx->npartial == ctx->n) { - ctx->partial[(unsigned char)ctx->npartial] = '\0'; - input->mods = ctx->mods; - ctx->npartial = 0; - ctx->mods = 0; - ctx->n = 0; - strcpy(input->symbol, ctx->partial); - return 1; - } - } - } else if (c == 033 && !*ctx->key) { - /* ESC at the beginning, save as a Meta/ESC (for default behaviour) */ - if ((ctx->flags & LIBTERMINPUT_ESC_ON_BLOCK) && ctx->stored_tail == ctx->stored_head) { - input->symbol[0] = (char)c; - input->symbol[1] = '\0'; - input->mods = ctx->mods; - ctx->mods = 0; - return 1; - } - ctx->meta += 1; - } else if (c == 0) { - /* CTRL on Space */ - input->symbol[0] = ' '; - input->symbol[1] = '\0'; - input->mods = ctx->mods | LIBTERMINPUT_CTRL; - ctx->mods = 0; - return 1; - } else if (c < (unsigned char)' ' && (char)c != '\t' && (char)c != '\b' && (char)c != '\n') { - /* CTRL on some some character key */ - input->symbol[0] = (char)c + '@'; - input->symbol[1] = '\0'; - input->mods = ctx->mods | LIBTERMINPUT_CTRL; - ctx->mods = 0; - return 1; - } else if ((c & 0xC0) == 0xC0 && c != 0xFF) { - /* Beginning of multibyte-character */ - ctx->n = 0; - for (tc = c; tc & 0x80; tc <<= 1) - ctx->n++; - if (ctx->n > 6) { - /* If overlong, return first byte a single-byte-character */ - input->symbol[0] = (char)c; - input->symbol[1] = '\0'; - input->mods = ctx->mods; - ctx->mods = 0; - return 1; - } - ctx->partial[0] = (char)c; - ctx->npartial = 1; - } else if (c & 0x80) { - /* 8th bit set to signify META */ - c ^= 0x80; - ctx->mods |= LIBTERMINPUT_META; - goto again; - } else { - /* Single-byte-character */ - input->symbol[0] = (char)c; - input->symbol[1] = '\0'; - input->mods = ctx->mods; - ctx->mods = 0; - return 1; - } - - input->symbol[0] = '\0'; - input->mods = -1; - return 1; -} - - -static void -encode_utf8(unsigned long long int codepoint, char buffer[7]) -{ - static const char masks[6] = {(char)0x00, (char)0xC0, (char)0xE0, (char)0xF0, (char)0xF8, (char)0xFC}; - static const unsigned long long int limits[6] = { - 1ULL << (7 + 0 * 6), - 1ULL << (5 + 1 * 6), - 1ULL << (4 + 2 * 6), - 1ULL << (3 + 3 * 6), - 1ULL << (2 + 4 * 6), - 1ULL << (1 + 5 * 6) - }; - size_t len; - for (len = 0; codepoint >= limits[len]; len++); - buffer[0] = masks[len]; - len += 1; - buffer[len] = '\0'; - for (; --len; codepoint >>= 6) - buffer[len] = (char)((codepoint & 0x3FULL) | 0x80ULL); - buffer[0] |= (char)codepoint; -} - - -static int -check_utf8_char(const char *s, size_t *lenp, size_t size) -{ - size_t i; - *lenp = 0; - if (!size) { - return 0; - } else if ((*s & 0x80) == 0) { - *lenp = 1; - return 1; - } else if ((*s & 0xE0) == 0xC0) { - *lenp = 2; - } else if ((*s & 0xF0) == 0xE0) { - *lenp = 3; - } else if ((*s & 0xF8) == 0xF0) { - *lenp = 4; - } else if ((*s & 0xFC) == 0xF8) { - *lenp = 5; - } else if ((*s & 0xFE) == 0xFC) { - *lenp = 6; - } else { - *lenp = 0; - return -1; - } - for (i = 1; i < *lenp; i++) { - if (i == size) - return 0; - if ((s[i] & 0xC0) != 0x80) - return -1; - } - return 1; -} - - -static unsigned long long int -utf8_decode(const char *s, size_t *ip) -{ - unsigned long long int cp = 0; - size_t len; - - if ((s[*ip] & 0x80) == 0) { - return (unsigned long long int)s[(*ip)++]; - } else if ((s[*ip] & 0xE0) == 0xC0) { - cp = (unsigned long long int)((unsigned char)s[(*ip)++] ^ 0xC0U); - len = 2; - goto need_1; - } else if ((s[*ip] & 0xF0) == 0xE0) { - cp = (unsigned long long int)((unsigned char)s[(*ip)++] ^ 0xE0U); - len = 3; - goto need_2; - } else if ((s[*ip] & 0xF8) == 0xF0) { - cp = (unsigned long long int)((unsigned char)s[(*ip)++] ^ 0xF0U); - len = 4; - goto need_3; - } else if ((s[*ip] & 0xFC) == 0xF8) { - cp = (unsigned long long int)((unsigned char)s[(*ip)++] ^ 0xF8U); - len = 5; - goto need_4; - } else if ((s[*ip] & 0xFE) == 0xFC) { - cp = (unsigned long long int)((unsigned char)s[(*ip)++] ^ 0xFCU); - len = 6; - goto need_5; - } - -need_5: - if ((s[*ip] & 0xC0) != 0x80) return 0; - cp <<= 6; - cp |= (unsigned long long int)((unsigned char)s[(*ip)++] ^ 0x80U); - -need_4: - if ((s[*ip] & 0xC0) != 0x80) return 0; - cp <<= 6; - cp |= (unsigned long long int)((unsigned char)s[(*ip)++] ^ 0x80U); - -need_3: - if ((s[*ip] & 0xC0) != 0x80) return 0; - cp <<= 6; - cp |= (unsigned long long int)((unsigned char)s[(*ip)++] ^ 0x80U); - -need_2: - if ((s[*ip] & 0xC0) != 0x80) return 0; - cp <<= 6; - cp |= (unsigned long long int)((unsigned char)s[(*ip)++] ^ 0x80U); - -need_1: - if ((s[*ip] & 0xC0) != 0x80) return 0; - cp <<= 6; - cp |= (unsigned long long int)((unsigned char)s[(*ip)++] ^ 0x80U); - - /* Let's ignore the 0x10FFFF upper bound. */ - - if (cp < 1ULL << (7 + 0 * 6)) - return 0; - if (cp < 1ULL << (5 + 1 * 6)) - return len > 2 ? 0ULL : cp; - if (cp < 1ULL << (4 + 2 * 6)) - return len > 3 ? 0ULL : cp; - if (cp < 1ULL << (3 + 3 * 6)) - return len > 4 ? 0ULL : cp; - if (cp < 1ULL << (2 + 4 * 6)) - return len > 5 ? 0ULL : cp; - if (cp < 1ULL << (1 + 5 * 6)) - return len > 6 ? 0ULL : cp; - - return 0; -} - - -static void -parse_sequence(union libterminput_input *input, struct libterminput_state *ctx) -{ - unsigned long long int *nums, numsbuf[6]; - size_t keylen, n, nnums = 0, pos; - char *p; - - /* Get number of numbers in the sequence, and allocate an array of at least 2 */ - if (ctx->key[0] == '[' && (ctx->key[1] == '<' ? isdigit(ctx->key[2]) : isdigit(ctx->key[1]))) - nnums += 1; - for (n = 2, p = ctx->key; *p; p++) { - if (*p == ';') { - n += 1; - nnums += 1; - } - } - nums = alloca(n * sizeof(*nums)); - nums[0] = nums[1] = 0; - - /* Read numbers and remove numbers and delimiters */ - for (keylen = 0, n = 0, p = ctx->key; *p; p++) { - if (*p == ';') { - nums[++n] = 0; /* We made sure above to allocate one extra */ - } else if (!isdigit(*p)) { - ctx->key[keylen++] = *p; - } else if (n < 3) { - if (nums[n] < (ULLONG_MAX - (*p & 15)) / 10) - nums[n] = nums[n] * 10 + (*p & 15); - else - nums[n] = ULLONG_MAX; - } - } - ctx->key[keylen] = '\0'; - - /* Get times and mods, and reset symbol, and more as keypress */ - input->type = LIBTERMINPUT_KEYPRESS; - input->keypress.symbol[0] = '\0'; - input->keypress.times = nums[0] + !nums[0]; - input->keypress.mods = nums[1] > 1 ? nums[1] - 1 : 0; - input->keypress.mods |= ctx->meta > 1 ? LIBTERMINPUT_META : 0; - - switch (ctx->key[0]) { - case '[': - switch (keylen) { - case 2: - switch (ctx->key[1]) { - case 'A': input->keypress.key = LIBTERMINPUT_UP; break; - case 'B': input->keypress.key = LIBTERMINPUT_DOWN; break; - case 'C': input->keypress.key = LIBTERMINPUT_RIGHT; break; - case 'D': input->keypress.key = LIBTERMINPUT_LEFT; break; - case 'E': input->keypress.key = LIBTERMINPUT_BEGIN; break; - case 'F': input->keypress.key = LIBTERMINPUT_END; break; - case 'G': input->keypress.key = LIBTERMINPUT_BEGIN; break; - case 'H': input->keypress.key = LIBTERMINPUT_HOME; break; - case 'M': - if (ctx->flags & LIBTERMINPUT_MACRO_ON_CSI_M) { - input->keypress.key = LIBTERMINPUT_MACRO; - } else if (nnums >= 3) { - /* Parsing for \e[?1000;1015h output. */ - nums[0] -= 32ULL; - decimal_mouse_tracking_set_press: - input->mouseevent.event = LIBTERMINPUT_PRESS; - decimal_mouse_tracking: - input->mouseevent.type = LIBTERMINPUT_MOUSEEVENT; - input->mouseevent.x = (size_t)nums[1] + (size_t)!nums[1]; - input->mouseevent.y = (size_t)nums[2] + (size_t)!nums[2]; - input->mouseevent.mods = (enum libterminput_mod)((nums[0] >> 2) & 7ULL); - if (nums[0] & 32) - input->mouseevent.event = LIBTERMINPUT_MOTION; - nums[0] = (nums[0] & 3ULL) | ((nums[0] >> 4) & ~3ULL); - if (nums[0] < 4) { - nums[0] = (nums[0] + 1) & 3; - if (!nums[0] && input->mouseevent.event == LIBTERMINPUT_PRESS) { - input->mouseevent.event = LIBTERMINPUT_RELEASE; - nums[0] = 1; - } - } - input->mouseevent.button = (enum libterminput_button)nums[0]; - } else if (!nnums & !(ctx->flags & LIBTERMINPUT_DECSET_1005)) { - /* Parsing output for legacy mouse tracking output. */ - ctx->mouse_tracking = 0; - nums = numsbuf; - nums[0] = (unsigned long long int)(unsigned char)ctx->stored[ctx->stored_tail++]; - nums[1] = (unsigned long long int)(unsigned char)ctx->stored[ctx->stored_tail++]; - nums[2] = (unsigned long long int)(unsigned char)ctx->stored[ctx->stored_tail++]; - nums[0] = (nums[0] - 32ULL) & 255ULL; - nums[1] = (nums[1] - 32ULL) & 255ULL; - nums[2] = (nums[2] - 32ULL) & 255ULL; - if (ctx->stored_head == ctx->stored_tail) - ctx->stored_head = ctx->stored_tail = 0; - goto decimal_mouse_tracking_set_press; - } else if (!nnums) { - /* Parsing for semi-legacy \e[?1000;1005h output. */ - ctx->mouse_tracking = 0; - nums = numsbuf; - pos = ctx->stored_tail; - if ((nums[0] = utf8_decode(ctx->stored, &ctx->stored_tail)) < 32 || - (nums[1] = utf8_decode(ctx->stored, &ctx->stored_tail)) < 32 || - (nums[2] = utf8_decode(ctx->stored, &ctx->stored_tail)) < 32) { - ctx->stored_tail = pos; - input->keypress.key = LIBTERMINPUT_MACRO; - return; - } - nums[0] = nums[0] - 32ULL; - nums[1] = nums[1] - 32ULL; - nums[2] = nums[2] - 32ULL; - if (ctx->stored_head == ctx->stored_tail) - ctx->stored_head = ctx->stored_tail = 0; - goto decimal_mouse_tracking_set_press; - } else { - goto suppress; - } - break; - case 'P': - input->keypress.key = LIBTERMINPUT_F1; - if (ctx->flags & LIBTERMINPUT_PAUSE_ON_CSI_P) - input->keypress.key = LIBTERMINPUT_PAUSE; - break; - case 'Q': - input->keypress.key = LIBTERMINPUT_F2; - break; - case 'R': - if ((ctx->flags & LIBTERMINPUT_AWAITING_CURSOR_POSITION) && nnums >= 2) { - input->position.type = LIBTERMINPUT_CURSOR_POSITION; - input->position.y = (size_t)nums[0] + (size_t)!nums[0]; - input->position.x = (size_t)nums[1] + (size_t)!nums[1]; - } else { - input->keypress.key = LIBTERMINPUT_F3; - } - break; - case 'S': - input->keypress.key = LIBTERMINPUT_F4; - break; - case 'T': - /* Parsing output for legacy mouse highlight tracking output. (\e[?1001h) */ - ctx->mouse_tracking = 0; - nums = numsbuf; - nums[0] = (unsigned long long int)(unsigned char)ctx->stored[ctx->stored_tail++]; - nums[1] = (unsigned long long int)(unsigned char)ctx->stored[ctx->stored_tail++]; - nums[2] = (unsigned long long int)(unsigned char)ctx->stored[ctx->stored_tail++]; - nums[3] = (unsigned long long int)(unsigned char)ctx->stored[ctx->stored_tail++]; - nums[4] = (unsigned long long int)(unsigned char)ctx->stored[ctx->stored_tail++]; - nums[5] = (unsigned long long int)(unsigned char)ctx->stored[ctx->stored_tail++]; - nums[0] = (nums[0] - 32ULL) & 255ULL; - nums[1] = (nums[1] - 32ULL) & 255ULL; - nums[2] = (nums[2] - 32ULL) & 255ULL; - nums[3] = (nums[3] - 32ULL) & 255ULL; - nums[4] = (nums[4] - 32ULL) & 255ULL; - nums[5] = (nums[5] - 32ULL) & 255ULL; - if (ctx->stored_head == ctx->stored_tail) - ctx->stored_head = ctx->stored_tail = 0; - input->mouseevent.type = LIBTERMINPUT_MOUSEEVENT; - input->mouseevent.event = LIBTERMINPUT_HIGHLIGHT_OUTSIDE; - input->mouseevent.mods = 0; - input->mouseevent.button = LIBTERMINPUT_BUTTON1; - input->mouseevent.start_x = (size_t)nums[0] + (size_t)!nums[0]; - input->mouseevent.start_y = (size_t)nums[1] + (size_t)!nums[1]; - input->mouseevent.end_x = (size_t)nums[2] + (size_t)!nums[2]; - input->mouseevent.end_y = (size_t)nums[3] + (size_t)!nums[3]; - input->mouseevent.x = (size_t)nums[4] + (size_t)!nums[4]; - input->mouseevent.y = (size_t)nums[5] + (size_t)!nums[5]; - break; - case 'U': input->keypress.key = LIBTERMINPUT_NEXT; break; - case 'V': input->keypress.key = LIBTERMINPUT_PRIOR; break; - case 'Z': - if (!(ctx->flags & LIBTERMINPUT_SEPARATE_BACKTAB)) { - input->keypress.key = LIBTERMINPUT_TAB; - input->keypress.mods |= LIBTERMINPUT_SHIFT; - } else { - input->keypress.key = LIBTERMINPUT_BACKTAB; - } - break; - case 'a': - input->keypress.key = LIBTERMINPUT_UP; - input->keypress.mods |= LIBTERMINPUT_SHIFT; - break; - case 'b': - input->keypress.key = LIBTERMINPUT_DOWN; - input->keypress.mods |= LIBTERMINPUT_SHIFT; - break; - case 'c': - input->keypress.key = LIBTERMINPUT_RIGHT; - input->keypress.mods |= LIBTERMINPUT_SHIFT; - break; - case 'd': - input->keypress.key = LIBTERMINPUT_LEFT; - input->keypress.mods |= LIBTERMINPUT_SHIFT; - break; - case 'n': - if (nnums == 1 && nums[0] == 0) { - input->type = LIBTERMINPUT_TERMINAL_IS_OK; - } else if (nnums == 1 && nums[0] == 3) { - input->type = LIBTERMINPUT_TERMINAL_IS_NOT_OK; - } else { - goto suppress; - } - break; - case 't': - /* Parsing output for legacy mouse highlight tracking output (\e[?1001h). */ - ctx->mouse_tracking = 0; - nums = numsbuf; - nums[0] = (unsigned long long int)(unsigned char)ctx->stored[ctx->stored_tail++]; - nums[1] = (unsigned long long int)(unsigned char)ctx->stored[ctx->stored_tail++]; - nums[0] = (nums[0] - 32ULL) & 255ULL; - nums[1] = (nums[1] - 32ULL) & 255ULL; - if (ctx->stored_head == ctx->stored_tail) - ctx->stored_head = ctx->stored_tail = 0; - input->mouseevent.type = LIBTERMINPUT_MOUSEEVENT; - input->mouseevent.event = LIBTERMINPUT_HIGHLIGHT_INSIDE; - input->mouseevent.mods = 0; - input->mouseevent.button = LIBTERMINPUT_BUTTON1; - input->mouseevent.x = (size_t)nums[0] + (size_t)!nums[0]; - input->mouseevent.y = (size_t)nums[1] + (size_t)!nums[1]; - break; - case 'u': - if (nums[0] > 0x10FFFFULL || (nums[0] & 0xFFF800ULL) == 0xD800ULL) { - input->type = LIBTERMINPUT_NONE; - break; - } - encode_utf8(nums[0], input->keypress.symbol); - input->keypress.times = 1; - break; - case '$': - input->keypress.mods |= LIBTERMINPUT_SHIFT; - if (nums[0] >= 200) - goto suppress; - goto tilde_case; - case '@': - if (ctx->flags & LIBTERMINPUT_INS_ON_CSI_AT) { - input->keypress.key = LIBTERMINPUT_INS; - break; - } - input->keypress.mods |= LIBTERMINPUT_SHIFT; - /* fall through */ - case '^': - input->keypress.mods |= LIBTERMINPUT_CTRL; - if (nums[0] >= 200) - goto suppress; - /* fall through */ - case '~': - tilde_case: - input->keypress.times = 1; - switch (nums[0]) { - case 1: input->keypress.key = LIBTERMINPUT_HOME; break; - case 2: input->keypress.key = LIBTERMINPUT_INS; break; - case 3: input->keypress.key = LIBTERMINPUT_DEL; break; - case 4: input->keypress.key = LIBTERMINPUT_END; break; - case 5: input->keypress.key = LIBTERMINPUT_PRIOR; break; - case 6: input->keypress.key = LIBTERMINPUT_NEXT; break; - case 7: input->keypress.key = LIBTERMINPUT_HOME; break; - case 8: input->keypress.key = LIBTERMINPUT_END; break; - case 9: input->keypress.key = LIBTERMINPUT_ESC; break; /* just made this one up */ - case 11: input->keypress.key = LIBTERMINPUT_F1; break; - case 12: input->keypress.key = LIBTERMINPUT_F2; break; - case 13: input->keypress.key = LIBTERMINPUT_F3; break; - case 14: input->keypress.key = LIBTERMINPUT_F4; break; - case 15: input->keypress.key = LIBTERMINPUT_F5; break; - case 17: input->keypress.key = LIBTERMINPUT_F6; break; - case 18: input->keypress.key = LIBTERMINPUT_F7; break; - case 19: input->keypress.key = LIBTERMINPUT_F8; break; - case 20: input->keypress.key = LIBTERMINPUT_F9; break; - case 21: input->keypress.key = LIBTERMINPUT_F10; break; - case 23: input->keypress.key = LIBTERMINPUT_F11; break; - case 24: input->keypress.key = LIBTERMINPUT_F12; break; - case 25: input->keypress.key = LIBTERMINPUT_F1; break; - case 26: input->keypress.key = LIBTERMINPUT_F2; break; - case 28: input->keypress.key = LIBTERMINPUT_F3; break; - case 29: input->keypress.key = LIBTERMINPUT_F4; break; - case 31: input->keypress.key = LIBTERMINPUT_F5; break; - case 32: input->keypress.key = LIBTERMINPUT_F6; break; - case 33: input->keypress.key = LIBTERMINPUT_F7; break; - case 34: input->keypress.key = LIBTERMINPUT_F8; break; - case 200: - ctx->bracketed_paste = 1; - input->type = LIBTERMINPUT_BRACKETED_PASTE_START; - return; - case 201: - ctx->bracketed_paste = 0; - input->type = LIBTERMINPUT_BRACKETED_PASTE_END; - return; - default: - goto suppress; - } - if (25 <= nums[0] && nums[0] <= 34) - input->keypress.mods |= LIBTERMINPUT_SHIFT; - break; - default: - goto suppress; - } - break; - case 3: - switch (ctx->key[1] == '[' ? ctx->key[2] : 0) { - case 'A': input->keypress.key = LIBTERMINPUT_F1; break; - case 'B': input->keypress.key = LIBTERMINPUT_F2; break; - case 'C': input->keypress.key = LIBTERMINPUT_F3; break; - case 'D': input->keypress.key = LIBTERMINPUT_F4; break; - case 'E': input->keypress.key = LIBTERMINPUT_F5; break; - default: - if (ctx->key[1] == '<' && (ctx->key[2] == 'M' || ctx->key[2] == 'm') && nnums >= 3) { - /* Parsing for \e[?1003;1006h output. */ - input->mouseevent.event = LIBTERMINPUT_PRESS; - if (ctx->key[2] == 'm') - input->mouseevent.event = LIBTERMINPUT_RELEASE; - goto decimal_mouse_tracking; - } else { - goto suppress; - } - } - break; - default: - goto suppress; - } - break; - - case 'O': - switch (!ctx->key[2] ? ctx->key[1] : 0) { - case 'A': input->keypress.key = LIBTERMINPUT_UP; break; - case 'B': input->keypress.key = LIBTERMINPUT_DOWN; break; - case 'C': input->keypress.key = LIBTERMINPUT_RIGHT; break; - case 'D': input->keypress.key = LIBTERMINPUT_LEFT; break; - case 'E': input->keypress.key = LIBTERMINPUT_BEGIN; break; /* not attested */ - case 'F': input->keypress.key = LIBTERMINPUT_END; break; - case 'G': input->keypress.key = LIBTERMINPUT_BEGIN; break; /* not attested */ - case 'H': input->keypress.key = LIBTERMINPUT_HOME; break; - case 'P': input->keypress.key = LIBTERMINPUT_F1; break; - case 'Q': input->keypress.key = LIBTERMINPUT_F2; break; - case 'R': input->keypress.key = LIBTERMINPUT_F3; break; - case 'S': input->keypress.key = LIBTERMINPUT_F4; break; - case 'p': input->keypress.key = LIBTERMINPUT_KEYPAD_0; break; - case 'q': input->keypress.key = LIBTERMINPUT_KEYPAD_1; break; - case 'r': input->keypress.key = LIBTERMINPUT_KEYPAD_2; break; - case 's': input->keypress.key = LIBTERMINPUT_KEYPAD_3; break; - case 't': input->keypress.key = LIBTERMINPUT_KEYPAD_4; break; - case 'u': input->keypress.key = LIBTERMINPUT_KEYPAD_5; break; - case 'v': input->keypress.key = LIBTERMINPUT_KEYPAD_6; break; - case 'w': input->keypress.key = LIBTERMINPUT_KEYPAD_7; break; - case 'x': input->keypress.key = LIBTERMINPUT_KEYPAD_8; break; - case 'y': input->keypress.key = LIBTERMINPUT_KEYPAD_9; break; - case 'k': input->keypress.key = LIBTERMINPUT_KEYPAD_PLUS; break; - case 'm': input->keypress.key = LIBTERMINPUT_KEYPAD_MINUS; break; - case 'j': input->keypress.key = LIBTERMINPUT_KEYPAD_TIMES; break; - case 'o': input->keypress.key = LIBTERMINPUT_KEYPAD_DIVISION; break; - case 'n': input->keypress.key = LIBTERMINPUT_KEYPAD_DECIMAL; break; - case 'l': input->keypress.key = LIBTERMINPUT_KEYPAD_COMMA; break; - case 'b': input->keypress.key = LIBTERMINPUT_KEYPAD_POINT; break; - case 'M': input->keypress.key = LIBTERMINPUT_KEYPAD_ENTER; break; - default: - goto suppress; - } - break; - - default: - /* This shouldn't happen (without goto) */ - suppress: - input->type = LIBTERMINPUT_NONE; - break; - } -} - - -static int -read_bracketed_paste(int fd, union libterminput_input *input, struct libterminput_state *ctx) -{ - ssize_t r; - size_t n; - - /* Unfortunately there is no standard for how to handle pasted ESC's, - * not even ESC [201~ or ESC ESC. Terminates seem to just paste ESC as - * is, so we cannot do anything about them, however, a good terminal - * would stop the paste at the ~ in ESC [201~, send ~ as normal, and - * then continue the brackated paste mode. */ - - if (ctx->stored_head - ctx->stored_tail) { - ctx->paused = 0; - n = ctx->stored_head - ctx->stored_tail; - if (!strncmp(&ctx->stored[ctx->stored_tail], "\033[201~", n < 6 ? n : 6)) { - if (n >= 6) { - ctx->stored_tail += 6; - if (ctx->stored_tail == ctx->stored_head) - ctx->stored_tail = ctx->stored_head = 0; - ctx->bracketed_paste = 0; - input->type = LIBTERMINPUT_BRACKETED_PASTE_END; - return 1; - } - input->text.nbytes = ctx->stored_head - ctx->stored_tail; - memcpy(input->text.bytes, &ctx->stored[ctx->stored_tail], input->text.nbytes); - r = read(fd, &input->text.bytes[input->text.nbytes], sizeof(input->text.bytes) - input->text.nbytes); - if (r <= 0) - return (int)r; - input->text.nbytes += (size_t)r; - ctx->stored_head = ctx->stored_tail = 0; - goto normal; - } - input->text.nbytes = ctx->stored_head - ctx->stored_tail; - memcpy(input->text.bytes, &ctx->stored[ctx->stored_tail], input->text.nbytes); - ctx->stored_head = ctx->stored_tail = 0; - goto normal; - } - - r = read(fd, input->text.bytes, sizeof(input->text.bytes)); - if (r <= 0) - return (int)r; - input->text.nbytes = (size_t)r; - -normal: - for (n = 0; n + 5 < input->text.nbytes; n++) { - if (input->text.bytes[n + 0] == '\033' && input->text.bytes[n + 1] == '[' && input->text.bytes[n + 2] == '2' && - input->text.bytes[n + 3] == '0' && input->text.bytes[n + 4] == '1' && input->text.bytes[n + 5] == '~') - break; - } - do { - if (n + 4 < input->text.nbytes) { - if (input->text.bytes[n + 0] == '\033' && input->text.bytes[n + 1] == '[' && input->text.bytes[n + 2] == '2' && - input->text.bytes[n + 3] == '0' && input->text.bytes[n + 4] == '1') - break; - n += 1; - } - if (n + 3 < input->text.nbytes) { - if (input->text.bytes[n + 0] == '\033' && input->text.bytes[n + 1] == '[' && input->text.bytes[n + 2] == '2' && - input->text.bytes[n + 3] == '0') - break; - n += 1; - } - if (n + 2 < input->text.nbytes) { - if (input->text.bytes[n + 0] == '\033' && input->text.bytes[n + 1] == '[' && input->text.bytes[n + 2] == '2') - break; - n += 1; - } - if (n + 1 < input->text.nbytes) { - if (input->text.bytes[n + 0] == '\033' && input->text.bytes[n + 1] == '[') - break; - n += 1; - } - if (n + 0 < input->text.nbytes) { - if (input->text.bytes[n + 0] == '\033') - break; - n += 1; - } - } while (0); - if (!n) { - if (input->text.nbytes < 6) { - input->text.type = LIBTERMINPUT_NONE; - memcpy(ctx->stored, input->text.bytes, input->text.nbytes); - ctx->stored_tail = 0; - ctx->stored_head = input->text.nbytes; - ctx->paused = 1; - return 1; - } - ctx->stored_tail = 0; - ctx->stored_head = input->text.nbytes - 6; - memcpy(ctx->stored, &input->text.bytes[6], ctx->stored_head); - if (ctx->stored_tail == ctx->stored_head) - ctx->stored_tail = ctx->stored_head = 0; - ctx->bracketed_paste = 0; - input->type = LIBTERMINPUT_BRACKETED_PASTE_END; - return 1; - } - ctx->stored_tail = 0; - ctx->stored_head = input->text.nbytes - n; - memcpy(ctx->stored, &input->text.bytes[n], ctx->stored_head); - input->text.nbytes = n; - input->text.type = LIBTERMINPUT_TEXT; - return 1; -} - - -int -libterminput_read(int fd, union libterminput_input *input, struct libterminput_state *ctx) -{ - struct input ret; - size_t n, m; - char *p; - int r; - ssize_t rd; - - if (!ctx->inited) { - ctx->inited = 1; - memset(input, 0, sizeof(*input)); - } else if (input->type == LIBTERMINPUT_KEYPRESS && input->keypress.times > 1) { - input->keypress.times -= 1; - return 1; - } - - if (ctx->bracketed_paste) - return read_bracketed_paste(fd, input, ctx); - if (!ctx->mouse_tracking) { - r = read_input(fd, &ret, ctx); - if (r <= 0) - return r; - } else if (ctx->mouse_tracking == 1) { - if (ctx->stored_tail == sizeof(ctx->stored)) { - memmove(ctx->stored, &ctx->stored[ctx->stored_tail], ctx->stored_head - ctx->stored_tail); - ctx->stored_tail -= ctx->stored_head; - ctx->stored_head = 0; - } - rd = read(fd, &ctx->stored[ctx->stored_head], 1); - if (rd <= 0) - return (int)rd; - ctx->stored_head += 1; - p = strchr(ctx->key, '\0'); - goto continue_incomplete; - } else { - if (ctx->stored_tail > sizeof(ctx->stored) - (size_t)ctx->mouse_tracking) { - memmove(ctx->stored, &ctx->stored[ctx->stored_tail], ctx->stored_head - ctx->stored_tail); - ctx->stored_tail -= ctx->stored_head; - ctx->stored_head = 0; - } - rd = read(fd, &ctx->stored[ctx->stored_head], (size_t)ctx->mouse_tracking - (ctx->stored_head - ctx->stored_tail)); - if (rd <= 0) - return (int)rd; - ctx->stored_head += (size_t)rd; - p = strchr(ctx->key, '\0'); - goto continue_incomplete; - } - -again: - if (!*ret.symbol) { - /* Incomplete input */ - if (ctx->meta < 3) { - /* Up to two Meta/ESC, wait until a third or something else is read */ - input->type = LIBTERMINPUT_NONE; - return 1; - } - /* Three ESC's */ - input->type = LIBTERMINPUT_KEYPRESS; - input->keypress.key = LIBTERMINPUT_ESC; - input->keypress.times = 3; - input->keypress.mods = 0; - input->keypress.symbol[0] = '\0'; - ctx->meta -= 3; - } else if (*ctx->key) { - /* Special keys */ - if (ret.mods) { - /* Special key was aborted, restart */ - *ctx->key = '\0'; - goto again; - } - /* Add new input to sequence */ - n = strlen(ctx->key); - m = strlen(ret.symbol); - if (n + m >= sizeof(ctx->key)) { - /* Abort if too long */ - input->type = LIBTERMINPUT_NONE; - return 1; - } - p = stpcpy(&ctx->key[n], ret.symbol); - /* Check if sequence is complete */ - continue_incomplete: - if (!isalpha(p[-1]) && p[-1] != '~' && p[-1] != '@' && p[-1] != '^' && p[-1] != '$') { - input->type = LIBTERMINPUT_NONE; - return 1; - } else if (ctx->key[0] == '[' && ctx->key[1] == '<' && p == &ctx->key[2]) { - input->type = LIBTERMINPUT_NONE; - return 1; - } else if (ctx->key[0] == '[' && ctx->key[1] == 'M' && (ctx->flags & LIBTERMINPUT_MACRO_ON_CSI_M)) { - /* complete */ - } else if (ctx->key[0] == '[' && ctx->key[1] == 'M' && (ctx->flags & LIBTERMINPUT_DECSET_1005)) { - ctx->mouse_tracking = 1; - if (ctx->stored_head == ctx->stored_tail) { - input->type = LIBTERMINPUT_NONE; - return 1; - } - n = ctx->stored_tail; - r = check_utf8_char(&ctx->stored[n], &m, ctx->stored_head - n); - if (r <= 0) - goto fallback_to_none_or_macro; - n += m; - r = check_utf8_char(&ctx->stored[n], &m, ctx->stored_head - n); - if (r <= 0) - goto fallback_to_none_or_macro; - n += m; - r = check_utf8_char(&ctx->stored[n], &m, ctx->stored_head - n); - if (r <= 0) { - fallback_to_none_or_macro: - if (!r) { - input->type = LIBTERMINPUT_NONE; - return 1; - } - ctx->mouse_tracking = 0; - input->type = LIBTERMINPUT_KEYPRESS; - input->keypress.key = LIBTERMINPUT_MACRO; - input->keypress.mods = ret.mods; - input->keypress.times = 1; - if (ctx->meta > 1) - input->keypress.mods |= LIBTERMINPUT_META; - ctx->meta = 0; - ctx->key[0] = '\0'; - return 1; - } - } else if (ctx->key[0] == '[' && ctx->key[1] == 'M' && ctx->stored_head - ctx->stored_tail < 3) { - ctx->mouse_tracking = 3; - input->type = LIBTERMINPUT_NONE; - return 1; - } else if (ctx->key[0] == '[' && ctx->key[1] == 't' && ctx->stored_head - ctx->stored_tail < 2) { - ctx->mouse_tracking = 2; - input->type = LIBTERMINPUT_NONE; - return 1; - } else if (ctx->key[0] == '[' && ctx->key[1] == 'T' && ctx->stored_head - ctx->stored_tail < 6) { - ctx->mouse_tracking = 6; - input->type = LIBTERMINPUT_NONE; - return 1; - } - /* Parse the complete sequence */ - parse_sequence(input, ctx); - /* Reset */ - ctx->meta = 0; - ctx->key[0] = '\0'; - } else if (ctx->meta && (!strcmp(ret.symbol, "[") || !strcmp(ret.symbol, "O"))) { - /* ESC [ or ESC 0 is used as the beginning of most special keys */ - strcpy(ctx->key, ret.symbol); - input->type = LIBTERMINPUT_NONE; - } else { - /* Character input and single-byte special keys */ - input->type = LIBTERMINPUT_KEYPRESS; - input->keypress.mods = ret.mods; - input->keypress.times = 1; - if (ctx->meta) { - /* Transfer meta modifier from state to input */ - input->keypress.mods |= LIBTERMINPUT_META; - ctx->meta = 0; - } - switch (ret.symbol[1] ? 0 : ret.symbol[0]) { - case 127: - case '\b': - input->keypress.key = LIBTERMINPUT_ERASE; - input->keypress.symbol[0] = '\0'; - break; - case '\t': - input->keypress.key = LIBTERMINPUT_TAB; - input->keypress.symbol[0] = '\0'; - break; - case '\n': - input->keypress.key = LIBTERMINPUT_ENTER; - input->keypress.symbol[0] = '\0'; - break; - case 033: - input->keypress.key = LIBTERMINPUT_ESC; - input->keypress.symbol[0] = '\0'; - break; - default: - input->keypress.key = LIBTERMINPUT_SYMBOL; - strcpy(input->keypress.symbol, ret.symbol); - break; - } - } - - return 1; -} - - -int -libterminput_set_flags(struct libterminput_state *ctx, enum libterminput_flags flags) -{ - ctx->flags |= flags; - return 0; -} - - -int -libterminput_clear_flags(struct libterminput_state *ctx, enum libterminput_flags flags) -{ - ctx->flags |= flags; - ctx->flags ^= flags; - return 0; -} - - -extern inline int libterminput_is_ready(const union libterminput_input *input, const struct libterminput_state *ctx); |