aboutsummaryrefslogtreecommitdiffstats
path: root/libterminput_read.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libterminput_read.c196
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;
+}