pipe-game

Un jeu de cartes à piper les une dans les autres - retour accueil

git clone git://bebou.netlib.re/pipe-game
Log | Files | Refs | README |

termbox2.h (126526B)


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