From 8f09401e3d7d238355391e4abf3746987c95c492 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Tue, 6 Apr 2021 17:53:09 +0200 Subject: Add support for bracketed paste MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- TODO | 2 +- interactive-test.c | 8 ++++++ libterminput.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++---- libterminput.h | 20 ++++++++++--- 4 files changed, 102 insertions(+), 10 deletions(-) diff --git a/TODO b/TODO index 2ed08cc..bd68fab 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,4 @@ +Add test Add README Add man page Add mouse input -Add bracked paste diff --git a/interactive-test.c b/interactive-test.c index cc41231..d393a96 100644 --- a/interactive-test.c +++ b/interactive-test.c @@ -86,6 +86,14 @@ main(void) 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 if (input.type == LIBTERMINPUT_BRACKETED_PASTE_START) { + printf("bracketed paste start\n"); + } else if (input.type == LIBTERMINPUT_BRACKETED_PASTE_END) { + printf("bracketed paste end\n"); + } else if (input.type == LIBTERMINPUT_TEXT) { + printf("text:\n"); + printf("\tlength: %zu\n", input.text.nbytes); + printf("\tdata: %.512s\n", input.text.bytes); } else { printf("other\n"); } diff --git a/libterminput.c b/libterminput.c index d721abd..62f4ca8 100644 --- a/libterminput.c +++ b/libterminput.c @@ -21,9 +21,10 @@ read_input(int fd, struct input *input, struct libterminput_state *ctx) int r; /* Get next byte from input */ - if (ctx->have_stored) { - ctx->have_stored = 0; - c = (unsigned char)ctx->stored; + 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, &c, 1); if (r <= 0) @@ -39,8 +40,7 @@ read_input(int fd, struct input *input, struct libterminput_state *ctx) ctx->n = 0; ctx->npartial = 0; ctx->mods = 0; - ctx->have_stored = 1; - ctx->stored = (char)c; + ctx->stored[ctx->stored_head++] = c; strcpy(input->symbol, ctx->partial); return 1; } else { @@ -185,6 +185,14 @@ parse_sequence(union libterminput_input *input, struct libterminput_state *ctx) 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: input->type = LIBTERMINPUT_NONE; return; @@ -251,6 +259,67 @@ parse_sequence(union libterminput_input *input, struct libterminput_state *ctx) } +static int +read_bracketed_paste(int fd, union libterminput_input *input, struct libterminput_state *ctx) +{ + ssize_t r; + size_t n; + + /* Unfortunately there is no standard for how to handle pasted ESC's, + * not even ESC [201~ or ESC ESC. Terminates seem to just paste ESC as + * is, so we cannot do anything about them, however, a good terminal + * would stop the paste at the ~ in ESC [201~, send ~ as normal, and + * then continue the brackated paste mode. */ + + if (ctx->stored_head - ctx->stored_tail) { + for (n = ctx->stored_tail; n + 6 < ctx->stored_head; n++) { + if (ctx->stored[n + 0] == '\033' && ctx->stored[n + 1] == '[' && ctx->stored[n + 2] == '2' && + ctx->stored[n + 3] == '0' && ctx->stored[n + 4] == '0' && ctx->stored[n + 5] == '~') + break; + } + if (n == ctx->stored_tail && ctx->stored_head - ctx->stored_tail >= 6) { + ctx->stored_tail += 6; + if (ctx->stored_tail == ctx->stored_head) + ctx->stored_tail = ctx->stored_head = 0; + input->type = LIBTERMINPUT_BRACKETED_PASTE_END; + return 0; + } + input->text.nbytes = ctx->stored_head - ctx->stored_tail; + input->text.type = LIBTERMINPUT_TEXT; + memcpy(input->text.bytes, &ctx->stored[ctx->stored_tail], n - ctx->stored_tail); + ctx->stored_tail = n; + if (ctx->stored_tail == ctx->stored_head) + ctx->stored_tail = ctx->stored_head = 0; + return 0; + } + + r = read(fd, input->text.bytes, sizeof(input->text.bytes)); + if (r <= 0) + return (int)r; + + input->text.nbytes = (size_t)r; + for (n = 0; n + 6 < input->text.nbytes; n++) { + if (input->text.bytes[n + 0] == '\033' && input->text.bytes[n + 1] == '[' && input->text.bytes[n + 2] == '2' && + input->text.bytes[n + 3] == '0' && input->text.bytes[n + 4] == '0' && input->text.bytes[n + 5] == '~') + break; + } + if (!n && input->text.nbytes >= 6) { + ctx->stored_tail = 0; + ctx->stored_head = input->text.nbytes - 6; + memcpy(ctx->stored, &input->text.bytes[6], ctx->stored_head); + if (ctx->stored_tail == ctx->stored_head) + ctx->stored_tail = ctx->stored_head = 0; + input->type = LIBTERMINPUT_BRACKETED_PASTE_END; + return 0; + } + ctx->stored_tail = 0; + ctx->stored_head = input->text.nbytes - n; + input->text.nbytes = n; + input->text.type = LIBTERMINPUT_TEXT; + return 0; +} + + int libterminput_read(int fd, union libterminput_input *input, struct libterminput_state *ctx) { @@ -267,6 +336,9 @@ libterminput_read(int fd, union libterminput_input *input, struct libterminput_s return 1; } + if (ctx->bracketed_paste) + return read_bracketed_paste(fd, input, ctx); + r = read_input(fd, &ret, ctx); if (r <= 0) return r; diff --git a/libterminput.h b/libterminput.h index 4e33d09..d2f505f 100644 --- a/libterminput.h +++ b/libterminput.h @@ -60,7 +60,10 @@ enum libterminput_key { enum libterminput_type { LIBTERMINPUT_NONE, - LIBTERMINPUT_KEYPRESS + LIBTERMINPUT_KEYPRESS, + LIBTERMINPUT_BRACKETED_PASTE_START, + LIBTERMINPUT_BRACKETED_PASTE_END, + LIBTERMINPUT_TEXT }; struct libterminput_keypress { @@ -71,9 +74,16 @@ struct libterminput_keypress { char symbol[7]; /* use if .key == LIBTERMINPUT_SYMBOL */ }; +struct libterminput_text { + enum libterminput_type type; + size_t nbytes; + char bytes[512]; +}; + union libterminput_input { enum libterminput_type type; - struct libterminput_keypress keypress; + struct libterminput_keypress keypress; /* use if .type == LIBTERMINPUT_KEYPRESS */ + struct libterminput_text text; /* use if .type == LIBTERMINPUT_TEXT */ }; @@ -83,13 +93,15 @@ union libterminput_input { struct libterminput_state { int inited; /* whether the input in initialised, not this struct */ enum libterminput_mod mods; + size_t stored_head; + size_t stored_tail; + char bracketed_paste; char meta; char n; - char have_stored; char npartial; - char stored; char partial[7]; char key[44]; + char stored[512]; }; -- cgit v1.2.3-70-g09d2