poe

Уређивач .po фајлова
git clone https://git.sr.ht/~strahinja/poe
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

termbox.h (123595B)


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