lavat

Un fork de lavat pour pouvoir y ajouter des titres - retour accueil

git clone git://bebou.netlib.re/lavat
Log | Files | Refs | README | LICENSE |

termbox.h (117502B)


      1 /*
      2 MIT License
      3 
      4 Copyright (c) 2010-2020 nsf <no.smile.face@gmail.com>
      5               2015-2022 Adam Saponara <as@php.net>
      6 
      7 Permission is hereby granted, free of charge, to any person obtaining a copy
      8 of this software and associated documentation files (the "Software"), to deal
      9 in the Software without restriction, including without limitation the rights
     10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     11 copies of the Software, and to permit persons to whom the Software is
     12 furnished to do so, subject to the following conditions:
     13 
     14 The above copyright notice and this permission notice shall be included in all
     15 copies or substantial portions of the Software.
     16 
     17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     23 SOFTWARE.
     24 */
     25 
     26 #ifndef __TERMBOX_H
     27 #define __TERMBOX_H
     28 
     29 #ifndef _XOPEN_SOURCE
     30 #define _XOPEN_SOURCE
     31 #endif
     32 
     33 #ifndef _DEFAULT_SOURCE
     34 #define _DEFAULT_SOURCE
     35 #endif
     36 
     37 #include <errno.h>
     38 #include <fcntl.h>
     39 #include <limits.h>
     40 #include <signal.h>
     41 #include <stdarg.h>
     42 #include <stdint.h>
     43 #include <stdio.h>
     44 #include <stdlib.h>
     45 #include <string.h>
     46 #include <sys/ioctl.h>
     47 #include <sys/select.h>
     48 #include <sys/stat.h>
     49 #include <sys/time.h>
     50 #include <sys/types.h>
     51 #include <termios.h>
     52 #include <unistd.h>
     53 #include <wchar.h>
     54 
     55 #ifdef __cplusplus
     56 extern "C" {
     57 #endif
     58 
     59 // __ffi_start
     60 
     61 #define TB_VERSION_STR "2.1.0-dev"
     62 
     63 #if defined(TB_LIB_OPTS) || 0 // __tb_lib_opts
     64 // Ensure consistent compile-time options when using as a library
     65 #undef TB_OPT_TRUECOLOR
     66 #undef TB_OPT_EGC
     67 #undef TB_OPT_PRINTF_BUF
     68 #undef TB_OPT_READ_BUF
     69 #define TB_OPT_TRUECOLOR
     70 #define TB_OPT_EGC
     71 #endif
     72 
     73 /* ASCII key constants (tb_event.key) */
     74 #define TB_KEY_CTRL_TILDE       0x00
     75 #define TB_KEY_CTRL_2           0x00 /* clash with 'CTRL_TILDE'     */
     76 #define TB_KEY_CTRL_A           0x01
     77 #define TB_KEY_CTRL_B           0x02
     78 #define TB_KEY_CTRL_C           0x03
     79 #define TB_KEY_CTRL_D           0x04
     80 #define TB_KEY_CTRL_E           0x05
     81 #define TB_KEY_CTRL_F           0x06
     82 #define TB_KEY_CTRL_G           0x07
     83 #define TB_KEY_BACKSPACE        0x08
     84 #define TB_KEY_CTRL_H           0x08 /* clash with 'CTRL_BACKSPACE' */
     85 #define TB_KEY_TAB              0x09
     86 #define TB_KEY_CTRL_I           0x09 /* clash with 'TAB'            */
     87 #define TB_KEY_CTRL_J           0x0a
     88 #define TB_KEY_CTRL_K           0x0b
     89 #define TB_KEY_CTRL_L           0x0c
     90 #define TB_KEY_ENTER            0x0d
     91 #define TB_KEY_CTRL_M           0x0d /* clash with 'ENTER'          */
     92 #define TB_KEY_CTRL_N           0x0e
     93 #define TB_KEY_CTRL_O           0x0f
     94 #define TB_KEY_CTRL_P           0x10
     95 #define TB_KEY_CTRL_Q           0x11
     96 #define TB_KEY_CTRL_R           0x12
     97 #define TB_KEY_CTRL_S           0x13
     98 #define TB_KEY_CTRL_T           0x14
     99 #define TB_KEY_CTRL_U           0x15
    100 #define TB_KEY_CTRL_V           0x16
    101 #define TB_KEY_CTRL_W           0x17
    102 #define TB_KEY_CTRL_X           0x18
    103 #define TB_KEY_CTRL_Y           0x19
    104 #define TB_KEY_CTRL_Z           0x1a
    105 #define TB_KEY_ESC              0x1b
    106 #define TB_KEY_CTRL_LSQ_BRACKET 0x1b /* clash with 'ESC'            */
    107 #define TB_KEY_CTRL_3           0x1b /* clash with 'ESC'            */
    108 #define TB_KEY_CTRL_4           0x1c
    109 #define TB_KEY_CTRL_BACKSLASH   0x1c /* clash with 'CTRL_4'         */
    110 #define TB_KEY_CTRL_5           0x1d
    111 #define TB_KEY_CTRL_RSQ_BRACKET 0x1d /* clash with 'CTRL_5'         */
    112 #define TB_KEY_CTRL_6           0x1e
    113 #define TB_KEY_CTRL_7           0x1f
    114 #define TB_KEY_CTRL_SLASH       0x1f /* clash with 'CTRL_7'         */
    115 #define TB_KEY_CTRL_UNDERSCORE  0x1f /* clash with 'CTRL_7'         */
    116 #define TB_KEY_SPACE            0x20
    117 #define TB_KEY_BACKSPACE2       0x7f
    118 #define TB_KEY_CTRL_8           0x7f /* clash with 'BACKSPACE2'     */
    119 
    120 #define tb_key_i(i)             0xffff - (i)
    121 /* Terminal-dependent key constants (tb_event.key) and terminfo capabilities */
    122 /* BEGIN codegen h */
    123 /* Produced by ./codegen.sh on Sun, 19 Sep 2021 01:02:02 +0000 */
    124 #define TB_KEY_F1               (0xffff - 0)
    125 #define TB_KEY_F2               (0xffff - 1)
    126 #define TB_KEY_F3               (0xffff - 2)
    127 #define TB_KEY_F4               (0xffff - 3)
    128 #define TB_KEY_F5               (0xffff - 4)
    129 #define TB_KEY_F6               (0xffff - 5)
    130 #define TB_KEY_F7               (0xffff - 6)
    131 #define TB_KEY_F8               (0xffff - 7)
    132 #define TB_KEY_F9               (0xffff - 8)
    133 #define TB_KEY_F10              (0xffff - 9)
    134 #define TB_KEY_F11              (0xffff - 10)
    135 #define TB_KEY_F12              (0xffff - 11)
    136 #define TB_KEY_INSERT           (0xffff - 12)
    137 #define TB_KEY_DELETE           (0xffff - 13)
    138 #define TB_KEY_HOME             (0xffff - 14)
    139 #define TB_KEY_END              (0xffff - 15)
    140 #define TB_KEY_PGUP             (0xffff - 16)
    141 #define TB_KEY_PGDN             (0xffff - 17)
    142 #define TB_KEY_ARROW_UP         (0xffff - 18)
    143 #define TB_KEY_ARROW_DOWN       (0xffff - 19)
    144 #define TB_KEY_ARROW_LEFT       (0xffff - 20)
    145 #define TB_KEY_ARROW_RIGHT      (0xffff - 21)
    146 #define TB_KEY_BACK_TAB         (0xffff - 22)
    147 #define TB_KEY_MOUSE_LEFT       (0xffff - 23)
    148 #define TB_KEY_MOUSE_RIGHT      (0xffff - 24)
    149 #define TB_KEY_MOUSE_MIDDLE     (0xffff - 25)
    150 #define TB_KEY_MOUSE_RELEASE    (0xffff - 26)
    151 #define TB_KEY_MOUSE_WHEEL_UP   (0xffff - 27)
    152 #define TB_KEY_MOUSE_WHEEL_DOWN (0xffff - 28)
    153 
    154 #define TB_CAP_F1               0
    155 #define TB_CAP_F2               1
    156 #define TB_CAP_F3               2
    157 #define TB_CAP_F4               3
    158 #define TB_CAP_F5               4
    159 #define TB_CAP_F6               5
    160 #define TB_CAP_F7               6
    161 #define TB_CAP_F8               7
    162 #define TB_CAP_F9               8
    163 #define TB_CAP_F10              9
    164 #define TB_CAP_F11              10
    165 #define TB_CAP_F12              11
    166 #define TB_CAP_INSERT           12
    167 #define TB_CAP_DELETE           13
    168 #define TB_CAP_HOME             14
    169 #define TB_CAP_END              15
    170 #define TB_CAP_PGUP             16
    171 #define TB_CAP_PGDN             17
    172 #define TB_CAP_ARROW_UP         18
    173 #define TB_CAP_ARROW_DOWN       19
    174 #define TB_CAP_ARROW_LEFT       20
    175 #define TB_CAP_ARROW_RIGHT      21
    176 #define TB_CAP_BACK_TAB         22
    177 #define TB_CAP__COUNT_KEYS      23
    178 #define TB_CAP_ENTER_CA         23
    179 #define TB_CAP_EXIT_CA          24
    180 #define TB_CAP_SHOW_CURSOR      25
    181 #define TB_CAP_HIDE_CURSOR      26
    182 #define TB_CAP_CLEAR_SCREEN     27
    183 #define TB_CAP_SGR0             28
    184 #define TB_CAP_UNDERLINE        29
    185 #define TB_CAP_BOLD             30
    186 #define TB_CAP_BLINK            31
    187 #define TB_CAP_ITALIC           32
    188 #define TB_CAP_REVERSE          33
    189 #define TB_CAP_ENTER_KEYPAD     34
    190 #define TB_CAP_EXIT_KEYPAD      35
    191 #define TB_CAP__COUNT           36
    192 /* END codegen h */
    193 
    194 /* Some hard-coded caps */
    195 #define TB_HARDCAP_ENTER_MOUSE  "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h"
    196 #define TB_HARDCAP_EXIT_MOUSE   "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l"
    197 
    198 /* Colors (numeric) and attributes (bitwise) (tb_cell.fg, tb_cell.bg) */
    199 #define TB_BLACK                0x0001
    200 #define TB_RED                  0x0002
    201 #define TB_GREEN                0x0003
    202 #define TB_YELLOW               0x0004
    203 #define TB_BLUE                 0x0005
    204 #define TB_MAGENTA              0x0006
    205 #define TB_CYAN                 0x0007
    206 #define TB_WHITE                0x0008
    207 #define TB_BOLD                 0x0100
    208 #define TB_UNDERLINE            0x0200
    209 #define TB_REVERSE              0x0400
    210 #define TB_ITALIC               0x0800
    211 #define TB_BLINK                0x1000
    212 #define TB_DEFAULT              0x2000
    213 #ifdef TB_OPT_TRUECOLOR
    214 #define TB_TRUECOLOR_BOLD      0x01000000
    215 #define TB_TRUECOLOR_UNDERLINE 0x02000000
    216 #define TB_TRUECOLOR_REVERSE   0x04000000
    217 #define TB_TRUECOLOR_ITALIC    0x08000000
    218 #define TB_TRUECOLOR_BLINK     0x10000000
    219 #define TB_TRUECOLOR_DEFAULT   0x20000000
    220 #endif
    221 
    222 /* Event types (tb_event.type) */
    223 #define TB_EVENT_KEY        1
    224 #define TB_EVENT_RESIZE     2
    225 #define TB_EVENT_MOUSE      3
    226 
    227 /* Key modifiers (bitwise) (tb_event.mod) */
    228 #define TB_MOD_ALT          1
    229 #define TB_MOD_CTRL         2
    230 #define TB_MOD_SHIFT        4
    231 #define TB_MOD_MOTION       8
    232 
    233 /* Input modes (bitwise) (tb_set_input_mode) */
    234 #define TB_INPUT_CURRENT    0
    235 #define TB_INPUT_ESC        1
    236 #define TB_INPUT_ALT        2
    237 #define TB_INPUT_MOUSE      4
    238 
    239 /* Output modes (tb_set_output_mode) */
    240 #define TB_OUTPUT_CURRENT   0
    241 #define TB_OUTPUT_NORMAL    1
    242 #define TB_OUTPUT_256       2
    243 #define TB_OUTPUT_216       3
    244 #define TB_OUTPUT_GRAYSCALE 4
    245 #ifdef TB_OPT_TRUECOLOR
    246 #define TB_OUTPUT_TRUECOLOR 5
    247 #endif
    248 
    249 /* Common function return values unless otherwise noted.
    250  *
    251  * Library behavior is undefined after receiving TB_ERR_MEM. Callers may
    252  * attempt reinitializing by freeing memory, invoking tb_shutdown, then
    253  * tb_init.
    254  */
    255 #define TB_OK                   0
    256 #define TB_ERR                  -1
    257 #define TB_ERR_NEED_MORE        -2
    258 #define TB_ERR_INIT_ALREADY     -3
    259 #define TB_ERR_INIT_OPEN        -4
    260 #define TB_ERR_MEM              -5
    261 #define TB_ERR_NO_EVENT         -6
    262 #define TB_ERR_NO_TERM          -7
    263 #define TB_ERR_NOT_INIT         -8
    264 #define TB_ERR_OUT_OF_BOUNDS    -9
    265 #define TB_ERR_READ             -10
    266 #define TB_ERR_RESIZE_IOCTL     -11
    267 #define TB_ERR_RESIZE_PIPE      -12
    268 #define TB_ERR_RESIZE_SIGACTION -13
    269 #define TB_ERR_POLL             -14
    270 #define TB_ERR_TCGETATTR        -15
    271 #define TB_ERR_TCSETATTR        -16
    272 #define TB_ERR_UNSUPPORTED_TERM -17
    273 #define TB_ERR_RESIZE_WRITE     -18
    274 #define TB_ERR_RESIZE_POLL      -19
    275 #define TB_ERR_RESIZE_READ      -20
    276 #define TB_ERR_RESIZE_SSCANF    -21
    277 #define TB_ERR_CAP_COLLISION    -22
    278 
    279 #define TB_ERR_SELECT           TB_ERR_POLL
    280 #define TB_ERR_RESIZE_SELECT    TB_ERR_RESIZE_POLL
    281 
    282 /* Function types to be used with tb_set_func() */
    283 #define TB_FUNC_EXTRACT_PRE     0
    284 #define TB_FUNC_EXTRACT_POST    1
    285 
    286 /* Define this to set the size of the buffer used in tb_printf()
    287  * and tb_sendf()
    288  */
    289 #ifndef TB_OPT_PRINTF_BUF
    290 #define TB_OPT_PRINTF_BUF 4096
    291 #endif
    292 
    293 /* Define this to set the size of the read buffer used when reading
    294  * from the tty
    295  */
    296 #ifndef TB_OPT_READ_BUF
    297 #define TB_OPT_READ_BUF 64
    298 #endif
    299 
    300 /* Define this for limited back compat with termbox v1 */
    301 #ifdef TB_OPT_V1_COMPAT
    302 #define tb_change_cell          tb_set_cell
    303 #define tb_put_cell(x, y, c)    tb_set_cell((x), (y), (c)->ch, (c)->fg, (c)->bg)
    304 #define tb_set_clear_attributes tb_set_clear_attrs
    305 #define tb_select_input_mode    tb_set_input_mode
    306 #define tb_select_output_mode   tb_set_output_mode
    307 #endif
    308 
    309 /* Define these to swap in a different allocator */
    310 #ifndef tb_malloc
    311 #define tb_malloc  malloc
    312 #define tb_realloc realloc
    313 #define tb_free    free
    314 #endif
    315 
    316 #ifdef TB_OPT_TRUECOLOR
    317 typedef uint32_t uintattr_t;
    318 #else
    319 typedef uint16_t uintattr_t;
    320 #endif
    321 
    322 /* The terminal screen is represented as 2d array of cells. The structure is
    323  * optimized for dealing with single-width (wcwidth()==1) Unicode code points,
    324  * however some support for grapheme clusters (e.g., combining diacritical
    325  * marks) and wide code points (e.g., Hiragana) is provided through ech, nech,
    326  * cech via tb_set_cell_ex(). ech is only valid when nech>0, otherwise ch is
    327  * used.
    328  *
    329  * For non-single-width code points, given N=wcwidth(ch)/wcswidth(ech):
    330  *
    331  *   when N==0: termbox forces a single-width cell. Callers should avoid this
    332  *              if aiming to render text accurately.
    333  *
    334  *    when N>1: termbox zeroes out the following N-1 cells and skips sending
    335  *              them to the tty. So, e.g., if the caller sets x=0,y=0 to an N==2
    336  *              code point, the caller's next set should be at x=2,y=0. Anything
    337  *              set at x=1,y=0 will be ignored. If there are not enough columns
    338  *              remaining on the line to render N width, spaces are sent
    339  *              instead.
    340  *
    341  * See tb_present() for implementation.
    342  */
    343 struct tb_cell {
    344     uint32_t ch;   /* a Unicode character */
    345     uintattr_t fg; /* bitwise foreground attributes */
    346     uintattr_t bg; /* bitwise background attributes */
    347 #ifdef TB_OPT_EGC
    348     uint32_t *ech; /* a grapheme cluster of Unicode code points */
    349     size_t nech;   /* length in bytes of ech, 0 means use ch instead of ech */
    350     size_t cech;   /* capacity in bytes of ech */
    351 #endif
    352 };
    353 
    354 /* An incoming event from the tty.
    355  *
    356  * Given the event type, the following fields are relevant:
    357  *
    358  *      when TB_EVENT_KEY: (key XOR ch, one will be zero), mod. Note there is
    359  *                         overlap between TB_MOD_CTRL and TB_KEY_CTRL_*.
    360  *                         TB_MOD_CTRL and TB_MOD_SHIFT are only set as
    361  *                         modifiers to TB_KEY_ARROW_*.
    362  *
    363  *   when TB_EVENT_RESIZE: w, h
    364  *
    365  *    when TB_EVENT_MOUSE: key (TB_KEY_MOUSE_*), x, y
    366  */
    367 struct tb_event {
    368     uint8_t type; /* one of TB_EVENT_* constants */
    369     uint8_t mod;  /* bitwise TB_MOD_* constants */
    370     uint16_t key; /* one of TB_KEY_* constants */
    371     uint32_t ch;  /* a Unicode code point */
    372     int32_t w;    /* resize width */
    373     int32_t h;    /* resize height */
    374     int32_t x;    /* mouse x */
    375     int32_t y;    /* mouse y */
    376 };
    377 
    378 /* Initializes the termbox library. This function should be called before any
    379  * other functions. tb_init() is equivalent to tb_init_file("/dev/tty"). After
    380  * successful initialization, the library must be finalized using the
    381  * tb_shutdown() function.
    382  */
    383 int tb_init(void);
    384 int tb_init_file(const char *path);
    385 int tb_init_fd(int ttyfd);
    386 int tb_init_rwfd(int rfd, int wfd);
    387 int tb_shutdown(void);
    388 
    389 /* Returns the size of the internal back buffer (which is the same as terminal's
    390  * window size in rows and columns). The internal buffer can be resized after
    391  * tb_clear() or tb_present() function calls. Both dimensions have an
    392  * unspecified negative value when called before tb_init() or after
    393  * tb_shutdown().
    394  */
    395 int tb_width(void);
    396 int tb_height(void);
    397 
    398 /* Clears the internal back buffer using TB_DEFAULT color or the
    399  * color/attributes set by tb_set_clear_attrs() function.
    400  */
    401 int tb_clear(void);
    402 int tb_set_clear_attrs(uintattr_t fg, uintattr_t bg);
    403 
    404 /* Synchronizes the internal back buffer with the terminal by writing to tty. */
    405 int tb_present(void);
    406 
    407 /* Sets the position of the cursor. Upper-left character is (0, 0). */
    408 int tb_set_cursor(int cx, int cy);
    409 int tb_hide_cursor(void);
    410 
    411 /* Set cell contents in the internal back buffer at the specified position.
    412  *
    413  * Use tb_set_cell_ex() for rendering grapheme clusters (e.g., combining
    414  * diacritical marks).
    415  *
    416  * Function tb_set_cell(x, y, ch, fg, bg) is equivalent to
    417  * tb_set_cell_ex(x, y, &ch, 1, fg, bg).
    418  *
    419  * Function tb_extend_cell() is a shortcut for appending 1 code point to
    420  * cell->ech.
    421  */
    422 int tb_set_cell(int x, int y, uint32_t ch, uintattr_t fg, uintattr_t bg);
    423 int tb_set_cell_ex(int x, int y, uint32_t *ch, size_t nch, uintattr_t fg,
    424     uintattr_t bg);
    425 int tb_extend_cell(int x, int y, uint32_t ch);
    426 
    427 /* Sets the input mode. Termbox has two input modes:
    428  *
    429  * 1. TB_INPUT_ESC
    430  *    When escape (\x1b) is in the buffer and there's no match for an escape
    431  *    sequence, a key event for TB_KEY_ESC is returned.
    432  *
    433  * 2. TB_INPUT_ALT
    434  *    When escape (\x1b) is in the buffer and there's no match for an escape
    435  *    sequence, the next keyboard event is returned with a TB_MOD_ALT modifier.
    436  *
    437  * You can also apply TB_INPUT_MOUSE via bitwise OR operation to either of the
    438  * modes (e.g., TB_INPUT_ESC | TB_INPUT_MOUSE) to receive TB_EVENT_MOUSE events.
    439  * If none of the main two modes were set, but the mouse mode was, TB_INPUT_ESC
    440  * mode is used. If for some reason you've decided to use
    441  * (TB_INPUT_ESC | TB_INPUT_ALT) combination, it will behave as if only
    442  * TB_INPUT_ESC was selected.
    443  *
    444  * If mode is TB_INPUT_CURRENT, the function returns the current input mode.
    445  *
    446  * The default input mode is TB_INPUT_ESC.
    447  */
    448 int tb_set_input_mode(int mode);
    449 
    450 /* Sets the termbox output mode. Termbox has multiple output modes:
    451  *
    452  * 1. TB_OUTPUT_NORMAL     => [0..8]
    453  *
    454  *    This mode provides 8 different colors:
    455  *      TB_BLACK, TB_RED, TB_GREEN, TB_YELLOW,
    456  *      TB_BLUE, TB_MAGENTA, TB_CYAN, TB_WHITE
    457  *
    458  *    Plus TB_DEFAULT which skips sending a color code (i.e., uses the
    459  *    terminal's default color).
    460  *
    461  *    Colors (including TB_DEFAULT) may be bitwise OR'd with attributes:
    462  *      TB_BOLD, TB_UNDERLINE, TB_REVERSE, TB_ITALIC, TB_BLINK
    463  *
    464  *    The value 0 is interpreted as TB_DEFAULT.
    465  *
    466  *    Some notes: TB_REVERSE can be applied as either fg or bg attributes for
    467  *    the same effect. TB_BOLD, TB_UNDERLINE, TB_ITALIC, TB_BLINK apply as fg
    468  *    attributes only, and are ignored as bg attributes.
    469  *
    470  *    Example usage:
    471  *      tb_set_cell(x, y, '@', TB_BLACK | TB_BOLD, TB_RED);
    472  *
    473  * 2. TB_OUTPUT_256        => [0..255]
    474  *
    475  *    In this mode you get 256 distinct colors:
    476  *      0x00 - 0x07: the 8 colors as in TB_OUTPUT_NORMAL
    477  *      0x08 - 0x0f: bright versions of the above
    478  *      0x10 - 0xe7: 216 different colors
    479  *      0xe8 - 0xff: 24 different shades of grey
    480  *
    481  *    Attributes may be bitwise OR'd as in TB_OUTPUT_NORMAL.
    482  *
    483  *    In this mode 0x00 represents TB_BLACK, so TB_DEFAULT must be used for
    484  *    default colors.
    485  *
    486  * 3. TB_OUTPUT_216        => [0..216]
    487  *
    488  *    This mode supports the 3rd range of TB_OUTPUT_256 only, but you don't
    489  *    need to provide an offset.
    490  *
    491  *    The value 0 is interpreted as TB_DEFAULT.
    492  *
    493  * 4. TB_OUTPUT_GRAYSCALE  => [0..24]
    494  *
    495  *    This mode supports the 4th range of TB_OUTPUT_256 only, but you don't
    496  *    need to provide an offset.
    497  *
    498  *    The value 0 is interpreted as TB_DEFAULT.
    499  *
    500  * 5. TB_OUTPUT_TRUECOLOR  => [0x000000..0xffffff]
    501  *
    502  *    This mode provides 24-bit color on supported terminals. The format is
    503  *    0xRRGGBB. Colors may be bitwise OR'd with `TB_TRUECOLOR_*` attributes.
    504  *
    505  *    In this mode 0x000000 represents black, so TB_TRUECOLOR_DEFAULT must be
    506  *    used for default colors.
    507  *
    508  * If mode is TB_OUTPUT_CURRENT, the function returns the current output mode.
    509  *
    510  * The default output mode is TB_OUTPUT_NORMAL.
    511  *
    512  * To use the terminal default color (i.e., to not send an escape code), pass
    513  * TB_DEFAULT (or TB_TRUECOLOR_DEFAULT in TB_OUTPUT_TRUECOLOR mode). For
    514  * convenience, the value 0 is interpreted as TB_DEFAULT in TB_OUTPUT_NORMAL,
    515  * TB_OUTPUT_216, and TB_OUTPUT_GRAYSCALE.
    516  *
    517  * Note, not all terminals support all output modes, especially beyond
    518  * TB_OUTPUT_NORMAL. There is also no very reliable way to determine color
    519  * support dynamically. If portability is desired, callers are recommended to
    520  * use TB_OUTPUT_NORMAL or make output mode end-user configurable.
    521  */
    522 int tb_set_output_mode(int mode);
    523 
    524 /* Wait for an event up to timeout_ms milliseconds and fill the event structure
    525  * with it. If no event is available within the timeout period, TB_ERR_NO_EVENT
    526  * is returned. On a resize event, the underlying select(2) call may be
    527  * interrupted, yielding a return code of TB_ERR_POLL. In this case, you may
    528  * check errno via tb_last_errno(). If it's EINTR, you can safely ignore that
    529  * and call tb_peek_event() again.
    530  */
    531 int tb_peek_event(struct tb_event *event, int timeout_ms);
    532 
    533 /* Same as tb_peek_event except no timeout. */
    534 int tb_poll_event(struct tb_event *event);
    535 
    536 /* Internal termbox FDs that can be used with poll() / select(). Must call
    537  * tb_poll_event() / tb_peek_event() if activity is detected. */
    538 int tb_get_fds(int *ttyfd, int *resizefd);
    539 
    540 /* Print and printf functions. Specify param out_w to determine width of printed
    541  * string.
    542  */
    543 int tb_print(int x, int y, uintattr_t fg, uintattr_t bg, const char *str);
    544 int tb_printf(int x, int y, uintattr_t fg, uintattr_t bg, const char *fmt, ...);
    545 int tb_print_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
    546     const char *str);
    547 int tb_printf_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
    548     const char *fmt, ...);
    549 
    550 /* Send raw bytes to terminal. */
    551 int tb_send(const char *buf, size_t nbuf);
    552 int tb_sendf(const char *fmt, ...);
    553 
    554 /* Set custom functions. fn_type is one of TB_FUNC_* constants, fn is a
    555  * compatible function pointer, or NULL to clear.
    556  *
    557  * TB_FUNC_EXTRACT_PRE:
    558  *   If specified, invoke this function BEFORE termbox tries to extract any
    559  *   escape sequences from the input buffer.
    560  *
    561  * TB_FUNC_EXTRACT_POST:
    562  *   If specified, invoke this function AFTER termbox tries (and fails) to
    563  *   extract any escape sequences from the input buffer.
    564  */
    565 int tb_set_func(int fn_type, int (*fn)(struct tb_event *, size_t *));
    566 
    567 /* Utility functions. */
    568 int tb_utf8_char_length(char c);
    569 int tb_utf8_char_to_unicode(uint32_t *out, const char *c);
    570 int tb_utf8_unicode_to_char(char *out, uint32_t c);
    571 int tb_last_errno(void);
    572 const char *tb_strerror(int err);
    573 struct tb_cell *tb_cell_buffer(void);
    574 int tb_has_truecolor(void);
    575 int tb_has_egc(void);
    576 const char *tb_version(void);
    577 
    578 #ifdef __cplusplus
    579 }
    580 #endif
    581 
    582 #endif /* __TERMBOX_H */
    583 
    584 #ifdef TB_IMPL
    585 
    586 #define if_err_return(rv, expr)                                                \
    587     if (((rv) = (expr)) != TB_OK)                                              \
    588     return (rv)
    589 #define if_err_break(rv, expr)                                                 \
    590     if (((rv) = (expr)) != TB_OK)                                              \
    591     break
    592 #define if_ok_return(rv, expr)                                                 \
    593     if (((rv) = (expr)) == TB_OK)                                              \
    594     return (rv)
    595 #define if_ok_or_need_more_return(rv, expr)                                    \
    596     if (((rv) = (expr)) == TB_OK || (rv) == TB_ERR_NEED_MORE)                  \
    597     return (rv)
    598 
    599 #define send_literal(rv, a)                                                    \
    600     if_err_return((rv), bytebuf_nputs(&global.out, (a), sizeof(a) - 1))
    601 
    602 #define send_num(rv, nbuf, n)                                                  \
    603     if_err_return((rv),                                                        \
    604         bytebuf_nputs(&global.out, (nbuf), convert_num((n), (nbuf))))
    605 
    606 #define snprintf_or_return(rv, str, sz, fmt, ...)                              \
    607     do {                                                                       \
    608         (rv) = snprintf((str), (sz), (fmt), __VA_ARGS__);                      \
    609         if ((rv) < 0 || (rv) >= (int)(sz))                                     \
    610             return TB_ERR;                                                     \
    611     } while (0)
    612 
    613 #define if_not_init_return()                                                   \
    614     if (!global.initialized)                                                   \
    615     return TB_ERR_NOT_INIT
    616 
    617 struct bytebuf_t {
    618     char *buf;
    619     size_t len;
    620     size_t cap;
    621 };
    622 
    623 struct cellbuf_t {
    624     int width;
    625     int height;
    626     struct tb_cell *cells;
    627 };
    628 
    629 struct cap_trie_t {
    630     char c;
    631     struct cap_trie_t *children;
    632     size_t nchildren;
    633     int is_leaf;
    634     uint16_t key;
    635     uint8_t mod;
    636 };
    637 
    638 struct tb_global_t {
    639     int ttyfd;
    640     int rfd;
    641     int wfd;
    642     int ttyfd_open;
    643     int resize_pipefd[2];
    644     int width;
    645     int height;
    646     int cursor_x;
    647     int cursor_y;
    648     int last_x;
    649     int last_y;
    650     uintattr_t fg;
    651     uintattr_t bg;
    652     uintattr_t last_fg;
    653     uintattr_t last_bg;
    654     int input_mode;
    655     int output_mode;
    656     char *terminfo;
    657     size_t nterminfo;
    658     const char *caps[TB_CAP__COUNT];
    659     struct cap_trie_t cap_trie;
    660     struct bytebuf_t in;
    661     struct bytebuf_t out;
    662     struct cellbuf_t back;
    663     struct cellbuf_t front;
    664     struct termios orig_tios;
    665     int has_orig_tios;
    666     int last_errno;
    667     int initialized;
    668     int (*fn_extract_esc_pre)(struct tb_event *, size_t *);
    669     int (*fn_extract_esc_post)(struct tb_event *, size_t *);
    670     char errbuf[1024];
    671 };
    672 
    673 static struct tb_global_t global = {0};
    674 
    675 /* BEGIN codegen c */
    676 /* Produced by ./codegen.sh on Sun, 19 Sep 2021 01:02:03 +0000 */
    677 
    678 static const int16_t terminfo_cap_indexes[] = {
    679     66,  // kf1 (TB_CAP_F1)
    680     68,  // kf2 (TB_CAP_F2)
    681     69,  // kf3 (TB_CAP_F3)
    682     70,  // kf4 (TB_CAP_F4)
    683     71,  // kf5 (TB_CAP_F5)
    684     72,  // kf6 (TB_CAP_F6)
    685     73,  // kf7 (TB_CAP_F7)
    686     74,  // kf8 (TB_CAP_F8)
    687     75,  // kf9 (TB_CAP_F9)
    688     67,  // kf10 (TB_CAP_F10)
    689     216, // kf11 (TB_CAP_F11)
    690     217, // kf12 (TB_CAP_F12)
    691     77,  // kich1 (TB_CAP_INSERT)
    692     59,  // kdch1 (TB_CAP_DELETE)
    693     76,  // khome (TB_CAP_HOME)
    694     164, // kend (TB_CAP_END)
    695     82,  // kpp (TB_CAP_PGUP)
    696     81,  // knp (TB_CAP_PGDN)
    697     87,  // kcuu1 (TB_CAP_ARROW_UP)
    698     61,  // kcud1 (TB_CAP_ARROW_DOWN)
    699     79,  // kcub1 (TB_CAP_ARROW_LEFT)
    700     83,  // kcuf1 (TB_CAP_ARROW_RIGHT)
    701     148, // kcbt (TB_CAP_BACK_TAB)
    702     28,  // smcup (TB_CAP_ENTER_CA)
    703     40,  // rmcup (TB_CAP_EXIT_CA)
    704     16,  // cnorm (TB_CAP_SHOW_CURSOR)
    705     13,  // civis (TB_CAP_HIDE_CURSOR)
    706     5,   // clear (TB_CAP_CLEAR_SCREEN)
    707     39,  // sgr0 (TB_CAP_SGR0)
    708     36,  // smul (TB_CAP_UNDERLINE)
    709     27,  // bold (TB_CAP_BOLD)
    710     26,  // blink (TB_CAP_BLINK)
    711     311, // sitm (TB_CAP_ITALIC)
    712     34,  // rev (TB_CAP_REVERSE)
    713     89,  // smkx (TB_CAP_ENTER_KEYPAD)
    714     88,  // rmkx (TB_CAP_EXIT_KEYPAD)
    715 };
    716 
    717 // xterm
    718 static const char *xterm_caps[] = {
    719     "\033OP",                  // kf1 (TB_CAP_F1)
    720     "\033OQ",                  // kf2 (TB_CAP_F2)
    721     "\033OR",                  // kf3 (TB_CAP_F3)
    722     "\033OS",                  // kf4 (TB_CAP_F4)
    723     "\033[15~",                // kf5 (TB_CAP_F5)
    724     "\033[17~",                // kf6 (TB_CAP_F6)
    725     "\033[18~",                // kf7 (TB_CAP_F7)
    726     "\033[19~",                // kf8 (TB_CAP_F8)
    727     "\033[20~",                // kf9 (TB_CAP_F9)
    728     "\033[21~",                // kf10 (TB_CAP_F10)
    729     "\033[23~",                // kf11 (TB_CAP_F11)
    730     "\033[24~",                // kf12 (TB_CAP_F12)
    731     "\033[2~",                 // kich1 (TB_CAP_INSERT)
    732     "\033[3~",                 // kdch1 (TB_CAP_DELETE)
    733     "\033OH",                  // khome (TB_CAP_HOME)
    734     "\033OF",                  // kend (TB_CAP_END)
    735     "\033[5~",                 // kpp (TB_CAP_PGUP)
    736     "\033[6~",                 // knp (TB_CAP_PGDN)
    737     "\033OA",                  // kcuu1 (TB_CAP_ARROW_UP)
    738     "\033OB",                  // kcud1 (TB_CAP_ARROW_DOWN)
    739     "\033OD",                  // kcub1 (TB_CAP_ARROW_LEFT)
    740     "\033OC",                  // kcuf1 (TB_CAP_ARROW_RIGHT)
    741     "\033[Z",                  // kcbt (TB_CAP_BACK_TAB)
    742     "\033[?1049h\033[22;0;0t", // smcup (TB_CAP_ENTER_CA)
    743     "\033[?1049l\033[23;0;0t", // rmcup (TB_CAP_EXIT_CA)
    744     "\033[?12l\033[?25h",      // cnorm (TB_CAP_SHOW_CURSOR)
    745     "\033[?25l",               // civis (TB_CAP_HIDE_CURSOR)
    746     "\033[H\033[2J",           // clear (TB_CAP_CLEAR_SCREEN)
    747     "\033(B\033[m",            // sgr0 (TB_CAP_SGR0)
    748     "\033[4m",                 // smul (TB_CAP_UNDERLINE)
    749     "\033[1m",                 // bold (TB_CAP_BOLD)
    750     "\033[5m",                 // blink (TB_CAP_BLINK)
    751     "\033[3m",                 // sitm (TB_CAP_ITALIC)
    752     "\033[7m",                 // rev (TB_CAP_REVERSE)
    753     "\033[?1h\033=",           // smkx (TB_CAP_ENTER_KEYPAD)
    754     "\033[?1l\033>",           // rmkx (TB_CAP_EXIT_KEYPAD)
    755 };
    756 
    757 // linux
    758 static const char *linux_caps[] = {
    759     "\033[[A",           // kf1 (TB_CAP_F1)
    760     "\033[[B",           // kf2 (TB_CAP_F2)
    761     "\033[[C",           // kf3 (TB_CAP_F3)
    762     "\033[[D",           // kf4 (TB_CAP_F4)
    763     "\033[[E",           // kf5 (TB_CAP_F5)
    764     "\033[17~",          // kf6 (TB_CAP_F6)
    765     "\033[18~",          // kf7 (TB_CAP_F7)
    766     "\033[19~",          // kf8 (TB_CAP_F8)
    767     "\033[20~",          // kf9 (TB_CAP_F9)
    768     "\033[21~",          // kf10 (TB_CAP_F10)
    769     "\033[23~",          // kf11 (TB_CAP_F11)
    770     "\033[24~",          // kf12 (TB_CAP_F12)
    771     "\033[2~",           // kich1 (TB_CAP_INSERT)
    772     "\033[3~",           // kdch1 (TB_CAP_DELETE)
    773     "\033[1~",           // khome (TB_CAP_HOME)
    774     "\033[4~",           // kend (TB_CAP_END)
    775     "\033[5~",           // kpp (TB_CAP_PGUP)
    776     "\033[6~",           // knp (TB_CAP_PGDN)
    777     "\033[A",            // kcuu1 (TB_CAP_ARROW_UP)
    778     "\033[B",            // kcud1 (TB_CAP_ARROW_DOWN)
    779     "\033[D",            // kcub1 (TB_CAP_ARROW_LEFT)
    780     "\033[C",            // kcuf1 (TB_CAP_ARROW_RIGHT)
    781     "\033[Z",            // kcbt (TB_CAP_BACK_TAB)
    782     "",                  // smcup (TB_CAP_ENTER_CA)
    783     "",                  // rmcup (TB_CAP_EXIT_CA)
    784     "\033[?25h\033[?0c", // cnorm (TB_CAP_SHOW_CURSOR)
    785     "\033[?25l\033[?1c", // civis (TB_CAP_HIDE_CURSOR)
    786     "\033[H\033[J",      // clear (TB_CAP_CLEAR_SCREEN)
    787     "\033[m\017",        // sgr0 (TB_CAP_SGR0)
    788     "\033[4m",           // smul (TB_CAP_UNDERLINE)
    789     "\033[1m",           // bold (TB_CAP_BOLD)
    790     "\033[5m",           // blink (TB_CAP_BLINK)
    791     "",                  // sitm (TB_CAP_ITALIC)
    792     "\033[7m",           // rev (TB_CAP_REVERSE)
    793     "",                  // smkx (TB_CAP_ENTER_KEYPAD)
    794     "",                  // rmkx (TB_CAP_EXIT_KEYPAD)
    795 };
    796 
    797 // screen
    798 static const char *screen_caps[] = {
    799     "\033OP",            // kf1 (TB_CAP_F1)
    800     "\033OQ",            // kf2 (TB_CAP_F2)
    801     "\033OR",            // kf3 (TB_CAP_F3)
    802     "\033OS",            // kf4 (TB_CAP_F4)
    803     "\033[15~",          // kf5 (TB_CAP_F5)
    804     "\033[17~",          // kf6 (TB_CAP_F6)
    805     "\033[18~",          // kf7 (TB_CAP_F7)
    806     "\033[19~",          // kf8 (TB_CAP_F8)
    807     "\033[20~",          // kf9 (TB_CAP_F9)
    808     "\033[21~",          // kf10 (TB_CAP_F10)
    809     "\033[23~",          // kf11 (TB_CAP_F11)
    810     "\033[24~",          // kf12 (TB_CAP_F12)
    811     "\033[2~",           // kich1 (TB_CAP_INSERT)
    812     "\033[3~",           // kdch1 (TB_CAP_DELETE)
    813     "\033[1~",           // khome (TB_CAP_HOME)
    814     "\033[4~",           // kend (TB_CAP_END)
    815     "\033[5~",           // kpp (TB_CAP_PGUP)
    816     "\033[6~",           // knp (TB_CAP_PGDN)
    817     "\033OA",            // kcuu1 (TB_CAP_ARROW_UP)
    818     "\033OB",            // kcud1 (TB_CAP_ARROW_DOWN)
    819     "\033OD",            // kcub1 (TB_CAP_ARROW_LEFT)
    820     "\033OC",            // kcuf1 (TB_CAP_ARROW_RIGHT)
    821     "\033[Z",            // kcbt (TB_CAP_BACK_TAB)
    822     "\033[?1049h",       // smcup (TB_CAP_ENTER_CA)
    823     "\033[?1049l",       // rmcup (TB_CAP_EXIT_CA)
    824     "\033[34h\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR)
    825     "\033[?25l",         // civis (TB_CAP_HIDE_CURSOR)
    826     "\033[H\033[J",      // clear (TB_CAP_CLEAR_SCREEN)
    827     "\033[m\017",        // sgr0 (TB_CAP_SGR0)
    828     "\033[4m",           // smul (TB_CAP_UNDERLINE)
    829     "\033[1m",           // bold (TB_CAP_BOLD)
    830     "\033[5m",           // blink (TB_CAP_BLINK)
    831     "",                  // sitm (TB_CAP_ITALIC)
    832     "\033[7m",           // rev (TB_CAP_REVERSE)
    833     "\033[?1h\033=",     // smkx (TB_CAP_ENTER_KEYPAD)
    834     "\033[?1l\033>",     // rmkx (TB_CAP_EXIT_KEYPAD)
    835 };
    836 
    837 // rxvt-256color
    838 static const char *rxvt_256color_caps[] = {
    839     "\033[11~",              // kf1 (TB_CAP_F1)
    840     "\033[12~",              // kf2 (TB_CAP_F2)
    841     "\033[13~",              // kf3 (TB_CAP_F3)
    842     "\033[14~",              // kf4 (TB_CAP_F4)
    843     "\033[15~",              // kf5 (TB_CAP_F5)
    844     "\033[17~",              // kf6 (TB_CAP_F6)
    845     "\033[18~",              // kf7 (TB_CAP_F7)
    846     "\033[19~",              // kf8 (TB_CAP_F8)
    847     "\033[20~",              // kf9 (TB_CAP_F9)
    848     "\033[21~",              // kf10 (TB_CAP_F10)
    849     "\033[23~",              // kf11 (TB_CAP_F11)
    850     "\033[24~",              // kf12 (TB_CAP_F12)
    851     "\033[2~",               // kich1 (TB_CAP_INSERT)
    852     "\033[3~",               // kdch1 (TB_CAP_DELETE)
    853     "\033[7~",               // khome (TB_CAP_HOME)
    854     "\033[8~",               // kend (TB_CAP_END)
    855     "\033[5~",               // kpp (TB_CAP_PGUP)
    856     "\033[6~",               // knp (TB_CAP_PGDN)
    857     "\033[A",                // kcuu1 (TB_CAP_ARROW_UP)
    858     "\033[B",                // kcud1 (TB_CAP_ARROW_DOWN)
    859     "\033[D",                // kcub1 (TB_CAP_ARROW_LEFT)
    860     "\033[C",                // kcuf1 (TB_CAP_ARROW_RIGHT)
    861     "\033[Z",                // kcbt (TB_CAP_BACK_TAB)
    862     "\0337\033[?47h",        // smcup (TB_CAP_ENTER_CA)
    863     "\033[2J\033[?47l\0338", // rmcup (TB_CAP_EXIT_CA)
    864     "\033[?25h",             // cnorm (TB_CAP_SHOW_CURSOR)
    865     "\033[?25l",             // civis (TB_CAP_HIDE_CURSOR)
    866     "\033[H\033[2J",         // clear (TB_CAP_CLEAR_SCREEN)
    867     "\033[m\017",            // sgr0 (TB_CAP_SGR0)
    868     "\033[4m",               // smul (TB_CAP_UNDERLINE)
    869     "\033[1m",               // bold (TB_CAP_BOLD)
    870     "\033[5m",               // blink (TB_CAP_BLINK)
    871     "",                      // sitm (TB_CAP_ITALIC)
    872     "\033[7m",               // rev (TB_CAP_REVERSE)
    873     "\033=",                 // smkx (TB_CAP_ENTER_KEYPAD)
    874     "\033>",                 // rmkx (TB_CAP_EXIT_KEYPAD)
    875 };
    876 
    877 // rxvt-unicode
    878 static const char *rxvt_unicode_caps[] = {
    879     "\033[11~",           // kf1 (TB_CAP_F1)
    880     "\033[12~",           // kf2 (TB_CAP_F2)
    881     "\033[13~",           // kf3 (TB_CAP_F3)
    882     "\033[14~",           // kf4 (TB_CAP_F4)
    883     "\033[15~",           // kf5 (TB_CAP_F5)
    884     "\033[17~",           // kf6 (TB_CAP_F6)
    885     "\033[18~",           // kf7 (TB_CAP_F7)
    886     "\033[19~",           // kf8 (TB_CAP_F8)
    887     "\033[20~",           // kf9 (TB_CAP_F9)
    888     "\033[21~",           // kf10 (TB_CAP_F10)
    889     "\033[23~",           // kf11 (TB_CAP_F11)
    890     "\033[24~",           // kf12 (TB_CAP_F12)
    891     "\033[2~",            // kich1 (TB_CAP_INSERT)
    892     "\033[3~",            // kdch1 (TB_CAP_DELETE)
    893     "\033[7~",            // khome (TB_CAP_HOME)
    894     "\033[8~",            // kend (TB_CAP_END)
    895     "\033[5~",            // kpp (TB_CAP_PGUP)
    896     "\033[6~",            // knp (TB_CAP_PGDN)
    897     "\033[A",             // kcuu1 (TB_CAP_ARROW_UP)
    898     "\033[B",             // kcud1 (TB_CAP_ARROW_DOWN)
    899     "\033[D",             // kcub1 (TB_CAP_ARROW_LEFT)
    900     "\033[C",             // kcuf1 (TB_CAP_ARROW_RIGHT)
    901     "\033[Z",             // kcbt (TB_CAP_BACK_TAB)
    902     "\033[?1049h",        // smcup (TB_CAP_ENTER_CA)
    903     "\033[r\033[?1049l",  // rmcup (TB_CAP_EXIT_CA)
    904     "\033[?12l\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR)
    905     "\033[?25l",          // civis (TB_CAP_HIDE_CURSOR)
    906     "\033[H\033[2J",      // clear (TB_CAP_CLEAR_SCREEN)
    907     "\033[m\033(B",       // sgr0 (TB_CAP_SGR0)
    908     "\033[4m",            // smul (TB_CAP_UNDERLINE)
    909     "\033[1m",            // bold (TB_CAP_BOLD)
    910     "\033[5m",            // blink (TB_CAP_BLINK)
    911     "\033[3m",            // sitm (TB_CAP_ITALIC)
    912     "\033[7m",            // rev (TB_CAP_REVERSE)
    913     "\033=",              // smkx (TB_CAP_ENTER_KEYPAD)
    914     "\033>",              // rmkx (TB_CAP_EXIT_KEYPAD)
    915 };
    916 
    917 // Eterm
    918 static const char *eterm_caps[] = {
    919     "\033[11~",              // kf1 (TB_CAP_F1)
    920     "\033[12~",              // kf2 (TB_CAP_F2)
    921     "\033[13~",              // kf3 (TB_CAP_F3)
    922     "\033[14~",              // kf4 (TB_CAP_F4)
    923     "\033[15~",              // kf5 (TB_CAP_F5)
    924     "\033[17~",              // kf6 (TB_CAP_F6)
    925     "\033[18~",              // kf7 (TB_CAP_F7)
    926     "\033[19~",              // kf8 (TB_CAP_F8)
    927     "\033[20~",              // kf9 (TB_CAP_F9)
    928     "\033[21~",              // kf10 (TB_CAP_F10)
    929     "\033[23~",              // kf11 (TB_CAP_F11)
    930     "\033[24~",              // kf12 (TB_CAP_F12)
    931     "\033[2~",               // kich1 (TB_CAP_INSERT)
    932     "\033[3~",               // kdch1 (TB_CAP_DELETE)
    933     "\033[7~",               // khome (TB_CAP_HOME)
    934     "\033[8~",               // kend (TB_CAP_END)
    935     "\033[5~",               // kpp (TB_CAP_PGUP)
    936     "\033[6~",               // knp (TB_CAP_PGDN)
    937     "\033[A",                // kcuu1 (TB_CAP_ARROW_UP)
    938     "\033[B",                // kcud1 (TB_CAP_ARROW_DOWN)
    939     "\033[D",                // kcub1 (TB_CAP_ARROW_LEFT)
    940     "\033[C",                // kcuf1 (TB_CAP_ARROW_RIGHT)
    941     "",                      // kcbt (TB_CAP_BACK_TAB)
    942     "\0337\033[?47h",        // smcup (TB_CAP_ENTER_CA)
    943     "\033[2J\033[?47l\0338", // rmcup (TB_CAP_EXIT_CA)
    944     "\033[?25h",             // cnorm (TB_CAP_SHOW_CURSOR)
    945     "\033[?25l",             // civis (TB_CAP_HIDE_CURSOR)
    946     "\033[H\033[2J",         // clear (TB_CAP_CLEAR_SCREEN)
    947     "\033[m\017",            // sgr0 (TB_CAP_SGR0)
    948     "\033[4m",               // smul (TB_CAP_UNDERLINE)
    949     "\033[1m",               // bold (TB_CAP_BOLD)
    950     "\033[5m",               // blink (TB_CAP_BLINK)
    951     "",                      // sitm (TB_CAP_ITALIC)
    952     "\033[7m",               // rev (TB_CAP_REVERSE)
    953     "",                      // smkx (TB_CAP_ENTER_KEYPAD)
    954     "",                      // rmkx (TB_CAP_EXIT_KEYPAD)
    955 };
    956 
    957 static struct {
    958     const char *name;
    959     const char **caps;
    960     const char *alias;
    961 } builtin_terms[] = {
    962     {"xterm",         xterm_caps,         ""    },
    963     {"linux",         linux_caps,         ""    },
    964     {"screen",        screen_caps,        "tmux"},
    965     {"rxvt-256color", rxvt_256color_caps, ""    },
    966     {"rxvt-unicode",  rxvt_unicode_caps,  "rxvt"},
    967     {"Eterm",         eterm_caps,         ""    },
    968     {NULL,            NULL,               NULL  },
    969 };
    970 
    971 /* END codegen c */
    972 
    973 static struct {
    974     const char *cap;
    975     const uint16_t key;
    976     const uint8_t mod;
    977 } builtin_mod_caps[] = {
    978   // xterm arrows
    979     {"\x1b[1;2A",    TB_KEY_ARROW_UP,    TB_MOD_SHIFT                           },
    980     {"\x1b[1;3A",    TB_KEY_ARROW_UP,    TB_MOD_ALT                             },
    981     {"\x1b[1;4A",    TB_KEY_ARROW_UP,    TB_MOD_ALT | TB_MOD_SHIFT              },
    982     {"\x1b[1;5A",    TB_KEY_ARROW_UP,    TB_MOD_CTRL                            },
    983     {"\x1b[1;6A",    TB_KEY_ARROW_UP,    TB_MOD_CTRL | TB_MOD_SHIFT             },
    984     {"\x1b[1;7A",    TB_KEY_ARROW_UP,    TB_MOD_CTRL | TB_MOD_ALT               },
    985     {"\x1b[1;8A",    TB_KEY_ARROW_UP,    TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
    986 
    987     {"\x1b[1;2B",    TB_KEY_ARROW_DOWN,  TB_MOD_SHIFT                           },
    988     {"\x1b[1;3B",    TB_KEY_ARROW_DOWN,  TB_MOD_ALT                             },
    989     {"\x1b[1;4B",    TB_KEY_ARROW_DOWN,  TB_MOD_ALT | TB_MOD_SHIFT              },
    990     {"\x1b[1;5B",    TB_KEY_ARROW_DOWN,  TB_MOD_CTRL                            },
    991     {"\x1b[1;6B",    TB_KEY_ARROW_DOWN,  TB_MOD_CTRL | TB_MOD_SHIFT             },
    992     {"\x1b[1;7B",    TB_KEY_ARROW_DOWN,  TB_MOD_CTRL | TB_MOD_ALT               },
    993     {"\x1b[1;8B",    TB_KEY_ARROW_DOWN,  TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
    994 
    995     {"\x1b[1;2C",    TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT                           },
    996     {"\x1b[1;3C",    TB_KEY_ARROW_RIGHT, TB_MOD_ALT                             },
    997     {"\x1b[1;4C",    TB_KEY_ARROW_RIGHT, TB_MOD_ALT | TB_MOD_SHIFT              },
    998     {"\x1b[1;5C",    TB_KEY_ARROW_RIGHT, TB_MOD_CTRL                            },
    999     {"\x1b[1;6C",    TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_SHIFT             },
   1000     {"\x1b[1;7C",    TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT               },
   1001     {"\x1b[1;8C",    TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1002 
   1003     {"\x1b[1;2D",    TB_KEY_ARROW_LEFT,  TB_MOD_SHIFT                           },
   1004     {"\x1b[1;3D",    TB_KEY_ARROW_LEFT,  TB_MOD_ALT                             },
   1005     {"\x1b[1;4D",    TB_KEY_ARROW_LEFT,  TB_MOD_ALT | TB_MOD_SHIFT              },
   1006     {"\x1b[1;5D",    TB_KEY_ARROW_LEFT,  TB_MOD_CTRL                            },
   1007     {"\x1b[1;6D",    TB_KEY_ARROW_LEFT,  TB_MOD_CTRL | TB_MOD_SHIFT             },
   1008     {"\x1b[1;7D",    TB_KEY_ARROW_LEFT,  TB_MOD_CTRL | TB_MOD_ALT               },
   1009     {"\x1b[1;8D",    TB_KEY_ARROW_LEFT,  TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1010 
   1011  // xterm keys
   1012     {"\x1b[1;2H",    TB_KEY_HOME,        TB_MOD_SHIFT                           },
   1013     {"\x1b[1;3H",    TB_KEY_HOME,        TB_MOD_ALT                             },
   1014     {"\x1b[1;4H",    TB_KEY_HOME,        TB_MOD_ALT | TB_MOD_SHIFT              },
   1015     {"\x1b[1;5H",    TB_KEY_HOME,        TB_MOD_CTRL                            },
   1016     {"\x1b[1;6H",    TB_KEY_HOME,        TB_MOD_CTRL | TB_MOD_SHIFT             },
   1017     {"\x1b[1;7H",    TB_KEY_HOME,        TB_MOD_CTRL | TB_MOD_ALT               },
   1018     {"\x1b[1;8H",    TB_KEY_HOME,        TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1019 
   1020     {"\x1b[1;2F",    TB_KEY_END,         TB_MOD_SHIFT                           },
   1021     {"\x1b[1;3F",    TB_KEY_END,         TB_MOD_ALT                             },
   1022     {"\x1b[1;4F",    TB_KEY_END,         TB_MOD_ALT | TB_MOD_SHIFT              },
   1023     {"\x1b[1;5F",    TB_KEY_END,         TB_MOD_CTRL                            },
   1024     {"\x1b[1;6F",    TB_KEY_END,         TB_MOD_CTRL | TB_MOD_SHIFT             },
   1025     {"\x1b[1;7F",    TB_KEY_END,         TB_MOD_CTRL | TB_MOD_ALT               },
   1026     {"\x1b[1;8F",    TB_KEY_END,         TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1027 
   1028     {"\x1b[2;2~",    TB_KEY_INSERT,      TB_MOD_SHIFT                           },
   1029     {"\x1b[2;3~",    TB_KEY_INSERT,      TB_MOD_ALT                             },
   1030     {"\x1b[2;4~",    TB_KEY_INSERT,      TB_MOD_ALT | TB_MOD_SHIFT              },
   1031     {"\x1b[2;5~",    TB_KEY_INSERT,      TB_MOD_CTRL                            },
   1032     {"\x1b[2;6~",    TB_KEY_INSERT,      TB_MOD_CTRL | TB_MOD_SHIFT             },
   1033     {"\x1b[2;7~",    TB_KEY_INSERT,      TB_MOD_CTRL | TB_MOD_ALT               },
   1034     {"\x1b[2;8~",    TB_KEY_INSERT,      TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1035 
   1036     {"\x1b[3;2~",    TB_KEY_DELETE,      TB_MOD_SHIFT                           },
   1037     {"\x1b[3;3~",    TB_KEY_DELETE,      TB_MOD_ALT                             },
   1038     {"\x1b[3;4~",    TB_KEY_DELETE,      TB_MOD_ALT | TB_MOD_SHIFT              },
   1039     {"\x1b[3;5~",    TB_KEY_DELETE,      TB_MOD_CTRL                            },
   1040     {"\x1b[3;6~",    TB_KEY_DELETE,      TB_MOD_CTRL | TB_MOD_SHIFT             },
   1041     {"\x1b[3;7~",    TB_KEY_DELETE,      TB_MOD_CTRL | TB_MOD_ALT               },
   1042     {"\x1b[3;8~",    TB_KEY_DELETE,      TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1043 
   1044     {"\x1b[5;2~",    TB_KEY_PGUP,        TB_MOD_SHIFT                           },
   1045     {"\x1b[5;3~",    TB_KEY_PGUP,        TB_MOD_ALT                             },
   1046     {"\x1b[5;4~",    TB_KEY_PGUP,        TB_MOD_ALT | TB_MOD_SHIFT              },
   1047     {"\x1b[5;5~",    TB_KEY_PGUP,        TB_MOD_CTRL                            },
   1048     {"\x1b[5;6~",    TB_KEY_PGUP,        TB_MOD_CTRL | TB_MOD_SHIFT             },
   1049     {"\x1b[5;7~",    TB_KEY_PGUP,        TB_MOD_CTRL | TB_MOD_ALT               },
   1050     {"\x1b[5;8~",    TB_KEY_PGUP,        TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1051 
   1052     {"\x1b[6;2~",    TB_KEY_PGDN,        TB_MOD_SHIFT                           },
   1053     {"\x1b[6;3~",    TB_KEY_PGDN,        TB_MOD_ALT                             },
   1054     {"\x1b[6;4~",    TB_KEY_PGDN,        TB_MOD_ALT | TB_MOD_SHIFT              },
   1055     {"\x1b[6;5~",    TB_KEY_PGDN,        TB_MOD_CTRL                            },
   1056     {"\x1b[6;6~",    TB_KEY_PGDN,        TB_MOD_CTRL | TB_MOD_SHIFT             },
   1057     {"\x1b[6;7~",    TB_KEY_PGDN,        TB_MOD_CTRL | TB_MOD_ALT               },
   1058     {"\x1b[6;8~",    TB_KEY_PGDN,        TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1059 
   1060     {"\x1b[1;2P",    TB_KEY_F1,          TB_MOD_SHIFT                           },
   1061     {"\x1b[1;3P",    TB_KEY_F1,          TB_MOD_ALT                             },
   1062     {"\x1b[1;4P",    TB_KEY_F1,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1063     {"\x1b[1;5P",    TB_KEY_F1,          TB_MOD_CTRL                            },
   1064     {"\x1b[1;6P",    TB_KEY_F1,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1065     {"\x1b[1;7P",    TB_KEY_F1,          TB_MOD_CTRL | TB_MOD_ALT               },
   1066     {"\x1b[1;8P",    TB_KEY_F1,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1067 
   1068     {"\x1b[1;2Q",    TB_KEY_F2,          TB_MOD_SHIFT                           },
   1069     {"\x1b[1;3Q",    TB_KEY_F2,          TB_MOD_ALT                             },
   1070     {"\x1b[1;4Q",    TB_KEY_F2,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1071     {"\x1b[1;5Q",    TB_KEY_F2,          TB_MOD_CTRL                            },
   1072     {"\x1b[1;6Q",    TB_KEY_F2,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1073     {"\x1b[1;7Q",    TB_KEY_F2,          TB_MOD_CTRL | TB_MOD_ALT               },
   1074     {"\x1b[1;8Q",    TB_KEY_F2,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1075 
   1076     {"\x1b[1;2R",    TB_KEY_F3,          TB_MOD_SHIFT                           },
   1077     {"\x1b[1;3R",    TB_KEY_F3,          TB_MOD_ALT                             },
   1078     {"\x1b[1;4R",    TB_KEY_F3,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1079     {"\x1b[1;5R",    TB_KEY_F3,          TB_MOD_CTRL                            },
   1080     {"\x1b[1;6R",    TB_KEY_F3,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1081     {"\x1b[1;7R",    TB_KEY_F3,          TB_MOD_CTRL | TB_MOD_ALT               },
   1082     {"\x1b[1;8R",    TB_KEY_F3,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1083 
   1084     {"\x1b[1;2S",    TB_KEY_F4,          TB_MOD_SHIFT                           },
   1085     {"\x1b[1;3S",    TB_KEY_F4,          TB_MOD_ALT                             },
   1086     {"\x1b[1;4S",    TB_KEY_F4,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1087     {"\x1b[1;5S",    TB_KEY_F4,          TB_MOD_CTRL                            },
   1088     {"\x1b[1;6S",    TB_KEY_F4,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1089     {"\x1b[1;7S",    TB_KEY_F4,          TB_MOD_CTRL | TB_MOD_ALT               },
   1090     {"\x1b[1;8S",    TB_KEY_F4,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1091 
   1092     {"\x1b[15;2~",   TB_KEY_F5,          TB_MOD_SHIFT                           },
   1093     {"\x1b[15;3~",   TB_KEY_F5,          TB_MOD_ALT                             },
   1094     {"\x1b[15;4~",   TB_KEY_F5,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1095     {"\x1b[15;5~",   TB_KEY_F5,          TB_MOD_CTRL                            },
   1096     {"\x1b[15;6~",   TB_KEY_F5,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1097     {"\x1b[15;7~",   TB_KEY_F5,          TB_MOD_CTRL | TB_MOD_ALT               },
   1098     {"\x1b[15;8~",   TB_KEY_F5,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1099 
   1100     {"\x1b[17;2~",   TB_KEY_F6,          TB_MOD_SHIFT                           },
   1101     {"\x1b[17;3~",   TB_KEY_F6,          TB_MOD_ALT                             },
   1102     {"\x1b[17;4~",   TB_KEY_F6,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1103     {"\x1b[17;5~",   TB_KEY_F6,          TB_MOD_CTRL                            },
   1104     {"\x1b[17;6~",   TB_KEY_F6,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1105     {"\x1b[17;7~",   TB_KEY_F6,          TB_MOD_CTRL | TB_MOD_ALT               },
   1106     {"\x1b[17;8~",   TB_KEY_F6,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1107 
   1108     {"\x1b[18;2~",   TB_KEY_F7,          TB_MOD_SHIFT                           },
   1109     {"\x1b[18;3~",   TB_KEY_F7,          TB_MOD_ALT                             },
   1110     {"\x1b[18;4~",   TB_KEY_F7,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1111     {"\x1b[18;5~",   TB_KEY_F7,          TB_MOD_CTRL                            },
   1112     {"\x1b[18;6~",   TB_KEY_F7,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1113     {"\x1b[18;7~",   TB_KEY_F7,          TB_MOD_CTRL | TB_MOD_ALT               },
   1114     {"\x1b[18;8~",   TB_KEY_F7,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1115 
   1116     {"\x1b[19;2~",   TB_KEY_F8,          TB_MOD_SHIFT                           },
   1117     {"\x1b[19;3~",   TB_KEY_F8,          TB_MOD_ALT                             },
   1118     {"\x1b[19;4~",   TB_KEY_F8,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1119     {"\x1b[19;5~",   TB_KEY_F8,          TB_MOD_CTRL                            },
   1120     {"\x1b[19;6~",   TB_KEY_F8,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1121     {"\x1b[19;7~",   TB_KEY_F8,          TB_MOD_CTRL | TB_MOD_ALT               },
   1122     {"\x1b[19;8~",   TB_KEY_F8,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1123 
   1124     {"\x1b[20;2~",   TB_KEY_F9,          TB_MOD_SHIFT                           },
   1125     {"\x1b[20;3~",   TB_KEY_F9,          TB_MOD_ALT                             },
   1126     {"\x1b[20;4~",   TB_KEY_F9,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1127     {"\x1b[20;5~",   TB_KEY_F9,          TB_MOD_CTRL                            },
   1128     {"\x1b[20;6~",   TB_KEY_F9,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1129     {"\x1b[20;7~",   TB_KEY_F9,          TB_MOD_CTRL | TB_MOD_ALT               },
   1130     {"\x1b[20;8~",   TB_KEY_F9,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1131 
   1132     {"\x1b[21;2~",   TB_KEY_F10,         TB_MOD_SHIFT                           },
   1133     {"\x1b[21;3~",   TB_KEY_F10,         TB_MOD_ALT                             },
   1134     {"\x1b[21;4~",   TB_KEY_F10,         TB_MOD_ALT | TB_MOD_SHIFT              },
   1135     {"\x1b[21;5~",   TB_KEY_F10,         TB_MOD_CTRL                            },
   1136     {"\x1b[21;6~",   TB_KEY_F10,         TB_MOD_CTRL | TB_MOD_SHIFT             },
   1137     {"\x1b[21;7~",   TB_KEY_F10,         TB_MOD_CTRL | TB_MOD_ALT               },
   1138     {"\x1b[21;8~",   TB_KEY_F10,         TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1139 
   1140     {"\x1b[23;2~",   TB_KEY_F11,         TB_MOD_SHIFT                           },
   1141     {"\x1b[23;3~",   TB_KEY_F11,         TB_MOD_ALT                             },
   1142     {"\x1b[23;4~",   TB_KEY_F11,         TB_MOD_ALT | TB_MOD_SHIFT              },
   1143     {"\x1b[23;5~",   TB_KEY_F11,         TB_MOD_CTRL                            },
   1144     {"\x1b[23;6~",   TB_KEY_F11,         TB_MOD_CTRL | TB_MOD_SHIFT             },
   1145     {"\x1b[23;7~",   TB_KEY_F11,         TB_MOD_CTRL | TB_MOD_ALT               },
   1146     {"\x1b[23;8~",   TB_KEY_F11,         TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1147 
   1148     {"\x1b[24;2~",   TB_KEY_F12,         TB_MOD_SHIFT                           },
   1149     {"\x1b[24;3~",   TB_KEY_F12,         TB_MOD_ALT                             },
   1150     {"\x1b[24;4~",   TB_KEY_F12,         TB_MOD_ALT | TB_MOD_SHIFT              },
   1151     {"\x1b[24;5~",   TB_KEY_F12,         TB_MOD_CTRL                            },
   1152     {"\x1b[24;6~",   TB_KEY_F12,         TB_MOD_CTRL | TB_MOD_SHIFT             },
   1153     {"\x1b[24;7~",   TB_KEY_F12,         TB_MOD_CTRL | TB_MOD_ALT               },
   1154     {"\x1b[24;8~",   TB_KEY_F12,         TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1155 
   1156  // rxvt arrows
   1157     {"\x1b[a",       TB_KEY_ARROW_UP,    TB_MOD_SHIFT                           },
   1158     {"\x1b\x1b[A",   TB_KEY_ARROW_UP,    TB_MOD_ALT                             },
   1159     {"\x1b\x1b[a",   TB_KEY_ARROW_UP,    TB_MOD_ALT | TB_MOD_SHIFT              },
   1160     {"\x1bOa",       TB_KEY_ARROW_UP,    TB_MOD_CTRL                            },
   1161     {"\x1b\x1bOa",   TB_KEY_ARROW_UP,    TB_MOD_CTRL | TB_MOD_ALT               },
   1162 
   1163     {"\x1b[b",       TB_KEY_ARROW_DOWN,  TB_MOD_SHIFT                           },
   1164     {"\x1b\x1b[B",   TB_KEY_ARROW_DOWN,  TB_MOD_ALT                             },
   1165     {"\x1b\x1b[b",   TB_KEY_ARROW_DOWN,  TB_MOD_ALT | TB_MOD_SHIFT              },
   1166     {"\x1bOb",       TB_KEY_ARROW_DOWN,  TB_MOD_CTRL                            },
   1167     {"\x1b\x1bOb",   TB_KEY_ARROW_DOWN,  TB_MOD_CTRL | TB_MOD_ALT               },
   1168 
   1169     {"\x1b[c",       TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT                           },
   1170     {"\x1b\x1b[C",   TB_KEY_ARROW_RIGHT, TB_MOD_ALT                             },
   1171     {"\x1b\x1b[c",   TB_KEY_ARROW_RIGHT, TB_MOD_ALT | TB_MOD_SHIFT              },
   1172     {"\x1bOc",       TB_KEY_ARROW_RIGHT, TB_MOD_CTRL                            },
   1173     {"\x1b\x1bOc",   TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT               },
   1174 
   1175     {"\x1b[d",       TB_KEY_ARROW_LEFT,  TB_MOD_SHIFT                           },
   1176     {"\x1b\x1b[D",   TB_KEY_ARROW_LEFT,  TB_MOD_ALT                             },
   1177     {"\x1b\x1b[d",   TB_KEY_ARROW_LEFT,  TB_MOD_ALT | TB_MOD_SHIFT              },
   1178     {"\x1bOd",       TB_KEY_ARROW_LEFT,  TB_MOD_CTRL                            },
   1179     {"\x1b\x1bOd",   TB_KEY_ARROW_LEFT,  TB_MOD_CTRL | TB_MOD_ALT               },
   1180 
   1181  // rxvt keys
   1182     {"\x1b[7$",      TB_KEY_HOME,        TB_MOD_SHIFT                           },
   1183     {"\x1b\x1b[7~",  TB_KEY_HOME,        TB_MOD_ALT                             },
   1184     {"\x1b\x1b[7$",  TB_KEY_HOME,        TB_MOD_ALT | TB_MOD_SHIFT              },
   1185     {"\x1b[7^",      TB_KEY_HOME,        TB_MOD_CTRL                            },
   1186     {"\x1b[7@",      TB_KEY_HOME,        TB_MOD_CTRL | TB_MOD_SHIFT             },
   1187     {"\x1b\x1b[7^",  TB_KEY_HOME,        TB_MOD_CTRL | TB_MOD_ALT               },
   1188     {"\x1b\x1b[7@",  TB_KEY_HOME,        TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1189 
   1190     {"\x1b\x1b[8~",  TB_KEY_END,         TB_MOD_ALT                             },
   1191     {"\x1b\x1b[8$",  TB_KEY_END,         TB_MOD_ALT | TB_MOD_SHIFT              },
   1192     {"\x1b[8^",      TB_KEY_END,         TB_MOD_CTRL                            },
   1193     {"\x1b\x1b[8^",  TB_KEY_END,         TB_MOD_CTRL | TB_MOD_ALT               },
   1194     {"\x1b\x1b[8@",  TB_KEY_END,         TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1195     {"\x1b[8@",      TB_KEY_END,         TB_MOD_CTRL | TB_MOD_SHIFT             },
   1196     {"\x1b[8$",      TB_KEY_END,         TB_MOD_SHIFT                           },
   1197 
   1198     {"\x1b\x1b[2~",  TB_KEY_INSERT,      TB_MOD_ALT                             },
   1199     {"\x1b\x1b[2$",  TB_KEY_INSERT,      TB_MOD_ALT | TB_MOD_SHIFT              },
   1200     {"\x1b[2^",      TB_KEY_INSERT,      TB_MOD_CTRL                            },
   1201     {"\x1b\x1b[2^",  TB_KEY_INSERT,      TB_MOD_CTRL | TB_MOD_ALT               },
   1202     {"\x1b\x1b[2@",  TB_KEY_INSERT,      TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1203     {"\x1b[2@",      TB_KEY_INSERT,      TB_MOD_CTRL | TB_MOD_SHIFT             },
   1204     {"\x1b[2$",      TB_KEY_INSERT,      TB_MOD_SHIFT                           },
   1205 
   1206     {"\x1b\x1b[3~",  TB_KEY_DELETE,      TB_MOD_ALT                             },
   1207     {"\x1b\x1b[3$",  TB_KEY_DELETE,      TB_MOD_ALT | TB_MOD_SHIFT              },
   1208     {"\x1b[3^",      TB_KEY_DELETE,      TB_MOD_CTRL                            },
   1209     {"\x1b\x1b[3^",  TB_KEY_DELETE,      TB_MOD_CTRL | TB_MOD_ALT               },
   1210     {"\x1b\x1b[3@",  TB_KEY_DELETE,      TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1211     {"\x1b[3@",      TB_KEY_DELETE,      TB_MOD_CTRL | TB_MOD_SHIFT             },
   1212     {"\x1b[3$",      TB_KEY_DELETE,      TB_MOD_SHIFT                           },
   1213 
   1214     {"\x1b\x1b[5~",  TB_KEY_PGUP,        TB_MOD_ALT                             },
   1215     {"\x1b\x1b[5$",  TB_KEY_PGUP,        TB_MOD_ALT | TB_MOD_SHIFT              },
   1216     {"\x1b[5^",      TB_KEY_PGUP,        TB_MOD_CTRL                            },
   1217     {"\x1b\x1b[5^",  TB_KEY_PGUP,        TB_MOD_CTRL | TB_MOD_ALT               },
   1218     {"\x1b\x1b[5@",  TB_KEY_PGUP,        TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1219     {"\x1b[5@",      TB_KEY_PGUP,        TB_MOD_CTRL | TB_MOD_SHIFT             },
   1220     {"\x1b[5$",      TB_KEY_PGUP,        TB_MOD_SHIFT                           },
   1221 
   1222     {"\x1b\x1b[6~",  TB_KEY_PGDN,        TB_MOD_ALT                             },
   1223     {"\x1b\x1b[6$",  TB_KEY_PGDN,        TB_MOD_ALT | TB_MOD_SHIFT              },
   1224     {"\x1b[6^",      TB_KEY_PGDN,        TB_MOD_CTRL                            },
   1225     {"\x1b\x1b[6^",  TB_KEY_PGDN,        TB_MOD_CTRL | TB_MOD_ALT               },
   1226     {"\x1b\x1b[6@",  TB_KEY_PGDN,        TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1227     {"\x1b[6@",      TB_KEY_PGDN,        TB_MOD_CTRL | TB_MOD_SHIFT             },
   1228     {"\x1b[6$",      TB_KEY_PGDN,        TB_MOD_SHIFT                           },
   1229 
   1230     {"\x1b\x1b[11~", TB_KEY_F1,          TB_MOD_ALT                             },
   1231     {"\x1b\x1b[23~", TB_KEY_F1,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1232     {"\x1b[11^",     TB_KEY_F1,          TB_MOD_CTRL                            },
   1233     {"\x1b\x1b[11^", TB_KEY_F1,          TB_MOD_CTRL | TB_MOD_ALT               },
   1234     {"\x1b\x1b[23^", TB_KEY_F1,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1235     {"\x1b[23^",     TB_KEY_F1,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1236     {"\x1b[23~",     TB_KEY_F1,          TB_MOD_SHIFT                           },
   1237 
   1238     {"\x1b\x1b[12~", TB_KEY_F2,          TB_MOD_ALT                             },
   1239     {"\x1b\x1b[24~", TB_KEY_F2,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1240     {"\x1b[12^",     TB_KEY_F2,          TB_MOD_CTRL                            },
   1241     {"\x1b\x1b[12^", TB_KEY_F2,          TB_MOD_CTRL | TB_MOD_ALT               },
   1242     {"\x1b\x1b[24^", TB_KEY_F2,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1243     {"\x1b[24^",     TB_KEY_F2,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1244     {"\x1b[24~",     TB_KEY_F2,          TB_MOD_SHIFT                           },
   1245 
   1246     {"\x1b\x1b[13~", TB_KEY_F3,          TB_MOD_ALT                             },
   1247     {"\x1b\x1b[25~", TB_KEY_F3,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1248     {"\x1b[13^",     TB_KEY_F3,          TB_MOD_CTRL                            },
   1249     {"\x1b\x1b[13^", TB_KEY_F3,          TB_MOD_CTRL | TB_MOD_ALT               },
   1250     {"\x1b\x1b[25^", TB_KEY_F3,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1251     {"\x1b[25^",     TB_KEY_F3,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1252     {"\x1b[25~",     TB_KEY_F3,          TB_MOD_SHIFT                           },
   1253 
   1254     {"\x1b\x1b[14~", TB_KEY_F4,          TB_MOD_ALT                             },
   1255     {"\x1b\x1b[26~", TB_KEY_F4,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1256     {"\x1b[14^",     TB_KEY_F4,          TB_MOD_CTRL                            },
   1257     {"\x1b\x1b[14^", TB_KEY_F4,          TB_MOD_CTRL | TB_MOD_ALT               },
   1258     {"\x1b\x1b[26^", TB_KEY_F4,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1259     {"\x1b[26^",     TB_KEY_F4,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1260     {"\x1b[26~",     TB_KEY_F4,          TB_MOD_SHIFT                           },
   1261 
   1262     {"\x1b\x1b[15~", TB_KEY_F5,          TB_MOD_ALT                             },
   1263     {"\x1b\x1b[28~", TB_KEY_F5,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1264     {"\x1b[15^",     TB_KEY_F5,          TB_MOD_CTRL                            },
   1265     {"\x1b\x1b[15^", TB_KEY_F5,          TB_MOD_CTRL | TB_MOD_ALT               },
   1266     {"\x1b\x1b[28^", TB_KEY_F5,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1267     {"\x1b[28^",     TB_KEY_F5,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1268     {"\x1b[28~",     TB_KEY_F5,          TB_MOD_SHIFT                           },
   1269 
   1270     {"\x1b\x1b[17~", TB_KEY_F6,          TB_MOD_ALT                             },
   1271     {"\x1b\x1b[29~", TB_KEY_F6,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1272     {"\x1b[17^",     TB_KEY_F6,          TB_MOD_CTRL                            },
   1273     {"\x1b\x1b[17^", TB_KEY_F6,          TB_MOD_CTRL | TB_MOD_ALT               },
   1274     {"\x1b\x1b[29^", TB_KEY_F6,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1275     {"\x1b[29^",     TB_KEY_F6,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1276     {"\x1b[29~",     TB_KEY_F6,          TB_MOD_SHIFT                           },
   1277 
   1278     {"\x1b\x1b[18~", TB_KEY_F7,          TB_MOD_ALT                             },
   1279     {"\x1b\x1b[31~", TB_KEY_F7,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1280     {"\x1b[18^",     TB_KEY_F7,          TB_MOD_CTRL                            },
   1281     {"\x1b\x1b[18^", TB_KEY_F7,          TB_MOD_CTRL | TB_MOD_ALT               },
   1282     {"\x1b\x1b[31^", TB_KEY_F7,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1283     {"\x1b[31^",     TB_KEY_F7,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1284     {"\x1b[31~",     TB_KEY_F7,          TB_MOD_SHIFT                           },
   1285 
   1286     {"\x1b\x1b[19~", TB_KEY_F8,          TB_MOD_ALT                             },
   1287     {"\x1b\x1b[32~", TB_KEY_F8,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1288     {"\x1b[19^",     TB_KEY_F8,          TB_MOD_CTRL                            },
   1289     {"\x1b\x1b[19^", TB_KEY_F8,          TB_MOD_CTRL | TB_MOD_ALT               },
   1290     {"\x1b\x1b[32^", TB_KEY_F8,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1291     {"\x1b[32^",     TB_KEY_F8,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1292     {"\x1b[32~",     TB_KEY_F8,          TB_MOD_SHIFT                           },
   1293 
   1294     {"\x1b\x1b[20~", TB_KEY_F9,          TB_MOD_ALT                             },
   1295     {"\x1b\x1b[33~", TB_KEY_F9,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1296     {"\x1b[20^",     TB_KEY_F9,          TB_MOD_CTRL                            },
   1297     {"\x1b\x1b[20^", TB_KEY_F9,          TB_MOD_CTRL | TB_MOD_ALT               },
   1298     {"\x1b\x1b[33^", TB_KEY_F9,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1299     {"\x1b[33^",     TB_KEY_F9,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1300     {"\x1b[33~",     TB_KEY_F9,          TB_MOD_SHIFT                           },
   1301 
   1302     {"\x1b\x1b[21~", TB_KEY_F10,         TB_MOD_ALT                             },
   1303     {"\x1b\x1b[34~", TB_KEY_F10,         TB_MOD_ALT | TB_MOD_SHIFT              },
   1304     {"\x1b[21^",     TB_KEY_F10,         TB_MOD_CTRL                            },
   1305     {"\x1b\x1b[21^", TB_KEY_F10,         TB_MOD_CTRL | TB_MOD_ALT               },
   1306     {"\x1b\x1b[34^", TB_KEY_F10,         TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1307     {"\x1b[34^",     TB_KEY_F10,         TB_MOD_CTRL | TB_MOD_SHIFT             },
   1308     {"\x1b[34~",     TB_KEY_F10,         TB_MOD_SHIFT                           },
   1309 
   1310     {"\x1b\x1b[23~", TB_KEY_F11,         TB_MOD_ALT                             },
   1311     {"\x1b\x1b[23$", TB_KEY_F11,         TB_MOD_ALT | TB_MOD_SHIFT              },
   1312     {"\x1b[23^",     TB_KEY_F11,         TB_MOD_CTRL                            },
   1313     {"\x1b\x1b[23^", TB_KEY_F11,         TB_MOD_CTRL | TB_MOD_ALT               },
   1314     {"\x1b\x1b[23@", TB_KEY_F11,         TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1315     {"\x1b[23@",     TB_KEY_F11,         TB_MOD_CTRL | TB_MOD_SHIFT             },
   1316     {"\x1b[23$",     TB_KEY_F11,         TB_MOD_SHIFT                           },
   1317 
   1318     {"\x1b\x1b[24~", TB_KEY_F12,         TB_MOD_ALT                             },
   1319     {"\x1b\x1b[24$", TB_KEY_F12,         TB_MOD_ALT | TB_MOD_SHIFT              },
   1320     {"\x1b[24^",     TB_KEY_F12,         TB_MOD_CTRL                            },
   1321     {"\x1b\x1b[24^", TB_KEY_F12,         TB_MOD_CTRL | TB_MOD_ALT               },
   1322     {"\x1b\x1b[24@", TB_KEY_F12,         TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1323     {"\x1b[24@",     TB_KEY_F12,         TB_MOD_CTRL | TB_MOD_SHIFT             },
   1324     {"\x1b[24$",     TB_KEY_F12,         TB_MOD_SHIFT                           },
   1325 
   1326  // linux console/putty arrows
   1327     {"\x1b[A",       TB_KEY_ARROW_UP,    TB_MOD_SHIFT                           },
   1328     {"\x1b[B",       TB_KEY_ARROW_DOWN,  TB_MOD_SHIFT                           },
   1329     {"\x1b[C",       TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT                           },
   1330     {"\x1b[D",       TB_KEY_ARROW_LEFT,  TB_MOD_SHIFT                           },
   1331 
   1332  // more putty arrows
   1333     {"\x1bOA",       TB_KEY_ARROW_UP,    TB_MOD_CTRL                            },
   1334     {"\x1b\x1bOA",   TB_KEY_ARROW_UP,    TB_MOD_CTRL | TB_MOD_ALT               },
   1335     {"\x1bOB",       TB_KEY_ARROW_DOWN,  TB_MOD_CTRL                            },
   1336     {"\x1b\x1bOB",   TB_KEY_ARROW_DOWN,  TB_MOD_CTRL | TB_MOD_ALT               },
   1337     {"\x1bOC",       TB_KEY_ARROW_RIGHT, TB_MOD_CTRL                            },
   1338     {"\x1b\x1bOC",   TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT               },
   1339     {"\x1bOD",       TB_KEY_ARROW_LEFT,  TB_MOD_CTRL                            },
   1340     {"\x1b\x1bOD",   TB_KEY_ARROW_LEFT,  TB_MOD_CTRL | TB_MOD_ALT               },
   1341 
   1342     {NULL,           0,                  0                                      },
   1343 };
   1344 
   1345 static const unsigned char utf8_length[256] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
   1346     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
   1347     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
   1348     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
   1349     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
   1350     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
   1351     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
   1352     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
   1353     1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
   1354     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
   1355     3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1};
   1356 
   1357 static const unsigned char utf8_mask[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
   1358 
   1359 static int tb_reset(void);
   1360 static int tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg,
   1361     size_t *out_w, const char *fmt, va_list vl);
   1362 static int init_term_attrs(void);
   1363 static int init_term_caps(void);
   1364 static int init_cap_trie(void);
   1365 static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod);
   1366 static int cap_trie_find(const char *buf, size_t nbuf, struct cap_trie_t **last,
   1367     size_t *depth);
   1368 static int cap_trie_deinit(struct cap_trie_t *node);
   1369 static int init_resize_handler(void);
   1370 static int send_init_escape_codes(void);
   1371 static int send_clear(void);
   1372 static int update_term_size(void);
   1373 static int update_term_size_via_esc(void);
   1374 static int init_cellbuf(void);
   1375 static int tb_deinit(void);
   1376 static int load_terminfo(void);
   1377 static int load_terminfo_from_path(const char *path, const char *term);
   1378 static int read_terminfo_path(const char *path);
   1379 static int parse_terminfo_caps(void);
   1380 static int load_builtin_caps(void);
   1381 static const char *get_terminfo_string(int16_t str_offsets_pos,
   1382     int16_t str_table_pos, int16_t str_table_len, int16_t str_index);
   1383 static int wait_event(struct tb_event *event, int timeout);
   1384 static int extract_event(struct tb_event *event);
   1385 static int extract_esc(struct tb_event *event);
   1386 static int extract_esc_user(struct tb_event *event, int is_post);
   1387 static int extract_esc_cap(struct tb_event *event);
   1388 static int extract_esc_mouse(struct tb_event *event);
   1389 static int resize_cellbufs(void);
   1390 static void handle_resize(int sig);
   1391 static int send_attr(uintattr_t fg, uintattr_t bg);
   1392 static int send_sgr(uintattr_t fg, uintattr_t bg, uintattr_t fg_is_default,
   1393     uintattr_t bg_is_default);
   1394 static int send_cursor_if(int x, int y);
   1395 static int send_char(int x, int y, uint32_t ch);
   1396 static int send_cluster(int x, int y, uint32_t *ch, size_t nch);
   1397 static int convert_num(uint32_t num, char *buf);
   1398 static int cell_cmp(struct tb_cell *a, struct tb_cell *b);
   1399 static int cell_copy(struct tb_cell *dst, struct tb_cell *src);
   1400 static int cell_set(struct tb_cell *cell, uint32_t *ch, size_t nch,
   1401     uintattr_t fg, uintattr_t bg);
   1402 static int cell_reserve_ech(struct tb_cell *cell, size_t n);
   1403 static int cell_free(struct tb_cell *cell);
   1404 static int cellbuf_init(struct cellbuf_t *c, int w, int h);
   1405 static int cellbuf_free(struct cellbuf_t *c);
   1406 static int cellbuf_clear(struct cellbuf_t *c);
   1407 static int cellbuf_get(struct cellbuf_t *c, int x, int y, struct tb_cell **out);
   1408 static int cellbuf_resize(struct cellbuf_t *c, int w, int h);
   1409 static int bytebuf_puts(struct bytebuf_t *b, const char *str);
   1410 static int bytebuf_nputs(struct bytebuf_t *b, const char *str, size_t nstr);
   1411 static int bytebuf_shift(struct bytebuf_t *b, size_t n);
   1412 static int bytebuf_flush(struct bytebuf_t *b, int fd);
   1413 static int bytebuf_reserve(struct bytebuf_t *b, size_t sz);
   1414 static int bytebuf_free(struct bytebuf_t *b);
   1415 
   1416 int tb_init(void) {
   1417     return tb_init_file("/dev/tty");
   1418 }
   1419 
   1420 int tb_init_file(const char *path) {
   1421     if (global.initialized) {
   1422         return TB_ERR_INIT_ALREADY;
   1423     }
   1424     int ttyfd = open(path, O_RDWR);
   1425     if (ttyfd < 0) {
   1426         global.last_errno = errno;
   1427         return TB_ERR_INIT_OPEN;
   1428     }
   1429     global.ttyfd_open = 1;
   1430     return tb_init_fd(ttyfd);
   1431 }
   1432 
   1433 int tb_init_fd(int ttyfd) {
   1434     return tb_init_rwfd(ttyfd, ttyfd);
   1435 }
   1436 
   1437 int tb_init_rwfd(int rfd, int wfd) {
   1438     int rv;
   1439 
   1440     tb_reset();
   1441     global.ttyfd = rfd == wfd && isatty(rfd) ? rfd : -1;
   1442     global.rfd = rfd;
   1443     global.wfd = wfd;
   1444 
   1445     do {
   1446         if_err_break(rv, init_term_attrs());
   1447         if_err_break(rv, init_term_caps());
   1448         if_err_break(rv, init_cap_trie());
   1449         if_err_break(rv, init_resize_handler());
   1450         if_err_break(rv, send_init_escape_codes());
   1451         if_err_break(rv, send_clear());
   1452         if_err_break(rv, update_term_size());
   1453         if_err_break(rv, init_cellbuf());
   1454         global.initialized = 1;
   1455     } while (0);
   1456 
   1457     if (rv != TB_OK) {
   1458         tb_deinit();
   1459     }
   1460 
   1461     return rv;
   1462 }
   1463 
   1464 int tb_shutdown(void) {
   1465     if_not_init_return();
   1466     tb_deinit();
   1467     return TB_OK;
   1468 }
   1469 
   1470 int tb_width(void) {
   1471     if_not_init_return();
   1472     return global.width;
   1473 }
   1474 
   1475 int tb_height(void) {
   1476     if_not_init_return();
   1477     return global.height;
   1478 }
   1479 
   1480 int tb_clear(void) {
   1481     if_not_init_return();
   1482     return cellbuf_clear(&global.back);
   1483 }
   1484 
   1485 int tb_set_clear_attrs(uintattr_t fg, uintattr_t bg) {
   1486     if_not_init_return();
   1487     global.fg = fg;
   1488     global.bg = bg;
   1489     return TB_OK;
   1490 }
   1491 
   1492 int tb_present(void) {
   1493     if_not_init_return();
   1494 
   1495     int rv;
   1496 
   1497     // TODO Assert global.back.(width,height) == global.front.(width,height)
   1498 
   1499     global.last_x = -1;
   1500     global.last_y = -1;
   1501 
   1502     int x, y, i;
   1503     for (y = 0; y < global.front.height; y++) {
   1504         for (x = 0; x < global.front.width;) {
   1505             struct tb_cell *back, *front;
   1506             if_err_return(rv, cellbuf_get(&global.back, x, y, &back));
   1507             if_err_return(rv, cellbuf_get(&global.front, x, y, &front));
   1508 
   1509             int w;
   1510             {
   1511 #ifdef TB_OPT_EGC
   1512                 if (back->nech > 0)
   1513                     w = wcswidth((wchar_t *)back->ech, back->nech);
   1514                 else
   1515 #endif
   1516                     /* wcwidth() simply returns -1 on overflow of wchar_t */
   1517                     w = wcwidth((wchar_t)back->ch);
   1518             }
   1519             if (w < 1) {
   1520                 w = 1;
   1521             }
   1522 
   1523             if (cell_cmp(back, front) != 0) {
   1524                 cell_copy(front, back);
   1525 
   1526                 send_attr(back->fg, back->bg);
   1527                 if (w > 1 && x >= global.front.width - (w - 1)) {
   1528                     for (i = x; i < global.front.width; i++) {
   1529                         send_char(i, y, ' ');
   1530                     }
   1531                 } else {
   1532                     {
   1533 #ifdef TB_OPT_EGC
   1534                         if (back->nech > 0)
   1535                             send_cluster(x, y, back->ech, back->nech);
   1536                         else
   1537 #endif
   1538                             send_char(x, y, back->ch);
   1539                     }
   1540                     for (i = 1; i < w; i++) {
   1541                         struct tb_cell *front_wide;
   1542                         if_err_return(rv,
   1543                             cellbuf_get(&global.front, x + i, y, &front_wide));
   1544                         if_err_return(rv,
   1545                             cell_set(front_wide, 0, 1, back->fg, back->bg));
   1546                     }
   1547                 }
   1548             }
   1549             x += w;
   1550         }
   1551     }
   1552 
   1553     if_err_return(rv, send_cursor_if(global.cursor_x, global.cursor_y));
   1554     if_err_return(rv, bytebuf_flush(&global.out, global.wfd));
   1555 
   1556     return TB_OK;
   1557 }
   1558 
   1559 int tb_set_cursor(int cx, int cy) {
   1560     if_not_init_return();
   1561     int rv;
   1562     if (cx < 0)
   1563         cx = 0;
   1564     if (cy < 0)
   1565         cy = 0;
   1566     if (global.cursor_x == -1) {
   1567         if_err_return(rv,
   1568             bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]));
   1569     }
   1570     if_err_return(rv, send_cursor_if(cx, cy));
   1571     global.cursor_x = cx;
   1572     global.cursor_y = cy;
   1573     return TB_OK;
   1574 }
   1575 
   1576 int tb_hide_cursor(void) {
   1577     if_not_init_return();
   1578     int rv;
   1579     if (global.cursor_x >= 0) {
   1580         if_err_return(rv,
   1581             bytebuf_puts(&global.out, global.caps[TB_CAP_HIDE_CURSOR]));
   1582     }
   1583     global.cursor_x = -1;
   1584     global.cursor_y = -1;
   1585     return TB_OK;
   1586 }
   1587 
   1588 int tb_set_cell(int x, int y, uint32_t ch, uintattr_t fg, uintattr_t bg) {
   1589     if_not_init_return();
   1590     return tb_set_cell_ex(x, y, &ch, 1, fg, bg);
   1591 }
   1592 
   1593 int tb_set_cell_ex(int x, int y, uint32_t *ch, size_t nch, uintattr_t fg,
   1594     uintattr_t bg) {
   1595     if_not_init_return();
   1596     int rv;
   1597     struct tb_cell *cell;
   1598     if_err_return(rv, cellbuf_get(&global.back, x, y, &cell));
   1599     if_err_return(rv, cell_set(cell, ch, nch, fg, bg));
   1600     return TB_OK;
   1601 }
   1602 
   1603 int tb_extend_cell(int x, int y, uint32_t ch) {
   1604     if_not_init_return();
   1605 #ifdef TB_OPT_EGC
   1606     int rv;
   1607     struct tb_cell *cell;
   1608     size_t nech;
   1609     if_err_return(rv, cellbuf_get(&global.back, x, y, &cell));
   1610     if (cell->nech > 0) { // append to ech
   1611         nech = cell->nech + 1;
   1612         if_err_return(rv, cell_reserve_ech(cell, nech));
   1613         cell->ech[nech - 1] = ch;
   1614     } else { // make new ech
   1615         nech = 2;
   1616         if_err_return(rv, cell_reserve_ech(cell, nech));
   1617         cell->ech[0] = cell->ch;
   1618         cell->ech[1] = ch;
   1619     }
   1620     cell->ech[nech] = '\0';
   1621     cell->nech = nech;
   1622     return TB_OK;
   1623 #else
   1624     (void)x;
   1625     (void)y;
   1626     (void)ch;
   1627     return TB_ERR;
   1628 #endif
   1629 }
   1630 
   1631 int tb_set_input_mode(int mode) {
   1632     if_not_init_return();
   1633     if (mode == TB_INPUT_CURRENT) {
   1634         return global.input_mode;
   1635     }
   1636 
   1637     if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == 0) {
   1638         mode |= TB_INPUT_ESC;
   1639     }
   1640 
   1641     if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == (TB_INPUT_ESC | TB_INPUT_ALT))
   1642     {
   1643         mode &= ~TB_INPUT_ALT;
   1644     }
   1645 
   1646     if (mode & TB_INPUT_MOUSE) {
   1647         bytebuf_puts(&global.out, TB_HARDCAP_ENTER_MOUSE);
   1648         bytebuf_flush(&global.out, global.wfd);
   1649     } else {
   1650         bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE);
   1651         bytebuf_flush(&global.out, global.wfd);
   1652     }
   1653 
   1654     global.input_mode = mode;
   1655     return TB_OK;
   1656 }
   1657 
   1658 int tb_set_output_mode(int mode) {
   1659     if_not_init_return();
   1660     switch (mode) {
   1661         case TB_OUTPUT_CURRENT:
   1662             return global.output_mode;
   1663         case TB_OUTPUT_NORMAL:
   1664         case TB_OUTPUT_256:
   1665         case TB_OUTPUT_216:
   1666         case TB_OUTPUT_GRAYSCALE:
   1667 #ifdef TB_OPT_TRUECOLOR
   1668         case TB_OUTPUT_TRUECOLOR:
   1669 #endif
   1670             global.output_mode = mode;
   1671             return TB_OK;
   1672     }
   1673     return TB_ERR;
   1674 }
   1675 
   1676 int tb_peek_event(struct tb_event *event, int timeout_ms) {
   1677     if_not_init_return();
   1678     return wait_event(event, timeout_ms);
   1679 }
   1680 
   1681 int tb_poll_event(struct tb_event *event) {
   1682     if_not_init_return();
   1683     return wait_event(event, -1);
   1684 }
   1685 
   1686 int tb_get_fds(int *ttyfd, int *resizefd) {
   1687     if_not_init_return();
   1688 
   1689     *ttyfd = global.rfd;
   1690     *resizefd = global.resize_pipefd[0];
   1691 
   1692     return TB_OK;
   1693 }
   1694 
   1695 int tb_print(int x, int y, uintattr_t fg, uintattr_t bg, const char *str) {
   1696     return tb_print_ex(x, y, fg, bg, NULL, str);
   1697 }
   1698 
   1699 int tb_print_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
   1700     const char *str) {
   1701     int rv;
   1702     uint32_t uni;
   1703     int w, ix = x;
   1704     if (out_w) {
   1705         *out_w = 0;
   1706     }
   1707     while (*str) {
   1708         str += tb_utf8_char_to_unicode(&uni, str);
   1709         w = wcwidth((wchar_t)uni);
   1710         if (w < 0) {
   1711             w = 1;
   1712         }
   1713         if (w == 0 && x > ix) {
   1714             if_err_return(rv, tb_extend_cell(x - 1, y, uni));
   1715         } else {
   1716             if_err_return(rv, tb_set_cell(x, y, uni, fg, bg));
   1717         }
   1718         x += w;
   1719         if (out_w) {
   1720             *out_w += w;
   1721         }
   1722     }
   1723     return TB_OK;
   1724 }
   1725 
   1726 int tb_printf(int x, int y, uintattr_t fg, uintattr_t bg, const char *fmt,
   1727     ...) {
   1728     int rv;
   1729     va_list vl;
   1730     va_start(vl, fmt);
   1731     rv = tb_printf_inner(x, y, fg, bg, NULL, fmt, vl);
   1732     va_end(vl);
   1733     return rv;
   1734 }
   1735 
   1736 int tb_printf_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
   1737     const char *fmt, ...) {
   1738     int rv;
   1739     va_list vl;
   1740     va_start(vl, fmt);
   1741     rv = tb_printf_inner(x, y, fg, bg, out_w, fmt, vl);
   1742     va_end(vl);
   1743     return rv;
   1744 }
   1745 
   1746 int tb_send(const char *buf, size_t nbuf) {
   1747     return bytebuf_nputs(&global.out, buf, nbuf);
   1748 }
   1749 
   1750 int tb_sendf(const char *fmt, ...) {
   1751     int rv;
   1752     char buf[TB_OPT_PRINTF_BUF];
   1753     va_list vl;
   1754     va_start(vl, fmt);
   1755     rv = vsnprintf(buf, sizeof(buf), fmt, vl);
   1756     va_end(vl);
   1757     if (rv < 0 || rv >= (int)sizeof(buf)) {
   1758         return TB_ERR;
   1759     }
   1760     return tb_send(buf, (size_t)rv);
   1761 }
   1762 
   1763 int tb_set_func(int fn_type, int (*fn)(struct tb_event *, size_t *)) {
   1764     switch (fn_type) {
   1765         case TB_FUNC_EXTRACT_PRE:
   1766             global.fn_extract_esc_pre = fn;
   1767             return TB_OK;
   1768         case TB_FUNC_EXTRACT_POST:
   1769             global.fn_extract_esc_post = fn;
   1770             return TB_OK;
   1771     }
   1772     return TB_ERR;
   1773 }
   1774 
   1775 struct tb_cell *tb_cell_buffer(void) {
   1776     if (!global.initialized)
   1777         return NULL;
   1778     return global.back.cells;
   1779 }
   1780 
   1781 int tb_utf8_char_length(char c) {
   1782     return utf8_length[(unsigned char)c];
   1783 }
   1784 
   1785 int tb_utf8_char_to_unicode(uint32_t *out, const char *c) {
   1786     if (*c == 0) {
   1787         return TB_ERR;
   1788     }
   1789 
   1790     int i;
   1791     unsigned char len = tb_utf8_char_length(*c);
   1792     unsigned char mask = utf8_mask[len - 1];
   1793     uint32_t result = c[0] & mask;
   1794     for (i = 1; i < len; ++i) {
   1795         result <<= 6;
   1796         result |= c[i] & 0x3f;
   1797     }
   1798 
   1799     *out = result;
   1800     return (int)len;
   1801 }
   1802 
   1803 int tb_utf8_unicode_to_char(char *out, uint32_t c) {
   1804     int len = 0;
   1805     int first;
   1806     int i;
   1807 
   1808     if (c < 0x80) {
   1809         first = 0;
   1810         len = 1;
   1811     } else if (c < 0x800) {
   1812         first = 0xc0;
   1813         len = 2;
   1814     } else if (c < 0x10000) {
   1815         first = 0xe0;
   1816         len = 3;
   1817     } else if (c < 0x200000) {
   1818         first = 0xf0;
   1819         len = 4;
   1820     } else if (c < 0x4000000) {
   1821         first = 0xf8;
   1822         len = 5;
   1823     } else {
   1824         first = 0xfc;
   1825         len = 6;
   1826     }
   1827 
   1828     for (i = len - 1; i > 0; --i) {
   1829         out[i] = (c & 0x3f) | 0x80;
   1830         c >>= 6;
   1831     }
   1832     out[0] = c | first;
   1833 
   1834     return len;
   1835 }
   1836 
   1837 int tb_last_errno(void) {
   1838     return global.last_errno;
   1839 }
   1840 
   1841 const char *tb_strerror(int err) {
   1842     switch (err) {
   1843         case TB_OK:
   1844             return "Success";
   1845         case TB_ERR_NEED_MORE:
   1846             return "Not enough input";
   1847         case TB_ERR_INIT_ALREADY:
   1848             return "Termbox initialized already";
   1849         case TB_ERR_MEM:
   1850             return "Out of memory";
   1851         case TB_ERR_NO_EVENT:
   1852             return "No event";
   1853         case TB_ERR_NO_TERM:
   1854             return "No TERM in environment";
   1855         case TB_ERR_NOT_INIT:
   1856             return "Termbox not initialized";
   1857         case TB_ERR_OUT_OF_BOUNDS:
   1858             return "Out of bounds";
   1859         case TB_ERR_UNSUPPORTED_TERM:
   1860             return "Unsupported terminal";
   1861         case TB_ERR_CAP_COLLISION:
   1862             return "Termcaps collision";
   1863         case TB_ERR_RESIZE_SSCANF:
   1864             return "Terminal width/height not received by sscanf() after "
   1865                    "resize";
   1866         case TB_ERR:
   1867         case TB_ERR_INIT_OPEN:
   1868         case TB_ERR_READ:
   1869         case TB_ERR_RESIZE_IOCTL:
   1870         case TB_ERR_RESIZE_PIPE:
   1871         case TB_ERR_RESIZE_SIGACTION:
   1872         case TB_ERR_POLL:
   1873         case TB_ERR_TCGETATTR:
   1874         case TB_ERR_TCSETATTR:
   1875         case TB_ERR_RESIZE_WRITE:
   1876         case TB_ERR_RESIZE_POLL:
   1877         case TB_ERR_RESIZE_READ:
   1878         default:
   1879             strerror_r(global.last_errno, global.errbuf, sizeof(global.errbuf));
   1880             return (const char *)global.errbuf;
   1881     }
   1882 }
   1883 
   1884 int tb_has_truecolor(void) {
   1885 #ifdef TB_OPT_TRUECOLOR
   1886     return 1;
   1887 #else
   1888     return 0;
   1889 #endif
   1890 }
   1891 
   1892 int tb_has_egc(void) {
   1893 #ifdef TB_OPT_EGC
   1894     return 1;
   1895 #else
   1896     return 0;
   1897 #endif
   1898 }
   1899 
   1900 const char *tb_version(void) {
   1901     return TB_VERSION_STR;
   1902 }
   1903 
   1904 static int tb_reset(void) {
   1905     int ttyfd_open = global.ttyfd_open;
   1906     memset(&global, 0, sizeof(global));
   1907     global.ttyfd = -1;
   1908     global.rfd = -1;
   1909     global.wfd = -1;
   1910     global.ttyfd_open = ttyfd_open;
   1911     global.resize_pipefd[0] = -1;
   1912     global.resize_pipefd[1] = -1;
   1913     global.width = -1;
   1914     global.height = -1;
   1915     global.cursor_x = -1;
   1916     global.cursor_y = -1;
   1917     global.last_x = -1;
   1918     global.last_y = -1;
   1919     global.fg = TB_DEFAULT;
   1920     global.bg = TB_DEFAULT;
   1921     global.last_fg = ~global.fg;
   1922     global.last_bg = ~global.bg;
   1923     global.input_mode = TB_INPUT_ESC;
   1924     global.output_mode = TB_OUTPUT_NORMAL;
   1925     return TB_OK;
   1926 }
   1927 
   1928 static int init_term_attrs(void) {
   1929     if (global.ttyfd < 0) {
   1930         return TB_OK;
   1931     }
   1932 
   1933     if (tcgetattr(global.ttyfd, &global.orig_tios) != 0) {
   1934         global.last_errno = errno;
   1935         return TB_ERR_TCGETATTR;
   1936     }
   1937 
   1938     struct termios tios;
   1939     memcpy(&tios, &global.orig_tios, sizeof(tios));
   1940     global.has_orig_tios = 1;
   1941 
   1942     cfmakeraw(&tios);
   1943     tios.c_cc[VMIN] = 1;
   1944     tios.c_cc[VTIME] = 0;
   1945 
   1946     if (tcsetattr(global.ttyfd, TCSAFLUSH, &tios) != 0) {
   1947         global.last_errno = errno;
   1948         return TB_ERR_TCSETATTR;
   1949     }
   1950 
   1951     return TB_OK;
   1952 }
   1953 
   1954 int tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
   1955     const char *fmt, va_list vl) {
   1956     int rv;
   1957     char buf[TB_OPT_PRINTF_BUF];
   1958     rv = vsnprintf(buf, sizeof(buf), fmt, vl);
   1959     if (rv < 0 || rv >= (int)sizeof(buf)) {
   1960         return TB_ERR;
   1961     }
   1962     return tb_print_ex(x, y, fg, bg, out_w, buf);
   1963 }
   1964 
   1965 static int init_term_caps(void) {
   1966     if (load_terminfo() == TB_OK) {
   1967         return parse_terminfo_caps();
   1968     }
   1969     return load_builtin_caps();
   1970 }
   1971 
   1972 static int init_cap_trie(void) {
   1973     int rv, i;
   1974 
   1975     // Add caps from terminfo or built-in
   1976     for (i = 0; i < TB_CAP__COUNT_KEYS; i++) {
   1977         if_err_return(rv, cap_trie_add(global.caps[i], tb_key_i(i), 0));
   1978     }
   1979 
   1980     // Add built-in mod caps
   1981     for (i = 0; builtin_mod_caps[i].cap != NULL; i++) {
   1982         rv = cap_trie_add(builtin_mod_caps[i].cap, builtin_mod_caps[i].key,
   1983             builtin_mod_caps[i].mod);
   1984         // Collisions are OK. This can happen if global.caps collides with
   1985         // builtin_mod_caps. It is desirable to give precedence to global.caps
   1986         // here.
   1987         if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) {
   1988             return rv;
   1989         }
   1990     }
   1991 
   1992     return TB_OK;
   1993 }
   1994 
   1995 static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod) {
   1996     struct cap_trie_t *next, *node = &global.cap_trie;
   1997     size_t i, j;
   1998     for (i = 0; cap[i] != '\0'; i++) {
   1999         char c = cap[i];
   2000         next = NULL;
   2001 
   2002         // Check if c is already a child of node
   2003         for (j = 0; j < node->nchildren; j++) {
   2004             if (node->children[j].c == c) {
   2005                 next = &node->children[j];
   2006                 break;
   2007             }
   2008         }
   2009         if (!next) {
   2010             // We need to add a new child to node
   2011             node->nchildren += 1;
   2012             node->children =
   2013                 tb_realloc(node->children, sizeof(*node) * node->nchildren);
   2014             if (!node->children) {
   2015                 return TB_ERR_MEM;
   2016             }
   2017             next = &node->children[node->nchildren - 1];
   2018             memset(next, 0, sizeof(*next));
   2019             next->c = c;
   2020         }
   2021 
   2022         // Continue
   2023         node = next;
   2024     }
   2025 
   2026     if (node->is_leaf) {
   2027         // Already a leaf here
   2028         return TB_ERR_CAP_COLLISION;
   2029     }
   2030 
   2031     node->is_leaf = 1;
   2032     node->key = key;
   2033     node->mod = mod;
   2034     return TB_OK;
   2035 }
   2036 
   2037 static int cap_trie_find(const char *buf, size_t nbuf, struct cap_trie_t **last,
   2038     size_t *depth) {
   2039     struct cap_trie_t *next, *node = &global.cap_trie;
   2040     size_t i, j;
   2041     *last = node;
   2042     *depth = 0;
   2043     for (i = 0; i < nbuf; i++) {
   2044         char c = buf[i];
   2045         next = NULL;
   2046 
   2047         // Find c in node.children
   2048         for (j = 0; j < node->nchildren; j++) {
   2049             if (node->children[j].c == c) {
   2050                 next = &node->children[j];
   2051                 break;
   2052             }
   2053         }
   2054         if (!next) {
   2055             // Not found
   2056             return TB_OK;
   2057         }
   2058         node = next;
   2059         *last = node;
   2060         *depth += 1;
   2061         if (node->is_leaf && node->nchildren < 1) {
   2062             break;
   2063         }
   2064     }
   2065     return TB_OK;
   2066 }
   2067 
   2068 static int cap_trie_deinit(struct cap_trie_t *node) {
   2069     size_t j;
   2070     for (j = 0; j < node->nchildren; j++) {
   2071         cap_trie_deinit(&node->children[j]);
   2072     }
   2073     if (node->children) {
   2074         tb_free(node->children);
   2075     }
   2076     memset(node, 0, sizeof(*node));
   2077     return TB_OK;
   2078 }
   2079 
   2080 static int init_resize_handler(void) {
   2081     if (pipe(global.resize_pipefd) != 0) {
   2082         global.last_errno = errno;
   2083         return TB_ERR_RESIZE_PIPE;
   2084     }
   2085 
   2086     struct sigaction sa;
   2087     memset(&sa, 0, sizeof(sa));
   2088     sa.sa_handler = handle_resize;
   2089     if (sigaction(SIGWINCH, &sa, NULL) != 0) {
   2090         global.last_errno = errno;
   2091         return TB_ERR_RESIZE_SIGACTION;
   2092     }
   2093 
   2094     return TB_OK;
   2095 }
   2096 
   2097 static int send_init_escape_codes(void) {
   2098     int rv;
   2099     if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_CA]));
   2100     if_err_return(rv,
   2101         bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_KEYPAD]));
   2102     if_err_return(rv,
   2103         bytebuf_puts(&global.out, global.caps[TB_CAP_HIDE_CURSOR]));
   2104     return TB_OK;
   2105 }
   2106 
   2107 static int send_clear(void) {
   2108     int rv;
   2109 
   2110     if_err_return(rv, send_attr(global.fg, global.bg));
   2111     if_err_return(rv,
   2112         bytebuf_puts(&global.out, global.caps[TB_CAP_CLEAR_SCREEN]));
   2113 
   2114     if_err_return(rv, send_cursor_if(global.cursor_x, global.cursor_y));
   2115     if_err_return(rv, bytebuf_flush(&global.out, global.wfd));
   2116 
   2117     global.last_x = -1;
   2118     global.last_y = -1;
   2119 
   2120     return TB_OK;
   2121 }
   2122 
   2123 static int update_term_size(void) {
   2124     int rv, ioctl_errno;
   2125 
   2126     if (global.ttyfd < 0) {
   2127         return TB_OK;
   2128     }
   2129 
   2130     struct winsize sz;
   2131     memset(&sz, 0, sizeof(sz));
   2132 
   2133     // Try ioctl TIOCGWINSZ
   2134     if (ioctl(global.ttyfd, TIOCGWINSZ, &sz) == 0) {
   2135         global.width = sz.ws_col;
   2136         global.height = sz.ws_row;
   2137         return TB_OK;
   2138     }
   2139     ioctl_errno = errno;
   2140 
   2141     // Try >cursor(9999,9999), >u7, <u6
   2142     if_ok_return(rv, update_term_size_via_esc());
   2143 
   2144     global.last_errno = ioctl_errno;
   2145     return TB_ERR_RESIZE_IOCTL;
   2146 }
   2147 
   2148 static int update_term_size_via_esc(void) {
   2149 #ifndef TB_RESIZE_FALLBACK_MS
   2150 #define TB_RESIZE_FALLBACK_MS 1000
   2151 #endif
   2152 
   2153     char *move_and_report = "\x1b[9999;9999H\x1b[6n";
   2154     ssize_t write_rv =
   2155         write(global.wfd, move_and_report, strlen(move_and_report));
   2156     if (write_rv != (ssize_t)strlen(move_and_report)) {
   2157         return TB_ERR_RESIZE_WRITE;
   2158     }
   2159 
   2160     fd_set fds;
   2161     FD_ZERO(&fds);
   2162     FD_SET(global.rfd, &fds);
   2163 
   2164     struct timeval timeout;
   2165     timeout.tv_sec = 0;
   2166     timeout.tv_usec = TB_RESIZE_FALLBACK_MS * 1000;
   2167 
   2168     int select_rv = select(global.rfd + 1, &fds, NULL, NULL, &timeout);
   2169 
   2170     if (select_rv != 1) {
   2171         global.last_errno = errno;
   2172         return TB_ERR_RESIZE_POLL;
   2173     }
   2174 
   2175     char buf[TB_OPT_READ_BUF];
   2176     ssize_t read_rv = read(global.rfd, buf, sizeof(buf) - 1);
   2177     if (read_rv < 1) {
   2178         global.last_errno = errno;
   2179         return TB_ERR_RESIZE_READ;
   2180     }
   2181     buf[read_rv] = '\0';
   2182 
   2183     int rw, rh;
   2184     if (sscanf(buf, "\x1b[%d;%dR", &rh, &rw) != 2) {
   2185         return TB_ERR_RESIZE_SSCANF;
   2186     }
   2187 
   2188     global.width = rw;
   2189     global.height = rh;
   2190     return TB_OK;
   2191 }
   2192 
   2193 static int init_cellbuf(void) {
   2194     int rv;
   2195     if_err_return(rv, cellbuf_init(&global.back, global.width, global.height));
   2196     if_err_return(rv, cellbuf_init(&global.front, global.width, global.height));
   2197     if_err_return(rv, cellbuf_clear(&global.back));
   2198     if_err_return(rv, cellbuf_clear(&global.front));
   2199     return TB_OK;
   2200 }
   2201 
   2202 static int tb_deinit(void) {
   2203     if (global.caps[0] != NULL && global.wfd >= 0) {
   2204         bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]);
   2205         bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]);
   2206         bytebuf_puts(&global.out, global.caps[TB_CAP_CLEAR_SCREEN]);
   2207         bytebuf_puts(&global.out, global.caps[TB_CAP_EXIT_CA]);
   2208         bytebuf_puts(&global.out, global.caps[TB_CAP_EXIT_KEYPAD]);
   2209         bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE);
   2210         bytebuf_flush(&global.out, global.wfd);
   2211     }
   2212     if (global.ttyfd >= 0) {
   2213         if (global.has_orig_tios) {
   2214             tcsetattr(global.ttyfd, TCSAFLUSH, &global.orig_tios);
   2215         }
   2216         if (global.ttyfd_open) {
   2217             close(global.ttyfd);
   2218             global.ttyfd_open = 0;
   2219         }
   2220     }
   2221 
   2222     sigaction(SIGWINCH, &(struct sigaction){.sa_handler = SIG_DFL}, NULL);
   2223     if (global.resize_pipefd[0] >= 0)
   2224         close(global.resize_pipefd[0]);
   2225     if (global.resize_pipefd[1] >= 0)
   2226         close(global.resize_pipefd[1]);
   2227 
   2228     cellbuf_free(&global.back);
   2229     cellbuf_free(&global.front);
   2230     bytebuf_free(&global.in);
   2231     bytebuf_free(&global.out);
   2232 
   2233     if (global.terminfo)
   2234         tb_free(global.terminfo);
   2235 
   2236     cap_trie_deinit(&global.cap_trie);
   2237 
   2238     tb_reset();
   2239     return TB_OK;
   2240 }
   2241 
   2242 static int load_terminfo(void) {
   2243     int rv;
   2244     char tmp[PATH_MAX];
   2245 
   2246     // See terminfo(5) "Fetching Compiled Descriptions" for a description of
   2247     // this behavior. Some of these paths are compile-time ncurses options, so
   2248     // best guesses are used here.
   2249     const char *term = getenv("TERM");
   2250     if (!term) {
   2251         return TB_ERR;
   2252     }
   2253 
   2254     // If TERMINFO is set, try that directory and stop
   2255     const char *terminfo = getenv("TERMINFO");
   2256     if (terminfo) {
   2257         return load_terminfo_from_path(terminfo, term);
   2258     }
   2259 
   2260     // Next try ~/.terminfo
   2261     const char *home = getenv("HOME");
   2262     if (home) {
   2263         snprintf_or_return(rv, tmp, sizeof(tmp), "%s/.terminfo", home);
   2264         if_ok_return(rv, load_terminfo_from_path(tmp, term));
   2265     }
   2266 
   2267     // Next try TERMINFO_DIRS
   2268     //
   2269     // Note, empty entries are supposed to be interpretted as the "compiled-in
   2270     // default", which is of course system-dependent. Previously /etc/terminfo
   2271     // was used here. Let's skip empty entries altogether rather than give
   2272     // precedence to a guess, and check common paths after this loop.
   2273     const char *dirs = getenv("TERMINFO_DIRS");
   2274     if (dirs) {
   2275         snprintf_or_return(rv, tmp, sizeof(tmp), "%s", dirs);
   2276         char *dir = strtok(tmp, ":");
   2277         while (dir) {
   2278             const char *cdir = dir;
   2279             if (*cdir != '\0') {
   2280                 if_ok_return(rv, load_terminfo_from_path(cdir, term));
   2281             }
   2282             dir = strtok(NULL, ":");
   2283         }
   2284     }
   2285 
   2286 #ifdef TB_TERMINFO_DIR
   2287     if_ok_return(rv, load_terminfo_from_path(TB_TERMINFO_DIR, term));
   2288 #endif
   2289     if_ok_return(rv, load_terminfo_from_path("/usr/local/etc/terminfo", term));
   2290     if_ok_return(rv,
   2291         load_terminfo_from_path("/usr/local/share/terminfo", term));
   2292     if_ok_return(rv, load_terminfo_from_path("/usr/local/lib/terminfo", term));
   2293     if_ok_return(rv, load_terminfo_from_path("/etc/terminfo", term));
   2294     if_ok_return(rv, load_terminfo_from_path("/usr/share/terminfo", term));
   2295     if_ok_return(rv, load_terminfo_from_path("/usr/lib/terminfo", term));
   2296     if_ok_return(rv, load_terminfo_from_path("/usr/share/lib/terminfo", term));
   2297     if_ok_return(rv, load_terminfo_from_path("/lib/terminfo", term));
   2298 
   2299     return TB_ERR;
   2300 }
   2301 
   2302 static int load_terminfo_from_path(const char *path, const char *term) {
   2303     int rv;
   2304     char tmp[PATH_MAX];
   2305 
   2306     // Look for term at this terminfo location, e.g., <terminfo>/x/xterm
   2307     snprintf_or_return(rv, tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term);
   2308     if_ok_return(rv, read_terminfo_path(tmp));
   2309 
   2310 #ifdef __APPLE__
   2311     // Try the Darwin equivalent path, e.g., <terminfo>/78/xterm
   2312     snprintf_or_return(rv, tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term);
   2313     return read_terminfo_path(tmp);
   2314 #endif
   2315 
   2316     return TB_ERR;
   2317 }
   2318 
   2319 static int read_terminfo_path(const char *path) {
   2320     FILE *fp = fopen(path, "rb");
   2321     if (!fp) {
   2322         return TB_ERR;
   2323     }
   2324 
   2325     struct stat st;
   2326     if (fstat(fileno(fp), &st) != 0) {
   2327         fclose(fp);
   2328         return TB_ERR;
   2329     }
   2330 
   2331     size_t fsize = st.st_size;
   2332     char *data = tb_malloc(fsize);
   2333     if (!data) {
   2334         fclose(fp);
   2335         return TB_ERR;
   2336     }
   2337 
   2338     if (fread(data, 1, fsize, fp) != fsize) {
   2339         fclose(fp);
   2340         tb_free(data);
   2341         return TB_ERR;
   2342     }
   2343 
   2344     global.terminfo = data;
   2345     global.nterminfo = fsize;
   2346 
   2347     fclose(fp);
   2348     return TB_OK;
   2349 }
   2350 
   2351 static int parse_terminfo_caps(void) {
   2352     // See term(5) "LEGACY STORAGE FORMAT" and "EXTENDED STORAGE FORMAT" for a
   2353     // description of this behavior.
   2354 
   2355     // Ensure there's at least a header's worth of data
   2356     if (global.nterminfo < 6) {
   2357         return TB_ERR;
   2358     }
   2359 
   2360     int16_t *header = (int16_t *)global.terminfo;
   2361     // header[0] the magic number (octal 0432 or 01036)
   2362     // header[1] the size, in bytes, of the names section
   2363     // header[2] the number of bytes in the boolean section
   2364     // header[3] the number of short integers in the numbers section
   2365     // header[4] the number of offsets (short integers) in the strings section
   2366     // header[5] the size, in bytes, of the string table
   2367 
   2368     // Legacy ints are 16-bit, extended ints are 32-bit
   2369     const int bytes_per_int = header[0] == 01036 ? 4  // 32-bit
   2370                                                  : 2; // 16-bit
   2371 
   2372     // > Between the boolean section and the number section, a null byte will be
   2373     // > inserted, if necessary, to ensure that the number section begins on an
   2374     // > even byte
   2375     const int align_offset = (header[1] + header[2]) % 2 != 0 ? 1 : 0;
   2376 
   2377     const int pos_str_offsets =
   2378         (6 * sizeof(int16_t)) // header (12 bytes)
   2379         + header[1]           // length of names section
   2380         + header[2]           // length of boolean section
   2381         + align_offset +
   2382         (header[3] * bytes_per_int); // length of numbers section
   2383 
   2384     const int pos_str_table =
   2385         pos_str_offsets +
   2386         (header[4] * sizeof(int16_t)); // length of string offsets table
   2387 
   2388     // Load caps
   2389     int i;
   2390     for (i = 0; i < TB_CAP__COUNT; i++) {
   2391         const char *cap = get_terminfo_string(pos_str_offsets, pos_str_table,
   2392             header[5], terminfo_cap_indexes[i]);
   2393         if (!cap) {
   2394             // Something is not right
   2395             return TB_ERR;
   2396         }
   2397         global.caps[i] = cap;
   2398     }
   2399 
   2400     return TB_OK;
   2401 }
   2402 
   2403 static int load_builtin_caps(void) {
   2404     int i, j;
   2405     const char *term = getenv("TERM");
   2406 
   2407     if (!term) {
   2408         return TB_ERR_NO_TERM;
   2409     }
   2410 
   2411     // Check for exact TERM match
   2412     for (i = 0; builtin_terms[i].name != NULL; i++) {
   2413         if (strcmp(term, builtin_terms[i].name) == 0) {
   2414             for (j = 0; j < TB_CAP__COUNT; j++) {
   2415                 global.caps[j] = builtin_terms[i].caps[j];
   2416             }
   2417             return TB_OK;
   2418         }
   2419     }
   2420 
   2421     // Check for partial TERM or alias match
   2422     for (i = 0; builtin_terms[i].name != NULL; i++) {
   2423         if (strstr(term, builtin_terms[i].name) != NULL ||
   2424             (*(builtin_terms[i].alias) != '\0' &&
   2425                 strstr(term, builtin_terms[i].alias) != NULL))
   2426         {
   2427             for (j = 0; j < TB_CAP__COUNT; j++) {
   2428                 global.caps[j] = builtin_terms[i].caps[j];
   2429             }
   2430             return TB_OK;
   2431         }
   2432     }
   2433 
   2434     return TB_ERR_UNSUPPORTED_TERM;
   2435 }
   2436 
   2437 static const char *get_terminfo_string(int16_t str_offsets_pos,
   2438     int16_t str_table_pos, int16_t str_table_len, int16_t str_index) {
   2439     const int16_t *str_offset =
   2440         (int16_t *)(global.terminfo + (int)str_offsets_pos +
   2441                     ((int)str_index * (int)sizeof(int16_t)));
   2442     if (*str_offset < 0) {
   2443         // A negative indicates the cap is absent from this terminal
   2444         return "";
   2445     }
   2446     if (*str_offset >= str_table_len) {
   2447         // Invalid string offset
   2448         return NULL;
   2449     }
   2450     if (((size_t)((int)str_table_pos + (int)*str_offset)) >= global.nterminfo) {
   2451         // Truncated/corrupt terminfo?
   2452         return NULL;
   2453     }
   2454     return (
   2455         const char *)(global.terminfo + (int)str_table_pos + (int)*str_offset);
   2456 }
   2457 
   2458 static int wait_event(struct tb_event *event, int timeout) {
   2459     int rv;
   2460     char buf[TB_OPT_READ_BUF];
   2461 
   2462     memset(event, 0, sizeof(*event));
   2463     if_ok_return(rv, extract_event(event));
   2464 
   2465     fd_set fds;
   2466     struct timeval tv;
   2467     tv.tv_sec = timeout / 1000;
   2468     tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
   2469 
   2470     do {
   2471         FD_ZERO(&fds);
   2472         FD_SET(global.rfd, &fds);
   2473         FD_SET(global.resize_pipefd[0], &fds);
   2474 
   2475         int maxfd = global.resize_pipefd[0] > global.rfd
   2476                         ? global.resize_pipefd[0]
   2477                         : global.rfd;
   2478 
   2479         int select_rv =
   2480             select(maxfd + 1, &fds, NULL, NULL, (timeout < 0) ? NULL : &tv);
   2481 
   2482         if (select_rv < 0) {
   2483             // Let EINTR/EAGAIN bubble up
   2484             global.last_errno = errno;
   2485             return TB_ERR_POLL;
   2486         } else if (select_rv == 0) {
   2487             return TB_ERR_NO_EVENT;
   2488         }
   2489 
   2490         int tty_has_events = (FD_ISSET(global.rfd, &fds));
   2491         int resize_has_events = (FD_ISSET(global.resize_pipefd[0], &fds));
   2492 
   2493         if (tty_has_events) {
   2494             ssize_t read_rv = read(global.rfd, buf, sizeof(buf));
   2495             if (read_rv < 0) {
   2496                 global.last_errno = errno;
   2497                 return TB_ERR_READ;
   2498             } else if (read_rv > 0) {
   2499                 bytebuf_nputs(&global.in, buf, read_rv);
   2500             }
   2501         }
   2502 
   2503         if (resize_has_events) {
   2504             int ignore = 0;
   2505             read(global.resize_pipefd[0], &ignore, sizeof(ignore));
   2506             // TODO Harden against errors encountered mid-resize
   2507             if_err_return(rv, update_term_size());
   2508             if_err_return(rv, resize_cellbufs());
   2509             event->type = TB_EVENT_RESIZE;
   2510             event->w = global.width;
   2511             event->h = global.height;
   2512             return TB_OK;
   2513         }
   2514 
   2515         memset(event, 0, sizeof(*event));
   2516         if_ok_return(rv, extract_event(event));
   2517     } while (timeout == -1);
   2518 
   2519     return rv;
   2520 }
   2521 
   2522 static int extract_event(struct tb_event *event) {
   2523     int rv;
   2524     struct bytebuf_t *in = &global.in;
   2525 
   2526     if (in->len == 0) {
   2527         return TB_ERR;
   2528     }
   2529 
   2530     if (in->buf[0] == '\x1b') {
   2531         // Escape sequence?
   2532         // In TB_INPUT_ESC, skip if the buffer is a single escape char
   2533         if (!((global.input_mode & TB_INPUT_ESC) && in->len == 1)) {
   2534             if_ok_or_need_more_return(rv, extract_esc(event));
   2535         }
   2536 
   2537         // Escape key?
   2538         if (global.input_mode & TB_INPUT_ESC) {
   2539             event->type = TB_EVENT_KEY;
   2540             event->ch = 0;
   2541             event->key = TB_KEY_ESC;
   2542             event->mod = 0;
   2543             bytebuf_shift(in, 1);
   2544             return TB_OK;
   2545         }
   2546 
   2547         // Recurse for alt key
   2548         event->mod |= TB_MOD_ALT;
   2549         bytebuf_shift(in, 1);
   2550         return extract_event(event);
   2551     }
   2552 
   2553     // ASCII control key?
   2554     if ((uint16_t)in->buf[0] < TB_KEY_SPACE || in->buf[0] == TB_KEY_BACKSPACE2)
   2555     {
   2556         event->type = TB_EVENT_KEY;
   2557         event->ch = 0;
   2558         event->key = (uint16_t)in->buf[0];
   2559         event->mod |= TB_MOD_CTRL;
   2560         bytebuf_shift(in, 1);
   2561         return TB_OK;
   2562     }
   2563 
   2564     // UTF-8?
   2565     if (in->len >= (size_t)tb_utf8_char_length(in->buf[0])) {
   2566         event->type = TB_EVENT_KEY;
   2567         tb_utf8_char_to_unicode(&event->ch, in->buf);
   2568         event->key = 0;
   2569         bytebuf_shift(in, tb_utf8_char_length(in->buf[0]));
   2570         return TB_OK;
   2571     }
   2572 
   2573     // Need more input
   2574     return TB_ERR;
   2575 }
   2576 
   2577 static int extract_esc(struct tb_event *event) {
   2578     int rv;
   2579     if_ok_or_need_more_return(rv, extract_esc_user(event, 0));
   2580     if_ok_or_need_more_return(rv, extract_esc_cap(event));
   2581     if_ok_or_need_more_return(rv, extract_esc_mouse(event));
   2582     if_ok_or_need_more_return(rv, extract_esc_user(event, 1));
   2583     return TB_ERR;
   2584 }
   2585 
   2586 static int extract_esc_user(struct tb_event *event, int is_post) {
   2587     int rv;
   2588     size_t consumed = 0;
   2589     struct bytebuf_t *in = &global.in;
   2590     int (*fn)(struct tb_event *, size_t *);
   2591 
   2592     fn = is_post ? global.fn_extract_esc_post : global.fn_extract_esc_pre;
   2593 
   2594     if (!fn) {
   2595         return TB_ERR;
   2596     }
   2597 
   2598     rv = fn(event, &consumed);
   2599     if (rv == TB_OK) {
   2600         bytebuf_shift(in, consumed);
   2601     }
   2602 
   2603     if_ok_or_need_more_return(rv, rv);
   2604     return TB_ERR;
   2605 }
   2606 
   2607 static int extract_esc_cap(struct tb_event *event) {
   2608     int rv;
   2609     struct bytebuf_t *in = &global.in;
   2610     struct cap_trie_t *node;
   2611     size_t depth;
   2612 
   2613     if_err_return(rv, cap_trie_find(in->buf, in->len, &node, &depth));
   2614     if (node->is_leaf) {
   2615         // Found a leaf node
   2616         event->type = TB_EVENT_KEY;
   2617         event->ch = 0;
   2618         event->key = node->key;
   2619         event->mod = node->mod;
   2620         bytebuf_shift(in, depth);
   2621         return TB_OK;
   2622     } else if (node->nchildren > 0 && in->len <= depth) {
   2623         // Found a branch node (not enough input)
   2624         return TB_ERR_NEED_MORE;
   2625     }
   2626 
   2627     return TB_ERR;
   2628 }
   2629 
   2630 static int extract_esc_mouse(struct tb_event *event) {
   2631     struct bytebuf_t *in = &global.in;
   2632 
   2633     enum type { TYPE_VT200 = 0, TYPE_1006, TYPE_1015, TYPE_MAX };
   2634 
   2635     char *cmp[TYPE_MAX] = {//
   2636         // X10 mouse encoding, the simplest one
   2637         // \x1b [ M Cb Cx Cy
   2638         [TYPE_VT200] = "\x1b[M",
   2639         // xterm 1006 extended mode or urxvt 1015 extended mode
   2640         // xterm: \x1b [ < Cb ; Cx ; Cy (M or m)
   2641         [TYPE_1006] = "\x1b[<",
   2642         // urxvt: \x1b [ Cb ; Cx ; Cy M
   2643         [TYPE_1015] = "\x1b["};
   2644 
   2645     enum type type = 0;
   2646     int ret = TB_ERR;
   2647 
   2648     // Unrolled at compile-time (probably)
   2649     for (; type < TYPE_MAX; type++) {
   2650         size_t size = strlen(cmp[type]);
   2651 
   2652         if (in->len >= size && (strncmp(cmp[type], in->buf, size)) == 0) {
   2653             break;
   2654         }
   2655     }
   2656 
   2657     if (type == TYPE_MAX) {
   2658         ret = TB_ERR; // No match
   2659         return ret;
   2660     }
   2661 
   2662     size_t buf_shift = 0;
   2663 
   2664     switch (type) {
   2665         case TYPE_VT200:
   2666             if (in->len >= 6) {
   2667                 int b = in->buf[3] - 0x20;
   2668                 int fail = 0;
   2669 
   2670                 switch (b & 3) {
   2671                     case 0:
   2672                         event->key = ((b & 64) != 0) ? TB_KEY_MOUSE_WHEEL_UP
   2673                                                      : TB_KEY_MOUSE_LEFT;
   2674                         break;
   2675                     case 1:
   2676                         event->key = ((b & 64) != 0) ? TB_KEY_MOUSE_WHEEL_DOWN
   2677                                                      : TB_KEY_MOUSE_MIDDLE;
   2678                         break;
   2679                     case 2:
   2680                         event->key = TB_KEY_MOUSE_RIGHT;
   2681                         break;
   2682                     case 3:
   2683                         event->key = TB_KEY_MOUSE_RELEASE;
   2684                         break;
   2685                     default:
   2686                         ret = TB_ERR;
   2687                         fail = 1;
   2688                         break;
   2689                 }
   2690 
   2691                 if (!fail) {
   2692                     if ((b & 32) != 0) {
   2693                         event->mod |= TB_MOD_MOTION;
   2694                     }
   2695 
   2696                     // the coord is 1,1 for upper left
   2697                     event->x = ((uint8_t)in->buf[4]) - 0x21;
   2698                     event->y = ((uint8_t)in->buf[5]) - 0x21;
   2699 
   2700                     ret = TB_OK;
   2701                 }
   2702 
   2703                 buf_shift = 6;
   2704             }
   2705             break;
   2706         case TYPE_1006:
   2707             // fallthrough
   2708         case TYPE_1015: {
   2709             size_t index_fail = (size_t)-1;
   2710 
   2711             enum {
   2712                 FIRST_M = 0,
   2713                 FIRST_SEMICOLON,
   2714                 LAST_SEMICOLON,
   2715                 FIRST_LAST_MAX
   2716             };
   2717 
   2718             size_t indices[FIRST_LAST_MAX] = {index_fail, index_fail,
   2719                 index_fail};
   2720             int m_is_capital = 0;
   2721 
   2722             for (size_t i = 0; i < in->len; i++) {
   2723                 if (in->buf[i] == ';') {
   2724                     if (indices[FIRST_SEMICOLON] == index_fail) {
   2725                         indices[FIRST_SEMICOLON] = i;
   2726                     } else {
   2727                         indices[LAST_SEMICOLON] = i;
   2728                     }
   2729                 } else if (indices[FIRST_M] == index_fail) {
   2730                     if (in->buf[i] == 'm' || in->buf[i] == 'M') {
   2731                         m_is_capital = (in->buf[i] == 'M');
   2732                         indices[FIRST_M] = i;
   2733                     }
   2734                 }
   2735             }
   2736 
   2737             if (indices[FIRST_M] == index_fail ||
   2738                 indices[FIRST_SEMICOLON] == index_fail ||
   2739                 indices[LAST_SEMICOLON] == index_fail)
   2740             {
   2741                 ret = TB_ERR;
   2742             } else {
   2743                 int start = (type == TYPE_1015 ? 2 : 3);
   2744 
   2745                 unsigned n1 = strtoul(&in->buf[start], NULL, 10);
   2746                 unsigned n2 =
   2747                     strtoul(&in->buf[indices[FIRST_SEMICOLON] + 1], NULL, 10);
   2748                 unsigned n3 =
   2749                     strtoul(&in->buf[indices[LAST_SEMICOLON] + 1], NULL, 10);
   2750 
   2751                 if (type == TYPE_1015) {
   2752                     n1 -= 0x20;
   2753                 }
   2754 
   2755                 int fail = 0;
   2756 
   2757                 switch (n1 & 3) {
   2758                     case 0:
   2759                         event->key = ((n1 & 64) != 0) ? TB_KEY_MOUSE_WHEEL_UP
   2760                                                       : TB_KEY_MOUSE_LEFT;
   2761                         break;
   2762                     case 1:
   2763                         event->key = ((n1 & 64) != 0) ? TB_KEY_MOUSE_WHEEL_DOWN
   2764                                                       : TB_KEY_MOUSE_MIDDLE;
   2765                         break;
   2766                     case 2:
   2767                         event->key = TB_KEY_MOUSE_RIGHT;
   2768                         break;
   2769                     case 3:
   2770                         event->key = TB_KEY_MOUSE_RELEASE;
   2771                         break;
   2772                     default:
   2773                         ret = TB_ERR;
   2774                         fail = 1;
   2775                         break;
   2776                 }
   2777 
   2778                 buf_shift = in->len;
   2779 
   2780                 if (!fail) {
   2781                     if (!m_is_capital) {
   2782                         // on xterm mouse release is signaled by lowercase m
   2783                         event->key = TB_KEY_MOUSE_RELEASE;
   2784                     }
   2785 
   2786                     if ((n1 & 32) != 0) {
   2787                         event->mod |= TB_MOD_MOTION;
   2788                     }
   2789 
   2790                     event->x = ((uint8_t)n2) - 1;
   2791                     event->y = ((uint8_t)n3) - 1;
   2792 
   2793                     ret = TB_OK;
   2794                 }
   2795             }
   2796         } break;
   2797         case TYPE_MAX:
   2798             ret = TB_ERR;
   2799     }
   2800 
   2801     if (buf_shift > 0) {
   2802         bytebuf_shift(in, buf_shift);
   2803     }
   2804 
   2805     if (ret == TB_OK) {
   2806         event->type = TB_EVENT_MOUSE;
   2807     }
   2808 
   2809     return ret;
   2810 }
   2811 
   2812 static int resize_cellbufs(void) {
   2813     int rv;
   2814     if_err_return(rv,
   2815         cellbuf_resize(&global.back, global.width, global.height));
   2816     if_err_return(rv,
   2817         cellbuf_resize(&global.front, global.width, global.height));
   2818     if_err_return(rv, cellbuf_clear(&global.front));
   2819     if_err_return(rv, send_clear());
   2820     return TB_OK;
   2821 }
   2822 
   2823 static void handle_resize(int sig) {
   2824     int errno_copy = errno;
   2825     write(global.resize_pipefd[1], &sig, sizeof(sig));
   2826     errno = errno_copy;
   2827 }
   2828 
   2829 static int send_attr(uintattr_t fg, uintattr_t bg) {
   2830     int rv;
   2831 
   2832     if (fg == global.last_fg && bg == global.last_bg) {
   2833         return TB_OK;
   2834     }
   2835 
   2836     if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]));
   2837 
   2838     uintattr_t cfg, cbg;
   2839     switch (global.output_mode) {
   2840         default:
   2841         case TB_OUTPUT_NORMAL:
   2842             cfg = fg & 0x0f;
   2843             cbg = bg & 0x0f;
   2844             break;
   2845 
   2846         case TB_OUTPUT_256:
   2847             cfg = fg & 0xff;
   2848             cbg = bg & 0xff;
   2849             break;
   2850 
   2851         case TB_OUTPUT_216:
   2852             cfg = fg & 0xff;
   2853             cbg = bg & 0xff;
   2854             if (cfg > 216)
   2855                 cfg = 216;
   2856             if (cbg > 216)
   2857                 cbg = 216;
   2858             cfg += 0x0f;
   2859             cbg += 0x0f;
   2860             break;
   2861 
   2862         case TB_OUTPUT_GRAYSCALE:
   2863             cfg = fg & 0xff;
   2864             cbg = bg & 0xff;
   2865             if (cfg > 24)
   2866                 cfg = 24;
   2867             if (cbg > 24)
   2868                 cbg = 24;
   2869             cfg += 0xe7;
   2870             cbg += 0xe7;
   2871             break;
   2872 
   2873 #ifdef TB_OPT_TRUECOLOR
   2874         case TB_OUTPUT_TRUECOLOR:
   2875             cfg = fg & 0xffffff;
   2876             cbg = bg & 0xffffff;
   2877             break;
   2878 #endif
   2879     }
   2880 
   2881     uintattr_t attr_bold, attr_blink, attr_italic, attr_underline, attr_reverse,
   2882         attr_default;
   2883 #ifdef TB_OPT_TRUECOLOR
   2884     if (global.output_mode == TB_OUTPUT_TRUECOLOR) {
   2885         attr_bold = TB_TRUECOLOR_BOLD;
   2886         attr_blink = TB_TRUECOLOR_BLINK;
   2887         attr_italic = TB_TRUECOLOR_ITALIC;
   2888         attr_underline = TB_TRUECOLOR_UNDERLINE;
   2889         attr_reverse = TB_TRUECOLOR_REVERSE;
   2890         attr_default = TB_TRUECOLOR_DEFAULT;
   2891     } else
   2892 #endif
   2893     {
   2894         attr_bold = TB_BOLD;
   2895         attr_blink = TB_BLINK;
   2896         attr_italic = TB_ITALIC;
   2897         attr_underline = TB_UNDERLINE;
   2898         attr_reverse = TB_REVERSE;
   2899         attr_default = TB_DEFAULT;
   2900     }
   2901 
   2902     /* For convenience (and some back compat), interpret 0 as default in some
   2903      * modes */
   2904     if (global.output_mode == TB_OUTPUT_NORMAL ||
   2905         global.output_mode == TB_OUTPUT_216 ||
   2906         global.output_mode == TB_OUTPUT_GRAYSCALE)
   2907     {
   2908         if ((fg & 0xff) == 0)
   2909             fg |= attr_default;
   2910         if ((bg & 0xff) == 0)
   2911             bg |= attr_default;
   2912     }
   2913 
   2914     if (fg & attr_bold)
   2915         if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BOLD]));
   2916 
   2917     if (fg & attr_blink)
   2918         if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BLINK]));
   2919 
   2920     if (fg & attr_underline)
   2921         if_err_return(rv,
   2922             bytebuf_puts(&global.out, global.caps[TB_CAP_UNDERLINE]));
   2923 
   2924     if (fg & attr_italic)
   2925         if_err_return(rv,
   2926             bytebuf_puts(&global.out, global.caps[TB_CAP_ITALIC]));
   2927 
   2928     if ((fg & attr_reverse) || (bg & attr_reverse))
   2929         if_err_return(rv,
   2930             bytebuf_puts(&global.out, global.caps[TB_CAP_REVERSE]));
   2931 
   2932     if_err_return(rv, send_sgr(cfg, cbg, fg & attr_default, bg & attr_default));
   2933 
   2934     global.last_fg = fg;
   2935     global.last_bg = bg;
   2936 
   2937     return TB_OK;
   2938 }
   2939 
   2940 static int send_sgr(uintattr_t cfg, uintattr_t cbg, uintattr_t fg_is_default,
   2941     uintattr_t bg_is_default) {
   2942     int rv;
   2943     char nbuf[32];
   2944 
   2945     if (fg_is_default && bg_is_default) {
   2946         return TB_OK;
   2947     }
   2948 
   2949     switch (global.output_mode) {
   2950         default:
   2951         case TB_OUTPUT_NORMAL:
   2952             send_literal(rv, "\x1b[");
   2953             if (!fg_is_default) {
   2954                 send_literal(rv, "3");
   2955                 send_num(rv, nbuf, cfg - 1);
   2956                 if (!bg_is_default) {
   2957                     send_literal(rv, ";");
   2958                 }
   2959             }
   2960             if (!bg_is_default) {
   2961                 send_literal(rv, "4");
   2962                 send_num(rv, nbuf, cbg - 1);
   2963             }
   2964             send_literal(rv, "m");
   2965             break;
   2966 
   2967         case TB_OUTPUT_256:
   2968         case TB_OUTPUT_216:
   2969         case TB_OUTPUT_GRAYSCALE:
   2970             send_literal(rv, "\x1b[");
   2971             if (!fg_is_default) {
   2972                 send_literal(rv, "38;5;");
   2973                 send_num(rv, nbuf, cfg);
   2974                 if (!bg_is_default) {
   2975                     send_literal(rv, ";");
   2976                 }
   2977             }
   2978             if (!bg_is_default) {
   2979                 send_literal(rv, "48;5;");
   2980                 send_num(rv, nbuf, cbg);
   2981             }
   2982             send_literal(rv, "m");
   2983             break;
   2984 
   2985 #ifdef TB_OPT_TRUECOLOR
   2986         case TB_OUTPUT_TRUECOLOR:
   2987             send_literal(rv, "\x1b[");
   2988             if (!fg_is_default) {
   2989                 send_literal(rv, "38;2;");
   2990                 send_num(rv, nbuf, (cfg >> 16) & 0xff);
   2991                 send_literal(rv, ";");
   2992                 send_num(rv, nbuf, (cfg >> 8) & 0xff);
   2993                 send_literal(rv, ";");
   2994                 send_num(rv, nbuf, cfg & 0xff);
   2995                 if (!bg_is_default) {
   2996                     send_literal(rv, ";");
   2997                 }
   2998             }
   2999             if (!bg_is_default) {
   3000                 send_literal(rv, "48;2;");
   3001                 send_num(rv, nbuf, (cbg >> 16) & 0xff);
   3002                 send_literal(rv, ";");
   3003                 send_num(rv, nbuf, (cbg >> 8) & 0xff);
   3004                 send_literal(rv, ";");
   3005                 send_num(rv, nbuf, cbg & 0xff);
   3006             }
   3007             send_literal(rv, "m");
   3008             break;
   3009 #endif
   3010     }
   3011     return TB_OK;
   3012 }
   3013 
   3014 static int send_cursor_if(int x, int y) {
   3015     int rv;
   3016     char nbuf[32];
   3017     if (x < 0 || y < 0) {
   3018         return TB_OK;
   3019     }
   3020     send_literal(rv, "\x1b[");
   3021     send_num(rv, nbuf, y + 1);
   3022     send_literal(rv, ";");
   3023     send_num(rv, nbuf, x + 1);
   3024     send_literal(rv, "H");
   3025     return TB_OK;
   3026 }
   3027 
   3028 static int send_char(int x, int y, uint32_t ch) {
   3029     return send_cluster(x, y, &ch, 1);
   3030 }
   3031 
   3032 static int send_cluster(int x, int y, uint32_t *ch, size_t nch) {
   3033     int rv;
   3034     char abuf[8];
   3035 
   3036     if (global.last_x != x - 1 || global.last_y != y) {
   3037         if_err_return(rv, send_cursor_if(x, y));
   3038     }
   3039     global.last_x = x;
   3040     global.last_y = y;
   3041 
   3042     int i;
   3043     for (i = 0; i < (int)nch; i++) {
   3044         uint32_t ach = *(ch + i);
   3045         int aw = tb_utf8_unicode_to_char(abuf, ach);
   3046         if (!ach) {
   3047             abuf[0] = ' ';
   3048         }
   3049         if_err_return(rv, bytebuf_nputs(&global.out, abuf, (size_t)aw));
   3050     }
   3051 
   3052     return TB_OK;
   3053 }
   3054 
   3055 static int convert_num(uint32_t num, char *buf) {
   3056     int i, l = 0;
   3057     char ch;
   3058     do {
   3059         /* '0' = 48; 48 + num%10 < 58 < MAX_8bitCHAR */
   3060         buf[l++] = (char)('0' + (num % 10));
   3061         num /= 10;
   3062     } while (num);
   3063     for (i = 0; i < l / 2; i++) {
   3064         ch = buf[i];
   3065         buf[i] = buf[l - 1 - i];
   3066         buf[l - 1 - i] = ch;
   3067     }
   3068     return l;
   3069 }
   3070 
   3071 static int cell_cmp(struct tb_cell *a, struct tb_cell *b) {
   3072     if (a->ch != b->ch || a->fg != b->fg || a->bg != b->bg) {
   3073         return 1;
   3074     }
   3075 #ifdef TB_OPT_EGC
   3076     if (a->nech != b->nech) {
   3077         return 1;
   3078     } else if (a->nech > 0) { // a->nech == b->nech
   3079         return memcmp(a->ech, b->ech, a->nech);
   3080     }
   3081 #endif
   3082     return 0;
   3083 }
   3084 
   3085 static int cell_copy(struct tb_cell *dst, struct tb_cell *src) {
   3086 #ifdef TB_OPT_EGC
   3087     if (src->nech > 0) {
   3088         return cell_set(dst, src->ech, src->nech, src->fg, src->bg);
   3089     }
   3090 #endif
   3091     return cell_set(dst, &src->ch, 1, src->fg, src->bg);
   3092 }
   3093 
   3094 static int cell_set(struct tb_cell *cell, uint32_t *ch, size_t nch,
   3095     uintattr_t fg, uintattr_t bg) {
   3096     cell->ch = ch ? *ch : 0;
   3097     cell->fg = fg;
   3098     cell->bg = bg;
   3099 #ifdef TB_OPT_EGC
   3100     if (nch <= 1) {
   3101         cell->nech = 0;
   3102     } else {
   3103         int rv;
   3104         if_err_return(rv, cell_reserve_ech(cell, nch + 1));
   3105         memcpy(cell->ech, ch, nch);
   3106         cell->ech[nch] = '\0';
   3107         cell->nech = nch;
   3108     }
   3109 #else
   3110     (void)nch;
   3111     (void)cell_reserve_ech;
   3112 #endif
   3113     return TB_OK;
   3114 }
   3115 
   3116 static int cell_reserve_ech(struct tb_cell *cell, size_t n) {
   3117 #ifdef TB_OPT_EGC
   3118     if (cell->cech >= n) {
   3119         return TB_OK;
   3120     }
   3121     if (!(cell->ech = tb_realloc(cell->ech, n * sizeof(cell->ch)))) {
   3122         return TB_ERR_MEM;
   3123     }
   3124     cell->cech = n;
   3125     return TB_OK;
   3126 #else
   3127     (void)cell;
   3128     (void)n;
   3129     return TB_ERR;
   3130 #endif
   3131 }
   3132 
   3133 static int cell_free(struct tb_cell *cell) {
   3134 #ifdef TB_OPT_EGC
   3135     if (cell->ech) {
   3136         tb_free(cell->ech);
   3137     }
   3138 #endif
   3139     memset(cell, 0, sizeof(*cell));
   3140     return TB_OK;
   3141 }
   3142 
   3143 static int cellbuf_init(struct cellbuf_t *c, int w, int h) {
   3144     c->cells = tb_malloc(sizeof(struct tb_cell) * w * h);
   3145     if (!c->cells) {
   3146         return TB_ERR_MEM;
   3147     }
   3148     memset(c->cells, 0, sizeof(struct tb_cell) * w * h);
   3149     c->width = w;
   3150     c->height = h;
   3151     return TB_OK;
   3152 }
   3153 
   3154 static int cellbuf_free(struct cellbuf_t *c) {
   3155     if (c->cells) {
   3156         int i;
   3157         for (i = 0; i < c->width * c->height; i++) {
   3158             cell_free(&c->cells[i]);
   3159         }
   3160         tb_free(c->cells);
   3161     }
   3162     memset(c, 0, sizeof(*c));
   3163     return TB_OK;
   3164 }
   3165 
   3166 static int cellbuf_clear(struct cellbuf_t *c) {
   3167     int rv, i;
   3168     uint32_t space = (uint32_t)' ';
   3169     for (i = 0; i < c->width * c->height; i++) {
   3170         if_err_return(rv,
   3171             cell_set(&c->cells[i], &space, 1, global.fg, global.bg));
   3172     }
   3173     return TB_OK;
   3174 }
   3175 
   3176 static int cellbuf_get(struct cellbuf_t *c, int x, int y,
   3177     struct tb_cell **out) {
   3178     if (x < 0 || x >= c->width || y < 0 || y >= c->height) {
   3179         *out = NULL;
   3180         return TB_ERR_OUT_OF_BOUNDS;
   3181     }
   3182     *out = &c->cells[(y * c->width) + x];
   3183     return TB_OK;
   3184 }
   3185 
   3186 static int cellbuf_resize(struct cellbuf_t *c, int w, int h) {
   3187     int rv;
   3188 
   3189     int ow = c->width;
   3190     int oh = c->height;
   3191 
   3192     if (ow == w && oh == h) {
   3193         return TB_OK;
   3194     }
   3195 
   3196     w = w < 1 ? 1 : w;
   3197     h = h < 1 ? 1 : h;
   3198 
   3199     int minw = (w < ow) ? w : ow;
   3200     int minh = (h < oh) ? h : oh;
   3201 
   3202     struct tb_cell *prev = c->cells;
   3203 
   3204     if_err_return(rv, cellbuf_init(c, w, h));
   3205     if_err_return(rv, cellbuf_clear(c));
   3206 
   3207     int x, y;
   3208     for (x = 0; x < minw; x++) {
   3209         for (y = 0; y < minh; y++) {
   3210             struct tb_cell *src, *dst;
   3211             src = &prev[(y * ow) + x];
   3212             if_err_return(rv, cellbuf_get(c, x, y, &dst));
   3213             if_err_return(rv, cell_copy(dst, src));
   3214         }
   3215     }
   3216 
   3217     tb_free(prev);
   3218 
   3219     return TB_OK;
   3220 }
   3221 
   3222 static int bytebuf_puts(struct bytebuf_t *b, const char *str) {
   3223     return bytebuf_nputs(b, str, (size_t)strlen(str));
   3224 }
   3225 
   3226 static int bytebuf_nputs(struct bytebuf_t *b, const char *str, size_t nstr) {
   3227     int rv;
   3228     if_err_return(rv, bytebuf_reserve(b, b->len + nstr + 1));
   3229     memcpy(b->buf + b->len, str, nstr);
   3230     b->len += nstr;
   3231     b->buf[b->len] = '\0';
   3232     return TB_OK;
   3233 }
   3234 
   3235 static int bytebuf_shift(struct bytebuf_t *b, size_t n) {
   3236     if (n > b->len) {
   3237         n = b->len;
   3238     }
   3239     size_t nmove = b->len - n;
   3240     memmove(b->buf, b->buf + n, nmove);
   3241     b->len -= n;
   3242     return TB_OK;
   3243 }
   3244 
   3245 static int bytebuf_flush(struct bytebuf_t *b, int fd) {
   3246     if (b->len <= 0) {
   3247         return TB_OK;
   3248     }
   3249     ssize_t write_rv = write(fd, b->buf, b->len);
   3250     if (write_rv < 0 || (size_t)write_rv != b->len) {
   3251         // Note, errno will be 0 on partial write
   3252         global.last_errno = errno;
   3253         return TB_ERR;
   3254     }
   3255     b->len = 0;
   3256     return TB_OK;
   3257 }
   3258 
   3259 static int bytebuf_reserve(struct bytebuf_t *b, size_t sz) {
   3260     if (b->cap >= sz) {
   3261         return TB_OK;
   3262     }
   3263     size_t newcap = b->cap > 0 ? b->cap : 1;
   3264     while (newcap < sz) {
   3265         newcap *= 2;
   3266     }
   3267     char *newbuf;
   3268     if (b->buf) {
   3269         newbuf = tb_realloc(b->buf, newcap);
   3270     } else {
   3271         newbuf = tb_malloc(newcap);
   3272     }
   3273     if (!newbuf) {
   3274         return TB_ERR_MEM;
   3275     }
   3276     b->buf = newbuf;
   3277     b->cap = newcap;
   3278     return TB_OK;
   3279 }
   3280 
   3281 static int bytebuf_free(struct bytebuf_t *b) {
   3282     if (b->buf) {
   3283         tb_free(b->buf);
   3284     }
   3285     memset(b, 0, sizeof(*b));
   3286     return TB_OK;
   3287 }
   3288 
   3289 #endif /* TB_IMPL */