aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <m@maandree.se>2025-02-19 19:53:53 +0100
committerMattias Andrée <m@maandree.se>2025-02-19 19:55:30 +0100
commita40d93ab5a064f8f2f9696acd1b57ef3603cd2fe (patch)
tree288671ad6f1b0d8c57a00b18b04f21b827eab24d
parentminor readability improvement (diff)
downloadlibterminput-a40d93ab5a064f8f2f9696acd1b57ef3603cd2fe.tar.gz
libterminput-a40d93ab5a064f8f2f9696acd1b57ef3603cd2fe.tar.bz2
libterminput-a40d93ab5a064f8f2f9696acd1b57ef3603cd2fe.tar.xz
misc cleanup, fixes, and clarifications, and escape sequence
Signed-off-by: Mattias Andrée <m@maandree.se>
-rw-r--r--Makefile6
-rw-r--r--common.h66
-rw-r--r--interactive-test.c47
-rw-r--r--libterminput.h44
-rw-r--r--libterminput_is_ready.330
-rw-r--r--libterminput_parse_csi_m_mouse_tracking__.c54
-rw-r--r--libterminput_parse_csi_small_t_mouse_tracking__.c24
-rw-r--r--libterminput_parse_csi_t_mouse_tracking__.c36
-rw-r--r--libterminput_parse_decimal_mouse_tracking__.c23
-rw-r--r--libterminput_parse_sequence__.c274
-rw-r--r--libterminput_read.c615
-rw-r--r--libterminput_read_bracketed_paste__.c3
-rw-r--r--libterminput_read_symbol__.c110
-rw-r--r--test.c38
14 files changed, 801 insertions, 569 deletions
diff --git a/Makefile b/Makefile
index e958335..552a2e3 100644
--- a/Makefile
+++ b/Makefile
@@ -24,6 +24,12 @@ OBJ =\
libterminput_check_utf8_char__.o\
libterminput_utf8_decode__.o\
libterminput_read_bracketed_paste__.o\
+ libterminput_parse_decimal_mouse_tracking__.o\
+ libterminput_parse_csi_m_mouse_tracking__.o\
+ libterminput_parse_csi_t_mouse_tracking__.o\
+ libterminput_parse_csi_small_t_mouse_tracking__.o\
+ libterminput_parse_sequence__.o\
+ libterminput_read_symbol__.o
HDR =\
libterminput.h\
diff --git a/common.h b/common.h
index 05b5269..ba96f50 100644
--- a/common.h
+++ b/common.h
@@ -3,6 +3,7 @@
#include <alloca.h>
#include <ctype.h>
+#include <errno.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
@@ -15,8 +16,18 @@
#endif
+/**
+ * Singlar read symbol
+ */
struct input {
+ /**
+ * Applied modifier keys
+ */
enum libterminput_mod mods;
+
+ /**
+ * The read symbol; NUL-byte terminated
+ */
char symbol[7];
};
@@ -64,5 +75,60 @@ HIDDEN unsigned long long int libterminput_utf8_decode__(const char *s, size_t *
*/
HIDDEN int libterminput_read_bracketed_paste__(int fd, union libterminput_input *input, struct libterminput_state *ctx);
+/**
+ * Parse mouse tracking event data
+ *
+ * @param input Output parameter for the parsed event
+ * @param nums The numbers assoicated with the event
+ */
+HIDDEN void libterminput_parse_decimal_mouse_tracking__(union libterminput_input *input, unsigned long long int nums[3]);
+
+/**
+ * Parse a CSI M mouse tracking event
+ *
+ * @param input Output parameter for the parsed event
+ * @param ctx State for the terminal, parts of the state may be stored in `input`
+ * @param nums Numbers insert reported for the event (between CSI and M)
+ * @param nnums Number of elements in `nums`
+ */
+HIDDEN void libterminput_parse_csi_m_mouse_tracking__(union libterminput_input *input, struct libterminput_state *ctx,
+ unsigned long long int *nums, size_t nnums);
+
+/**
+ * Parse a CSI T mouse tracking event
+ *
+ * @param input Output parameter for the parsed event
+ * @param ctx State for the terminal, parts of the state may be stored in `input`
+ */
+HIDDEN void libterminput_parse_csi_t_mouse_tracking__(union libterminput_input *input, struct libterminput_state *ctx);
+
+/**
+ * Parse a CSI t mouse tracking event
+ *
+ * @param input Output parameter for the parsed event
+ * @param ctx State for the terminal, parts of the state may be stored in `input`
+ */
+HIDDEN void libterminput_parse_csi_small_t_mouse_tracking__(union libterminput_input *input, struct libterminput_state *ctx);
+
+/**
+ * Parse a complete, atomic input sequence out side of a bracketed paste
+ *
+ * @param input Output parameter for the parsed event
+ * @param ctx State for the terminal, parts of the state may be stored in `input`
+ */
+HIDDEN void libterminput_parse_sequence__(union libterminput_input *input, struct libterminput_state *ctx);
+
+/**
+ * Read a singular symbol 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
+ *
+ * @throws Any reason specified for read(3)
+ */
+HIDDEN int libterminput_read_symbol__(int fd, struct input *input, struct libterminput_state *ctx);
+
#undef HIDDEN
diff --git a/interactive-test.c b/interactive-test.c
index d982ec5..687e391 100644
--- a/interactive-test.c
+++ b/interactive-test.c
@@ -1,4 +1,7 @@
/* See LICENSE file for copyright and license details. */
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -8,16 +11,32 @@
#include "libterminput.h"
+static volatile sig_atomic_t interrupted = 0;
+
+
+static void
+sigint_handler(int signo)
+{
+ (void) signo;
+ interrupted = 1;
+}
+
+
int
main(void)
{
struct libterminput_state ctx;
union libterminput_input input;
struct termios stty, saved_stty;
- int r, print_state;
+ int r, print_state, flags;
+ struct sigaction sa;
memset(&ctx, 0, sizeof(ctx));
+ memset(&sa, 0, sizeof(sa)); /* importantly, SA_RESTART is cleared from sa.sa_flags */
+ sa.sa_handler = &sigint_handler;
+ sigaction(SIGINT, &sa, NULL);
+
if (getenv("TEST_LIBTERMINPUT_DECSET_1005")) {
fprintf(stderr, "LIBTERMINPUT_DECSET_1005 set\n");
libterminput_set_flags(&ctx, LIBTERMINPUT_DECSET_1005);
@@ -60,6 +79,18 @@ main(void)
return 1;
}
+ flags = fcntl(STDIN_FILENO, F_GETFL);
+ if (flags < 0) {
+ perror("fcntl STDIN_FILENO F_GETFL");
+ return 1;
+ } else if (!(flags & O_NONBLOCK)) {
+ if (fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK) < 0) {
+ perror("fcntl STDIN_FILENO F_SETFL <old>|O_NONBLOCK");
+ return 1;
+ }
+ }
+
+again:
while ((r = libterminput_read(STDIN_FILENO, &input, &ctx)) > 0) {
if (input.type == LIBTERMINPUT_NONE) {
printf("none\n");
@@ -198,9 +229,19 @@ main(void)
}
}
- if (r < 0)
+ if (r < 0 && !interrupted) {
+ if (errno == EAGAIN) {
+ fd_set fdset;
+ FD_ZERO(&fdset);
+ FD_SET(STDIN_FILENO, &fdset);
+ select(1, &fdset, NULL, NULL, NULL);
+ goto again;
+ }
perror("libterminput_read STDIN_FILENO");
+ }
+ if (!(flags & O_NONBLOCK))
+ fcntl(STDIN_FILENO, F_SETFL, flags);
tcsetattr(STDERR_FILENO, TCSAFLUSH, &saved_stty);
- return -r;
+ return -r && !interrupted;
}
diff --git a/libterminput.h b/libterminput.h
index e39befb..b0c55af 100644
--- a/libterminput.h
+++ b/libterminput.h
@@ -554,25 +554,28 @@ union libterminput_input {
/**
* The current input state and configurations
*
- * This struct should be considered opaque
+ * NB! This struct should be considered opaque
*
* Initialised with by setting all bytes to 0
*/
struct libterminput_state {
- int inited; /* whether the input in initialised, not this struct */
- enum libterminput_mod mods;
- enum libterminput_flags flags;
- char bracketed_paste;
- char mouse_tracking;
- char meta;
- char n;
- size_t stored_head;
- size_t stored_tail;
- char paused;
- char npartial;
- char partial[7];
- char key[44];
- char stored[512];
+ int inited; /* whether the input in initialised, not this struct */
+ enum libterminput_mod mods; /* currently active modifier keys */
+ enum libterminput_flags flags; /* applied behaviour flags */
+ unsigned char bracketed_paste; /* 1 if in bracketed paste, 0 otherwise */
+ unsigned char mouse_tracking; /* length of mouse tracking data, 0 if not processing an mouse tracking event,
+ * 1 if undetermined */
+ unsigned char meta; /* number of captured meta/escape bytes */
+ unsigned char n; /* bytes length of partially read character */
+ size_t stored_head; /* index of first available byte in `.stored` */
+ size_t stored_tail; /* index of next byte to write in `.stored` */
+ unsigned char paused : 1; /* 1 if the available buffered input is incomplete */
+ unsigned char blocked : 1; /* 1 if the available no data was available for read(2) */
+ unsigned char queued : 1; /* 1 if a keypress is queued for output if there is no more data at next read(2) */
+ unsigned char npartial; /* number of bytes available in `.partial` */
+ char partial[7]; /* partially read character */
+ char key[44]; /* processed bytes the identify the pressed key or non-key-press event */
+ char stored[512]; /* buffered input not yet processed */
};
@@ -583,7 +586,7 @@ struct libterminput_state {
* @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
- *
+ *
* @throws Any reason specified for read(3)
*/
int libterminput_read(int fd, union libterminput_input *input, struct libterminput_state *ctx);
@@ -593,6 +596,15 @@ int libterminput_read(int fd, union libterminput_input *input, struct libterminp
* by `libterminput_read` without performing any additional
* read(3) operation
*
+ * This function should only be used if using blocking
+ * read(3) operations. The flag LIBTERMINPUT_ESC_ON_BLOCK
+ * is only meaningful for non-blocking read. With non-blocking
+ * read, the application should call libterminput_read(3)
+ * and check for EAGAIN error rather than using this function,
+ * as EAGAIN errors on read can cause libterminput_read(3) to
+ * output keypresses it would otherwise not output, but in
+ * doing so not report EAGAIN.
+ *
* @param input Input gathered by `libterminput_read`
* @param ctx State for the terminal
* @return 1 if there there is more input available, 0 otherwise
diff --git a/libterminput_is_ready.3 b/libterminput_is_ready.3
index 08e6fd6..27b98f6 100644
--- a/libterminput_is_ready.3
+++ b/libterminput_is_ready.3
@@ -24,6 +24,33 @@ is buffered data but the library knows it
it not enough to return something they,
it will also return that there is nothing
buffered.
+.PP
+The
+.BR libterminput_is_ready ()
+function should only be used if using blocking
+read operations (terminal file descriptor configured with
+.I O_NONBLOCK
+cleared). The flag
+.I LIBTERMINPUT_ESC_ON_BLOCK
+is only meaningful for non-blocking reads. With non-blocking
+reads, the application should call
+.BR libterminput_read (3)
+and check for
+.I EAGAIN
+error rather than using the
+.BR libterminput_is_ready ()
+function, as
+.I EAGAIN
+errors on read can cause the
+.BR libterminput_read (3)
+function to output keypresses it would otherwise not output,
+but in doing so not report
+.I EAGAIN
+(although
+.I errno
+will still be set to
+.IR EAGAIN ,
+so the application can still check if this occurred).
.SH RETURN VALUE
The
@@ -57,4 +84,5 @@ None.
None.
.SH SEE ALSO
-.BR libterminput_read (3)
+.BR libterminput_read (3),
+.BR fcntl (3)
diff --git a/libterminput_parse_csi_m_mouse_tracking__.c b/libterminput_parse_csi_m_mouse_tracking__.c
new file mode 100644
index 0000000..f943c04
--- /dev/null
+++ b/libterminput_parse_csi_m_mouse_tracking__.c
@@ -0,0 +1,54 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+void
+libterminput_parse_csi_m_mouse_tracking__(union libterminput_input *input, struct libterminput_state *ctx,
+ unsigned long long int *nums, size_t nnums)
+{
+ unsigned long long int numsbuf[3];
+ size_t pos;
+
+ if (nnums >= 3U) {
+ /* Parsing for \e[?1000;1015h output */
+ nums[0] -= 32ULL;
+
+ } else if (!nnums && (ctx->flags & LIBTERMINPUT_DECSET_1005)) {
+ /* 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;
+
+ } else if (!nnums) {
+ /* 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;
+
+ } else {
+ input->type = LIBTERMINPUT_NONE;
+ return;
+ }
+
+ input->mouseevent.event = LIBTERMINPUT_PRESS;
+ libterminput_parse_decimal_mouse_tracking__(input, nums);
+}
diff --git a/libterminput_parse_csi_small_t_mouse_tracking__.c b/libterminput_parse_csi_small_t_mouse_tracking__.c
new file mode 100644
index 0000000..14a1cf7
--- /dev/null
+++ b/libterminput_parse_csi_small_t_mouse_tracking__.c
@@ -0,0 +1,24 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+void
+libterminput_parse_csi_small_t_mouse_tracking__(union libterminput_input *input, struct libterminput_state *ctx)
+{
+ unsigned long long int nums[2];
+
+ /* Parsing output for legacy mouse highlight tracking output (\e[?1001h) */
+ ctx->mouse_tracking = 0;
+ 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];
+}
diff --git a/libterminput_parse_csi_t_mouse_tracking__.c b/libterminput_parse_csi_t_mouse_tracking__.c
new file mode 100644
index 0000000..47e4505
--- /dev/null
+++ b/libterminput_parse_csi_t_mouse_tracking__.c
@@ -0,0 +1,36 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+void
+libterminput_parse_csi_t_mouse_tracking__(union libterminput_input *input, struct libterminput_state *ctx)
+{
+ unsigned long long int nums[6];
+
+ /* Parsing output for legacy mouse highlight tracking output. (\e[?1001h) */
+ ctx->mouse_tracking = 0;
+ 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];
+}
diff --git a/libterminput_parse_decimal_mouse_tracking__.c b/libterminput_parse_decimal_mouse_tracking__.c
new file mode 100644
index 0000000..01b6040
--- /dev/null
+++ b/libterminput_parse_decimal_mouse_tracking__.c
@@ -0,0 +1,23 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+void
+libterminput_parse_decimal_mouse_tracking__(union libterminput_input *input, unsigned long long int nums[3])
+{
+ 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];
+}
diff --git a/libterminput_parse_sequence__.c b/libterminput_parse_sequence__.c
new file mode 100644
index 0000000..aaf0fb4
--- /dev/null
+++ b/libterminput_parse_sequence__.c
@@ -0,0 +1,274 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+void
+libterminput_parse_sequence__(union libterminput_input *input, struct libterminput_state *ctx)
+{
+ unsigned long long int *nums;
+ size_t keylen, n, nnums = 0;
+ 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 = 2U, p = ctx->key; *p; p++) {
+ if (*p == ';') {
+ n += 1U;
+ nnums += 1U;
+ }
+ }
+ 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 < 3U) {
+ 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
+ libterminput_parse_csi_m_mouse_tracking__(input, ctx, nums, nnums);
+ 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 >= 2U) {
+ 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':
+ libterminput_parse_csi_t_mouse_tracking__(input, ctx);
+ 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_BACKTAB;
+ } else {
+ input->keypress.key = LIBTERMINPUT_TAB;
+ input->keypress.mods |= LIBTERMINPUT_SHIFT;
+ }
+ break;
+ case 'a': input->keypress.key = LIBTERMINPUT_UP; goto shift;
+ case 'b': input->keypress.key = LIBTERMINPUT_DOWN; goto shift;
+ case 'c': input->keypress.key = LIBTERMINPUT_RIGHT; goto shift;
+ case 'd': input->keypress.key = LIBTERMINPUT_LEFT; goto shift;
+ case 'n':
+ if (nnums == 1U && nums[0] == 0)
+ input->type = LIBTERMINPUT_TERMINAL_IS_OK;
+ else if (nnums == 1U && nums[0] == 3)
+ input->type = LIBTERMINPUT_TERMINAL_IS_NOT_OK;
+ else
+ goto suppress;
+ break;
+ case 't':
+ libterminput_parse_csi_small_t_mouse_tracking__(input, ctx);
+ 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; goto shift;
+ case 26: input->keypress.key = LIBTERMINPUT_F2; goto shift;
+ case 28: input->keypress.key = LIBTERMINPUT_F3; goto shift;
+ case 29: input->keypress.key = LIBTERMINPUT_F4; goto shift;
+ case 31: input->keypress.key = LIBTERMINPUT_F5; goto shift;
+ case 32: input->keypress.key = LIBTERMINPUT_F6; goto shift;
+ case 33: input->keypress.key = LIBTERMINPUT_F7; goto shift;
+ case 34: input->keypress.key = LIBTERMINPUT_F8; goto shift;
+ 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;
+ }
+ 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 >= 3U) {
+ /* Parsing for \e[?1003;1006h output. */
+ input->mouseevent.event = LIBTERMINPUT_PRESS;
+ if (ctx->key[2] == 'm')
+ input->mouseevent.event = LIBTERMINPUT_RELEASE;
+ libterminput_parse_decimal_mouse_tracking__(input, nums);
+ } else {
+ goto suppress;
+ }
+ }
+ break;
+
+ default:
+ goto suppress;
+ }
+ break;
+
+ case '?': /* '?' is attested as synonym for 'O' when the next character is in "pqrstuvwxymlnM" */
+ 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 'I': input->keypress.key = LIBTERMINPUT_F12; break;
+ case 'J': input->keypress.key = LIBTERMINPUT_F1; goto shift;
+ case 'K': input->keypress.key = LIBTERMINPUT_F2; goto shift;
+ case 'L': input->keypress.key = LIBTERMINPUT_F3; goto shift;
+ case 'M': input->keypress.key = LIBTERMINPUT_KEYPAD_ENTER; break;
+ case 'N': input->keypress.key = LIBTERMINPUT_F4; goto shift;
+ 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 'T': input->keypress.key = LIBTERMINPUT_F5; break;
+ case 'U': input->keypress.key = LIBTERMINPUT_F6; break;
+ case 'V': input->keypress.key = LIBTERMINPUT_F7; break; /* not attested */
+ case 'W': input->keypress.key = LIBTERMINPUT_F8; break; /* not attested */
+ case 'X': input->keypress.key = LIBTERMINPUT_F9; break; /* not attested */
+ case 'Y': input->keypress.key = LIBTERMINPUT_F10; break;
+ case 'Z': input->keypress.key = LIBTERMINPUT_F11; break; /* not attested */
+ case 'b': input->keypress.key = LIBTERMINPUT_KEYPAD_POINT; break;
+ case 'e': input->keypress.key = LIBTERMINPUT_F7; break;
+ case 'f': input->keypress.key = LIBTERMINPUT_F8; break;
+ case 'j': input->keypress.key = LIBTERMINPUT_KEYPAD_TIMES; break;
+ case 'k': input->keypress.key = LIBTERMINPUT_KEYPAD_PLUS; break;
+ case 'l': input->keypress.key = LIBTERMINPUT_KEYPAD_COMMA; break;
+ case 'm': input->keypress.key = LIBTERMINPUT_KEYPAD_MINUS; break;
+ case 'n': input->keypress.key = LIBTERMINPUT_KEYPAD_DECIMAL; break;
+ case 'o': input->keypress.key = LIBTERMINPUT_KEYPAD_DIVISION; 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;
+ default:
+ goto suppress;
+ }
+ break;
+ shift:
+ input->keypress.mods |= LIBTERMINPUT_SHIFT;
+ break;
+
+ default:
+ /* This shouldn't happen (without goto) */
+ suppress:
+ input->type = LIBTERMINPUT_NONE;
+ break;
+ }
+}
diff --git a/libterminput_read.c b/libterminput_read.c
index 0c46585..0ca3c36 100644
--- a/libterminput_read.c
+++ b/libterminput_read.c
@@ -2,515 +2,56 @@
#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 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 if (!nnums) {
- /* 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 {
- 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;
+ 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 = 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;
+ 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;
+ return 1;
}
- 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;
+ 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], (size_t)ctx->mouse_tracking - (ctx->stored_head - ctx->stored_tail));
+ 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;
@@ -520,11 +61,21 @@ libterminput_read(int fd, union libterminput_input *input, struct libterminput_s
again:
if (!*ret.symbol) {
- /* Incomplete input */
+ /* 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 */
- input->type = LIBTERMINPUT_NONE;
- return 1;
+ /* 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;
+ goto none;
+ }
+ goto none;
}
/* Three ESC's */
input->type = LIBTERMINPUT_KEYPRESS;
@@ -533,6 +84,7 @@ again:
input->keypress.mods = 0;
input->keypress.symbol[0] = '\0';
ctx->meta -= 3;
+
} else if (*ctx->key) {
/* Special keys */
if (ret.mods) {
@@ -545,42 +97,29 @@ again:
m = strlen(ret.symbol);
if (n + m >= sizeof(ctx->key)) {
/* Abort if too long */
- input->type = LIBTERMINPUT_NONE;
- return 1;
+ 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] != '$') {
- input->type = LIBTERMINPUT_NONE;
- return 1;
+ goto none;
} else if (ctx->key[0] == '[' && ctx->key[1] == '<' && p == &ctx->key[2]) {
- input->type = LIBTERMINPUT_NONE;
- return 1;
+ goto none;
} else if (ctx->key[0] == '[' && ctx->key[1] == 'M' && (ctx->flags & LIBTERMINPUT_MACRO_ON_CSI_M)) {
/* complete */
+ /* TODO optionally this should also be the case if blocked */
} 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;
+ 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;
@@ -592,28 +131,27 @@ again:
ctx->key[0] = '\0';
return 1;
}
- } else if (ctx->key[0] == '[' && ctx->key[1] == 'M' && ctx->stored_head - ctx->stored_tail < 3) {
+ } else if (ctx->key[0] == '[' && ctx->key[1] == 'M' && ctx->stored_head - ctx->stored_tail < 3U) {
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) {
+ goto none;
+ } else if (ctx->key[0] == '[' && ctx->key[1] == 't' && ctx->stored_head - ctx->stored_tail < 2U) {
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) {
+ goto none;
+ } else if (ctx->key[0] == '[' && ctx->key[1] == 'T' && ctx->stored_head - ctx->stored_tail < 6U) {
ctx->mouse_tracking = 6;
- input->type = LIBTERMINPUT_NONE;
- return 1;
+ goto none;
}
/* Parse the complete sequence */
- parse_sequence(input, ctx);
+ libterminput_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 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;
@@ -624,30 +162,23 @@ again:
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;
- 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;
+ 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;
- strcpy(input->keypress.symbol, ret.symbol);
+ stpcpy(input->keypress.symbol, ret.symbol);
break;
}
}
return 1;
+
+none:
+ input->type = LIBTERMINPUT_NONE;
+ return 1;
}
diff --git a/libterminput_read_bracketed_paste__.c b/libterminput_read_bracketed_paste__.c
index 0c7a81f..02fecc5 100644
--- a/libterminput_read_bracketed_paste__.c
+++ b/libterminput_read_bracketed_paste__.c
@@ -84,7 +84,8 @@ libterminput_read_bracketed_paste__(int fd, union libterminput_input *input, str
}
/* If the input is solely a truncation of the bracketed paste
- * end marker, output that we do not have any complete input */
+ * end marker, output that we do not have any complete input,
+ * and pause as the available buffered input is incomplete */
if (input->text.nbytes < 6U) {
input->text.type = LIBTERMINPUT_NONE;
memcpy(ctx->stored, input->text.bytes, input->text.nbytes);
diff --git a/libterminput_read_symbol__.c b/libterminput_read_symbol__.c
new file mode 100644
index 0000000..34f4b21
--- /dev/null
+++ b/libterminput_read_symbol__.c
@@ -0,0 +1,110 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+int
+libterminput_read_symbol__(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 = 1U;
+ ctx->stored_head = (size_t)r;
+ }
+ }
+
+ /* Check if symbol is complete or can be completed, and split out modifier */
+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 */
+ 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;
+ }
+
+ /* Incomplete symbol */
+ input->symbol[0] = '\0';
+ input->mods = -1;
+ return 1;
+}
diff --git a/test.c b/test.c
index 4fd7b0f..25a8266 100644
--- a/test.c
+++ b/test.c
@@ -1,5 +1,6 @@
/* See LICENSE file for copyright and license details. */
#include <errno.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -15,6 +16,7 @@ static const struct keypress {
enum libterminput_mod mods;
enum libterminput_flags flags;
} keypresses[] = {
+ /* TODO test new ESC O keys and test replacing ESC O with ESC ? */
{"\033[[", "A", LIBTERMINPUT_F1, 0, 0},
{"\033[[", "B", LIBTERMINPUT_F2, 0, 0},
{"\033[[", "C", LIBTERMINPUT_F3, 0, 0},
@@ -335,12 +337,21 @@ static int fds[2];
static void
type_mem(const char *str, size_t len, enum libterminput_type type)
{
+ int flags, r;
alarm(5);
if (len)
TEST(write(fds[1], str, len) == (ssize_t)len);
- do {
- TEST(libterminput_read(fds[0], &input, &ctx) == 1);
- } while (input.type == LIBTERMINPUT_NONE && libterminput_is_ready(&input, &ctx));
+ TEST((flags = fcntl(fds[0], F_GETFL)) >= 0);
+ if (flags & O_NONBLOCK) {
+ do {
+ r = libterminput_read(fds[0], &input, &ctx);
+ TEST(r == 1 || (r == -1 && errno == EAGAIN));
+ } while (input.type == LIBTERMINPUT_NONE && r > 0);
+ } else {
+ do {
+ TEST(libterminput_read(fds[0], &input, &ctx) == 1);
+ } while (input.type == LIBTERMINPUT_NONE && libterminput_is_ready(&input, &ctx));
+ }
TEST(input.type == type);
}
@@ -348,16 +359,25 @@ static void
keypress_(const char *str1, const char *str2, const char *str3, const char *str4,
enum libterminput_key key, enum libterminput_mod mods, unsigned long long int times)
{
+ int flags, r;
unsigned long long int times_;
size_t i;
alarm(5);
stpcpy(stpcpy(stpcpy(stpcpy(buffer, str1), str2), str3), str4);
if (*buffer)
TEST(write(fds[1], buffer, strlen(buffer)) == (ssize_t)strlen(buffer));
+ TEST((flags = fcntl(fds[0], F_GETFL)) >= 0);
for (times_ = times; times_; times_--) {
- do {
- TEST(libterminput_read(fds[0], &input, &ctx) == 1);
- } while (input.type == LIBTERMINPUT_NONE && libterminput_is_ready(&input, &ctx));
+ if (flags & O_NONBLOCK) {
+ do {
+ r = libterminput_read(fds[0], &input, &ctx);
+ TEST(r == 1 || (r == -1 && errno == EAGAIN));
+ } while (input.type == LIBTERMINPUT_NONE && r > 0);
+ } else {
+ do {
+ TEST(libterminput_read(fds[0], &input, &ctx) == 1);
+ } while (input.type == LIBTERMINPUT_NONE && libterminput_is_ready(&input, &ctx));
+ }
TEST(input.type == LIBTERMINPUT_KEYPRESS);
TEST(input.keypress.key == key);
TEST(input.keypress.mods == mods);
@@ -497,10 +517,14 @@ int
main(void)
{
size_t i;
+ int flags;
memset(&ctx, 0, sizeof(ctx));
TEST(!pipe(fds));
+ flags = fcntl(fds[0], F_GETFL);
+ TEST(flags >= 0);
+
for (i = 0; keypresses[i].part1; i++) {
libterminput_set_flags(&ctx, keypresses[i].flags);
KEYPRESS(keypresses[i].part1, keypresses[i].part2, keypresses[i].key, keypresses[i].mods);
@@ -598,7 +622,9 @@ main(void)
KEYPRESS_SPECIAL_CHAR('\t', LIBTERMINPUT_TAB);
KEYPRESS_SPECIAL_CHAR('\n', LIBTERMINPUT_ENTER);
libterminput_set_flags(&ctx, LIBTERMINPUT_ESC_ON_BLOCK);
+ TEST(fcntl(fds[0], F_SETFL, flags | O_NONBLOCK) >= 0);
KEYPRESS_SPECIAL_CHAR('\033', LIBTERMINPUT_ESC);
+ TEST(fcntl(fds[0], F_SETFL, flags) >= 0);
libterminput_clear_flags(&ctx, LIBTERMINPUT_ESC_ON_BLOCK);
TYPE("text", LIBTERMINPUT_KEYPRESS);