summaryrefslogtreecommitdiff
path: root/tests/input_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/input_test.c')
-rw-r--r--tests/input_test.c353
1 files changed, 353 insertions, 0 deletions
diff --git a/tests/input_test.c b/tests/input_test.c
new file mode 100644
index 0000000..6ee2caa
--- /dev/null
+++ b/tests/input_test.c
@@ -0,0 +1,353 @@
+#include "term/term.h"
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static struct term_keyboard_state st;
+static struct term_modes m;
+static char out[128];
+static size_t out_len;
+
+static void mode(unsigned kitty_flags)
+{
+ st = (struct term_keyboard_state){0};
+ m = (struct term_modes){ .keyboard = kitty_flags, .grid = { .x = 80, .y = 24 } };
+}
+
+static void enc(struct term_key_event ev)
+{
+ if (!ev.action) ev.action = TERM_KEY_PRESS;
+ term_keyboard_update(&st, &ev);
+ struct term_input_result r =
+ term_input_encode_key(&st, &ev, &m, out, sizeof(out));
+ out_len = r.len;
+}
+#define K(...) enc((struct term_key_event){ __VA_ARGS__ })
+
+static void typed(uint32_t cp, unsigned mods)
+{
+ K(.codepoint = cp, .base = cp, .text = cp, .mods = mods);
+}
+
+static bool consumed;
+static void enc_mouse(struct term_mouse_event ev)
+{
+ if (ev.pixel.x == 0 && ev.pixel.y == 0)
+ ev.pixel = ev.pos;
+ struct term_input_result r =
+ term_input_encode_mouse(&ev, &m, out, sizeof(out));
+ out_len = r.len;
+ consumed = r.status != TERM_INPUT_UNHANDLED;
+}
+#define MOUSE(x_, y_, ...) enc_mouse((struct term_mouse_event){ .pos = { .x = (x_), .y = (y_) }, __VA_ARGS__ })
+
+static void expect_at(const char *s, int line)
+{
+ if (out_len != strlen(s) || memcmp(out, s, out_len)) {
+ fprintf(stderr, "line %d: expected %s, got %.*s (%zu bytes)\n", line, s,
+ (int)out_len, out, out_len);
+ abort();
+ }
+}
+#define expect(s) expect_at(s, __LINE__)
+
+int main(void)
+{
+ mode(0);
+ K(.key = TERM_KEY_UP); expect("\033[A");
+ K(.key = TERM_KEY_HOME); expect("\033[H");
+ K(.key = TERM_KEY_DELETE); expect("\033[3~");
+ K(.key = TERM_KEY_PAGE_UP); expect("\033[5~");
+ K(.key = TERM_KEY_F1); expect("\033OP");
+ K(.key = TERM_KEY_F5); expect("\033[15~");
+ K(.key = TERM_KEY_F12); expect("\033[24~");
+ K(.key = TERM_KEY_RETURN); expect("\r");
+ K(.key = TERM_KEY_ESCAPE); expect("\033");
+ K(.key = TERM_KEY_TAB); expect("\t");
+ K(.key = TERM_KEY_TAB, .mods = TERM_MOD_SHIFT); expect("\033[Z");
+ K(.key = TERM_KEY_BACKSPACE); expect("\177");
+ K(.key = TERM_KEY_RIGHT, .mods = TERM_MOD_CTRL); expect("\033[1;5C");
+ K(.key = TERM_KEY_F5, .mods = TERM_MOD_SHIFT); expect("\033[15;2~");
+ K(.key = TERM_KEY_DELETE, .mods = TERM_MOD_CTRL); expect("\033[3;5~");
+
+ m.backarrow_bs = true;
+ K(.key = TERM_KEY_BACKSPACE); expect("\010");
+ m.backarrow_bs = false;
+
+ m.app_cursor = true;
+ K(.key = TERM_KEY_UP); expect("\033OA");
+ K(.key = TERM_KEY_END); expect("\033OF");
+
+ K(.key = TERM_KEY_UP, .mods = TERM_MOD_CTRL); expect("\033[1;5A");
+ m.app_cursor = false;
+
+ typed('a', 0); expect("a");
+ typed(0x00E9, 0); expect("\xc3\xa9");
+ K(.codepoint = 'a', .shifted = 'A', .base = 'a', .text = 'A',
+ .mods = TERM_MOD_SHIFT); expect("A");
+ K(.action = TERM_KEY_RELEASE, .codepoint = 'a', .text = 'a'); expect("");
+
+ typed('c', TERM_MOD_CTRL); expect("\003");
+ typed('a', TERM_MOD_CTRL); expect("\001");
+ typed('\\', TERM_MOD_CTRL); expect("\034");
+ typed(' ', TERM_MOD_CTRL); assert(out_len == 1 && out[0] == 0);
+
+ K(.codepoint = '2', .shifted = '@', .base = '2', .text = '@',
+ .mods = TERM_MOD_CTRL | TERM_MOD_SHIFT);
+ assert(out_len == 1 && out[0] == 0);
+
+ typed('b', TERM_MOD_ALT); expect("\033b");
+ typed('b', TERM_MOD_ALT | TERM_MOD_CTRL); expect("\033\002");
+ K(.key = TERM_KEY_BACKSPACE, .mods = TERM_MOD_ALT); expect("\033\177");
+ K(.key = TERM_KEY_RETURN, .mods = TERM_MOD_ALT); expect("\033\r");
+
+ K(.key = TERM_KEY_LEFT, .mods = TERM_MOD_ALT); expect("\033[1;3D");
+
+ K(.codepoint = 'b', .base = 'b', .text = 0x222B, .text_deferred = true);
+ expect("");
+
+ K(.key = TERM_KEY_KP_DELETE, .keypad = TERM_KEY_KP_DECIMAL); expect("\033[3~");
+ K(.key = TERM_KEY_KP_ENTER, .keypad = TERM_KEY_KP_ENTER); expect("\r");
+ K(.key = TERM_KEY_KP_LEFT, .keypad = TERM_KEY_KP_4); expect("\033[D");
+
+ m.app_keypad = true;
+ K(.key = TERM_KEY_KP_END, .keypad = TERM_KEY_KP_1); expect("\033Oq");
+ K(.key = TERM_KEY_KP_ENTER, .keypad = TERM_KEY_KP_ENTER); expect("\033OM");
+ K(.key = TERM_KEY_KP_ADD, .keypad = TERM_KEY_KP_ADD); expect("\033Ok");
+ m.app_keypad = false;
+
+ mode(1);
+ K(.key = TERM_KEY_ESCAPE); expect("\033[27u");
+ K(.key = TERM_KEY_DELETE); expect("\033[3~");
+ K(.key = TERM_KEY_F3); expect("\033[13~");
+ K(.key = TERM_KEY_KP_ENTER, .keypad = TERM_KEY_KP_ENTER); expect("\033[57414u");
+ typed('i', TERM_MOD_CTRL); expect("\033[105;5u");
+ K(.key = TERM_KEY_RETURN, .mods = TERM_MOD_CTRL); expect("\033[13;5u");
+
+ typed('a', 0); expect("a");
+ K(.key = TERM_KEY_RETURN); expect("\r");
+
+ K(.key = TERM_KEY_UP, .action = TERM_KEY_RELEASE); expect("");
+ K(.key = TERM_KEY_LSHIFT); expect("");
+
+ mode(1 | 4);
+ K(.codepoint = 'a', .shifted = 'A', .base = 'a', .text = 'A',
+ .mods = TERM_MOD_CTRL | TERM_MOD_SHIFT); expect("\033[97:65;6u");
+
+ mode(1 | 2 | 8);
+ K(.key = TERM_KEY_ESCAPE); expect("\033[27;1u");
+ K(.key = TERM_KEY_ESCAPE, .action = TERM_KEY_REPEAT); expect("\033[27;1:2u");
+ K(.key = TERM_KEY_ESCAPE, .action = TERM_KEY_RELEASE); expect("\033[27;1:3u");
+ K(.key = TERM_KEY_UP, .action = TERM_KEY_REPEAT); expect("\033[1;1:2A");
+
+ mode(31);
+ K(.codepoint = 1092, .base = 'a', .text = 1092);
+ expect("\033[1092::97;1;1092u");
+ K(.codepoint = 'a', .shifted = 'A', .base = 'a', .text = 'A',
+ .mods = TERM_MOD_SHIFT); expect("\033[97:65;2;65u");
+
+ K(.action = TERM_KEY_RELEASE, .codepoint = 'a', .base = 'a', .text = 'a');
+ expect("\033[97;1:3u");
+
+ mode(1 | 2 | 8);
+ K(.key = TERM_KEY_LSHIFT, .mods = TERM_MOD_SHIFT); expect("\033[57441;2u");
+ K(.key = TERM_KEY_RSHIFT, .mods = TERM_MOD_SHIFT); expect("\033[57447;2u");
+ K(.key = TERM_KEY_LSHIFT, .action = TERM_KEY_RELEASE, .mods = TERM_MOD_SHIFT);
+ expect("\033[57441;2:3u");
+ K(.key = TERM_KEY_RSHIFT, .action = TERM_KEY_RELEASE, .mods = TERM_MOD_SHIFT);
+ expect("\033[57447;1:3u");
+
+ K(.key = TERM_KEY_ISO_LEVEL3_SHIFT); expect("\033[57453;1u");
+ K(.key = TERM_KEY_ISO_LEVEL5_SHIFT); expect("\033[57454;1u");
+ K(.key = TERM_KEY_CAPS_LOCK); expect("\033[57358;65u");
+
+ mode(1 | 2 | 4);
+ K(.key = TERM_KEY_CAPS_LOCK); expect("");
+ K(.key = TERM_KEY_CAPS_LOCK, .action = TERM_KEY_RELEASE); expect("");
+
+ mode(1 | 2 | 4 | 8);
+ K(.key = TERM_KEY_CAPS_LOCK); expect("\033[57358;65u");
+ K(.key = TERM_KEY_CAPS_LOCK, .action = TERM_KEY_RELEASE,
+ .mods = TERM_MOD_CAPS); expect("\033[57358;65:3u");
+
+ mode(1);
+ K(.key = TERM_KEY_LEFT, .mods = TERM_MOD_NUM); expect("\033[1;129D");
+ K(.key = TERM_KEY_LEFT); expect("\033[D");
+ K(.key = TERM_KEY_F1, .mods = TERM_MOD_CAPS); expect("\033[1;65P");
+ K(.codepoint = 'a', .base = 'a', .mods = TERM_MOD_CTRL | TERM_MOD_CAPS);
+ expect("\033[97;69u");
+ K(.codepoint = 'a', .base = 'a', .mods = TERM_MOD_CTRL);
+ expect("\033[97;5u");
+ K(.codepoint = 'a', .base = 'a', .text = 'A', .mods = TERM_MOD_CAPS);
+ expect("A");
+ K(.key = TERM_KEY_RETURN, .mods = TERM_MOD_CAPS); expect("\r");
+
+ mode(1 | 8);
+ K(.codepoint = 'a', .base = 'a', .text = 'A', .mods = TERM_MOD_CAPS);
+ expect("\033[97;65u");
+ K(.key = TERM_KEY_RETURN, .mods = TERM_MOD_CAPS); expect("\033[13;65u");
+
+ mode(0);
+ K(.key = TERM_KEY_LEFT, .mods = TERM_MOD_NUM); expect("\033[D");
+ K(.key = TERM_KEY_F1, .mods = TERM_MOD_CAPS); expect("\033OP");
+ K(.codepoint = 'a', .base = 'a', .mods = TERM_MOD_CTRL | TERM_MOD_CAPS);
+ expect("\001");
+
+ mode(0);
+ MOUSE(2, 1, .kind = TERM_MOUSE_PRESS);
+ assert(out_len == 0 && !consumed);
+
+ m.mouse = TERM_MOUSE_NORMAL;
+ MOUSE(2, 1, .kind = TERM_MOUSE_PRESS); expect("\033[M\040\043\042");
+ MOUSE(2, 1, .kind = TERM_MOUSE_RELEASE); expect("\033[M\043\043\042");
+ MOUSE(2, 1, .kind = TERM_MOUSE_MOTION); expect("");
+
+ MOUSE(2, 1, .mods = TERM_MOD_SHIFT);
+ assert(out_len == 0 && !consumed);
+
+ MOUSE(200, 99, .kind = TERM_MOUSE_PRESS);
+ assert(out_len == 0 && consumed);
+
+ m.mouse_encoding = TERM_MOUSE_ENC_SGR;
+ MOUSE(2, 1, .kind = TERM_MOUSE_PRESS); expect("\033[<0;3;2M");
+ MOUSE(2, 1, .kind = TERM_MOUSE_RELEASE); expect("\033[<0;3;2m");
+ MOUSE(2, 1, .mods = TERM_MOD_CTRL); expect("\033[<16;3;2M");
+ MOUSE(2, 1, .button = TERM_MOUSE_WHEEL_UP); expect("\033[<64;3;2M");
+ m.mouse_encoding = TERM_MOUSE_ENC_SGR_PIXELS;
+ MOUSE(2, 1, .pixel = { .x = 25, .y = 17 }); expect("\033[<0;26;18M");
+ m.mouse_encoding = TERM_MOUSE_ENC_URXVT;
+ MOUSE(2, 1, .kind = TERM_MOUSE_PRESS); expect("\033[32;3;2M");
+ m.mouse_encoding = TERM_MOUSE_ENC_X10;
+
+ m.mouse = TERM_MOUSE_BUTTON;
+ MOUSE(2, 1, .kind = TERM_MOUSE_MOTION, .button = 3); expect("");
+ MOUSE(2, 1, .kind = TERM_MOUSE_MOTION, .button = 0);
+ expect("\033[M\100\043\042");
+ m.mouse_encoding = TERM_MOUSE_ENC_SGR;
+ MOUSE(2, 1, .kind = TERM_MOUSE_MOTION, .button = 0, .mods = TERM_MOD_CTRL);
+ expect("\033[<48;3;2M");
+ m.mouse = TERM_MOUSE_ANY;
+ MOUSE(2, 1, .kind = TERM_MOUSE_MOTION, .button = 3); expect("\033[<35;3;2M");
+ m.mouse_encoding = TERM_MOUSE_ENC_X10;
+ MOUSE(2, 1, .button = TERM_MOUSE_WHEEL_UP); expect("\033[M\140\043\042");
+
+ m.mouse = TERM_MOUSE_X10;
+ MOUSE(2, 1, .kind = TERM_MOUSE_PRESS); expect("\033[M\040\043\042");
+ MOUSE(2, 1, .kind = TERM_MOUSE_RELEASE); expect("");
+
+ {
+ struct term_keyboard_state s2 = {0};
+ struct term_modes m2 = { .keyboard = 1 | 2 | 8, .grid = { .x = 80, .y = 24 } };
+ char b2[64];
+
+ struct term_key_event sh = {
+ .key = TERM_KEY_LSHIFT, .action = TERM_KEY_PRESS,
+ .mods = TERM_MOD_SHIFT,
+ };
+ term_keyboard_update(&s2, &sh);
+ struct term_key_event rsh = {
+ .key = TERM_KEY_RSHIFT, .action = TERM_KEY_PRESS,
+ .mods = TERM_MOD_SHIFT,
+ };
+ term_keyboard_update(&s2, &rsh);
+
+ struct term_key_event rel = {
+ .key = TERM_KEY_LSHIFT, .action = TERM_KEY_RELEASE,
+ .mods = TERM_MOD_SHIFT,
+ };
+ term_keyboard_update(&s2, &rel);
+ struct term_input_result r =
+ term_input_encode_key(&s2, &rel, &m2, b2, sizeof(b2));
+ assert(r.status == TERM_INPUT_BYTES);
+ assert(!memcmp(b2, "\033[57441;2:3u", r.len));
+
+ struct term_key_event rrel = {
+ .key = TERM_KEY_RSHIFT, .action = TERM_KEY_RELEASE,
+ .mods = TERM_MOD_SHIFT,
+ };
+ term_keyboard_update(&s2, &rrel);
+ r = term_input_encode_key(&s2, &rrel, &m2, b2, sizeof(b2));
+ assert(!memcmp(b2, "\033[57447;1:3u", r.len));
+ }
+
+ {
+ char tiny[4];
+ struct term_input_result r = term_input_encode_key(
+ &st,
+ &(struct term_key_event){
+ .key = TERM_KEY_DELETE, .action = TERM_KEY_PRESS,
+ .mods = TERM_MOD_CTRL | TERM_MOD_ALT | TERM_MOD_SHIFT,
+ },
+ &(struct term_modes){ .grid = { .x = 80, .y = 24 } },
+ tiny, sizeof(tiny));
+ assert(r.status == TERM_INPUT_BUFFER_FULL && r.len == 0);
+ }
+
+ {
+ struct term_keyboard_state s5 = {0};
+ struct term_key_event bad = {
+ .key = UINT16_MAX, .action = TERM_KEY_PRESS,
+ };
+ term_keyboard_update(&s5, &bad);
+ assert(!term_keyboard_key_down(&s5, TERM_KEY_LSHIFT));
+ struct term_input_result r =
+ term_input_encode_key(&s5, &bad, &m, out, sizeof(out));
+ assert(r.status == TERM_INPUT_UNHANDLED && r.len == 0);
+
+ struct term_key_event bad_kp = {
+ .key = TERM_KEY_KP_1, .keypad = UINT16_MAX,
+ .action = TERM_KEY_PRESS,
+ };
+ m.app_keypad = true;
+ r = term_input_encode_key(&s5, &bad_kp, &m, out, sizeof(out));
+ assert(r.len == 0);
+ m.app_keypad = false;
+ }
+
+ {
+ struct term_key_event deferred = {
+ .action = TERM_KEY_PRESS, .codepoint = 'a', .text = 'a',
+ .text_deferred = true,
+ };
+ struct term_input_result r = term_input_encode_key(
+ &st, &deferred, &(struct term_modes){0}, out, sizeof(out));
+ assert(r.status == TERM_INPUT_UNHANDLED && r.len == 0);
+ struct term_modes kitty = { .keyboard = TERM_KEYBOARD_ALL_KEYS };
+ r = term_input_encode_key(&st, &deferred, &kitty, out, sizeof(out));
+ assert(r.status == TERM_INPUT_BYTES && r.len == strlen("\033[97u"));
+ assert(!memcmp(out, "\033[97u", r.len));
+ deferred.action = TERM_KEY_RELEASE;
+ r = term_input_encode_key(&st, &deferred, &kitty, out, sizeof(out));
+ assert(r.status == TERM_INPUT_CONSUMED && r.len == 0);
+ }
+ m.mouse = TERM_MOUSE_ANY;
+ m.mouse_encoding = TERM_MOUSE_ENC_SGR;
+ MOUSE(2, 1, .button = TERM_MOUSE_BUTTON_EXTRA_1); expect("\033[<8;3;2M");
+ MOUSE(2, 1, .button = TERM_MOUSE_WHEEL_LEFT); expect("\033[<66;3;2M");
+ MOUSE(2, 1, .button = TERM_MOUSE_WHEEL_RIGHT); expect("\033[<67;3;2M");
+
+ {
+ /* A host sizes the paste buffer from the text alone. */
+ static const char text[] = "ls\r\n\033[31m\u202erm\x7f\t\xe4\xb8\xad";
+ static const char want[] = "\033[200~ls\r[31mrm\t\xe4\xb8\xad\033[201~";
+ char buf[sizeof(text) - 1 + TERM_PASTE_EXTRA];
+ struct term_modes bracketed = { .bracketed_paste = true };
+ struct term_input_result r =
+ term_input_encode_paste(&bracketed, text, sizeof(text) - 1, buf, sizeof(buf));
+ assert(r.status == TERM_INPUT_BYTES && r.len == sizeof(want) - 1);
+ assert(!memcmp(buf, want, r.len));
+
+ r = term_input_encode_paste(&bracketed, text, sizeof(text) - 1, buf, r.len - 1);
+ assert(r.status == TERM_INPUT_BUFFER_FULL && r.len == 0);
+
+ r = term_input_encode_paste(&bracketed, "\033\a\u2066", 5, buf, sizeof(buf));
+ assert(r.status == TERM_INPUT_CONSUMED && r.len == 0);
+
+ struct term_modes plain = {0};
+ r = term_input_encode_paste(&plain, "a\nb", 3, buf, 3);
+ assert(r.status == TERM_INPUT_BYTES && r.len == 3 && !memcmp(buf, "a\rb", 3));
+ }
+ puts("PASS: terminal key, mouse, and paste encoding");
+ return 0;
+}