reorder

Un outil pour réarranger les lignes de stdin - retour accueil

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

termbox2.h (126816B)


      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-dev"
     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));
   1782         cell->ech[nech - 1] = ch;
   1783     } else { // make new ech
   1784         nech = 2;
   1785         if_err_return(rv, cell_reserve_ech(cell, nech));
   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;
   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     if (out_w) *out_w = 0;
   1883 
   1884     while (*str) {
   1885         rv = tb_utf8_char_to_unicode(&uni, str);
   1886 
   1887         if (rv < 0) {
   1888             uni = 0xfffd; // replace invalid UTF-8 char with U+FFFD
   1889             str += rv * -1;
   1890         } else if (rv > 0) {
   1891             str += rv;
   1892         } else {
   1893             break; // shouldn't get here
   1894         }
   1895 
   1896         if (uni == '\n') { // TODO: \r, \t, \v, \f, etc?
   1897             x = ix;
   1898             y += 1;
   1899             continue;
   1900         } else if (!iswprint((wint_t)uni)) {
   1901             uni = 0xfffd; // replace non-printable with U+FFFD
   1902         }
   1903 
   1904         w = wcwidth((wchar_t)uni);
   1905         if (w < 0) {
   1906             return TB_ERR;   // shouldn't happen if iswprint
   1907         } else if (w == 0) { // combining character
   1908             if (cellbuf_in_bounds(&global.back, x - 1, y)) {
   1909                 if_err_return(rv, tb_extend_cell(x - 1, y, uni));
   1910             }
   1911         } else {
   1912             if (cellbuf_in_bounds(&global.back, x, y)) {
   1913                 if_err_return(rv, tb_set_cell(x, y, uni, fg, bg));
   1914             }
   1915         }
   1916 
   1917         x += w;
   1918         if (out_w) *out_w += w;
   1919     }
   1920 
   1921     return TB_OK;
   1922 }
   1923 
   1924 int tb_printf(int x, int y, uintattr_t fg, uintattr_t bg, const char *fmt,
   1925     ...) {
   1926     int rv;
   1927     va_list vl;
   1928     va_start(vl, fmt);
   1929     rv = tb_printf_inner(x, y, fg, bg, NULL, fmt, vl);
   1930     va_end(vl);
   1931     return rv;
   1932 }
   1933 
   1934 int tb_printf_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
   1935     const char *fmt, ...) {
   1936     int rv;
   1937     va_list vl;
   1938     va_start(vl, fmt);
   1939     rv = tb_printf_inner(x, y, fg, bg, out_w, fmt, vl);
   1940     va_end(vl);
   1941     return rv;
   1942 }
   1943 
   1944 int tb_send(const char *buf, size_t nbuf) {
   1945     return bytebuf_nputs(&global.out, buf, nbuf);
   1946 }
   1947 
   1948 int tb_sendf(const char *fmt, ...) {
   1949     int rv;
   1950     char buf[TB_OPT_PRINTF_BUF];
   1951     va_list vl;
   1952     va_start(vl, fmt);
   1953     rv = vsnprintf(buf, sizeof(buf), fmt, vl);
   1954     va_end(vl);
   1955     if (rv < 0 || rv >= (int)sizeof(buf)) {
   1956         return TB_ERR;
   1957     }
   1958     return tb_send(buf, (size_t)rv);
   1959 }
   1960 
   1961 int tb_set_func(int fn_type, int (*fn)(struct tb_event *, size_t *)) {
   1962     switch (fn_type) {
   1963         case TB_FUNC_EXTRACT_PRE:
   1964             global.fn_extract_esc_pre = fn;
   1965             return TB_OK;
   1966         case TB_FUNC_EXTRACT_POST:
   1967             global.fn_extract_esc_post = fn;
   1968             return TB_OK;
   1969     }
   1970     return TB_ERR;
   1971 }
   1972 
   1973 struct tb_cell *tb_cell_buffer(void) {
   1974     if (!global.initialized) return NULL;
   1975     return global.back.cells;
   1976 }
   1977 
   1978 int tb_utf8_char_length(char c) {
   1979     return utf8_length[(unsigned char)c];
   1980 }
   1981 
   1982 int tb_utf8_char_to_unicode(uint32_t *out, const char *c) {
   1983     if (*c == '\0') return 0;
   1984 
   1985     int i;
   1986     unsigned char len = tb_utf8_char_length(*c);
   1987     unsigned char mask = utf8_mask[len - 1];
   1988     uint32_t result = c[0] & mask;
   1989     for (i = 1; i < len && c[i] != '\0'; ++i) {
   1990         result <<= 6;
   1991         result |= c[i] & 0x3f;
   1992     }
   1993 
   1994     if (i != len) return i * -1;
   1995 
   1996     *out = result;
   1997     return (int)len;
   1998 }
   1999 
   2000 int tb_utf8_unicode_to_char(char *out, uint32_t c) {
   2001     int len = 0;
   2002     int first;
   2003     int i;
   2004 
   2005     if (c < 0x80) {
   2006         first = 0;
   2007         len = 1;
   2008     } else if (c < 0x800) {
   2009         first = 0xc0;
   2010         len = 2;
   2011     } else if (c < 0x10000) {
   2012         first = 0xe0;
   2013         len = 3;
   2014     } else if (c < 0x200000) {
   2015         first = 0xf0;
   2016         len = 4;
   2017     } else if (c < 0x4000000) {
   2018         first = 0xf8;
   2019         len = 5;
   2020     } else {
   2021         first = 0xfc;
   2022         len = 6;
   2023     }
   2024 
   2025     for (i = len - 1; i > 0; --i) {
   2026         out[i] = (c & 0x3f) | 0x80;
   2027         c >>= 6;
   2028     }
   2029     out[0] = c | first;
   2030     out[len] = '\0';
   2031 
   2032     return len;
   2033 }
   2034 
   2035 int tb_last_errno(void) {
   2036     return global.last_errno;
   2037 }
   2038 
   2039 const char *tb_strerror(int err) {
   2040     switch (err) {
   2041         case TB_OK:
   2042             return "Success";
   2043         case TB_ERR_NEED_MORE:
   2044             return "Not enough input";
   2045         case TB_ERR_INIT_ALREADY:
   2046             return "Termbox initialized already";
   2047         case TB_ERR_MEM:
   2048             return "Out of memory";
   2049         case TB_ERR_NO_EVENT:
   2050             return "No event";
   2051         case TB_ERR_NO_TERM:
   2052             return "No TERM in environment";
   2053         case TB_ERR_NOT_INIT:
   2054             return "Termbox not initialized";
   2055         case TB_ERR_OUT_OF_BOUNDS:
   2056             return "Out of bounds";
   2057         case TB_ERR_UNSUPPORTED_TERM:
   2058             return "Unsupported terminal";
   2059         case TB_ERR_CAP_COLLISION:
   2060             return "Termcaps collision";
   2061         case TB_ERR_RESIZE_SSCANF:
   2062             return "Terminal width/height not received by sscanf() after "
   2063                    "resize";
   2064         case TB_ERR:
   2065         case TB_ERR_INIT_OPEN:
   2066         case TB_ERR_READ:
   2067         case TB_ERR_RESIZE_IOCTL:
   2068         case TB_ERR_RESIZE_PIPE:
   2069         case TB_ERR_RESIZE_SIGACTION:
   2070         case TB_ERR_POLL:
   2071         case TB_ERR_TCGETATTR:
   2072         case TB_ERR_TCSETATTR:
   2073         case TB_ERR_RESIZE_WRITE:
   2074         case TB_ERR_RESIZE_POLL:
   2075         case TB_ERR_RESIZE_READ:
   2076         default:
   2077             strerror_r(global.last_errno, global.errbuf, sizeof(global.errbuf));
   2078             return (const char *)global.errbuf;
   2079     }
   2080 }
   2081 
   2082 int tb_has_truecolor(void) {
   2083 #if TB_OPT_ATTR_W >= 32
   2084     return 1;
   2085 #else
   2086     return 0;
   2087 #endif
   2088 }
   2089 
   2090 int tb_has_egc(void) {
   2091 #ifdef TB_OPT_EGC
   2092     return 1;
   2093 #else
   2094     return 0;
   2095 #endif
   2096 }
   2097 
   2098 int tb_attr_width(void) {
   2099     return TB_OPT_ATTR_W;
   2100 }
   2101 
   2102 const char *tb_version(void) {
   2103     return TB_VERSION_STR;
   2104 }
   2105 
   2106 static int tb_reset(void) {
   2107     int ttyfd_open = global.ttyfd_open;
   2108     memset(&global, 0, sizeof(global));
   2109     global.ttyfd = -1;
   2110     global.rfd = -1;
   2111     global.wfd = -1;
   2112     global.ttyfd_open = ttyfd_open;
   2113     global.resize_pipefd[0] = -1;
   2114     global.resize_pipefd[1] = -1;
   2115     global.width = -1;
   2116     global.height = -1;
   2117     global.cursor_x = -1;
   2118     global.cursor_y = -1;
   2119     global.last_x = -1;
   2120     global.last_y = -1;
   2121     global.fg = TB_DEFAULT;
   2122     global.bg = TB_DEFAULT;
   2123     global.last_fg = ~global.fg;
   2124     global.last_bg = ~global.bg;
   2125     global.input_mode = TB_INPUT_ESC;
   2126     global.output_mode = TB_OUTPUT_NORMAL;
   2127     return TB_OK;
   2128 }
   2129 
   2130 static int init_term_attrs(void) {
   2131     if (global.ttyfd < 0) {
   2132         return TB_OK;
   2133     }
   2134 
   2135     if (tcgetattr(global.ttyfd, &global.orig_tios) != 0) {
   2136         global.last_errno = errno;
   2137         return TB_ERR_TCGETATTR;
   2138     }
   2139 
   2140     struct termios tios;
   2141     memcpy(&tios, &global.orig_tios, sizeof(tios));
   2142     global.has_orig_tios = 1;
   2143 
   2144     cfmakeraw(&tios);
   2145     tios.c_cc[VMIN] = 1;
   2146     tios.c_cc[VTIME] = 0;
   2147 
   2148     if (tcsetattr(global.ttyfd, TCSAFLUSH, &tios) != 0) {
   2149         global.last_errno = errno;
   2150         return TB_ERR_TCSETATTR;
   2151     }
   2152 
   2153     return TB_OK;
   2154 }
   2155 
   2156 int tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
   2157     const char *fmt, va_list vl) {
   2158     int rv;
   2159     char buf[TB_OPT_PRINTF_BUF];
   2160     rv = vsnprintf(buf, sizeof(buf), fmt, vl);
   2161     if (rv < 0 || rv >= (int)sizeof(buf)) {
   2162         return TB_ERR;
   2163     }
   2164     return tb_print_ex(x, y, fg, bg, out_w, buf);
   2165 }
   2166 
   2167 static int init_term_caps(void) {
   2168     if (load_terminfo() == TB_OK) {
   2169         return parse_terminfo_caps();
   2170     }
   2171     return load_builtin_caps();
   2172 }
   2173 
   2174 static int init_cap_trie(void) {
   2175     int rv, i;
   2176 
   2177     // Add caps from terminfo or built-in
   2178     //
   2179     // Collisions are expected as some terminfo entries have dupes. (For
   2180     // example, att605-pc collides on TB_CAP_F4 and TB_CAP_DELETE.) First cap
   2181     // in TB_CAP_* index order will win.
   2182     //
   2183     // TODO: Reorder TB_CAP_* so more critical caps come first.
   2184     for (i = 0; i < TB_CAP__COUNT_KEYS; i++) {
   2185         rv = cap_trie_add(global.caps[i], tb_key_i(i), 0);
   2186         if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv;
   2187     }
   2188 
   2189     // Add built-in mod caps
   2190     //
   2191     // Collisions are OK here as well. This can happen if global.caps collides
   2192     // with builtin_mod_caps. It is desirable to give precedence to global.caps
   2193     // here.
   2194     for (i = 0; builtin_mod_caps[i].cap != NULL; i++) {
   2195         rv = cap_trie_add(builtin_mod_caps[i].cap, builtin_mod_caps[i].key,
   2196             builtin_mod_caps[i].mod);
   2197         if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv;
   2198     }
   2199 
   2200     return TB_OK;
   2201 }
   2202 
   2203 static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod) {
   2204     struct cap_trie_t *next, *node = &global.cap_trie;
   2205     size_t i, j;
   2206 
   2207     if (!cap || strlen(cap) <= 0) return TB_OK; // Nothing to do for empty caps
   2208 
   2209     for (i = 0; cap[i] != '\0'; i++) {
   2210         char c = cap[i];
   2211         next = NULL;
   2212 
   2213         // Check if c is already a child of node
   2214         for (j = 0; j < node->nchildren; j++) {
   2215             if (node->children[j].c == c) {
   2216                 next = &node->children[j];
   2217                 break;
   2218             }
   2219         }
   2220         if (!next) {
   2221             // We need to add a new child to node
   2222             node->nchildren += 1;
   2223             node->children =
   2224                 tb_realloc(node->children, sizeof(*node) * node->nchildren);
   2225             if (!node->children) {
   2226                 return TB_ERR_MEM;
   2227             }
   2228             next = &node->children[node->nchildren - 1];
   2229             memset(next, 0, sizeof(*next));
   2230             next->c = c;
   2231         }
   2232 
   2233         // Continue
   2234         node = next;
   2235     }
   2236 
   2237     if (node->is_leaf) {
   2238         // Already a leaf here
   2239         return TB_ERR_CAP_COLLISION;
   2240     }
   2241 
   2242     node->is_leaf = 1;
   2243     node->key = key;
   2244     node->mod = mod;
   2245     return TB_OK;
   2246 }
   2247 
   2248 static int cap_trie_find(const char *buf, size_t nbuf, struct cap_trie_t **last,
   2249     size_t *depth) {
   2250     struct cap_trie_t *next, *node = &global.cap_trie;
   2251     size_t i, j;
   2252     *last = node;
   2253     *depth = 0;
   2254     for (i = 0; i < nbuf; i++) {
   2255         char c = buf[i];
   2256         next = NULL;
   2257 
   2258         // Find c in node.children
   2259         for (j = 0; j < node->nchildren; j++) {
   2260             if (node->children[j].c == c) {
   2261                 next = &node->children[j];
   2262                 break;
   2263             }
   2264         }
   2265         if (!next) {
   2266             // Not found
   2267             return TB_OK;
   2268         }
   2269         node = next;
   2270         *last = node;
   2271         *depth += 1;
   2272         if (node->is_leaf && node->nchildren < 1) {
   2273             break;
   2274         }
   2275     }
   2276     return TB_OK;
   2277 }
   2278 
   2279 static int cap_trie_deinit(struct cap_trie_t *node) {
   2280     size_t j;
   2281     for (j = 0; j < node->nchildren; j++) {
   2282         cap_trie_deinit(&node->children[j]);
   2283     }
   2284     if (node->children) {
   2285         tb_free(node->children);
   2286     }
   2287     memset(node, 0, sizeof(*node));
   2288     return TB_OK;
   2289 }
   2290 
   2291 static int init_resize_handler(void) {
   2292     if (pipe(global.resize_pipefd) != 0) {
   2293         global.last_errno = errno;
   2294         return TB_ERR_RESIZE_PIPE;
   2295     }
   2296 
   2297     struct sigaction sa;
   2298     memset(&sa, 0, sizeof(sa));
   2299     sa.sa_handler = handle_resize;
   2300     if (sigaction(SIGWINCH, &sa, NULL) != 0) {
   2301         global.last_errno = errno;
   2302         return TB_ERR_RESIZE_SIGACTION;
   2303     }
   2304 
   2305     return TB_OK;
   2306 }
   2307 
   2308 static int send_init_escape_codes(void) {
   2309     int rv;
   2310     if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_CA]));
   2311     if_err_return(rv,
   2312         bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_KEYPAD]));
   2313     if_err_return(rv,
   2314         bytebuf_puts(&global.out, global.caps[TB_CAP_HIDE_CURSOR]));
   2315     return TB_OK;
   2316 }
   2317 
   2318 static int send_clear(void) {
   2319     int rv;
   2320 
   2321     if_err_return(rv, send_attr(global.fg, global.bg));
   2322     if_err_return(rv,
   2323         bytebuf_puts(&global.out, global.caps[TB_CAP_CLEAR_SCREEN]));
   2324 
   2325     if_err_return(rv, send_cursor_if(global.cursor_x, global.cursor_y));
   2326     if_err_return(rv, bytebuf_flush(&global.out, global.wfd));
   2327 
   2328     global.last_x = -1;
   2329     global.last_y = -1;
   2330 
   2331     return TB_OK;
   2332 }
   2333 
   2334 static int update_term_size(void) {
   2335     int rv, ioctl_errno;
   2336 
   2337     if (global.ttyfd < 0) {
   2338         return TB_OK;
   2339     }
   2340 
   2341     struct winsize sz;
   2342     memset(&sz, 0, sizeof(sz));
   2343 
   2344     // Try ioctl TIOCGWINSZ
   2345     if (ioctl(global.ttyfd, TIOCGWINSZ, &sz) == 0) {
   2346         global.width = sz.ws_col;
   2347         global.height = sz.ws_row;
   2348         return TB_OK;
   2349     }
   2350     ioctl_errno = errno;
   2351 
   2352     // Try >cursor(9999,9999), >u7, <u6
   2353     if_ok_return(rv, update_term_size_via_esc());
   2354 
   2355     global.last_errno = ioctl_errno;
   2356     return TB_ERR_RESIZE_IOCTL;
   2357 }
   2358 
   2359 static int update_term_size_via_esc(void) {
   2360 #ifndef TB_RESIZE_FALLBACK_MS
   2361 #define TB_RESIZE_FALLBACK_MS 1000
   2362 #endif
   2363 
   2364     char *move_and_report = "\x1b[9999;9999H\x1b[6n";
   2365     ssize_t write_rv =
   2366         write(global.wfd, move_and_report, strlen(move_and_report));
   2367     if (write_rv != (ssize_t)strlen(move_and_report)) {
   2368         return TB_ERR_RESIZE_WRITE;
   2369     }
   2370 
   2371     fd_set fds;
   2372     FD_ZERO(&fds);
   2373     FD_SET(global.rfd, &fds);
   2374 
   2375     struct timeval timeout;
   2376     timeout.tv_sec = 0;
   2377     timeout.tv_usec = TB_RESIZE_FALLBACK_MS * 1000;
   2378 
   2379     int select_rv = select(global.rfd + 1, &fds, NULL, NULL, &timeout);
   2380 
   2381     if (select_rv != 1) {
   2382         global.last_errno = errno;
   2383         return TB_ERR_RESIZE_POLL;
   2384     }
   2385 
   2386     char buf[TB_OPT_READ_BUF];
   2387     ssize_t read_rv = read(global.rfd, buf, sizeof(buf) - 1);
   2388     if (read_rv < 1) {
   2389         global.last_errno = errno;
   2390         return TB_ERR_RESIZE_READ;
   2391     }
   2392     buf[read_rv] = '\0';
   2393 
   2394     int rw, rh;
   2395     if (sscanf(buf, "\x1b[%d;%dR", &rh, &rw) != 2) {
   2396         return TB_ERR_RESIZE_SSCANF;
   2397     }
   2398 
   2399     global.width = rw;
   2400     global.height = rh;
   2401     return TB_OK;
   2402 }
   2403 
   2404 static int init_cellbuf(void) {
   2405     int rv;
   2406     if_err_return(rv, cellbuf_init(&global.back, global.width, global.height));
   2407     if_err_return(rv, cellbuf_init(&global.front, global.width, global.height));
   2408     if_err_return(rv, cellbuf_clear(&global.back));
   2409     if_err_return(rv, cellbuf_clear(&global.front));
   2410     return TB_OK;
   2411 }
   2412 
   2413 static int tb_deinit(void) {
   2414     if (global.caps[0] != NULL && global.wfd >= 0) {
   2415         bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]);
   2416         bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]);
   2417         bytebuf_puts(&global.out, global.caps[TB_CAP_CLEAR_SCREEN]);
   2418         bytebuf_puts(&global.out, global.caps[TB_CAP_EXIT_CA]);
   2419         bytebuf_puts(&global.out, global.caps[TB_CAP_EXIT_KEYPAD]);
   2420         bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE);
   2421         bytebuf_flush(&global.out, global.wfd);
   2422     }
   2423     if (global.ttyfd >= 0) {
   2424         if (global.has_orig_tios) {
   2425             tcsetattr(global.ttyfd, TCSAFLUSH, &global.orig_tios);
   2426         }
   2427         if (global.ttyfd_open) {
   2428             close(global.ttyfd);
   2429             global.ttyfd_open = 0;
   2430         }
   2431     }
   2432 
   2433     sigaction(SIGWINCH, &(struct sigaction){.sa_handler = SIG_DFL}, NULL);
   2434     if (global.resize_pipefd[0] >= 0) close(global.resize_pipefd[0]);
   2435     if (global.resize_pipefd[1] >= 0) close(global.resize_pipefd[1]);
   2436 
   2437     cellbuf_free(&global.back);
   2438     cellbuf_free(&global.front);
   2439     bytebuf_free(&global.in);
   2440     bytebuf_free(&global.out);
   2441 
   2442     if (global.terminfo) tb_free(global.terminfo);
   2443 
   2444     cap_trie_deinit(&global.cap_trie);
   2445 
   2446     tb_reset();
   2447     return TB_OK;
   2448 }
   2449 
   2450 static int load_terminfo(void) {
   2451     int rv;
   2452     char tmp[TB_PATH_MAX];
   2453 
   2454     // See terminfo(5) "Fetching Compiled Descriptions" for a description of
   2455     // this behavior. Some of these paths are compile-time ncurses options, so
   2456     // best guesses are used here.
   2457     const char *term = getenv("TERM");
   2458     if (!term) {
   2459         return TB_ERR;
   2460     }
   2461 
   2462     // If TERMINFO is set, try that directory and stop
   2463     const char *terminfo = getenv("TERMINFO");
   2464     if (terminfo) {
   2465         return load_terminfo_from_path(terminfo, term);
   2466     }
   2467 
   2468     // Next try ~/.terminfo
   2469     const char *home = getenv("HOME");
   2470     if (home) {
   2471         snprintf_or_return(rv, tmp, sizeof(tmp), "%s/.terminfo", home);
   2472         if_ok_return(rv, load_terminfo_from_path(tmp, term));
   2473     }
   2474 
   2475     // Next try TERMINFO_DIRS
   2476     //
   2477     // Note, empty entries are supposed to be interpretted as the "compiled-in
   2478     // default", which is of course system-dependent. Previously /etc/terminfo
   2479     // was used here. Let's skip empty entries altogether rather than give
   2480     // precedence to a guess, and check common paths after this loop.
   2481     const char *dirs = getenv("TERMINFO_DIRS");
   2482     if (dirs) {
   2483         snprintf_or_return(rv, tmp, sizeof(tmp), "%s", dirs);
   2484         char *dir = strtok(tmp, ":");
   2485         while (dir) {
   2486             const char *cdir = dir;
   2487             if (*cdir != '\0') {
   2488                 if_ok_return(rv, load_terminfo_from_path(cdir, term));
   2489             }
   2490             dir = strtok(NULL, ":");
   2491         }
   2492     }
   2493 
   2494 #ifdef TB_TERMINFO_DIR
   2495     if_ok_return(rv, load_terminfo_from_path(TB_TERMINFO_DIR, term));
   2496 #endif
   2497     if_ok_return(rv, load_terminfo_from_path("/usr/local/etc/terminfo", term));
   2498     if_ok_return(rv,
   2499         load_terminfo_from_path("/usr/local/share/terminfo", term));
   2500     if_ok_return(rv, load_terminfo_from_path("/usr/local/lib/terminfo", term));
   2501     if_ok_return(rv, load_terminfo_from_path("/etc/terminfo", term));
   2502     if_ok_return(rv, load_terminfo_from_path("/usr/share/terminfo", term));
   2503     if_ok_return(rv, load_terminfo_from_path("/usr/lib/terminfo", term));
   2504     if_ok_return(rv, load_terminfo_from_path("/usr/share/lib/terminfo", term));
   2505     if_ok_return(rv, load_terminfo_from_path("/lib/terminfo", term));
   2506 
   2507     return TB_ERR;
   2508 }
   2509 
   2510 static int load_terminfo_from_path(const char *path, const char *term) {
   2511     int rv;
   2512     char tmp[TB_PATH_MAX];
   2513 
   2514     // Look for term at this terminfo location, e.g., <terminfo>/x/xterm
   2515     snprintf_or_return(rv, tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term);
   2516     if_ok_return(rv, read_terminfo_path(tmp));
   2517 
   2518 #ifdef __APPLE__
   2519     // Try the Darwin equivalent path, e.g., <terminfo>/78/xterm
   2520     snprintf_or_return(rv, tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term);
   2521     return read_terminfo_path(tmp);
   2522 #endif
   2523 
   2524     return TB_ERR;
   2525 }
   2526 
   2527 static int read_terminfo_path(const char *path) {
   2528     FILE *fp = fopen(path, "rb");
   2529     if (!fp) {
   2530         return TB_ERR;
   2531     }
   2532 
   2533     struct stat st;
   2534     if (fstat(fileno(fp), &st) != 0) {
   2535         fclose(fp);
   2536         return TB_ERR;
   2537     }
   2538 
   2539     size_t fsize = st.st_size;
   2540     char *data = tb_malloc(fsize);
   2541     if (!data) {
   2542         fclose(fp);
   2543         return TB_ERR;
   2544     }
   2545 
   2546     if (fread(data, 1, fsize, fp) != fsize) {
   2547         fclose(fp);
   2548         tb_free(data);
   2549         return TB_ERR;
   2550     }
   2551 
   2552     global.terminfo = data;
   2553     global.nterminfo = fsize;
   2554 
   2555     fclose(fp);
   2556     return TB_OK;
   2557 }
   2558 
   2559 static int parse_terminfo_caps(void) {
   2560     // See term(5) "LEGACY STORAGE FORMAT" and "EXTENDED STORAGE FORMAT" for a
   2561     // description of this behavior.
   2562 
   2563     // Ensure there's at least a header's worth of data
   2564     if (global.nterminfo < 6) {
   2565         return TB_ERR;
   2566     }
   2567 
   2568     int16_t *header = (int16_t *)global.terminfo;
   2569     // header[0] the magic number (octal 0432 or 01036)
   2570     // header[1] the size, in bytes, of the names section
   2571     // header[2] the number of bytes in the boolean section
   2572     // header[3] the number of short integers in the numbers section
   2573     // header[4] the number of offsets (short integers) in the strings section
   2574     // header[5] the size, in bytes, of the string table
   2575 
   2576     // Legacy ints are 16-bit, extended ints are 32-bit
   2577     const int bytes_per_int = header[0] == 01036 ? 4  // 32-bit
   2578                                                  : 2; // 16-bit
   2579 
   2580     // > Between the boolean section and the number section, a null byte will be
   2581     // > inserted, if necessary, to ensure that the number section begins on an
   2582     // > even byte
   2583     const int align_offset = (header[1] + header[2]) % 2 != 0 ? 1 : 0;
   2584 
   2585     const int pos_str_offsets =
   2586         (6 * sizeof(int16_t)) // header (12 bytes)
   2587         + header[1]           // length of names section
   2588         + header[2]           // length of boolean section
   2589         + align_offset +
   2590         (header[3] * bytes_per_int); // length of numbers section
   2591 
   2592     const int pos_str_table =
   2593         pos_str_offsets +
   2594         (header[4] * sizeof(int16_t)); // length of string offsets table
   2595 
   2596     // Load caps
   2597     int i;
   2598     for (i = 0; i < TB_CAP__COUNT; i++) {
   2599         const char *cap = get_terminfo_string(pos_str_offsets, header[4],
   2600             pos_str_table, header[5], terminfo_cap_indexes[i]);
   2601         if (!cap) {
   2602             // Something is not right
   2603             return TB_ERR;
   2604         }
   2605         global.caps[i] = cap;
   2606     }
   2607 
   2608     return TB_OK;
   2609 }
   2610 
   2611 static int load_builtin_caps(void) {
   2612     int i, j;
   2613     const char *term = getenv("TERM");
   2614 
   2615     if (!term) {
   2616         return TB_ERR_NO_TERM;
   2617     }
   2618 
   2619     // Check for exact TERM match
   2620     for (i = 0; builtin_terms[i].name != NULL; i++) {
   2621         if (strcmp(term, builtin_terms[i].name) == 0) {
   2622             for (j = 0; j < TB_CAP__COUNT; j++) {
   2623                 global.caps[j] = builtin_terms[i].caps[j];
   2624             }
   2625             return TB_OK;
   2626         }
   2627     }
   2628 
   2629     // Check for partial TERM or alias match
   2630     for (i = 0; builtin_terms[i].name != NULL; i++) {
   2631         if (strstr(term, builtin_terms[i].name) != NULL ||
   2632             (*(builtin_terms[i].alias) != '\0' &&
   2633                 strstr(term, builtin_terms[i].alias) != NULL))
   2634         {
   2635             for (j = 0; j < TB_CAP__COUNT; j++) {
   2636                 global.caps[j] = builtin_terms[i].caps[j];
   2637             }
   2638             return TB_OK;
   2639         }
   2640     }
   2641 
   2642     return TB_ERR_UNSUPPORTED_TERM;
   2643 }
   2644 
   2645 static const char *get_terminfo_string(int16_t str_offsets_pos,
   2646     int16_t str_offsets_len, int16_t str_table_pos, int16_t str_table_len,
   2647     int16_t str_index) {
   2648     const int str_byte_index = (int)str_index * (int)sizeof(int16_t);
   2649     if (str_byte_index >= (int)str_offsets_len * (int)sizeof(int16_t)) {
   2650         // An offset beyond the table indicates absent
   2651         // See `convert_strings` in tinfo `read_entry.c`
   2652         return "";
   2653     }
   2654     const int16_t *str_offset =
   2655         (int16_t *)(global.terminfo + (int)str_offsets_pos + str_byte_index);
   2656     if ((char *)str_offset >= global.terminfo + global.nterminfo) {
   2657         // str_offset points beyond end of entry
   2658         // Truncated/corrupt terminfo entry?
   2659         return NULL;
   2660     }
   2661     if (*str_offset < 0 || *str_offset >= str_table_len) {
   2662         // A negative offset indicates absent
   2663         // An offset beyond the table indicates absent
   2664         // See `convert_strings` in tinfo `read_entry.c`
   2665         return "";
   2666     }
   2667     if (((size_t)((int)str_table_pos + (int)*str_offset)) >= global.nterminfo) {
   2668         // string points beyond end of entry
   2669         // Truncated/corrupt terminfo entry?
   2670         return NULL;
   2671     }
   2672     return (
   2673         const char *)(global.terminfo + (int)str_table_pos + (int)*str_offset);
   2674 }
   2675 
   2676 static int wait_event(struct tb_event *event, int timeout) {
   2677     int rv;
   2678     char buf[TB_OPT_READ_BUF];
   2679 
   2680     memset(event, 0, sizeof(*event));
   2681     if_ok_return(rv, extract_event(event));
   2682 
   2683     fd_set fds;
   2684     struct timeval tv;
   2685     tv.tv_sec = timeout / 1000;
   2686     tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
   2687 
   2688     do {
   2689         FD_ZERO(&fds);
   2690         FD_SET(global.rfd, &fds);
   2691         FD_SET(global.resize_pipefd[0], &fds);
   2692 
   2693         int maxfd = global.resize_pipefd[0] > global.rfd
   2694                         ? global.resize_pipefd[0]
   2695                         : global.rfd;
   2696 
   2697         int select_rv =
   2698             select(maxfd + 1, &fds, NULL, NULL, (timeout < 0) ? NULL : &tv);
   2699 
   2700         if (select_rv < 0) {
   2701             // Let EINTR/EAGAIN bubble up
   2702             global.last_errno = errno;
   2703             return TB_ERR_POLL;
   2704         } else if (select_rv == 0) {
   2705             return TB_ERR_NO_EVENT;
   2706         }
   2707 
   2708         int tty_has_events = (FD_ISSET(global.rfd, &fds));
   2709         int resize_has_events = (FD_ISSET(global.resize_pipefd[0], &fds));
   2710 
   2711         if (tty_has_events) {
   2712             ssize_t read_rv = read(global.rfd, buf, sizeof(buf));
   2713             if (read_rv < 0) {
   2714                 global.last_errno = errno;
   2715                 return TB_ERR_READ;
   2716             } else if (read_rv > 0) {
   2717                 bytebuf_nputs(&global.in, buf, read_rv);
   2718             }
   2719         }
   2720 
   2721         if (resize_has_events) {
   2722             int ignore = 0;
   2723             read(global.resize_pipefd[0], &ignore, sizeof(ignore));
   2724             // TODO: Harden against errors encountered mid-resize
   2725             if_err_return(rv, update_term_size());
   2726             if_err_return(rv, resize_cellbufs());
   2727             event->type = TB_EVENT_RESIZE;
   2728             event->w = global.width;
   2729             event->h = global.height;
   2730             return TB_OK;
   2731         }
   2732 
   2733         memset(event, 0, sizeof(*event));
   2734         if_ok_return(rv, extract_event(event));
   2735     } while (timeout == -1);
   2736 
   2737     return rv;
   2738 }
   2739 
   2740 static int extract_event(struct tb_event *event) {
   2741     int rv;
   2742     struct bytebuf_t *in = &global.in;
   2743 
   2744     if (in->len == 0) {
   2745         return TB_ERR;
   2746     }
   2747 
   2748     if (in->buf[0] == '\x1b') {
   2749         // Escape sequence?
   2750         // In TB_INPUT_ESC, skip if the buffer is a single escape char
   2751         if (!((global.input_mode & TB_INPUT_ESC) && in->len == 1)) {
   2752             if_ok_or_need_more_return(rv, extract_esc(event));
   2753         }
   2754 
   2755         // Escape key?
   2756         if (global.input_mode & TB_INPUT_ESC) {
   2757             event->type = TB_EVENT_KEY;
   2758             event->ch = 0;
   2759             event->key = TB_KEY_ESC;
   2760             event->mod = 0;
   2761             bytebuf_shift(in, 1);
   2762             return TB_OK;
   2763         }
   2764 
   2765         // Recurse for alt key
   2766         event->mod |= TB_MOD_ALT;
   2767         bytebuf_shift(in, 1);
   2768         return extract_event(event);
   2769     }
   2770 
   2771     // ASCII control key?
   2772     if ((uint16_t)in->buf[0] < TB_KEY_SPACE || in->buf[0] == TB_KEY_BACKSPACE2)
   2773     {
   2774         event->type = TB_EVENT_KEY;
   2775         event->ch = 0;
   2776         event->key = (uint16_t)in->buf[0];
   2777         event->mod |= TB_MOD_CTRL;
   2778         bytebuf_shift(in, 1);
   2779         return TB_OK;
   2780     }
   2781 
   2782     // UTF-8?
   2783     if (in->len >= (size_t)tb_utf8_char_length(in->buf[0])) {
   2784         event->type = TB_EVENT_KEY;
   2785         tb_utf8_char_to_unicode(&event->ch, in->buf);
   2786         event->key = 0;
   2787         bytebuf_shift(in, tb_utf8_char_length(in->buf[0]));
   2788         return TB_OK;
   2789     }
   2790 
   2791     // Need more input
   2792     return TB_ERR;
   2793 }
   2794 
   2795 static int extract_esc(struct tb_event *event) {
   2796     int rv;
   2797     if_ok_or_need_more_return(rv, extract_esc_user(event, 0));
   2798     if_ok_or_need_more_return(rv, extract_esc_cap(event));
   2799     if_ok_or_need_more_return(rv, extract_esc_mouse(event));
   2800     if_ok_or_need_more_return(rv, extract_esc_user(event, 1));
   2801     return TB_ERR;
   2802 }
   2803 
   2804 static int extract_esc_user(struct tb_event *event, int is_post) {
   2805     int rv;
   2806     size_t consumed = 0;
   2807     struct bytebuf_t *in = &global.in;
   2808     int (*fn)(struct tb_event *, size_t *);
   2809 
   2810     fn = is_post ? global.fn_extract_esc_post : global.fn_extract_esc_pre;
   2811 
   2812     if (!fn) {
   2813         return TB_ERR;
   2814     }
   2815 
   2816     rv = fn(event, &consumed);
   2817     if (rv == TB_OK) {
   2818         bytebuf_shift(in, consumed);
   2819     }
   2820 
   2821     if_ok_or_need_more_return(rv, rv);
   2822     return TB_ERR;
   2823 }
   2824 
   2825 static int extract_esc_cap(struct tb_event *event) {
   2826     int rv;
   2827     struct bytebuf_t *in = &global.in;
   2828     struct cap_trie_t *node;
   2829     size_t depth;
   2830 
   2831     if_err_return(rv, cap_trie_find(in->buf, in->len, &node, &depth));
   2832     if (node->is_leaf) {
   2833         // Found a leaf node
   2834         event->type = TB_EVENT_KEY;
   2835         event->ch = 0;
   2836         event->key = node->key;
   2837         event->mod = node->mod;
   2838         bytebuf_shift(in, depth);
   2839         return TB_OK;
   2840     } else if (node->nchildren > 0 && in->len <= depth) {
   2841         // Found a branch node (not enough input)
   2842         return TB_ERR_NEED_MORE;
   2843     }
   2844 
   2845     return TB_ERR;
   2846 }
   2847 
   2848 static int extract_esc_mouse(struct tb_event *event) {
   2849     struct bytebuf_t *in = &global.in;
   2850 
   2851     enum type { TYPE_VT200 = 0, TYPE_1006, TYPE_1015, TYPE_MAX };
   2852 
   2853     const char *cmp[TYPE_MAX] = {//
   2854         // X10 mouse encoding, the simplest one
   2855         // \x1b [ M Cb Cx Cy
   2856         [TYPE_VT200] = "\x1b[M",
   2857         // xterm 1006 extended mode or urxvt 1015 extended mode
   2858         // xterm: \x1b [ < Cb ; Cx ; Cy (M or m)
   2859         [TYPE_1006] = "\x1b[<",
   2860         // urxvt: \x1b [ Cb ; Cx ; Cy M
   2861         [TYPE_1015] = "\x1b["};
   2862 
   2863     enum type type = 0;
   2864     int ret = TB_ERR;
   2865 
   2866     // Unrolled at compile-time (probably)
   2867     for (; type < TYPE_MAX; type++) {
   2868         size_t size = strlen(cmp[type]);
   2869 
   2870         if (in->len >= size && (strncmp(cmp[type], in->buf, size)) == 0) {
   2871             break;
   2872         }
   2873     }
   2874 
   2875     if (type == TYPE_MAX) {
   2876         ret = TB_ERR; // No match
   2877         return ret;
   2878     }
   2879 
   2880     size_t buf_shift = 0;
   2881 
   2882     switch (type) {
   2883         case TYPE_VT200:
   2884             if (in->len >= 6) {
   2885                 int b = in->buf[3] - 0x20;
   2886                 int fail = 0;
   2887 
   2888                 switch (b & 3) {
   2889                     case 0:
   2890                         event->key = ((b & 64) != 0) ? TB_KEY_MOUSE_WHEEL_UP
   2891                                                      : TB_KEY_MOUSE_LEFT;
   2892                         break;
   2893                     case 1:
   2894                         event->key = ((b & 64) != 0) ? TB_KEY_MOUSE_WHEEL_DOWN
   2895                                                      : TB_KEY_MOUSE_MIDDLE;
   2896                         break;
   2897                     case 2:
   2898                         event->key = TB_KEY_MOUSE_RIGHT;
   2899                         break;
   2900                     case 3:
   2901                         event->key = TB_KEY_MOUSE_RELEASE;
   2902                         break;
   2903                     default:
   2904                         ret = TB_ERR;
   2905                         fail = 1;
   2906                         break;
   2907                 }
   2908 
   2909                 if (!fail) {
   2910                     if ((b & 32) != 0) {
   2911                         event->mod |= TB_MOD_MOTION;
   2912                     }
   2913 
   2914                     // the coord is 1,1 for upper left
   2915                     event->x = ((uint8_t)in->buf[4]) - 0x21;
   2916                     event->y = ((uint8_t)in->buf[5]) - 0x21;
   2917 
   2918                     ret = TB_OK;
   2919                 }
   2920 
   2921                 buf_shift = 6;
   2922             }
   2923             break;
   2924         case TYPE_1006:
   2925             // fallthrough
   2926         case TYPE_1015: {
   2927             size_t index_fail = (size_t)-1;
   2928 
   2929             enum {
   2930                 FIRST_M = 0,
   2931                 FIRST_SEMICOLON,
   2932                 LAST_SEMICOLON,
   2933                 FIRST_LAST_MAX
   2934             };
   2935 
   2936             size_t indices[FIRST_LAST_MAX] = {index_fail, index_fail,
   2937                 index_fail};
   2938             int m_is_capital = 0;
   2939 
   2940             for (size_t i = 0; i < in->len; i++) {
   2941                 if (in->buf[i] == ';') {
   2942                     if (indices[FIRST_SEMICOLON] == index_fail) {
   2943                         indices[FIRST_SEMICOLON] = i;
   2944                     } else {
   2945                         indices[LAST_SEMICOLON] = i;
   2946                     }
   2947                 } else if (indices[FIRST_M] == index_fail) {
   2948                     if (in->buf[i] == 'm' || in->buf[i] == 'M') {
   2949                         m_is_capital = (in->buf[i] == 'M');
   2950                         indices[FIRST_M] = i;
   2951                     }
   2952                 }
   2953             }
   2954 
   2955             if (indices[FIRST_M] == index_fail ||
   2956                 indices[FIRST_SEMICOLON] == index_fail ||
   2957                 indices[LAST_SEMICOLON] == index_fail)
   2958             {
   2959                 ret = TB_ERR;
   2960             } else {
   2961                 int start = (type == TYPE_1015 ? 2 : 3);
   2962 
   2963                 unsigned n1 = strtoul(&in->buf[start], NULL, 10);
   2964                 unsigned n2 =
   2965                     strtoul(&in->buf[indices[FIRST_SEMICOLON] + 1], NULL, 10);
   2966                 unsigned n3 =
   2967                     strtoul(&in->buf[indices[LAST_SEMICOLON] + 1], NULL, 10);
   2968 
   2969                 if (type == TYPE_1015) {
   2970                     n1 -= 0x20;
   2971                 }
   2972 
   2973                 int fail = 0;
   2974 
   2975                 switch (n1 & 3) {
   2976                     case 0:
   2977                         event->key = ((n1 & 64) != 0) ? TB_KEY_MOUSE_WHEEL_UP
   2978                                                       : TB_KEY_MOUSE_LEFT;
   2979                         break;
   2980                     case 1:
   2981                         event->key = ((n1 & 64) != 0) ? TB_KEY_MOUSE_WHEEL_DOWN
   2982                                                       : TB_KEY_MOUSE_MIDDLE;
   2983                         break;
   2984                     case 2:
   2985                         event->key = TB_KEY_MOUSE_RIGHT;
   2986                         break;
   2987                     case 3:
   2988                         event->key = TB_KEY_MOUSE_RELEASE;
   2989                         break;
   2990                     default:
   2991                         ret = TB_ERR;
   2992                         fail = 1;
   2993                         break;
   2994                 }
   2995 
   2996                 buf_shift = in->len;
   2997 
   2998                 if (!fail) {
   2999                     if (!m_is_capital) {
   3000                         // on xterm mouse release is signaled by lowercase m
   3001                         event->key = TB_KEY_MOUSE_RELEASE;
   3002                     }
   3003 
   3004                     if ((n1 & 32) != 0) {
   3005                         event->mod |= TB_MOD_MOTION;
   3006                     }
   3007 
   3008                     event->x = ((uint8_t)n2) - 1;
   3009                     event->y = ((uint8_t)n3) - 1;
   3010 
   3011                     ret = TB_OK;
   3012                 }
   3013             }
   3014         } break;
   3015         case TYPE_MAX:
   3016             ret = TB_ERR;
   3017     }
   3018 
   3019     if (buf_shift > 0) {
   3020         bytebuf_shift(in, buf_shift);
   3021     }
   3022 
   3023     if (ret == TB_OK) {
   3024         event->type = TB_EVENT_MOUSE;
   3025     }
   3026 
   3027     return ret;
   3028 }
   3029 
   3030 static int resize_cellbufs(void) {
   3031     int rv;
   3032     if_err_return(rv,
   3033         cellbuf_resize(&global.back, global.width, global.height));
   3034     if_err_return(rv,
   3035         cellbuf_resize(&global.front, global.width, global.height));
   3036     if_err_return(rv, cellbuf_clear(&global.front));
   3037     if_err_return(rv, send_clear());
   3038     return TB_OK;
   3039 }
   3040 
   3041 static void handle_resize(int sig) {
   3042     int errno_copy = errno;
   3043     write(global.resize_pipefd[1], &sig, sizeof(sig));
   3044     errno = errno_copy;
   3045 }
   3046 
   3047 static int send_attr(uintattr_t fg, uintattr_t bg) {
   3048     int rv;
   3049 
   3050     if (fg == global.last_fg && bg == global.last_bg) {
   3051         return TB_OK;
   3052     }
   3053 
   3054     if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]));
   3055 
   3056     uint32_t cfg, cbg;
   3057     switch (global.output_mode) {
   3058         default:
   3059         case TB_OUTPUT_NORMAL:
   3060             // The minus 1 below is because our colors are 1-indexed starting
   3061             // from black. Black is represented by a 30, 40, 90, or 100 for fg,
   3062             // bg, bright fg, or bright bg respectively. Red is 31, 41, 91,
   3063             // 101, etc.
   3064             cfg = (fg & TB_BRIGHT ? 90 : 30) + (fg & 0x0f) - 1;
   3065             cbg = (bg & TB_BRIGHT ? 100 : 40) + (bg & 0x0f) - 1;
   3066             break;
   3067 
   3068         case TB_OUTPUT_256:
   3069             cfg = fg & 0xff;
   3070             cbg = bg & 0xff;
   3071             if (fg & TB_HI_BLACK) cfg = 0;
   3072             if (bg & TB_HI_BLACK) cbg = 0;
   3073             break;
   3074 
   3075         case TB_OUTPUT_216:
   3076             cfg = fg & 0xff;
   3077             cbg = bg & 0xff;
   3078             if (cfg > 216) cfg = 216;
   3079             if (cbg > 216) cbg = 216;
   3080             cfg += 0x0f;
   3081             cbg += 0x0f;
   3082             break;
   3083 
   3084         case TB_OUTPUT_GRAYSCALE:
   3085             cfg = fg & 0xff;
   3086             cbg = bg & 0xff;
   3087             if (cfg > 24) cfg = 24;
   3088             if (cbg > 24) cbg = 24;
   3089             cfg += 0xe7;
   3090             cbg += 0xe7;
   3091             break;
   3092 
   3093 #if TB_OPT_ATTR_W >= 32
   3094         case TB_OUTPUT_TRUECOLOR:
   3095             cfg = fg & 0xffffff;
   3096             cbg = bg & 0xffffff;
   3097             if (fg & TB_HI_BLACK) cfg = 0;
   3098             if (bg & TB_HI_BLACK) cbg = 0;
   3099             break;
   3100 #endif
   3101     }
   3102 
   3103     if (fg & TB_BOLD)
   3104         if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BOLD]));
   3105 
   3106     if (fg & TB_BLINK)
   3107         if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BLINK]));
   3108 
   3109     if (fg & TB_UNDERLINE)
   3110         if_err_return(rv,
   3111             bytebuf_puts(&global.out, global.caps[TB_CAP_UNDERLINE]));
   3112 
   3113     if (fg & TB_ITALIC)
   3114         if_err_return(rv,
   3115             bytebuf_puts(&global.out, global.caps[TB_CAP_ITALIC]));
   3116 
   3117     if (fg & TB_DIM)
   3118         if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_DIM]));
   3119 
   3120 #if TB_OPT_ATTR_W == 64
   3121     if (fg & TB_STRIKEOUT)
   3122         if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_STRIKEOUT));
   3123 
   3124     if (fg & TB_UNDERLINE_2)
   3125         if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_UNDERLINE_2));
   3126 
   3127     if (fg & TB_OVERLINE)
   3128         if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_OVERLINE));
   3129 
   3130     if (fg & TB_INVISIBLE)
   3131         if_err_return(rv,
   3132             bytebuf_puts(&global.out, global.caps[TB_CAP_INVISIBLE]));
   3133 #endif
   3134 
   3135     if ((fg & TB_REVERSE) || (bg & TB_REVERSE))
   3136         if_err_return(rv,
   3137             bytebuf_puts(&global.out, global.caps[TB_CAP_REVERSE]));
   3138 
   3139     int fg_is_default = (fg & 0xff) == 0;
   3140     int bg_is_default = (bg & 0xff) == 0;
   3141     if (global.output_mode == TB_OUTPUT_256) {
   3142         if (fg & TB_HI_BLACK) fg_is_default = 0;
   3143         if (bg & TB_HI_BLACK) bg_is_default = 0;
   3144     }
   3145 #if TB_OPT_ATTR_W >= 32
   3146     if (global.output_mode == TB_OUTPUT_TRUECOLOR) {
   3147         fg_is_default = ((fg & 0xffffff) == 0) && ((fg & TB_HI_BLACK) == 0);
   3148         bg_is_default = ((bg & 0xffffff) == 0) && ((bg & TB_HI_BLACK) == 0);
   3149     }
   3150 #endif
   3151 
   3152     if_err_return(rv, send_sgr(cfg, cbg, fg_is_default, bg_is_default));
   3153 
   3154     global.last_fg = fg;
   3155     global.last_bg = bg;
   3156 
   3157     return TB_OK;
   3158 }
   3159 
   3160 static int send_sgr(uint32_t cfg, uint32_t cbg, int fg_is_default,
   3161     int bg_is_default) {
   3162     int rv;
   3163     char nbuf[32];
   3164 
   3165     if (fg_is_default && bg_is_default) {
   3166         return TB_OK;
   3167     }
   3168 
   3169     switch (global.output_mode) {
   3170         default:
   3171         case TB_OUTPUT_NORMAL:
   3172             send_literal(rv, "\x1b[");
   3173             if (!fg_is_default) {
   3174                 send_num(rv, nbuf, cfg);
   3175                 if (!bg_is_default) {
   3176                     send_literal(rv, ";");
   3177                 }
   3178             }
   3179             if (!bg_is_default) {
   3180                 send_num(rv, nbuf, cbg);
   3181             }
   3182             send_literal(rv, "m");
   3183             break;
   3184 
   3185         case TB_OUTPUT_256:
   3186         case TB_OUTPUT_216:
   3187         case TB_OUTPUT_GRAYSCALE:
   3188             send_literal(rv, "\x1b[");
   3189             if (!fg_is_default) {
   3190                 send_literal(rv, "38;5;");
   3191                 send_num(rv, nbuf, cfg);
   3192                 if (!bg_is_default) {
   3193                     send_literal(rv, ";");
   3194                 }
   3195             }
   3196             if (!bg_is_default) {
   3197                 send_literal(rv, "48;5;");
   3198                 send_num(rv, nbuf, cbg);
   3199             }
   3200             send_literal(rv, "m");
   3201             break;
   3202 
   3203 #if TB_OPT_ATTR_W >= 32
   3204         case TB_OUTPUT_TRUECOLOR:
   3205             send_literal(rv, "\x1b[");
   3206             if (!fg_is_default) {
   3207                 send_literal(rv, "38;2;");
   3208                 send_num(rv, nbuf, (cfg >> 16) & 0xff);
   3209                 send_literal(rv, ";");
   3210                 send_num(rv, nbuf, (cfg >> 8) & 0xff);
   3211                 send_literal(rv, ";");
   3212                 send_num(rv, nbuf, cfg & 0xff);
   3213                 if (!bg_is_default) {
   3214                     send_literal(rv, ";");
   3215                 }
   3216             }
   3217             if (!bg_is_default) {
   3218                 send_literal(rv, "48;2;");
   3219                 send_num(rv, nbuf, (cbg >> 16) & 0xff);
   3220                 send_literal(rv, ";");
   3221                 send_num(rv, nbuf, (cbg >> 8) & 0xff);
   3222                 send_literal(rv, ";");
   3223                 send_num(rv, nbuf, cbg & 0xff);
   3224             }
   3225             send_literal(rv, "m");
   3226             break;
   3227 #endif
   3228     }
   3229     return TB_OK;
   3230 }
   3231 
   3232 static int send_cursor_if(int x, int y) {
   3233     int rv;
   3234     char nbuf[32];
   3235     if (x < 0 || y < 0) {
   3236         return TB_OK;
   3237     }
   3238     send_literal(rv, "\x1b[");
   3239     send_num(rv, nbuf, y + 1);
   3240     send_literal(rv, ";");
   3241     send_num(rv, nbuf, x + 1);
   3242     send_literal(rv, "H");
   3243     return TB_OK;
   3244 }
   3245 
   3246 static int send_char(int x, int y, uint32_t ch) {
   3247     return send_cluster(x, y, &ch, 1);
   3248 }
   3249 
   3250 static int send_cluster(int x, int y, uint32_t *ch, size_t nch) {
   3251     int rv;
   3252     char chu8[8];
   3253 
   3254     if (global.last_x != x - 1 || global.last_y != y) {
   3255         if_err_return(rv, send_cursor_if(x, y));
   3256     }
   3257     global.last_x = x;
   3258     global.last_y = y;
   3259 
   3260     int i;
   3261     for (i = 0; i < (int)nch; i++) {
   3262         uint32_t ch32 = *(ch + i);
   3263         if (!iswprint((wint_t)ch32)) {
   3264             ch32 = 0xfffd; // replace non-printable codepoints with U+FFFD
   3265         }
   3266         int chu8_len = tb_utf8_unicode_to_char(chu8, ch32);
   3267         if_err_return(rv, bytebuf_nputs(&global.out, chu8, (size_t)chu8_len));
   3268     }
   3269 
   3270     return TB_OK;
   3271 }
   3272 
   3273 static int convert_num(uint32_t num, char *buf) {
   3274     int i, l = 0;
   3275     char ch;
   3276     do {
   3277         buf[l++] = (char)('0' + (num % 10));
   3278         num /= 10;
   3279     } while (num);
   3280     for (i = 0; i < l / 2; i++) {
   3281         ch = buf[i];
   3282         buf[i] = buf[l - 1 - i];
   3283         buf[l - 1 - i] = ch;
   3284     }
   3285     return l;
   3286 }
   3287 
   3288 static int cell_cmp(struct tb_cell *a, struct tb_cell *b) {
   3289     if (a->ch != b->ch || a->fg != b->fg || a->bg != b->bg) {
   3290         return 1;
   3291     }
   3292 #ifdef TB_OPT_EGC
   3293     if (a->nech != b->nech) {
   3294         return 1;
   3295     } else if (a->nech > 0) { // a->nech == b->nech
   3296         return memcmp(a->ech, b->ech, a->nech);
   3297     }
   3298 #endif
   3299     return 0;
   3300 }
   3301 
   3302 static int cell_copy(struct tb_cell *dst, struct tb_cell *src) {
   3303 #ifdef TB_OPT_EGC
   3304     if (src->nech > 0) {
   3305         return cell_set(dst, src->ech, src->nech, src->fg, src->bg);
   3306     }
   3307 #endif
   3308     return cell_set(dst, &src->ch, 1, src->fg, src->bg);
   3309 }
   3310 
   3311 static int cell_set(struct tb_cell *cell, uint32_t *ch, size_t nch,
   3312     uintattr_t fg, uintattr_t bg) {
   3313     cell->ch = ch ? *ch : 0;
   3314     cell->fg = fg;
   3315     cell->bg = bg;
   3316 #ifdef TB_OPT_EGC
   3317     if (nch <= 1) {
   3318         cell->nech = 0;
   3319     } else {
   3320         int rv;
   3321         if_err_return(rv, cell_reserve_ech(cell, nch + 1));
   3322         memcpy(cell->ech, ch, sizeof(ch) * nch);
   3323         cell->ech[nch] = '\0';
   3324         cell->nech = nch;
   3325     }
   3326 #else
   3327     (void)nch;
   3328     (void)cell_reserve_ech;
   3329 #endif
   3330     return TB_OK;
   3331 }
   3332 
   3333 static int cell_reserve_ech(struct tb_cell *cell, size_t n) {
   3334 #ifdef TB_OPT_EGC
   3335     if (cell->cech >= n) {
   3336         return TB_OK;
   3337     }
   3338     if (!(cell->ech = tb_realloc(cell->ech, n * sizeof(cell->ch)))) {
   3339         return TB_ERR_MEM;
   3340     }
   3341     cell->cech = n;
   3342     return TB_OK;
   3343 #else
   3344     (void)cell;
   3345     (void)n;
   3346     return TB_ERR;
   3347 #endif
   3348 }
   3349 
   3350 static int cell_free(struct tb_cell *cell) {
   3351 #ifdef TB_OPT_EGC
   3352     if (cell->ech) {
   3353         tb_free(cell->ech);
   3354     }
   3355 #endif
   3356     memset(cell, 0, sizeof(*cell));
   3357     return TB_OK;
   3358 }
   3359 
   3360 static int cellbuf_init(struct cellbuf_t *c, int w, int h) {
   3361     c->cells = tb_malloc(sizeof(struct tb_cell) * w * h);
   3362     if (!c->cells) {
   3363         return TB_ERR_MEM;
   3364     }
   3365     memset(c->cells, 0, sizeof(struct tb_cell) * w * h);
   3366     c->width = w;
   3367     c->height = h;
   3368     return TB_OK;
   3369 }
   3370 
   3371 static int cellbuf_free(struct cellbuf_t *c) {
   3372     if (c->cells) {
   3373         int i;
   3374         for (i = 0; i < c->width * c->height; i++) {
   3375             cell_free(&c->cells[i]);
   3376         }
   3377         tb_free(c->cells);
   3378     }
   3379     memset(c, 0, sizeof(*c));
   3380     return TB_OK;
   3381 }
   3382 
   3383 static int cellbuf_clear(struct cellbuf_t *c) {
   3384     int rv, i;
   3385     uint32_t space = (uint32_t)' ';
   3386     for (i = 0; i < c->width * c->height; i++) {
   3387         if_err_return(rv,
   3388             cell_set(&c->cells[i], &space, 1, global.fg, global.bg));
   3389     }
   3390     return TB_OK;
   3391 }
   3392 
   3393 static int cellbuf_get(struct cellbuf_t *c, int x, int y,
   3394     struct tb_cell **out) {
   3395     if (!cellbuf_in_bounds(c, x, y)) {
   3396         *out = NULL;
   3397         return TB_ERR_OUT_OF_BOUNDS;
   3398     }
   3399     *out = &c->cells[(y * c->width) + x];
   3400     return TB_OK;
   3401 }
   3402 
   3403 static int cellbuf_in_bounds(struct cellbuf_t *c, int x, int y) {
   3404     if (x < 0 || x >= c->width || y < 0 || y >= c->height) {
   3405         return 0;
   3406     }
   3407     return 1;
   3408 }
   3409 
   3410 static int cellbuf_resize(struct cellbuf_t *c, int w, int h) {
   3411     int rv;
   3412 
   3413     int ow = c->width;
   3414     int oh = c->height;
   3415 
   3416     if (ow == w && oh == h) {
   3417         return TB_OK;
   3418     }
   3419 
   3420     w = w < 1 ? 1 : w;
   3421     h = h < 1 ? 1 : h;
   3422 
   3423     int minw = (w < ow) ? w : ow;
   3424     int minh = (h < oh) ? h : oh;
   3425 
   3426     struct tb_cell *prev = c->cells;
   3427 
   3428     if_err_return(rv, cellbuf_init(c, w, h));
   3429     if_err_return(rv, cellbuf_clear(c));
   3430 
   3431     int x, y;
   3432     for (x = 0; x < minw; x++) {
   3433         for (y = 0; y < minh; y++) {
   3434             struct tb_cell *src, *dst;
   3435             src = &prev[(y * ow) + x];
   3436             if_err_return(rv, cellbuf_get(c, x, y, &dst));
   3437             if_err_return(rv, cell_copy(dst, src));
   3438         }
   3439     }
   3440 
   3441     tb_free(prev);
   3442 
   3443     return TB_OK;
   3444 }
   3445 
   3446 static int bytebuf_puts(struct bytebuf_t *b, const char *str) {
   3447     if (!str || strlen(str) <= 0) return TB_OK; // Nothing to do for empty caps
   3448     return bytebuf_nputs(b, str, (size_t)strlen(str));
   3449 }
   3450 
   3451 static int bytebuf_nputs(struct bytebuf_t *b, const char *str, size_t nstr) {
   3452     int rv;
   3453     if_err_return(rv, bytebuf_reserve(b, b->len + nstr + 1));
   3454     memcpy(b->buf + b->len, str, nstr);
   3455     b->len += nstr;
   3456     b->buf[b->len] = '\0';
   3457     return TB_OK;
   3458 }
   3459 
   3460 static int bytebuf_shift(struct bytebuf_t *b, size_t n) {
   3461     if (n > b->len) {
   3462         n = b->len;
   3463     }
   3464     size_t nmove = b->len - n;
   3465     memmove(b->buf, b->buf + n, nmove);
   3466     b->len -= n;
   3467     return TB_OK;
   3468 }
   3469 
   3470 static int bytebuf_flush(struct bytebuf_t *b, int fd) {
   3471     if (b->len <= 0) {
   3472         return TB_OK;
   3473     }
   3474     ssize_t write_rv = write(fd, b->buf, b->len);
   3475     if (write_rv < 0 || (size_t)write_rv != b->len) {
   3476         // Note, errno will be 0 on partial write
   3477         global.last_errno = errno;
   3478         return TB_ERR;
   3479     }
   3480     b->len = 0;
   3481     return TB_OK;
   3482 }
   3483 
   3484 static int bytebuf_reserve(struct bytebuf_t *b, size_t sz) {
   3485     if (b->cap >= sz) {
   3486         return TB_OK;
   3487     }
   3488     size_t newcap = b->cap > 0 ? b->cap : 1;
   3489     while (newcap < sz) {
   3490         newcap *= 2;
   3491     }
   3492     char *newbuf;
   3493     if (b->buf) {
   3494         newbuf = tb_realloc(b->buf, newcap);
   3495     } else {
   3496         newbuf = tb_malloc(newcap);
   3497     }
   3498     if (!newbuf) {
   3499         return TB_ERR_MEM;
   3500     }
   3501     b->buf = newbuf;
   3502     b->cap = newcap;
   3503     return TB_OK;
   3504 }
   3505 
   3506 static int bytebuf_free(struct bytebuf_t *b) {
   3507     if (b->buf) {
   3508         tb_free(b->buf);
   3509     }
   3510     memset(b, 0, sizeof(*b));
   3511     return TB_OK;
   3512 }
   3513 
   3514 #endif // TB_IMPL