#include "term/term.h" #include #include #include #include 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; }