diff options
-rw-r--r-- | .gitignore | 9 | ||||
-rw-r--r-- | LICENSE | 15 | ||||
-rw-r--r-- | Makefile | 44 | ||||
-rw-r--r-- | config.mk | 8 | ||||
-rw-r--r-- | interactive-test.c | 99 | ||||
-rw-r--r-- | libterminput.c | 345 | ||||
-rw-r--r-- | libterminput.h | 106 |
7 files changed, 626 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e41cec2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*~ +*\#* +*.a +*.o +*.so +*.su +*.out +/test +/interactive-test @@ -0,0 +1,15 @@ +ISC License + +© 2021 Mattias Andrée <maandree@kth.se> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b6913f6 --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +.POSIX: + +CONFIGFILE = config.mk +include $(CONFIGFILE) + + +OBJ =\ + interactive-test.o\ + libterminput.o + +HDR =\ + libterminput.h + + +all: libterminput.a interactive-test +$(OBJ): $(@:.o=.c) $(HDR) + +.c.o: + $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +.o.a: + $(AR) rc $@ $< + $(AR) -s $@ + +interactive-test: interactive-test.o libterminput.a + $(CC) -o $@ interactive-test.o libterminput.a $(LDFLAGS) + +install: libterminput.a + mkdir -p -- "$(DESTDIR)$(PREFIX)/lib" + mkdir -p -- "$(DESTDIR)$(PREFIX)/include" + cp -- libterminput.a "$(DESTDIR)$(PREFIX)/lib/" + cp -- libterminput.h "$(DESTDIR)$(PREFIX)/include/" + +uninstall: + rm -- "$(DESTDIR)$(PREFIX)/lib/libterminput.a" + rm -- "$(DESTDIR)$(PREFIX)/include/libterminput.h" + +clean: + -rm -f -- *.o *.a interactive-test + +.SUFFIXES: +.SUFFIXES: .a .o .c + +.PHONY: all install uninstall clean diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..9404b9a --- /dev/null +++ b/config.mk @@ -0,0 +1,8 @@ +PREFIX = /usr +MANPREFIX = $(PREFIX)/share/man + +CC = cc + +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 +CFLAGS = -std=c99 -O2 +LDFLAGS = -s diff --git a/interactive-test.c b/interactive-test.c new file mode 100644 index 0000000..cc41231 --- /dev/null +++ b/interactive-test.c @@ -0,0 +1,99 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdio.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#include "libterminput.h" + + +int +main(void) +{ + struct libterminput_state ctx; + union libterminput_input input; + struct termios stty, saved_stty; + int r; + + memset(&ctx, 0, sizeof(ctx)); + + if (tcgetattr(STDIN_FILENO, &stty)) { + perror("tcgetattr STDIN_FILENO"); + return 1; + } + saved_stty = stty; + stty.c_lflag &= (tcflag_t)~(ECHO | ICANON); + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &stty)) { + perror("tcsetattr STDIN_FILENO TCSAFLUSH"); + return 1; + } + + while ((r = libterminput_read(STDIN_FILENO, &input, &ctx)) > 0) { + if (input.type == LIBTERMINPUT_NONE) { + printf("none\n"); + } else if (input.type == LIBTERMINPUT_KEYPRESS) { + printf("keypress:\n"); + switch (input.keypress.key) { + case LIBTERMINPUT_SYMBOL: + printf("\t%s: %s\n", "key: symbol", input.keypress.symbol); + break; + case LIBTERMINPUT_UP: printf("\t%s: %s\n", "key", "up"); break; + case LIBTERMINPUT_DOWN: printf("\t%s: %s\n", "key", "down"); break; + case LIBTERMINPUT_RIGHT: printf("\t%s: %s\n", "key", "right"); break; + case LIBTERMINPUT_BEGIN: printf("\t%s: %s\n", "key", "begin"); break; + case LIBTERMINPUT_PAUSE: printf("\t%s: %s\n", "key", "pause"); break; + case LIBTERMINPUT_TAB: printf("\t%s: %s\n", "key", "tab"); break; + case LIBTERMINPUT_F1: printf("\t%s: %s\n", "key", "f1"); break; + case LIBTERMINPUT_F2: printf("\t%s: %s\n", "key", "f2"); break; + case LIBTERMINPUT_F3: printf("\t%s: %s\n", "key", "f3"); break; + case LIBTERMINPUT_F4: printf("\t%s: %s\n", "key", "f4"); break; + case LIBTERMINPUT_F5: printf("\t%s: %s\n", "key", "f5"); break; + case LIBTERMINPUT_F6: printf("\t%s: %s\n", "key", "f6"); break; + case LIBTERMINPUT_F7: printf("\t%s: %s\n", "key", "f7"); break; + case LIBTERMINPUT_F8: printf("\t%s: %s\n", "key", "f8"); break; + case LIBTERMINPUT_F9: printf("\t%s: %s\n", "key", "f9"); break; + case LIBTERMINPUT_F10: printf("\t%s: %s\n", "key", "f10"); break; + case LIBTERMINPUT_F11: printf("\t%s: %s\n", "key", "f11"); break; + case LIBTERMINPUT_F12: printf("\t%s: %s\n", "key", "f12"); break; + case LIBTERMINPUT_HOME: printf("\t%s: %s\n", "key", "home"); break; + case LIBTERMINPUT_INS: printf("\t%s: %s\n", "key", "ins"); break; + case LIBTERMINPUT_DEL: printf("\t%s: %s\n", "key", "del"); break; + case LIBTERMINPUT_END: printf("\t%s: %s\n", "key", "end"); break; + case LIBTERMINPUT_PRIOR: printf("\t%s: %s\n", "key", "prior"); break; + case LIBTERMINPUT_NEXT: printf("\t%s: %s\n", "key", "next"); break; + case LIBTERMINPUT_ERASE: printf("\t%s: %s\n", "key", "erase"); break; + case LIBTERMINPUT_ENTER: printf("\t%s: %s\n", "key", "enter"); break; + case LIBTERMINPUT_ESC: printf("\t%s: %s\n", "key", "esc"); break; + case LIBTERMINPUT_KEYPAD_0: printf("\t%s: %s\n", "key", "keypad 0"); break; + case LIBTERMINPUT_KEYPAD_1: printf("\t%s: %s\n", "key", "keypad 1"); break; + case LIBTERMINPUT_KEYPAD_2: printf("\t%s: %s\n", "key", "keypad 2"); break; + case LIBTERMINPUT_KEYPAD_3: printf("\t%s: %s\n", "key", "keypad 3"); break; + case LIBTERMINPUT_KEYPAD_4: printf("\t%s: %s\n", "key", "keypad 4"); break; + case LIBTERMINPUT_KEYPAD_5: printf("\t%s: %s\n", "key", "keypad 5"); break; + case LIBTERMINPUT_KEYPAD_6: printf("\t%s: %s\n", "key", "keypad 6"); break; + case LIBTERMINPUT_KEYPAD_7: printf("\t%s: %s\n", "key", "keypad 7"); break; + case LIBTERMINPUT_KEYPAD_8: printf("\t%s: %s\n", "key", "keypad 8"); break; + case LIBTERMINPUT_KEYPAD_9: printf("\t%s: %s\n", "key", "keypad 9"); break; + case LIBTERMINPUT_KEYPAD_MINUS: printf("\t%s: %s\n", "key", "keypad minus"); break; + case LIBTERMINPUT_KEYPAD_COMMA: printf("\t%s: %s\n", "key", "keypad comma"); break; + case LIBTERMINPUT_KEYPAD_POINT: printf("\t%s: %s\n", "key", "keypad point"); break; + case LIBTERMINPUT_KEYPAD_ENTER: printf("\t%s: %s\n", "key", "keypad enter"); break; + default: + printf("\t%s: %s\n", "key", "other"); + break; + } + printf("\t%s: %s\n", "shift", (input.keypress.mods & LIBTERMINPUT_SHIFT) ? "yes" : "no"); + printf("\t%s: %s\n", "meta", (input.keypress.mods & LIBTERMINPUT_META) ? "yes" : "no"); + printf("\t%s: %s\n", "ctrl", (input.keypress.mods & LIBTERMINPUT_CTRL) ? "yes" : "no"); + printf("\t%s: %s (%llu)\n", "will repeat", input.keypress.times > 1 ? "yes" : "no", input.keypress.times); + } else { + printf("other\n"); + } + } + + if (r < 0) + perror("libterminput_read STDIN_FILENO"); + + tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_stty); + return -r; +} diff --git a/libterminput.c b/libterminput.c new file mode 100644 index 0000000..a255373 --- /dev/null +++ b/libterminput.c @@ -0,0 +1,345 @@ +/* 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; + int r; + + /* Get next byte from input */ + if (ctx->have_stored) { + ctx->have_stored = 0; + c = (unsigned char)ctx->stored; + } else { + r = read(fd, &c, 1); + if (r <= 0) + return r; + } + + 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[ctx->npartial] = '\0'; + ctx->n = 0; + ctx->npartial = 0; + ctx->mods = 0; + ctx->have_stored = 1; + ctx->stored = (char)c; + strcpy(input->symbol, ctx->partial); + return 1; + } else { + /* Store byte, and if done, return */ + ctx->partial[ctx->npartial++] = c; + if (ctx->npartial == ctx->n) { + ctx->partial[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 */ + 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) { + /* 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] = c; + input->symbol[1] = '\0'; + input->mods = ctx->mods; + ctx->mods = 0; + return 1; + } + ctx->partial[0] = c; + ctx->npartial = 1; + } else { + /* Single-byte-character or stray multi-byte continuation byte */ + input->symbol[0] = 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; + size_t keylen, n; + char *p; + + /* Get number of numbers in the sequence, and allocate an array of at least 2 */ + for (n = 2, p = ctx->key; *p; p++) + n += *p == ';'; + 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 'G': input->keypress.key = LIBTERMINPUT_BEGIN; break; + case 'P': input->keypress.key = LIBTERMINPUT_PAUSE; break; + case 'Z': + input->keypress.key = LIBTERMINPUT_TAB; + input->keypress.mods |= LIBTERMINPUT_SHIFT; + break; + 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 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; + default: + input->type = LIBTERMINPUT_NONE; + return; + } + if (25 <= nums[0] && nums[0] <= 34) + input->keypress.mods |= LIBTERMINPUT_SHIFT; + break; + default: + input->type = LIBTERMINPUT_NONE; + break; + } + 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: + input->type = LIBTERMINPUT_NONE; + break; + } + break; + default: + input->type = LIBTERMINPUT_NONE; + break; + } + break; + + case 'O': + switch (!ctx->key[2] ? ctx->key[1] : 0) { + case 'H': input->keypress.key = LIBTERMINPUT_HOME; break; + case 'F': input->keypress.key = LIBTERMINPUT_END; 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 'm': input->keypress.key = LIBTERMINPUT_KEYPAD_MINUS; 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: + input->type = LIBTERMINPUT_NONE; + break; + } + break; + + default: + /* This shouldn't happen */ + 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; + + 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; + } + + r = read_input(fd, &ret, ctx); + if (r <= 0) + return r; + +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 = ret.mods; + 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 */ + if (!isalpha(p[-1]) && p[-1] != '~') { + 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; + default: + input->keypress.key = LIBTERMINPUT_SYMBOL; + strcpy(input->keypress.symbol, ret.symbol); + break; + } + } + + return 1; +} diff --git a/libterminput.h b/libterminput.h new file mode 100644 index 0000000..b86a2bd --- /dev/null +++ b/libterminput.h @@ -0,0 +1,106 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef LIBTERMINPUT_H +#define LIBTERMINPUT_H + +#include <stddef.h> + + +enum libterminput_mod { + LIBTERMINPUT_SHIFT = 0x01, + LIBTERMINPUT_META = 0x02, + LIBTERMINPUT_CTRL = 0x04 +}; + +enum libterminput_key { + LIBTERMINPUT_SYMBOL, + LIBTERMINPUT_UP, + LIBTERMINPUT_DOWN, + LIBTERMINPUT_RIGHT, + LIBTERMINPUT_LEFT, + LIBTERMINPUT_BEGIN, /* keypad 5 without numlock */ + LIBTERMINPUT_PAUSE, + LIBTERMINPUT_TAB, /* backtab if with shift */ + LIBTERMINPUT_F1, + LIBTERMINPUT_F2, + LIBTERMINPUT_F3, + LIBTERMINPUT_F4, + LIBTERMINPUT_F5, + LIBTERMINPUT_F6, + LIBTERMINPUT_F7, + LIBTERMINPUT_F8, + LIBTERMINPUT_F9, + LIBTERMINPUT_F10, + LIBTERMINPUT_F11, + LIBTERMINPUT_F12, + LIBTERMINPUT_HOME, + LIBTERMINPUT_INS, + LIBTERMINPUT_DEL, + LIBTERMINPUT_END, + LIBTERMINPUT_PRIOR, /* page up */ + LIBTERMINPUT_NEXT, /* page down */ + LIBTERMINPUT_ERASE, /* backspace */ + LIBTERMINPUT_ENTER, /* return */ + LIBTERMINPUT_ESC, + LIBTERMINPUT_KEYPAD_0, + LIBTERMINPUT_KEYPAD_1, + LIBTERMINPUT_KEYPAD_2, + LIBTERMINPUT_KEYPAD_3, + LIBTERMINPUT_KEYPAD_4, + LIBTERMINPUT_KEYPAD_5, + LIBTERMINPUT_KEYPAD_6, + LIBTERMINPUT_KEYPAD_7, + LIBTERMINPUT_KEYPAD_8, + LIBTERMINPUT_KEYPAD_9, + LIBTERMINPUT_KEYPAD_MINUS, + LIBTERMINPUT_KEYPAD_COMMA, + LIBTERMINPUT_KEYPAD_POINT, + LIBTERMINPUT_KEYPAD_ENTER +}; + +enum libterminput_type { + LIBTERMINPUT_NONE, + LIBTERMINPUT_KEYPRESS +}; + +struct libterminput_keypress { + enum libterminput_type type; + enum libterminput_key key; + unsigned long long int times; /* if .times > 1, next will be the same, but will .times -= 1 */ + enum libterminput_mod mods; + char symbol[7]; /* use if .key == LIBTERMINPUT_SYMBOL */ +}; + +union libterminput_input { + enum libterminput_type type; + struct libterminput_keypress keypress; +}; + + +/** + * This struct should be considered opaque + */ +struct libterminput_state { + int inited; /* whether the input in initialised, not this struct */ + enum libterminput_mod mods; + char meta; + char n; + char have_stored; + char npartial; + char stored; + char partial[7]; + char key[44]; +}; + + +/** + * Get input from the terminal + * + * @param fd The file descriptor to the terminal + * @param input Output parameter for input + * @param ctx State for the terminal, parts of the state may be stored in `input` + * @return 1 normally, 0 on end of input, -1 on error + */ +int libterminput_read(int fd, union libterminput_input *input, struct libterminput_state *ctx); + + +#endif |