diff options
Diffstat (limited to 'libterminput_read.c')
-rw-r--r-- | libterminput_read.c | 653 |
1 files changed, 653 insertions, 0 deletions
diff --git a/libterminput_read.c b/libterminput_read.c new file mode 100644 index 0000000..50095bd --- /dev/null +++ b/libterminput_read.c @@ -0,0 +1,653 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +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 +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] = libterminput_utf8_decode__(ctx->stored, &ctx->stored_tail)) < 32 || + (nums[1] = libterminput_utf8_decode__(ctx->stored, &ctx->stored_tail)) < 32 || + (nums[2] = libterminput_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; + } + libterminput_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; + } +} + + +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 libterminput_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 = libterminput_check_utf8_char__(&ctx->stored[n], ctx->stored_head - n, &m); + if (r <= 0) + goto fallback_to_none_or_macro; + n += m; + r = libterminput_check_utf8_char__(&ctx->stored[n], ctx->stored_head - n, &m); + if (r <= 0) + goto fallback_to_none_or_macro; + n += m; + r = libterminput_check_utf8_char__(&ctx->stored[n], ctx->stored_head - n, &m); + 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; +} |