diff options
Diffstat (limited to '')
-rw-r--r-- | libterminput_read.c | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/libterminput_read.c b/libterminput_read.c new file mode 100644 index 0000000..9324573 --- /dev/null +++ b/libterminput_read.c @@ -0,0 +1,196 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libterminput_read(int fd, union libterminput_input *input, struct libterminput_state *ctx) +{ + struct input ret; + size_t n, m; + char *p; + int r, i; + ssize_t rd; + + if (!ctx->inited) { + /* Initialise structures */ + ctx->inited = 1; + memset(input, 0, sizeof(*input)); + /* The user should have set all bytes in `*ctx` to 0, and it doesn't + * contain any pointers, and in fact all initial values should be 0, + * so we do not need to modify `*ctx` except mark it as initialised */ + } else if (input->type == LIBTERMINPUT_KEYPRESS && input->keypress.times > 1) { + /* For repeated input, unless counter is reset by user, report + * the same event again (and decrease the counter) */ + input->keypress.times -= 1; + return 1; + } + + /* If in a bracketed paste, use the reading function for that mode */ + if (ctx->bracketed_paste) + return libterminput_read_bracketed_paste__(fd, input, ctx); + + /* If we are on an incomplete mouse tracking event, read more raw input, + * otherwise read one symbol */ + if (!ctx->mouse_tracking) { + r = libterminput_read_symbol__(fd, &ret, ctx); + if (r <= 0) { + if (!r || ctx->blocked || !ctx->queued || errno != EAGAIN) + return r; + ctx->blocked = 1; + ctx->queued = 0; + input->type = LIBTERMINPUT_KEYPRESS; + ctx->mods = 0; + ctx->meta = 0; + ctx->key[0] = '\0'; + return 1; + } + ctx->blocked = 0; + ctx->queued = 0; + } 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], sizeof(ctx->stored) - ctx->stored_head); + 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, specifically only Meta/ESC's */ + if (ctx->meta < 3) { + /* Up to two Meta/ESC, wait until a third or something else is read, + * or if ESC on block, report as ESC or Meta+ESC */ + if (ctx->flags & LIBTERMINPUT_ESC_ON_BLOCK) { + input->keypress.key = LIBTERMINPUT_ESC; + input->keypress.times = 1; + input->keypress.mods = ctx->mods; + input->keypress.symbol[0] = '\0'; + if (ctx->meta > 1) + input->keypress.mods |= LIBTERMINPUT_META; + ctx->queued = 1; + input->type = LIBTERMINPUT_NONE; + return 1; + } + goto none; + } + /* 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 */ + goto none; + } + 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] != '$') { + goto none; + } else if (ctx->key[0] == '[' && ctx->key[1] == '<' && p == &ctx->key[2]) { + goto none; + } 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_MACRO_ON_BLOCK) && + ctx->stored_head - ctx->stored_tail == 0) { + input->keypress.key = LIBTERMINPUT_MACRO; + input->keypress.times = 1; + input->keypress.mods = ctx->mods; + input->keypress.symbol[0] = '\0'; + if (ctx->meta > 1) + input->keypress.mods |= LIBTERMINPUT_META; + ctx->queued = 1; + input->type = LIBTERMINPUT_NONE; + return 1; + } else if (ctx->key[0] == '[' && ctx->key[1] == 'M' && (ctx->flags & LIBTERMINPUT_DECSET_1005)) { + ctx->mouse_tracking = 1; + n = ctx->stored_tail; + for (i = 0; i < 3; i++) { + r = libterminput_check_utf8_char__(&ctx->stored[n], ctx->stored_head - n, &m); + if (r > 0) { + n += m; + continue; + } + if (!r) + goto none; + 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 < 3U) { + ctx->mouse_tracking = 3; + goto none; + } else if (ctx->key[0] == '[' && ctx->key[1] == 't' && ctx->stored_head - ctx->stored_tail < 2U) { + ctx->mouse_tracking = 2; + goto none; + } else if (ctx->key[0] == '[' && ctx->key[1] == 'T' && ctx->stored_head - ctx->stored_tail < 6U) { + ctx->mouse_tracking = 6; + goto none; + } + /* Parse the complete sequence */ + libterminput_parse_sequence__(input, ctx); + /* Reset */ + ctx->meta = 0; + ctx->key[0] = '\0'; + + } else if (ctx->meta && (!strcmp(ret.symbol, "[") || !strcmp(ret.symbol, "O") || !strcmp(ret.symbol, "?"))) { + /* ESC [, ESC O, or ESC ? is used as the beginning of most special keys */ + stpcpy(ctx->key, ret.symbol); + goto 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; + } + input->keypress.symbol[0] = '\0'; + switch (ret.symbol[1] ? 0 : ret.symbol[0]) { + case 127: + case '\b': input->keypress.key = LIBTERMINPUT_ERASE; break; + case '\t': input->keypress.key = LIBTERMINPUT_TAB; break; + case '\n': input->keypress.key = LIBTERMINPUT_ENTER; break; + case 033: input->keypress.key = LIBTERMINPUT_ESC; break; + default: + input->keypress.key = LIBTERMINPUT_SYMBOL; + stpcpy(input->keypress.symbol, ret.symbol); + break; + } + } + + return 1; + +none: + NOTHING(input); + return 1; +} |