diff options
Diffstat (limited to 'tests/input_test.c')
| -rw-r--r-- | tests/input_test.c | 353 |
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; +} |
