[READ-ONLY] Mirror of https://github.com/bombshell-dev/tty. Platform independent 2D layout engine for terminal applications based on Clay bomb.sh/docs/tty
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286/* 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 <stdint.h>
/* ── 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
/* ── 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