/* input.h — VT/ANSI escape sequence parser for terminal input * * Decodes raw terminal bytes into structured InputEvent records. Handles * escape sequences (arrow keys, function keys, etc.), mouse protocols * (VT200, SGR, urxvt), UTF-8 multibyte characters, and control keys. * * * Usage: * 1. input does not allocate any memory itself, so start by allocating * input_size() bytes of memory. * 2. Call input_init(mem, esc_latency_ms, terminfo, len, ti) to get * an InputState (terminfo and ti may be NULL for xterm defaults). * 3. When bytes arrive from stdin, call input_scan(st, buf, len, now). * 4. Read events with input_count(st) and input_event(st, i). * 5. Check input_delay(st): if non-zero, re-call input_scan() with * an empty buffer (len=0) after that many milliseconds to resolve * a pending lone ESC. * * Example: * * void *mem = malloc(input_size()); * struct InputState *st = input_init(mem, 50, NULL, 0, NULL); * * // in your event loop: * int accepted = input_scan(st, buf, nread, now_ms); * for (int i = 0; i < input_count(st); i++) { * struct InputEvent *ev = input_event(st, i); * // handle ev->type, ev->key, ev->ch, ev->mod ... * } * int delay = input_delay(st); * if (delay > 0) { * // schedule another input_scan(st, NULL, 0, now_ms) after delay ms * } * * Gotchas: * * - input_scan() may accept fewer bytes than provided if the internal * buffer (SCAN_BUFFER_SIZE) is full. The caller must re-feed the remainder. * - Event pointers from input_event() are invalidated by the next * input_scan() call. Copy any data you need before scanning again. * - At most 128 events are produced per scan. If saturated, the last * slot is overwritten. In practice this is unreachable. * - A lone ESC byte is ambiguous: it could be the Escape key or the * start of a sequence. The parser holds it until either more bytes * arrive or the ESC latency expires. You must honour input_delay() and * re-call input_scan() with len=0 to flush it. * - The `now` parameter must be monotonically increasing milliseconds * (e.g. Date.now()). The parser uses it solely for ESC latency * disambiguation. */ #ifndef INPUT_H #define INPUT_H #include /* ── Event types ──────────────────────────────────────────────────── */ #define EVENT_KEY 1 #define EVENT_MOUSE 2 #define EVENT_RESIZE 3 #define EVENT_CURSOR 4 #define EVENT_CAPABILITY 5 /* ── Capability keys (EVENT_CAPABILITY) ───────────────────────────── */ /* key field values for EVENT_CAPABILITY events. The value field (ch) * carries the payload: for colors, packed 0x00RRGGBB; for booleans, * 1=true/0=false; for colordepth, 0="16", 1="256", 2="truecolor". */ #define CAP_FOREGROUND_COLOR 1 #define CAP_BACKGROUND_COLOR 2 #define CAP_CURSOR_COLOR 3 #define CAP_COLORDEPTH 4 #define CAP_SYNC_OUTPUT 5 #define CAP_KITTY_KEYBOARD 6 #define CAP_KITTY_GRAPHICS 7 #define CAP_POINTER_SHAPE 8 #define CAP_COLOR_SCHEME 9 /* CAP_COLOR_SCHEME ch values (DECDSR 997 Ps): 1=dark, 2=light. */ /* ── Modifier flags (bitwise) ─────────────────────────────────────── */ #define MOD_ALT 1 #define MOD_CTRL 2 #define MOD_SHIFT 4 #define MOD_MOTION 8 #define MOD_RELEASE 16 /* ── Kitty action types ──────────────────────────────────────────── */ #define ACTION_NONE 0 #define ACTION_PRESS 1 #define ACTION_REPEAT 2 #define ACTION_RELEASE 3 /* ── Key codes ────────────────────────────────────────────────────── */ /* Function keys */ #define KEY_F1 0xFFFF #define KEY_F2 0xFFFE #define KEY_F3 0xFFFD #define KEY_F4 0xFFFC #define KEY_F5 0xFFFB #define KEY_F6 0xFFFA #define KEY_F7 0xFFF9 #define KEY_F8 0xFFF8 #define KEY_F9 0xFFF7 #define KEY_F10 0xFFF6 #define KEY_F11 0xFFF5 #define KEY_F12 0xFFF4 /* Navigation */ #define KEY_ARROW_UP 0xFFF3 #define KEY_ARROW_DOWN 0xFFF2 #define KEY_ARROW_LEFT 0xFFF1 #define KEY_ARROW_RIGHT 0xFFF0 #define KEY_HOME 0xFFEF #define KEY_END 0xFFEE #define KEY_INSERT 0xFFED #define KEY_DELETE 0xFFEC #define KEY_PGUP 0xFFEB #define KEY_PGDN 0xFFEA #define KEY_BACKTAB 0xFFE9 /* Numpad */ #define KEY_NUMPAD_0 0xFFE2 #define KEY_NUMPAD_1 0xFFE1 #define KEY_NUMPAD_2 0xFFE0 #define KEY_NUMPAD_3 0xFFDF #define KEY_NUMPAD_4 0xFFDE #define KEY_NUMPAD_5 0xFFDD #define KEY_NUMPAD_6 0xFFDC #define KEY_NUMPAD_7 0xFFDB #define KEY_NUMPAD_8 0xFFDA #define KEY_NUMPAD_9 0xFFD9 #define KEY_NUMPAD_DECIMAL 0xFFD8 #define KEY_NUMPAD_DIVIDE 0xFFD7 #define KEY_NUMPAD_MULTIPLY 0xFFD6 #define KEY_NUMPAD_SUBTRACT 0xFFD5 #define KEY_NUMPAD_ADD 0xFFD4 #define KEY_NUMPAD_ENTER 0xFFD3 #define KEY_NUMPAD_EQUAL 0xFFD2 /* Modifier keys */ #define KEY_SHIFT_LEFT 0xFFD1 #define KEY_SHIFT_RIGHT 0xFFD0 #define KEY_CONTROL_LEFT 0xFFCF #define KEY_CONTROL_RIGHT 0xFFCE #define KEY_ALT_LEFT 0xFFCD #define KEY_ALT_RIGHT 0xFFCC #define KEY_SUPER_LEFT 0xFFCB #define KEY_SUPER_RIGHT 0xFFCA #define KEY_HYPER_LEFT 0xFFC9 #define KEY_HYPER_RIGHT 0xFFC8 #define KEY_META_LEFT 0xFFC7 #define KEY_META_RIGHT 0xFFC6 #define KEY_CAPS_LOCK 0xFFC5 #define KEY_NUM_LOCK 0xFFC4 #define KEY_SCROLL_LOCK 0xFFC3 /* Mouse */ #define KEY_MOUSE_LEFT 0xFFE8 #define KEY_MOUSE_RIGHT 0xFFE7 #define KEY_MOUSE_MIDDLE 0xFFE6 #define KEY_MOUSE_RELEASE 0xFFE5 #define KEY_MOUSE_WHEEL_UP 0xFFE4 #define KEY_MOUSE_WHEEL_DOWN 0xFFE3 /* Special keys (ASCII range) */ #define KEY_ESC 0x1B #define KEY_ENTER 0x0D #define KEY_TAB 0x09 #define KEY_BACKSPACE 0x7F #define KEY_SPACE 0x20 /** * Parsed terminal input event. All fields are represented as numbers for * efficient transport via linear memory. A semantic layer is added on top in * input.ts * * @field type Event kind: EVENT_KEY, EVENT_MOUSE, or EVENT_RESIZE. * @field mod Bitwise combination of MOD_ALT, MOD_CTRL, MOD_SHIFT, MOD_MOTION. * @field key KEY_* constant for special keys, raw byte for control chars, * or 0 when the event is a printable character (see ch). * @field ch Unicode codepoint for printable characters, 0 otherwise. * @field x Mouse column (0-based). Only valid for EVENT_MOUSE. * @field y Mouse row (0-based). Only valid for EVENT_MOUSE. * @field w Terminal width. Only valid for EVENT_RESIZE. * @field h Terminal height. Only valid for EVENT_RESIZE. * @field action Kitty event type: ACTION_NONE/PRESS/REPEAT/RELEASE. * @field shifted Shifted key codepoint (Kitty alternate keys). * @field base Base layout key codepoint (Kitty alternate keys). * @field text_len Number of valid codepoints in text[] (0-8). * @field text Associated text codepoints (Kitty enhancement level 16+). */ #define MAX_TEXT_CODEPOINTS 8 struct InputEvent { uint8_t type; uint8_t mod; uint16_t key; uint32_t ch; int32_t x; int32_t y; int32_t w; int32_t h; uint8_t action; uint32_t shifted; uint32_t base; uint32_t text[MAX_TEXT_CODEPOINTS]; uint8_t text_len; }; /** * A struct in which the entire input state can be stored. It is deliberately * opaque since it comprises the private state of the input scanner. */ struct InputState; /** * Return the number of bytes needed to allocate an InputState. * * @return Required arena size in bytes (8-byte aligned). */ int input_size(void); /** * Initialize an input parser in the provided memory. * * @param mem Pointer to at least input_size() bytes. * @param esc_latency_ms ESC disambiguation latency in milliseconds. * @param terminfo Raw compiled terminfo entry whose key_* string * capabilities seed the sequence trie (they take * precedence over the xterm defaults), or NULL. * @param terminfo_len Byte length of terminfo, or 0. * @param initial_colors The terminal's static max_colors value, used * when emitting a colordepth denial event to pick * the right tier ("16" vs "256"). Pass 0 for the * 256-color baseline. * @return Initialized parser state. */ struct InputState *input_init(void *mem, int esc_latency_ms, const uint8_t *terminfo, int terminfo_len, int initial_colors); /** * Feed raw bytes into the parser and produce events. * * @param st Parser state from input_init(). * @param buf Input bytes to parse. * @param len Number of bytes in buf. * @param now Current timestamp in milliseconds (e.g. Date.now()). * Used for ESC latency disambiguation. * @return Number of bytes accepted. May be less than len if the * internal buffer is full (caller should re-feed the rest). */ int input_scan(struct InputState *st, const char *buf, int len, double now); /** * Return the number of events produced by the last input_scan() call. * * @param st Parser state. * @return Event count (0 to 128). */ int input_count(struct InputState *st); /** * Return a pointer to the event at the given index. * * @param st Parser state. * @param index Event index (0-based). * @return Pointer to the event, or NULL if index is out of range. */ struct InputEvent *input_event(struct InputState *st, int index); /** * Return the ESC latency delay if an ambiguous ESC is pending. * * @param st Parser state. * @return esc_latency_ms if a lone ESC is buffered, 0 otherwise. * When non-zero, the caller should re-call input_scan() * with an empty buffer after this many milliseconds. */ int input_delay(struct InputState *st); #endif