termbox2.h (188802B)


      1 /*
      2 MIT License
      3 
      4 Copyright (c) 2015-2026 Adam Saponara <as@php.net>
      5               2010-2020 nsf <no.smile.face@gmail.com>
      6 
      7 Permission is hereby granted, free of charge, to any person obtaining a copy
      8 of this software and associated documentation files (the "Software"), to deal
      9 in the Software without restriction, including without limitation the rights
     10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     11 copies of the Software, and to permit persons to whom the Software is
     12 furnished to do so, subject to the following conditions:
     13 
     14 The above copyright notice and this permission notice shall be included in all
     15 copies or substantial portions of the Software.
     16 
     17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     23 SOFTWARE.
     24 */
     25 #ifndef TERMBOX_H_INCL
     26 #define TERMBOX_H_INCL
     27 
     28 #ifndef _XOPEN_SOURCE
     29 #define _XOPEN_SOURCE
     30 #endif
     31 
     32 #ifndef _DEFAULT_SOURCE
     33 #define _DEFAULT_SOURCE
     34 #endif
     35 
     36 #include <errno.h>
     37 #include <fcntl.h>
     38 #include <limits.h>
     39 #include <signal.h>
     40 #include <stdarg.h>
     41 #include <stdint.h>
     42 #include <stdio.h>
     43 #include <stdlib.h>
     44 #include <string.h>
     45 #include <sys/ioctl.h>
     46 #include <sys/select.h>
     47 #include <sys/stat.h>
     48 #include <sys/time.h>
     49 #include <sys/types.h>
     50 #include <termios.h>
     51 #include <unistd.h>
     52 
     53 #ifdef PATH_MAX
     54 #define TB_PATH_MAX PATH_MAX
     55 #else
     56 #define TB_PATH_MAX 4096
     57 #endif
     58 
     59 #ifdef __cplusplus
     60 extern "C" {
     61 #endif
     62 
     63 // __ffi_start
     64 
     65 #define TB_VERSION_STR "2.7.0-dev"
     66 
     67 /* The following compile-time options are supported:
     68  *
     69  *     `TB_OPT_ATTR_W`: Integer width of `fg` and `bg` attributes. Valid values
     70  *                      (assuming system support) are 16, 32, and 64. (See
     71  *                      `uintattr_t`). 32 or 64 enables output mode
     72  *                      `TB_OUTPUT_TRUECOLOR`. 64 enables additional style
     73  *                      attributes. (See `tb_set_output_mode`.) Larger values
     74  *                      consume more memory in exchange for more features.
     75  *                      Defaults to 16.
     76  *
     77  *        `TB_OPT_EGC`: If set, enable extended grapheme cluster support
     78  *                      (`tb_extend_cell`, `tb_set_cell_ex`). Consumes more
     79  *                      memory. Defaults off.
     80  *
     81  * `TB_OPT_PRINTF_BUF`: Write buffer size for printf operations. Represents the
     82  *                      largest string that can be sent in one call to
     83  *                      `tb_print*` and `tb_send*` functions. Defaults to 4096.
     84  *
     85  *   `TB_OPT_READ_BUF`: Read buffer size for tty reads. Defaults to 64.
     86  *
     87  * `TB_OPT_LIBC_WCHAR`: If set, use libc's `wcwidth(3)`, `iswprint(3)`, etc
     88  *                      instead of the built-in Unicode-aware versions. Note,
     89  *                      libc's are locale-dependent and the caller must
     90  *                      `setlocale(3)` `LC_CTYPE` to UTF-8. Defaults to
     91  *                      built-in.
     92  *
     93  *  `TB_OPT_TRUECOLOR`: Deprecated. Sets `TB_OPT_ATTR_W` to 32 if not already
     94  *                      set.
     95  */
     96 
     97 #if defined(TB_LIB_OPTS) || 0 // __tb_lib_opts
     98 /* Ensure consistent compile-time options when using as a shared library */
     99 #undef TB_OPT_ATTR_W
    100 #undef TB_OPT_EGC
    101 #undef TB_OPT_PRINTF_BUF
    102 #undef TB_OPT_READ_BUF
    103 #undef TB_OPT_LIBC_WCHAR
    104 #define TB_OPT_ATTR_W 64
    105 #define TB_OPT_EGC
    106 #endif
    107 
    108 /* Ensure sane `TB_OPT_ATTR_W` (16, 32, or 64) */
    109 #if defined TB_OPT_ATTR_W && TB_OPT_ATTR_W == 16
    110 #elif defined TB_OPT_ATTR_W && TB_OPT_ATTR_W == 32
    111 #elif defined TB_OPT_ATTR_W && TB_OPT_ATTR_W == 64
    112 #else
    113 #undef TB_OPT_ATTR_W
    114 #if defined TB_OPT_TRUECOLOR // Deprecated. Back-compat for old flag.
    115 #define TB_OPT_ATTR_W 32
    116 #else
    117 #define TB_OPT_ATTR_W 16
    118 #endif
    119 #endif
    120 
    121 /* Include wchar if opting libc */
    122 #ifdef TB_OPT_LIBC_WCHAR
    123 #include <wchar.h>
    124 #include <wctype.h>
    125 #endif
    126 
    127 /* ASCII key constants (`tb_event.key`) */
    128 #define TB_KEY_CTRL_TILDE       0x00
    129 #define TB_KEY_CTRL_2           0x00 // clash with `CTRL_TILDE`
    130 #define TB_KEY_CTRL_A           0x01
    131 #define TB_KEY_CTRL_B           0x02
    132 #define TB_KEY_CTRL_C           0x03
    133 #define TB_KEY_CTRL_D           0x04
    134 #define TB_KEY_CTRL_E           0x05
    135 #define TB_KEY_CTRL_F           0x06
    136 #define TB_KEY_CTRL_G           0x07
    137 #define TB_KEY_BACKSPACE        0x08
    138 #define TB_KEY_CTRL_H           0x08 // clash with `CTRL_BACKSPACE`
    139 #define TB_KEY_TAB              0x09
    140 #define TB_KEY_CTRL_I           0x09 // clash with `TAB`
    141 #define TB_KEY_CTRL_J           0x0a
    142 #define TB_KEY_CTRL_K           0x0b
    143 #define TB_KEY_CTRL_L           0x0c
    144 #define TB_KEY_ENTER            0x0d
    145 #define TB_KEY_CTRL_M           0x0d // clash with `ENTER`
    146 #define TB_KEY_CTRL_N           0x0e
    147 #define TB_KEY_CTRL_O           0x0f
    148 #define TB_KEY_CTRL_P           0x10
    149 #define TB_KEY_CTRL_Q           0x11
    150 #define TB_KEY_CTRL_R           0x12
    151 #define TB_KEY_CTRL_S           0x13
    152 #define TB_KEY_CTRL_T           0x14
    153 #define TB_KEY_CTRL_U           0x15
    154 #define TB_KEY_CTRL_V           0x16
    155 #define TB_KEY_CTRL_W           0x17
    156 #define TB_KEY_CTRL_X           0x18
    157 #define TB_KEY_CTRL_Y           0x19
    158 #define TB_KEY_CTRL_Z           0x1a
    159 #define TB_KEY_ESC              0x1b
    160 #define TB_KEY_CTRL_LSQ_BRACKET 0x1b // clash with 'ESC'
    161 #define TB_KEY_CTRL_3           0x1b // clash with 'ESC'
    162 #define TB_KEY_CTRL_4           0x1c
    163 #define TB_KEY_CTRL_BACKSLASH   0x1c // clash with 'CTRL_4'
    164 #define TB_KEY_CTRL_5           0x1d
    165 #define TB_KEY_CTRL_RSQ_BRACKET 0x1d // clash with 'CTRL_5'
    166 #define TB_KEY_CTRL_6           0x1e
    167 #define TB_KEY_CTRL_7           0x1f
    168 #define TB_KEY_CTRL_SLASH       0x1f // clash with 'CTRL_7'
    169 #define TB_KEY_CTRL_UNDERSCORE  0x1f // clash with 'CTRL_7'
    170 #define TB_KEY_SPACE            0x20
    171 #define TB_KEY_BACKSPACE2       0x7f
    172 #define TB_KEY_CTRL_8           0x7f // clash with 'BACKSPACE2'
    173 
    174 #define tb_key_i(i)             0xffff - (i)
    175 /* Terminal-dependent key constants (`tb_event.key`) and terminfo caps */
    176 /* BEGIN codegen h */
    177 /* Produced by ./codegen.sh on Tue, 03 Sep 2024 04:17:47 +0000 */
    178 #define TB_KEY_F1               (0xffff - 0)
    179 #define TB_KEY_F2               (0xffff - 1)
    180 #define TB_KEY_F3               (0xffff - 2)
    181 #define TB_KEY_F4               (0xffff - 3)
    182 #define TB_KEY_F5               (0xffff - 4)
    183 #define TB_KEY_F6               (0xffff - 5)
    184 #define TB_KEY_F7               (0xffff - 6)
    185 #define TB_KEY_F8               (0xffff - 7)
    186 #define TB_KEY_F9               (0xffff - 8)
    187 #define TB_KEY_F10              (0xffff - 9)
    188 #define TB_KEY_F11              (0xffff - 10)
    189 #define TB_KEY_F12              (0xffff - 11)
    190 #define TB_KEY_INSERT           (0xffff - 12)
    191 #define TB_KEY_DELETE           (0xffff - 13)
    192 #define TB_KEY_HOME             (0xffff - 14)
    193 #define TB_KEY_END              (0xffff - 15)
    194 #define TB_KEY_PGUP             (0xffff - 16)
    195 #define TB_KEY_PGDN             (0xffff - 17)
    196 #define TB_KEY_ARROW_UP         (0xffff - 18)
    197 #define TB_KEY_ARROW_DOWN       (0xffff - 19)
    198 #define TB_KEY_ARROW_LEFT       (0xffff - 20)
    199 #define TB_KEY_ARROW_RIGHT      (0xffff - 21)
    200 #define TB_KEY_BACK_TAB         (0xffff - 22)
    201 #define TB_KEY_MOUSE_LEFT       (0xffff - 23)
    202 #define TB_KEY_MOUSE_RIGHT      (0xffff - 24)
    203 #define TB_KEY_MOUSE_MIDDLE     (0xffff - 25)
    204 #define TB_KEY_MOUSE_RELEASE    (0xffff - 26)
    205 #define TB_KEY_MOUSE_WHEEL_UP   (0xffff - 27)
    206 #define TB_KEY_MOUSE_WHEEL_DOWN (0xffff - 28)
    207 
    208 #define TB_CAP_F1               0
    209 #define TB_CAP_F2               1
    210 #define TB_CAP_F3               2
    211 #define TB_CAP_F4               3
    212 #define TB_CAP_F5               4
    213 #define TB_CAP_F6               5
    214 #define TB_CAP_F7               6
    215 #define TB_CAP_F8               7
    216 #define TB_CAP_F9               8
    217 #define TB_CAP_F10              9
    218 #define TB_CAP_F11              10
    219 #define TB_CAP_F12              11
    220 #define TB_CAP_INSERT           12
    221 #define TB_CAP_DELETE           13
    222 #define TB_CAP_HOME             14
    223 #define TB_CAP_END              15
    224 #define TB_CAP_PGUP             16
    225 #define TB_CAP_PGDN             17
    226 #define TB_CAP_ARROW_UP         18
    227 #define TB_CAP_ARROW_DOWN       19
    228 #define TB_CAP_ARROW_LEFT       20
    229 #define TB_CAP_ARROW_RIGHT      21
    230 #define TB_CAP_BACK_TAB         22
    231 #define TB_CAP__COUNT_KEYS      23
    232 #define TB_CAP_ENTER_CA         23
    233 #define TB_CAP_EXIT_CA          24
    234 #define TB_CAP_SHOW_CURSOR      25
    235 #define TB_CAP_HIDE_CURSOR      26
    236 #define TB_CAP_CLEAR_SCREEN     27
    237 #define TB_CAP_SGR0             28
    238 #define TB_CAP_UNDERLINE        29
    239 #define TB_CAP_BOLD             30
    240 #define TB_CAP_BLINK            31
    241 #define TB_CAP_ITALIC           32
    242 #define TB_CAP_REVERSE          33
    243 #define TB_CAP_ENTER_KEYPAD     34
    244 #define TB_CAP_EXIT_KEYPAD      35
    245 #define TB_CAP_DIM              36
    246 #define TB_CAP_INVISIBLE        37
    247 #define TB_CAP__COUNT           38
    248 /* END codegen h */
    249 
    250 /* Some hard-coded caps */
    251 #define TB_HARDCAP_ENTER_MOUSE  "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h"
    252 #define TB_HARDCAP_EXIT_MOUSE   "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l"
    253 #define TB_HARDCAP_STRIKEOUT    "\x1b[9m"
    254 #define TB_HARDCAP_UNDERLINE_2  "\x1b[21m"
    255 #define TB_HARDCAP_OVERLINE     "\x1b[53m"
    256 
    257 /* Colors (numeric) and attributes (bitwise) (`tb_cell.fg`, `tb_cell.bg`) */
    258 #define TB_DEFAULT              0x0000
    259 #define TB_BLACK                0x0001
    260 #define TB_RED                  0x0002
    261 #define TB_GREEN                0x0003
    262 #define TB_YELLOW               0x0004
    263 #define TB_BLUE                 0x0005
    264 #define TB_MAGENTA              0x0006
    265 #define TB_CYAN                 0x0007
    266 #define TB_WHITE                0x0008
    267 
    268 #if TB_OPT_ATTR_W == 16
    269 #define TB_BOLD      0x0100
    270 #define TB_UNDERLINE 0x0200
    271 #define TB_REVERSE   0x0400
    272 #define TB_ITALIC    0x0800
    273 #define TB_BLINK     0x1000
    274 #define TB_HI_BLACK  0x2000
    275 #define TB_BRIGHT    0x4000
    276 #define TB_DIM       0x8000
    277 #define TB_256_BLACK TB_HI_BLACK // `TB_256_BLACK` is deprecated
    278 #else
    279 // `TB_OPT_ATTR_W` is 32 or 64
    280 #define TB_BOLD                0x01000000
    281 #define TB_UNDERLINE           0x02000000
    282 #define TB_REVERSE             0x04000000
    283 #define TB_ITALIC              0x08000000
    284 #define TB_BLINK               0x10000000
    285 #define TB_HI_BLACK            0x20000000
    286 #define TB_BRIGHT              0x40000000
    287 #define TB_DIM                 0x80000000
    288 #define TB_TRUECOLOR_BOLD      TB_BOLD // `TB_TRUECOLOR_*` is deprecated
    289 #define TB_TRUECOLOR_UNDERLINE TB_UNDERLINE
    290 #define TB_TRUECOLOR_REVERSE   TB_REVERSE
    291 #define TB_TRUECOLOR_ITALIC    TB_ITALIC
    292 #define TB_TRUECOLOR_BLINK     TB_BLINK
    293 #define TB_TRUECOLOR_BLACK     TB_HI_BLACK
    294 #endif
    295 
    296 #if TB_OPT_ATTR_W == 64
    297 #define TB_STRIKEOUT   0x0000000100000000
    298 #define TB_UNDERLINE_2 0x0000000200000000
    299 #define TB_OVERLINE    0x0000000400000000
    300 #define TB_INVISIBLE   0x0000000800000000
    301 #endif
    302 
    303 /* Event types (`tb_event.type`) */
    304 #define TB_EVENT_KEY        1
    305 #define TB_EVENT_RESIZE     2
    306 #define TB_EVENT_MOUSE      3
    307 
    308 /* Key modifiers (bitwise) (`tb_event.mod`) */
    309 #define TB_MOD_ALT          1
    310 #define TB_MOD_CTRL         2
    311 #define TB_MOD_SHIFT        4
    312 #define TB_MOD_MOTION       8
    313 
    314 /* Input modes (bitwise) (`tb_set_input_mode`) */
    315 #define TB_INPUT_CURRENT    0
    316 #define TB_INPUT_ESC        1
    317 #define TB_INPUT_ALT        2
    318 #define TB_INPUT_MOUSE      4
    319 
    320 /* Output modes (`tb_set_output_mode`) */
    321 #define TB_OUTPUT_CURRENT   0
    322 #define TB_OUTPUT_NORMAL    1
    323 #define TB_OUTPUT_256       2
    324 #define TB_OUTPUT_216       3
    325 #define TB_OUTPUT_GRAYSCALE 4
    326 #if TB_OPT_ATTR_W >= 32
    327 #define TB_OUTPUT_TRUECOLOR 5
    328 #endif
    329 
    330 /* Common function return values unless otherwise noted.
    331  *
    332  * Library behavior is undefined after receiving `TB_ERR_MEM`. Callers may
    333  * attempt reinitializing by freeing memory, invoking `tb_shutdown`, then
    334  * `tb_init`.
    335  */
    336 #define TB_OK                   0
    337 #define TB_ERR                  -1
    338 #define TB_ERR_NEED_MORE        -2
    339 #define TB_ERR_INIT_ALREADY     -3
    340 #define TB_ERR_INIT_OPEN        -4
    341 #define TB_ERR_MEM              -5
    342 #define TB_ERR_NO_EVENT         -6
    343 #define TB_ERR_NO_TERM          -7
    344 #define TB_ERR_NOT_INIT         -8
    345 #define TB_ERR_OUT_OF_BOUNDS    -9
    346 #define TB_ERR_READ             -10
    347 #define TB_ERR_RESIZE_IOCTL     -11
    348 #define TB_ERR_RESIZE_PIPE      -12
    349 #define TB_ERR_RESIZE_SIGACTION -13
    350 #define TB_ERR_POLL             -14
    351 #define TB_ERR_TCGETATTR        -15
    352 #define TB_ERR_TCSETATTR        -16
    353 #define TB_ERR_UNSUPPORTED_TERM -17
    354 #define TB_ERR_RESIZE_WRITE     -18
    355 #define TB_ERR_RESIZE_POLL      -19
    356 #define TB_ERR_RESIZE_READ      -20
    357 #define TB_ERR_RESIZE_SSCANF    -21
    358 #define TB_ERR_CAP_COLLISION    -22
    359 
    360 #define TB_ERR_SELECT           TB_ERR_POLL
    361 #define TB_ERR_RESIZE_SELECT    TB_ERR_RESIZE_POLL
    362 
    363 /* Deprecated. Function types to be used with `tb_set_func`. */
    364 #define TB_FUNC_EXTRACT_PRE     0
    365 #define TB_FUNC_EXTRACT_POST    1
    366 
    367 /* Define this to set the size of the buffer used in `tb_printf` and
    368  * `tb_sendf`.
    369  */
    370 #ifndef TB_OPT_PRINTF_BUF
    371 #define TB_OPT_PRINTF_BUF 4096
    372 #endif
    373 
    374 /* Define this to set the size of the buffer used when reading from the tty. */
    375 #ifndef TB_OPT_READ_BUF
    376 #define TB_OPT_READ_BUF 64
    377 #endif
    378 
    379 /* Define this for limited back compat with termbox v1. */
    380 #ifdef TB_OPT_V1_COMPAT
    381 #define tb_change_cell          tb_set_cell
    382 #define tb_put_cell(x, y, c)    tb_set_cell((x), (y), (c)->ch, (c)->fg, (c)->bg)
    383 #define tb_set_clear_attributes tb_set_clear_attrs
    384 #define tb_select_input_mode    tb_set_input_mode
    385 #define tb_select_output_mode   tb_set_output_mode
    386 #endif
    387 
    388 /* Define these to swap in a different allocator. */
    389 #ifndef tb_malloc
    390 #define tb_malloc  malloc
    391 #define tb_realloc realloc
    392 #define tb_free    free
    393 #endif
    394 
    395 #if TB_OPT_ATTR_W == 64
    396 typedef uint64_t uintattr_t;
    397 #elif TB_OPT_ATTR_W == 32
    398 typedef uint32_t uintattr_t;
    399 #else // 16
    400 typedef uint16_t uintattr_t;
    401 #endif
    402 
    403 /* A cell in a 2d grid representing the terminal screen.
    404  *
    405  * The terminal screen is represented as 2d array of cells. The structure is
    406  * optimized for dealing with single-width (`wcwidth==1`) Unicode codepoints,
    407  * however some support for grapheme clusters (e.g., combining diacritical
    408  * marks) and wide codepoints (e.g., Hiragana) is provided through `ech`,
    409  * `nech`, and `cech` via `tb_set_cell_ex`. `ech` is only valid when `nech>0`,
    410  * otherwise `ch` is used.
    411  *
    412  * For non-single-width codepoints, given `W=wcwidth(ch || ech)`:
    413  *
    414  * when `W<=0`: termbox forces a single-width cell. Callers should avoid this
    415  *              if aiming to render text accurately. Callers may use
    416  *              `tb_set_cell_ex` or `tb_print*` to render `W==0` combining
    417  *              characters.
    418  *
    419  * when `W>=2`: termbox zeroes out the following `W-1` cells and skips sending
    420  *              them to the tty. So, e.g., if the caller sets `x=0,y=0` to a
    421  *              `W==2` codepoint, the caller's next set should be at `x=2,y=0`.
    422  *              Anything set at `x=1,y=0` will be ignored. If there are not
    423  *              enough columns remaining on the line to render `W` cells,
    424  *              spaces are sent instead.
    425  *
    426  * See `tb_present` for implementation.
    427  */
    428 struct tb_cell {
    429     uint32_t ch;   // a Unicode codepoint
    430     uintattr_t fg; // bitwise foreground attributes
    431     uintattr_t bg; // bitwise background attributes
    432 #ifdef TB_OPT_EGC
    433     uint32_t *ech; // a grapheme cluster of Unicode codepoints, 0-terminated
    434     size_t nech;   // num elements in ech, 0 means use ch instead of ech
    435     size_t cech;   // num elements allocated for ech
    436 #endif
    437 };
    438 
    439 /* An incoming event from the tty.
    440  *
    441  * Given the event type, the following fields are relevant:
    442  *
    443  *    when `TB_EVENT_KEY`: `key` xor `ch` (one will be zero) and `mod`. Note
    444  *                         there is overlap between `TB_MOD_CTRL` and
    445  *                         `TB_KEY_CTRL_*`. `TB_MOD_CTRL` and `TB_MOD_SHIFT`
    446  *                         are only set as modifiers to `TB_KEY_ARROW_*`.
    447  *
    448  * when `TB_EVENT_RESIZE`: `w` and `h`
    449  *
    450  *  when `TB_EVENT_MOUSE`: `key` (`TB_KEY_MOUSE_*`), `x`, and `y`
    451  */
    452 struct tb_event {
    453     uint8_t type; // one of `TB_EVENT_*` constants
    454     uint8_t mod;  // bitwise `TB_MOD_*` constants
    455     uint16_t key; // one of `TB_KEY_*` constants
    456     uint32_t ch;  // a Unicode codepoint
    457     int32_t w;    // resize width
    458     int32_t h;    // resize height
    459     int32_t x;    // mouse x
    460     int32_t y;    // mouse y
    461 };
    462 
    463 /* Initialize the termbox library. This function should be called before any
    464  * other functions. `tb_init` is equivalent to `tb_init_file("/dev/tty")`.
    465  * After successful initialization, the library must be finalized using
    466  * `tb_shutdown`.
    467  */
    468 int tb_init(void);
    469 int tb_init_file(const char *path);
    470 int tb_init_fd(int ttyfd);
    471 int tb_init_rwfd(int rfd, int wfd);
    472 int tb_shutdown(void);
    473 
    474 /* Return the size of the internal back buffer (which is the same as terminal's
    475  * window size in rows and columns). The internal buffer can be resized after
    476  * `tb_clear` or `tb_present` calls. Both dimensions have an unspecified
    477  * negative value when called before `tb_init` or after `tb_shutdown`.
    478  */
    479 int tb_width(void);
    480 int tb_height(void);
    481 
    482 /* Clear the internal back buffer using `TB_DEFAULT` or the attributes set by
    483  * `tb_set_clear_attrs`.
    484  */
    485 int tb_clear(void);
    486 int tb_set_clear_attrs(uintattr_t fg, uintattr_t bg);
    487 
    488 /* Synchronize the internal back buffer with the terminal by writing to tty. */
    489 int tb_present(void);
    490 
    491 /* Clear the internal front buffer effectively forcing a complete re-render of
    492  * the back buffer to the tty. It is not necessary to call this under normal
    493  * circumstances.
    494  */
    495 int tb_invalidate(void);
    496 
    497 /* Set the position of the cursor. Upper-left cell is (0, 0). */
    498 int tb_set_cursor(int cx, int cy);
    499 int tb_hide_cursor(void);
    500 
    501 /* Set cell contents in the internal back buffer at the specified position.
    502  *
    503  * Use `tb_set_cell_ex` for rendering grapheme clusters (e.g., combining
    504  * diacritical marks).
    505  *
    506  * Calling `tb_set_cell(x, y, ch, fg, bg)` is equivalent to
    507  * `tb_set_cell_ex(x, y, &ch, 1, fg, bg)`.
    508  *
    509  * `tb_extend_cell` is a shortcut for appending 1 codepoint to `tb_cell.ech`.
    510  *
    511  * Non-printable (`iswprint(3)`) codepoints are replaced with `U+FFFD` at
    512  * render time.
    513  */
    514 int tb_set_cell(int x, int y, uint32_t ch, uintattr_t fg, uintattr_t bg);
    515 int tb_set_cell_ex(int x, int y, uint32_t *ch, size_t nch, uintattr_t fg,
    516     uintattr_t bg);
    517 int tb_extend_cell(int x, int y, uint32_t ch);
    518 
    519 /* Return a pointer to the cell at the specified position.
    520  *
    521  * Cell memory may be invalid or freed after subsequent library calls, so
    522  * callers must copy any data that they need to persist across calls. Modifying
    523  * cell memory results in undefined behavior.
    524  *
    525  * Callers may use pointer math to access cells relative to the requested one.
    526  * The cell grid memory layout is a contiguous array indexable by the
    527  * expression `(y * width) + x`.
    528  *
    529  * If `back` is non-zero, return cell from the internal back buffer. Otherwise,
    530  * return cell from the front buffer. Note the front buffer is updated on each
    531  * call to `tb_present`, whereas the back buffer is updated immediately by
    532  * `tb_set_cell` and other functions that modify cell contents.
    533  *
    534  * If the position is invalid, `TB_ERR_OUT_OF_BOUNDS` is returned.
    535  */
    536 int tb_get_cell(int x, int y, int back, struct tb_cell **cell);
    537 
    538 /* Set the input mode. Termbox has two input modes:
    539  *
    540  * 1. `TB_INPUT_ESC`
    541  *    When escape (`\x1b`) is in the buffer and there's no match for an escape
    542  *    sequence, a key event for `TB_KEY_ESC` is returned.
    543  *
    544  * 2. `TB_INPUT_ALT`
    545  *    When escape (`\x1b`) is in the buffer and there's no match for an escape
    546  *    sequence, the next keyboard event is returned with a `TB_MOD_ALT`
    547  *    modifier.
    548  *
    549  * You can also apply `TB_INPUT_MOUSE` via bitwise OR operation to either of
    550  * the modes (e.g., `TB_INPUT_ESC | TB_INPUT_MOUSE`) to receive
    551  * `TB_EVENT_MOUSE` events. If none of the main two modes were set, but the
    552  * mouse mode was, `TB_INPUT_ESC` is used. If for some reason you've decided to
    553  * use `TB_INPUT_ESC | TB_INPUT_ALT`, it will behave as if only `TB_INPUT_ESC`
    554  * was selected.
    555  *
    556  * If mode is `TB_INPUT_CURRENT`, return the current input mode.
    557  *
    558  * The default input mode is `TB_INPUT_ESC`.
    559  */
    560 int tb_set_input_mode(int mode);
    561 
    562 /* Set the output mode. Termbox has multiple output modes:
    563  *
    564  * 1. `TB_OUTPUT_NORMAL`     => [0..8]
    565  *
    566  *    This mode provides 8 different colors:
    567  *      `TB_BLACK`, `TB_RED`, `TB_GREEN`, `TB_YELLOW`,
    568  *      `TB_BLUE`, `TB_MAGENTA`, `TB_CYAN`, `TB_WHITE`
    569  *
    570  *    Plus `TB_DEFAULT` which skips sending a color code (i.e., uses the
    571  *    terminal's default color).
    572  *
    573  *    Colors (including `TB_DEFAULT`) may be bitwise OR'd with attributes:
    574  *      `TB_BOLD`, `TB_UNDERLINE`, `TB_REVERSE`, `TB_ITALIC`, `TB_BLINK`,
    575  *      `TB_BRIGHT`, `TB_DIM`
    576  *
    577  *    The following style attributes are also available if compiled with
    578  *    `TB_OPT_ATTR_W` set to 64:
    579  *      `TB_STRIKEOUT`, `TB_UNDERLINE_2`, `TB_OVERLINE`, `TB_INVISIBLE`
    580  *
    581  *    As in all modes, the value 0 is interpreted as `TB_DEFAULT` for
    582  *    convenience.
    583  *
    584  *    Some notes: `TB_REVERSE` and `TB_BRIGHT` can be applied as either `fg` or
    585  *    `bg` attributes for the same effect. The rest of the attributes apply to
    586  *    `fg` only and are ignored as `bg` attributes.
    587  *
    588  *    Example usage: `tb_set_cell(x, y, '@', TB_BLACK | TB_BOLD, TB_RED)`
    589  *
    590  * 2. `TB_OUTPUT_256`        => [0..255] + `TB_HI_BLACK`
    591  *
    592  *    In this mode you get 256 distinct colors (plus default):
    593  *                0x00   (1): `TB_DEFAULT`
    594  *       `TB_HI_BLACK`   (1): `TB_BLACK` in `TB_OUTPUT_NORMAL`
    595  *          0x01..0x07   (7): the next 7 colors as in `TB_OUTPUT_NORMAL`
    596  *          0x08..0x0f   (8): bright versions of the above
    597  *          0x10..0xe7 (216): 216 different colors
    598  *          0xe8..0xff  (24): 24 different shades of gray
    599  *
    600  *    All `TB_*` style attributes except `TB_BRIGHT` may be bitwise OR'd as in
    601  *    `TB_OUTPUT_NORMAL`.
    602  *
    603  *    Note `TB_HI_BLACK` must be used for black, as 0x00 represents default.
    604  *
    605  * 3. `TB_OUTPUT_216`        => [0..216]
    606  *
    607  *    This mode supports the 216-color range of `TB_OUTPUT_256` only, but you
    608  *    don't need to provide an offset:
    609  *                0x00   (1): `TB_DEFAULT`
    610  *          0x01..0xd8 (216): 216 different colors
    611  *
    612  * 4. `TB_OUTPUT_GRAYSCALE`  => [0..24]
    613  *
    614  *    This mode supports the 24-color range of `TB_OUTPUT_256` only, but you
    615  *    don't need to provide an offset:
    616  *                0x00   (1): `TB_DEFAULT`
    617  *          0x01..0x18  (24): 24 different shades of gray
    618  *
    619  * 5. `TB_OUTPUT_TRUECOLOR`  => [0x000000..0xffffff] + `TB_HI_BLACK`
    620  *
    621  *    This mode provides 24-bit color on supported terminals. The format is
    622  *    0xRRGGBB.
    623  *
    624  *    All `TB_*` style attributes except `TB_BRIGHT` may be bitwise OR'd as in
    625  *    `TB_OUTPUT_NORMAL`.
    626  *
    627  *    Note `TB_HI_BLACK` must be used for black, as 0x000000 represents
    628  *    default.
    629  *
    630  * To use the terminal default color (i.e., to not send an escape code), pass
    631  * `TB_DEFAULT`. For convenience, the value 0 is interpreted as `TB_DEFAULT` in
    632  * all modes.
    633  *
    634  * Note, cell attributes persist after switching output modes. Any translation
    635  * between, for example, `TB_OUTPUT_NORMAL`'s `TB_RED` and
    636  * `TB_OUTPUT_TRUECOLOR`'s 0xff0000 must be performed by the caller. Also note
    637  * that cells previously rendered in one mode may persist unchanged until the
    638  * front buffer is cleared (such as after a resize event) at which point it
    639  * will be re-interpreted and flushed according to the current mode. Callers
    640  * may invoke `tb_invalidate` if it is desirable to immediately re-interpret
    641  * and flush the entire screen according to the current mode.
    642  *
    643  * Note, not all terminals support all output modes, especially beyond
    644  * `TB_OUTPUT_NORMAL`. There is also no very reliable way to determine color
    645  * support dynamically. If portability is desired, callers are recommended to
    646  * use `TB_OUTPUT_NORMAL` or make output mode end-user configurable. The same
    647  * advice applies to style attributes.
    648  *
    649  * If mode is `TB_OUTPUT_CURRENT`, return the current output mode.
    650  *
    651  * The default output mode is `TB_OUTPUT_NORMAL`.
    652  */
    653 int tb_set_output_mode(int mode);
    654 
    655 /* Wait for an event up to `timeout_ms` milliseconds and populate `event` with
    656  * it. If no event is available within the timeout period, `TB_ERR_NO_EVENT`
    657  * is returned. On a resize event, the underlying `select(2)` call may be
    658  * interrupted, yielding a return code of `TB_ERR_POLL`. In this case, you may
    659  * check `errno` via `tb_last_errno`. If it's `EINTR`, you may elect to ignore
    660  * that and call `tb_peek_event` again.
    661  */
    662 int tb_peek_event(struct tb_event *event, int timeout_ms);
    663 
    664 /* Same as `tb_peek_event` except no timeout. */
    665 int tb_poll_event(struct tb_event *event);
    666 
    667 /* Internal termbox fds that can be used with `poll(2)`, `select(2)`, etc.
    668  * externally. Callers must invoke `tb_poll_event` or `tb_peek_event` if
    669  * fds become readable.
    670  */
    671 int tb_get_fds(int *ttyfd, int *resizefd);
    672 
    673 /* Print and printf functions. Specify param `out_w` to determine width of
    674  * printed string. Strings are interpreted as UTF-8.
    675  *
    676  * Non-printable characters (`iswprint(3)`) and truncated UTF-8 byte sequences
    677  * are replaced with U+FFFD.
    678  *
    679  * Newlines (`\n`) are supported with the caveat that `out_w` will return the
    680  * width of the string as if it were on a single line.
    681  *
    682  * If the starting coordinate is out of bounds, `TB_ERR_OUT_OF_BOUNDS` is
    683  * returned. If the starting coordinate is in bounds, but goes out of bounds,
    684  * then the out-of-bounds portions of the string are ignored.
    685  *
    686  * For finer control, use `tb_set_cell`.
    687  */
    688 int tb_print(int x, int y, uintattr_t fg, uintattr_t bg, const char *str);
    689 int tb_printf(int x, int y, uintattr_t fg, uintattr_t bg, const char *fmt, ...);
    690 int tb_print_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
    691     const char *str);
    692 int tb_printf_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
    693     const char *fmt, ...);
    694 
    695 /* Send raw bytes to terminal. */
    696 int tb_send(const char *buf, size_t nbuf);
    697 int tb_sendf(const char *fmt, ...);
    698 
    699 /* Deprecated. Set custom callbacks. `fn_type` is one of `TB_FUNC_*` constants,
    700  * `fn` is a compatible function pointer, or NULL to clear.
    701  *
    702  * `TB_FUNC_EXTRACT_PRE`:
    703  *   If specified, invoke this function BEFORE termbox tries to extract any
    704  *   escape sequences from the input buffer.
    705  *
    706  * `TB_FUNC_EXTRACT_POST`:
    707  *   If specified, invoke this function AFTER termbox tries (and fails) to
    708  *   extract any escape sequences from the input buffer.
    709  */
    710 int tb_set_func(int fn_type, int (*fn)(struct tb_event *, size_t *));
    711 
    712 /* Return byte length of codepoint given first byte of UTF-8 sequence (1-6). */
    713 int tb_utf8_char_length(char c);
    714 
    715 /* Convert UTF-8 null-terminated byte sequence to UTF-32 codepoint.
    716  *
    717  * If `c` is an empty C string, return 0. `out` is left unchanged.
    718  *
    719  * If a null byte is encountered in the middle of the codepoint, return a
    720  * negative number indicating how many bytes were processed. `out` is left
    721  * unchanged.
    722  *
    723  * Otherwise, return byte length of codepoint (1-6).
    724  */
    725 int tb_utf8_char_to_unicode(uint32_t *out, const char *c);
    726 
    727 /* Convert UTF-32 codepoint to UTF-8 null-terminated byte sequence.
    728  *
    729  * `out` must be char[7] or greater. Return byte length of codepoint (1-6).
    730  */
    731 int tb_utf8_unicode_to_char(char *out, uint32_t c);
    732 
    733 /* Library utility functions */
    734 int tb_last_errno(void);
    735 const char *tb_strerror(int err);
    736 struct tb_cell *tb_cell_buffer(void); // Deprecated
    737 int tb_has_truecolor(void);
    738 int tb_has_egc(void);
    739 int tb_attr_width(void);
    740 const char *tb_version(void);
    741 int tb_iswprint(uint32_t ch);
    742 int tb_wcwidth(uint32_t ch);
    743 
    744 /* Deprecation notice!
    745  *
    746  * The following will be removed in version 3.x (ABI version 3):
    747  *
    748  *   `TB_256_BLACK`           (use `TB_HI_BLACK`)
    749  *   `TB_OPT_TRUECOLOR`       (use `TB_OPT_ATTR_W`)
    750  *   `TB_TRUECOLOR_BOLD`      (use `TB_BOLD`)
    751  *   `TB_TRUECOLOR_UNDERLINE` (use `TB_UNDERLINE`)
    752  *   `TB_TRUECOLOR_REVERSE`   (use `TB_REVERSE`)
    753  *   `TB_TRUECOLOR_ITALIC`    (use `TB_ITALIC`)
    754  *   `TB_TRUECOLOR_BLINK`     (use `TB_BLINK`)
    755  *   `TB_TRUECOLOR_BLACK`     (use `TB_HI_BLACK`)
    756  *   `tb_cell_buffer`
    757  *   `tb_set_func`
    758  *   `TB_FUNC_EXTRACT_PRE`
    759  *   `TB_FUNC_EXTRACT_POST`
    760  */
    761 
    762 #ifdef __cplusplus
    763 }
    764 #endif
    765 
    766 #endif // TERMBOX_H_INCL
    767 
    768 #ifdef TB_IMPL
    769 
    770 #define if_err_return(rv, expr)                                               \
    771     if (((rv) = (expr)) != TB_OK) return (rv)
    772 #define if_err_break(rv, expr)                                                \
    773     if (((rv) = (expr)) != TB_OK) break
    774 #define if_ok_return(rv, expr)                                                \
    775     if (((rv) = (expr)) == TB_OK) return (rv)
    776 #define if_ok_or_need_more_return(rv, expr)                                   \
    777     if (((rv) = (expr)) == TB_OK || (rv) == TB_ERR_NEED_MORE) return (rv)
    778 
    779 #define send_literal(rv, a)                                                   \
    780     if_err_return((rv), bytebuf_nputs(&global.out, (a), sizeof(a) - 1))
    781 
    782 #define send_num(rv, nbuf, n)                                                 \
    783     if_err_return((rv),                                                       \
    784         bytebuf_nputs(&global.out, (nbuf), convert_num((n), (nbuf))))
    785 
    786 #define snprintf_or_return(rv, str, sz, fmt, ...)                             \
    787     do {                                                                      \
    788         (rv) = snprintf((str), (sz), (fmt), __VA_ARGS__);                     \
    789         if ((rv) < 0 || (rv) >= (int)(sz)) return TB_ERR;                     \
    790     } while (0)
    791 
    792 #define if_not_init_return()                                                  \
    793     if (!global.initialized) return TB_ERR_NOT_INIT
    794 
    795 struct bytebuf {
    796     char *buf;
    797     size_t len;
    798     size_t cap;
    799 };
    800 
    801 struct cellbuf {
    802     int width;
    803     int height;
    804     struct tb_cell *cells;
    805 };
    806 
    807 struct cap_trie {
    808     char c;
    809     struct cap_trie *children;
    810     size_t nchildren;
    811     int is_leaf;
    812     uint16_t key;
    813     uint8_t mod;
    814 };
    815 
    816 struct tb_global {
    817     int ttyfd;
    818     int rfd;
    819     int wfd;
    820     int ttyfd_open;
    821     int resize_pipefd[2];
    822     int width;
    823     int height;
    824     int cursor_x;
    825     int cursor_y;
    826     int last_x;
    827     int last_y;
    828     uintattr_t fg;
    829     uintattr_t bg;
    830     uintattr_t last_fg;
    831     uintattr_t last_bg;
    832     int input_mode;
    833     int output_mode;
    834     char *terminfo;
    835     size_t nterminfo;
    836     const char *caps[TB_CAP__COUNT];
    837     struct cap_trie cap_trie;
    838     struct bytebuf in;
    839     struct bytebuf out;
    840     struct cellbuf back;
    841     struct cellbuf front;
    842     struct termios orig_tios;
    843     int has_orig_tios;
    844     int last_errno;
    845     int initialized;
    846     int (*fn_extract_esc_pre)(struct tb_event *, size_t *);
    847     int (*fn_extract_esc_post)(struct tb_event *, size_t *);
    848     char errbuf[1024];
    849 };
    850 
    851 static struct tb_global global = {0};
    852 
    853 /* BEGIN codegen c */
    854 /* Produced by ./codegen.sh on Tue, 03 Sep 2024 04:17:48 +0000 */
    855 
    856 static const int16_t terminfo_cap_indexes[] = {
    857     66,  // kf1 (TB_CAP_F1)
    858     68,  // kf2 (TB_CAP_F2)
    859     69,  // kf3 (TB_CAP_F3)
    860     70,  // kf4 (TB_CAP_F4)
    861     71,  // kf5 (TB_CAP_F5)
    862     72,  // kf6 (TB_CAP_F6)
    863     73,  // kf7 (TB_CAP_F7)
    864     74,  // kf8 (TB_CAP_F8)
    865     75,  // kf9 (TB_CAP_F9)
    866     67,  // kf10 (TB_CAP_F10)
    867     216, // kf11 (TB_CAP_F11)
    868     217, // kf12 (TB_CAP_F12)
    869     77,  // kich1 (TB_CAP_INSERT)
    870     59,  // kdch1 (TB_CAP_DELETE)
    871     76,  // khome (TB_CAP_HOME)
    872     164, // kend (TB_CAP_END)
    873     82,  // kpp (TB_CAP_PGUP)
    874     81,  // knp (TB_CAP_PGDN)
    875     87,  // kcuu1 (TB_CAP_ARROW_UP)
    876     61,  // kcud1 (TB_CAP_ARROW_DOWN)
    877     79,  // kcub1 (TB_CAP_ARROW_LEFT)
    878     83,  // kcuf1 (TB_CAP_ARROW_RIGHT)
    879     148, // kcbt (TB_CAP_BACK_TAB)
    880     28,  // smcup (TB_CAP_ENTER_CA)
    881     40,  // rmcup (TB_CAP_EXIT_CA)
    882     16,  // cnorm (TB_CAP_SHOW_CURSOR)
    883     13,  // civis (TB_CAP_HIDE_CURSOR)
    884     5,   // clear (TB_CAP_CLEAR_SCREEN)
    885     39,  // sgr0 (TB_CAP_SGR0)
    886     36,  // smul (TB_CAP_UNDERLINE)
    887     27,  // bold (TB_CAP_BOLD)
    888     26,  // blink (TB_CAP_BLINK)
    889     311, // sitm (TB_CAP_ITALIC)
    890     34,  // rev (TB_CAP_REVERSE)
    891     89,  // smkx (TB_CAP_ENTER_KEYPAD)
    892     88,  // rmkx (TB_CAP_EXIT_KEYPAD)
    893     30,  // dim (TB_CAP_DIM)
    894     32,  // invis (TB_CAP_INVISIBLE)
    895 };
    896 
    897 // xterm
    898 static const char *xterm_caps[] = {
    899     "\033OP",                  // kf1 (TB_CAP_F1)
    900     "\033OQ",                  // kf2 (TB_CAP_F2)
    901     "\033OR",                  // kf3 (TB_CAP_F3)
    902     "\033OS",                  // kf4 (TB_CAP_F4)
    903     "\033[15~",                // kf5 (TB_CAP_F5)
    904     "\033[17~",                // kf6 (TB_CAP_F6)
    905     "\033[18~",                // kf7 (TB_CAP_F7)
    906     "\033[19~",                // kf8 (TB_CAP_F8)
    907     "\033[20~",                // kf9 (TB_CAP_F9)
    908     "\033[21~",                // kf10 (TB_CAP_F10)
    909     "\033[23~",                // kf11 (TB_CAP_F11)
    910     "\033[24~",                // kf12 (TB_CAP_F12)
    911     "\033[2~",                 // kich1 (TB_CAP_INSERT)
    912     "\033[3~",                 // kdch1 (TB_CAP_DELETE)
    913     "\033OH",                  // khome (TB_CAP_HOME)
    914     "\033OF",                  // kend (TB_CAP_END)
    915     "\033[5~",                 // kpp (TB_CAP_PGUP)
    916     "\033[6~",                 // knp (TB_CAP_PGDN)
    917     "\033OA",                  // kcuu1 (TB_CAP_ARROW_UP)
    918     "\033OB",                  // kcud1 (TB_CAP_ARROW_DOWN)
    919     "\033OD",                  // kcub1 (TB_CAP_ARROW_LEFT)
    920     "\033OC",                  // kcuf1 (TB_CAP_ARROW_RIGHT)
    921     "\033[Z",                  // kcbt (TB_CAP_BACK_TAB)
    922     "\033[?1049h\033[22;0;0t", // smcup (TB_CAP_ENTER_CA)
    923     "\033[?1049l\033[23;0;0t", // rmcup (TB_CAP_EXIT_CA)
    924     "\033[?12l\033[?25h",      // cnorm (TB_CAP_SHOW_CURSOR)
    925     "\033[?25l",               // civis (TB_CAP_HIDE_CURSOR)
    926     "\033[H\033[2J",           // clear (TB_CAP_CLEAR_SCREEN)
    927     "\033(B\033[m",            // sgr0 (TB_CAP_SGR0)
    928     "\033[4m",                 // smul (TB_CAP_UNDERLINE)
    929     "\033[1m",                 // bold (TB_CAP_BOLD)
    930     "\033[5m",                 // blink (TB_CAP_BLINK)
    931     "\033[3m",                 // sitm (TB_CAP_ITALIC)
    932     "\033[7m",                 // rev (TB_CAP_REVERSE)
    933     "\033[?1h\033=",           // smkx (TB_CAP_ENTER_KEYPAD)
    934     "\033[?1l\033>",           // rmkx (TB_CAP_EXIT_KEYPAD)
    935     "\033[2m",                 // dim (TB_CAP_DIM)
    936     "\033[8m",                 // invis (TB_CAP_INVISIBLE)
    937 };
    938 
    939 // linux
    940 static const char *linux_caps[] = {
    941     "\033[[A",           // kf1 (TB_CAP_F1)
    942     "\033[[B",           // kf2 (TB_CAP_F2)
    943     "\033[[C",           // kf3 (TB_CAP_F3)
    944     "\033[[D",           // kf4 (TB_CAP_F4)
    945     "\033[[E",           // kf5 (TB_CAP_F5)
    946     "\033[17~",          // kf6 (TB_CAP_F6)
    947     "\033[18~",          // kf7 (TB_CAP_F7)
    948     "\033[19~",          // kf8 (TB_CAP_F8)
    949     "\033[20~",          // kf9 (TB_CAP_F9)
    950     "\033[21~",          // kf10 (TB_CAP_F10)
    951     "\033[23~",          // kf11 (TB_CAP_F11)
    952     "\033[24~",          // kf12 (TB_CAP_F12)
    953     "\033[2~",           // kich1 (TB_CAP_INSERT)
    954     "\033[3~",           // kdch1 (TB_CAP_DELETE)
    955     "\033[1~",           // khome (TB_CAP_HOME)
    956     "\033[4~",           // kend (TB_CAP_END)
    957     "\033[5~",           // kpp (TB_CAP_PGUP)
    958     "\033[6~",           // knp (TB_CAP_PGDN)
    959     "\033[A",            // kcuu1 (TB_CAP_ARROW_UP)
    960     "\033[B",            // kcud1 (TB_CAP_ARROW_DOWN)
    961     "\033[D",            // kcub1 (TB_CAP_ARROW_LEFT)
    962     "\033[C",            // kcuf1 (TB_CAP_ARROW_RIGHT)
    963     "\033\011",          // kcbt (TB_CAP_BACK_TAB)
    964     "",                  // smcup (TB_CAP_ENTER_CA)
    965     "",                  // rmcup (TB_CAP_EXIT_CA)
    966     "\033[?25h\033[?0c", // cnorm (TB_CAP_SHOW_CURSOR)
    967     "\033[?25l\033[?1c", // civis (TB_CAP_HIDE_CURSOR)
    968     "\033[H\033[J",      // clear (TB_CAP_CLEAR_SCREEN)
    969     "\033[m\017",        // sgr0 (TB_CAP_SGR0)
    970     "\033[4m",           // smul (TB_CAP_UNDERLINE)
    971     "\033[1m",           // bold (TB_CAP_BOLD)
    972     "\033[5m",           // blink (TB_CAP_BLINK)
    973     "",                  // sitm (TB_CAP_ITALIC)
    974     "\033[7m",           // rev (TB_CAP_REVERSE)
    975     "",                  // smkx (TB_CAP_ENTER_KEYPAD)
    976     "",                  // rmkx (TB_CAP_EXIT_KEYPAD)
    977     "\033[2m",           // dim (TB_CAP_DIM)
    978     "",                  // invis (TB_CAP_INVISIBLE)
    979 };
    980 
    981 // screen
    982 static const char *screen_caps[] = {
    983     "\033OP",            // kf1 (TB_CAP_F1)
    984     "\033OQ",            // kf2 (TB_CAP_F2)
    985     "\033OR",            // kf3 (TB_CAP_F3)
    986     "\033OS",            // kf4 (TB_CAP_F4)
    987     "\033[15~",          // kf5 (TB_CAP_F5)
    988     "\033[17~",          // kf6 (TB_CAP_F6)
    989     "\033[18~",          // kf7 (TB_CAP_F7)
    990     "\033[19~",          // kf8 (TB_CAP_F8)
    991     "\033[20~",          // kf9 (TB_CAP_F9)
    992     "\033[21~",          // kf10 (TB_CAP_F10)
    993     "\033[23~",          // kf11 (TB_CAP_F11)
    994     "\033[24~",          // kf12 (TB_CAP_F12)
    995     "\033[2~",           // kich1 (TB_CAP_INSERT)
    996     "\033[3~",           // kdch1 (TB_CAP_DELETE)
    997     "\033[1~",           // khome (TB_CAP_HOME)
    998     "\033[4~",           // kend (TB_CAP_END)
    999     "\033[5~",           // kpp (TB_CAP_PGUP)
   1000     "\033[6~",           // knp (TB_CAP_PGDN)
   1001     "\033OA",            // kcuu1 (TB_CAP_ARROW_UP)
   1002     "\033OB",            // kcud1 (TB_CAP_ARROW_DOWN)
   1003     "\033OD",            // kcub1 (TB_CAP_ARROW_LEFT)
   1004     "\033OC",            // kcuf1 (TB_CAP_ARROW_RIGHT)
   1005     "\033[Z",            // kcbt (TB_CAP_BACK_TAB)
   1006     "\033[?1049h",       // smcup (TB_CAP_ENTER_CA)
   1007     "\033[?1049l",       // rmcup (TB_CAP_EXIT_CA)
   1008     "\033[34h\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR)
   1009     "\033[?25l",         // civis (TB_CAP_HIDE_CURSOR)
   1010     "\033[H\033[J",      // clear (TB_CAP_CLEAR_SCREEN)
   1011     "\033[m\017",        // sgr0 (TB_CAP_SGR0)
   1012     "\033[4m",           // smul (TB_CAP_UNDERLINE)
   1013     "\033[1m",           // bold (TB_CAP_BOLD)
   1014     "\033[5m",           // blink (TB_CAP_BLINK)
   1015     "",                  // sitm (TB_CAP_ITALIC)
   1016     "\033[7m",           // rev (TB_CAP_REVERSE)
   1017     "\033[?1h\033=",     // smkx (TB_CAP_ENTER_KEYPAD)
   1018     "\033[?1l\033>",     // rmkx (TB_CAP_EXIT_KEYPAD)
   1019     "\033[2m",           // dim (TB_CAP_DIM)
   1020     "",                  // invis (TB_CAP_INVISIBLE)
   1021 };
   1022 
   1023 // rxvt-256color
   1024 static const char *rxvt_256color_caps[] = {
   1025     "\033[11~",              // kf1 (TB_CAP_F1)
   1026     "\033[12~",              // kf2 (TB_CAP_F2)
   1027     "\033[13~",              // kf3 (TB_CAP_F3)
   1028     "\033[14~",              // kf4 (TB_CAP_F4)
   1029     "\033[15~",              // kf5 (TB_CAP_F5)
   1030     "\033[17~",              // kf6 (TB_CAP_F6)
   1031     "\033[18~",              // kf7 (TB_CAP_F7)
   1032     "\033[19~",              // kf8 (TB_CAP_F8)
   1033     "\033[20~",              // kf9 (TB_CAP_F9)
   1034     "\033[21~",              // kf10 (TB_CAP_F10)
   1035     "\033[23~",              // kf11 (TB_CAP_F11)
   1036     "\033[24~",              // kf12 (TB_CAP_F12)
   1037     "\033[2~",               // kich1 (TB_CAP_INSERT)
   1038     "\033[3~",               // kdch1 (TB_CAP_DELETE)
   1039     "\033[7~",               // khome (TB_CAP_HOME)
   1040     "\033[8~",               // kend (TB_CAP_END)
   1041     "\033[5~",               // kpp (TB_CAP_PGUP)
   1042     "\033[6~",               // knp (TB_CAP_PGDN)
   1043     "\033[A",                // kcuu1 (TB_CAP_ARROW_UP)
   1044     "\033[B",                // kcud1 (TB_CAP_ARROW_DOWN)
   1045     "\033[D",                // kcub1 (TB_CAP_ARROW_LEFT)
   1046     "\033[C",                // kcuf1 (TB_CAP_ARROW_RIGHT)
   1047     "\033[Z",                // kcbt (TB_CAP_BACK_TAB)
   1048     "\0337\033[?47h",        // smcup (TB_CAP_ENTER_CA)
   1049     "\033[2J\033[?47l\0338", // rmcup (TB_CAP_EXIT_CA)
   1050     "\033[?25h",             // cnorm (TB_CAP_SHOW_CURSOR)
   1051     "\033[?25l",             // civis (TB_CAP_HIDE_CURSOR)
   1052     "\033[H\033[2J",         // clear (TB_CAP_CLEAR_SCREEN)
   1053     "\033[m\017",            // sgr0 (TB_CAP_SGR0)
   1054     "\033[4m",               // smul (TB_CAP_UNDERLINE)
   1055     "\033[1m",               // bold (TB_CAP_BOLD)
   1056     "\033[5m",               // blink (TB_CAP_BLINK)
   1057     "",                      // sitm (TB_CAP_ITALIC)
   1058     "\033[7m",               // rev (TB_CAP_REVERSE)
   1059     "\033=",                 // smkx (TB_CAP_ENTER_KEYPAD)
   1060     "\033>",                 // rmkx (TB_CAP_EXIT_KEYPAD)
   1061     "",                      // dim (TB_CAP_DIM)
   1062     "",                      // invis (TB_CAP_INVISIBLE)
   1063 };
   1064 
   1065 // rxvt-unicode
   1066 static const char *rxvt_unicode_caps[] = {
   1067     "\033[11~",           // kf1 (TB_CAP_F1)
   1068     "\033[12~",           // kf2 (TB_CAP_F2)
   1069     "\033[13~",           // kf3 (TB_CAP_F3)
   1070     "\033[14~",           // kf4 (TB_CAP_F4)
   1071     "\033[15~",           // kf5 (TB_CAP_F5)
   1072     "\033[17~",           // kf6 (TB_CAP_F6)
   1073     "\033[18~",           // kf7 (TB_CAP_F7)
   1074     "\033[19~",           // kf8 (TB_CAP_F8)
   1075     "\033[20~",           // kf9 (TB_CAP_F9)
   1076     "\033[21~",           // kf10 (TB_CAP_F10)
   1077     "\033[23~",           // kf11 (TB_CAP_F11)
   1078     "\033[24~",           // kf12 (TB_CAP_F12)
   1079     "\033[2~",            // kich1 (TB_CAP_INSERT)
   1080     "\033[3~",            // kdch1 (TB_CAP_DELETE)
   1081     "\033[7~",            // khome (TB_CAP_HOME)
   1082     "\033[8~",            // kend (TB_CAP_END)
   1083     "\033[5~",            // kpp (TB_CAP_PGUP)
   1084     "\033[6~",            // knp (TB_CAP_PGDN)
   1085     "\033[A",             // kcuu1 (TB_CAP_ARROW_UP)
   1086     "\033[B",             // kcud1 (TB_CAP_ARROW_DOWN)
   1087     "\033[D",             // kcub1 (TB_CAP_ARROW_LEFT)
   1088     "\033[C",             // kcuf1 (TB_CAP_ARROW_RIGHT)
   1089     "\033[Z",             // kcbt (TB_CAP_BACK_TAB)
   1090     "\033[?1049h",        // smcup (TB_CAP_ENTER_CA)
   1091     "\033[r\033[?1049l",  // rmcup (TB_CAP_EXIT_CA)
   1092     "\033[?12l\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR)
   1093     "\033[?25l",          // civis (TB_CAP_HIDE_CURSOR)
   1094     "\033[H\033[2J",      // clear (TB_CAP_CLEAR_SCREEN)
   1095     "\033[m\033(B",       // sgr0 (TB_CAP_SGR0)
   1096     "\033[4m",            // smul (TB_CAP_UNDERLINE)
   1097     "\033[1m",            // bold (TB_CAP_BOLD)
   1098     "\033[5m",            // blink (TB_CAP_BLINK)
   1099     "\033[3m",            // sitm (TB_CAP_ITALIC)
   1100     "\033[7m",            // rev (TB_CAP_REVERSE)
   1101     "\033=",              // smkx (TB_CAP_ENTER_KEYPAD)
   1102     "\033>",              // rmkx (TB_CAP_EXIT_KEYPAD)
   1103     "",                   // dim (TB_CAP_DIM)
   1104     "",                   // invis (TB_CAP_INVISIBLE)
   1105 };
   1106 
   1107 // Eterm
   1108 static const char *eterm_caps[] = {
   1109     "\033[11~",              // kf1 (TB_CAP_F1)
   1110     "\033[12~",              // kf2 (TB_CAP_F2)
   1111     "\033[13~",              // kf3 (TB_CAP_F3)
   1112     "\033[14~",              // kf4 (TB_CAP_F4)
   1113     "\033[15~",              // kf5 (TB_CAP_F5)
   1114     "\033[17~",              // kf6 (TB_CAP_F6)
   1115     "\033[18~",              // kf7 (TB_CAP_F7)
   1116     "\033[19~",              // kf8 (TB_CAP_F8)
   1117     "\033[20~",              // kf9 (TB_CAP_F9)
   1118     "\033[21~",              // kf10 (TB_CAP_F10)
   1119     "\033[23~",              // kf11 (TB_CAP_F11)
   1120     "\033[24~",              // kf12 (TB_CAP_F12)
   1121     "\033[2~",               // kich1 (TB_CAP_INSERT)
   1122     "\033[3~",               // kdch1 (TB_CAP_DELETE)
   1123     "\033[7~",               // khome (TB_CAP_HOME)
   1124     "\033[8~",               // kend (TB_CAP_END)
   1125     "\033[5~",               // kpp (TB_CAP_PGUP)
   1126     "\033[6~",               // knp (TB_CAP_PGDN)
   1127     "\033[A",                // kcuu1 (TB_CAP_ARROW_UP)
   1128     "\033[B",                // kcud1 (TB_CAP_ARROW_DOWN)
   1129     "\033[D",                // kcub1 (TB_CAP_ARROW_LEFT)
   1130     "\033[C",                // kcuf1 (TB_CAP_ARROW_RIGHT)
   1131     "",                      // kcbt (TB_CAP_BACK_TAB)
   1132     "\0337\033[?47h",        // smcup (TB_CAP_ENTER_CA)
   1133     "\033[2J\033[?47l\0338", // rmcup (TB_CAP_EXIT_CA)
   1134     "\033[?25h",             // cnorm (TB_CAP_SHOW_CURSOR)
   1135     "\033[?25l",             // civis (TB_CAP_HIDE_CURSOR)
   1136     "\033[H\033[2J",         // clear (TB_CAP_CLEAR_SCREEN)
   1137     "\033[m\017",            // sgr0 (TB_CAP_SGR0)
   1138     "\033[4m",               // smul (TB_CAP_UNDERLINE)
   1139     "\033[1m",               // bold (TB_CAP_BOLD)
   1140     "\033[5m",               // blink (TB_CAP_BLINK)
   1141     "",                      // sitm (TB_CAP_ITALIC)
   1142     "\033[7m",               // rev (TB_CAP_REVERSE)
   1143     "",                      // smkx (TB_CAP_ENTER_KEYPAD)
   1144     "",                      // rmkx (TB_CAP_EXIT_KEYPAD)
   1145     "",                      // dim (TB_CAP_DIM)
   1146     "",                      // invis (TB_CAP_INVISIBLE)
   1147 };
   1148 
   1149 static struct {
   1150     const char *name;
   1151     const char **caps;
   1152     const char *alias;
   1153 } builtin_terms[] = {
   1154     {"xterm",         xterm_caps,         ""    },
   1155     {"linux",         linux_caps,         ""    },
   1156     {"screen",        screen_caps,        "tmux"},
   1157     {"rxvt-256color", rxvt_256color_caps, ""    },
   1158     {"rxvt-unicode",  rxvt_unicode_caps,  "rxvt"},
   1159     {"Eterm",         eterm_caps,         ""    },
   1160     {NULL,            NULL,               NULL  },
   1161 };
   1162 
   1163 /* END codegen c */
   1164 
   1165 static struct {
   1166     const char *cap;
   1167     const uint16_t key;
   1168     const uint8_t mod;
   1169 } builtin_mod_caps[] = {
   1170     // xterm arrows
   1171     {"\x1b[1;2A",    TB_KEY_ARROW_UP,    TB_MOD_SHIFT                           },
   1172     {"\x1b[1;3A",    TB_KEY_ARROW_UP,    TB_MOD_ALT                             },
   1173     {"\x1b[1;4A",    TB_KEY_ARROW_UP,    TB_MOD_ALT | TB_MOD_SHIFT              },
   1174     {"\x1b[1;5A",    TB_KEY_ARROW_UP,    TB_MOD_CTRL                            },
   1175     {"\x1b[1;6A",    TB_KEY_ARROW_UP,    TB_MOD_CTRL | TB_MOD_SHIFT             },
   1176     {"\x1b[1;7A",    TB_KEY_ARROW_UP,    TB_MOD_CTRL | TB_MOD_ALT               },
   1177     {"\x1b[1;8A",    TB_KEY_ARROW_UP,    TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1178 
   1179     {"\x1b[1;2B",    TB_KEY_ARROW_DOWN,  TB_MOD_SHIFT                           },
   1180     {"\x1b[1;3B",    TB_KEY_ARROW_DOWN,  TB_MOD_ALT                             },
   1181     {"\x1b[1;4B",    TB_KEY_ARROW_DOWN,  TB_MOD_ALT | TB_MOD_SHIFT              },
   1182     {"\x1b[1;5B",    TB_KEY_ARROW_DOWN,  TB_MOD_CTRL                            },
   1183     {"\x1b[1;6B",    TB_KEY_ARROW_DOWN,  TB_MOD_CTRL | TB_MOD_SHIFT             },
   1184     {"\x1b[1;7B",    TB_KEY_ARROW_DOWN,  TB_MOD_CTRL | TB_MOD_ALT               },
   1185     {"\x1b[1;8B",    TB_KEY_ARROW_DOWN,  TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1186 
   1187     {"\x1b[1;2C",    TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT                           },
   1188     {"\x1b[1;3C",    TB_KEY_ARROW_RIGHT, TB_MOD_ALT                             },
   1189     {"\x1b[1;4C",    TB_KEY_ARROW_RIGHT, TB_MOD_ALT | TB_MOD_SHIFT              },
   1190     {"\x1b[1;5C",    TB_KEY_ARROW_RIGHT, TB_MOD_CTRL                            },
   1191     {"\x1b[1;6C",    TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_SHIFT             },
   1192     {"\x1b[1;7C",    TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT               },
   1193     {"\x1b[1;8C",    TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1194 
   1195     {"\x1b[1;2D",    TB_KEY_ARROW_LEFT,  TB_MOD_SHIFT                           },
   1196     {"\x1b[1;3D",    TB_KEY_ARROW_LEFT,  TB_MOD_ALT                             },
   1197     {"\x1b[1;4D",    TB_KEY_ARROW_LEFT,  TB_MOD_ALT | TB_MOD_SHIFT              },
   1198     {"\x1b[1;5D",    TB_KEY_ARROW_LEFT,  TB_MOD_CTRL                            },
   1199     {"\x1b[1;6D",    TB_KEY_ARROW_LEFT,  TB_MOD_CTRL | TB_MOD_SHIFT             },
   1200     {"\x1b[1;7D",    TB_KEY_ARROW_LEFT,  TB_MOD_CTRL | TB_MOD_ALT               },
   1201     {"\x1b[1;8D",    TB_KEY_ARROW_LEFT,  TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1202 
   1203     // xterm keys
   1204     {"\x1b[1;2H",    TB_KEY_HOME,        TB_MOD_SHIFT                           },
   1205     {"\x1b[1;3H",    TB_KEY_HOME,        TB_MOD_ALT                             },
   1206     {"\x1b[1;4H",    TB_KEY_HOME,        TB_MOD_ALT | TB_MOD_SHIFT              },
   1207     {"\x1b[1;5H",    TB_KEY_HOME,        TB_MOD_CTRL                            },
   1208     {"\x1b[1;6H",    TB_KEY_HOME,        TB_MOD_CTRL | TB_MOD_SHIFT             },
   1209     {"\x1b[1;7H",    TB_KEY_HOME,        TB_MOD_CTRL | TB_MOD_ALT               },
   1210     {"\x1b[1;8H",    TB_KEY_HOME,        TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1211 
   1212     {"\x1b[1;2F",    TB_KEY_END,         TB_MOD_SHIFT                           },
   1213     {"\x1b[1;3F",    TB_KEY_END,         TB_MOD_ALT                             },
   1214     {"\x1b[1;4F",    TB_KEY_END,         TB_MOD_ALT | TB_MOD_SHIFT              },
   1215     {"\x1b[1;5F",    TB_KEY_END,         TB_MOD_CTRL                            },
   1216     {"\x1b[1;6F",    TB_KEY_END,         TB_MOD_CTRL | TB_MOD_SHIFT             },
   1217     {"\x1b[1;7F",    TB_KEY_END,         TB_MOD_CTRL | TB_MOD_ALT               },
   1218     {"\x1b[1;8F",    TB_KEY_END,         TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1219 
   1220     {"\x1b[2;2~",    TB_KEY_INSERT,      TB_MOD_SHIFT                           },
   1221     {"\x1b[2;3~",    TB_KEY_INSERT,      TB_MOD_ALT                             },
   1222     {"\x1b[2;4~",    TB_KEY_INSERT,      TB_MOD_ALT | TB_MOD_SHIFT              },
   1223     {"\x1b[2;5~",    TB_KEY_INSERT,      TB_MOD_CTRL                            },
   1224     {"\x1b[2;6~",    TB_KEY_INSERT,      TB_MOD_CTRL | TB_MOD_SHIFT             },
   1225     {"\x1b[2;7~",    TB_KEY_INSERT,      TB_MOD_CTRL | TB_MOD_ALT               },
   1226     {"\x1b[2;8~",    TB_KEY_INSERT,      TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1227 
   1228     {"\x1b[3;2~",    TB_KEY_DELETE,      TB_MOD_SHIFT                           },
   1229     {"\x1b[3;3~",    TB_KEY_DELETE,      TB_MOD_ALT                             },
   1230     {"\x1b[3;4~",    TB_KEY_DELETE,      TB_MOD_ALT | TB_MOD_SHIFT              },
   1231     {"\x1b[3;5~",    TB_KEY_DELETE,      TB_MOD_CTRL                            },
   1232     {"\x1b[3;6~",    TB_KEY_DELETE,      TB_MOD_CTRL | TB_MOD_SHIFT             },
   1233     {"\x1b[3;7~",    TB_KEY_DELETE,      TB_MOD_CTRL | TB_MOD_ALT               },
   1234     {"\x1b[3;8~",    TB_KEY_DELETE,      TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1235 
   1236     {"\x1b[5;2~",    TB_KEY_PGUP,        TB_MOD_SHIFT                           },
   1237     {"\x1b[5;3~",    TB_KEY_PGUP,        TB_MOD_ALT                             },
   1238     {"\x1b[5;4~",    TB_KEY_PGUP,        TB_MOD_ALT | TB_MOD_SHIFT              },
   1239     {"\x1b[5;5~",    TB_KEY_PGUP,        TB_MOD_CTRL                            },
   1240     {"\x1b[5;6~",    TB_KEY_PGUP,        TB_MOD_CTRL | TB_MOD_SHIFT             },
   1241     {"\x1b[5;7~",    TB_KEY_PGUP,        TB_MOD_CTRL | TB_MOD_ALT               },
   1242     {"\x1b[5;8~",    TB_KEY_PGUP,        TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1243 
   1244     {"\x1b[6;2~",    TB_KEY_PGDN,        TB_MOD_SHIFT                           },
   1245     {"\x1b[6;3~",    TB_KEY_PGDN,        TB_MOD_ALT                             },
   1246     {"\x1b[6;4~",    TB_KEY_PGDN,        TB_MOD_ALT | TB_MOD_SHIFT              },
   1247     {"\x1b[6;5~",    TB_KEY_PGDN,        TB_MOD_CTRL                            },
   1248     {"\x1b[6;6~",    TB_KEY_PGDN,        TB_MOD_CTRL | TB_MOD_SHIFT             },
   1249     {"\x1b[6;7~",    TB_KEY_PGDN,        TB_MOD_CTRL | TB_MOD_ALT               },
   1250     {"\x1b[6;8~",    TB_KEY_PGDN,        TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1251 
   1252     {"\x1b[1;2P",    TB_KEY_F1,          TB_MOD_SHIFT                           },
   1253     {"\x1b[1;3P",    TB_KEY_F1,          TB_MOD_ALT                             },
   1254     {"\x1b[1;4P",    TB_KEY_F1,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1255     {"\x1b[1;5P",    TB_KEY_F1,          TB_MOD_CTRL                            },
   1256     {"\x1b[1;6P",    TB_KEY_F1,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1257     {"\x1b[1;7P",    TB_KEY_F1,          TB_MOD_CTRL | TB_MOD_ALT               },
   1258     {"\x1b[1;8P",    TB_KEY_F1,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1259 
   1260     {"\x1b[1;2Q",    TB_KEY_F2,          TB_MOD_SHIFT                           },
   1261     {"\x1b[1;3Q",    TB_KEY_F2,          TB_MOD_ALT                             },
   1262     {"\x1b[1;4Q",    TB_KEY_F2,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1263     {"\x1b[1;5Q",    TB_KEY_F2,          TB_MOD_CTRL                            },
   1264     {"\x1b[1;6Q",    TB_KEY_F2,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1265     {"\x1b[1;7Q",    TB_KEY_F2,          TB_MOD_CTRL | TB_MOD_ALT               },
   1266     {"\x1b[1;8Q",    TB_KEY_F2,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1267 
   1268     {"\x1b[1;2R",    TB_KEY_F3,          TB_MOD_SHIFT                           },
   1269     {"\x1b[1;3R",    TB_KEY_F3,          TB_MOD_ALT                             },
   1270     {"\x1b[1;4R",    TB_KEY_F3,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1271     {"\x1b[1;5R",    TB_KEY_F3,          TB_MOD_CTRL                            },
   1272     {"\x1b[1;6R",    TB_KEY_F3,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1273     {"\x1b[1;7R",    TB_KEY_F3,          TB_MOD_CTRL | TB_MOD_ALT               },
   1274     {"\x1b[1;8R",    TB_KEY_F3,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1275 
   1276     {"\x1b[1;2S",    TB_KEY_F4,          TB_MOD_SHIFT                           },
   1277     {"\x1b[1;3S",    TB_KEY_F4,          TB_MOD_ALT                             },
   1278     {"\x1b[1;4S",    TB_KEY_F4,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1279     {"\x1b[1;5S",    TB_KEY_F4,          TB_MOD_CTRL                            },
   1280     {"\x1b[1;6S",    TB_KEY_F4,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1281     {"\x1b[1;7S",    TB_KEY_F4,          TB_MOD_CTRL | TB_MOD_ALT               },
   1282     {"\x1b[1;8S",    TB_KEY_F4,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1283 
   1284     {"\x1b[15;2~",   TB_KEY_F5,          TB_MOD_SHIFT                           },
   1285     {"\x1b[15;3~",   TB_KEY_F5,          TB_MOD_ALT                             },
   1286     {"\x1b[15;4~",   TB_KEY_F5,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1287     {"\x1b[15;5~",   TB_KEY_F5,          TB_MOD_CTRL                            },
   1288     {"\x1b[15;6~",   TB_KEY_F5,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1289     {"\x1b[15;7~",   TB_KEY_F5,          TB_MOD_CTRL | TB_MOD_ALT               },
   1290     {"\x1b[15;8~",   TB_KEY_F5,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1291 
   1292     {"\x1b[17;2~",   TB_KEY_F6,          TB_MOD_SHIFT                           },
   1293     {"\x1b[17;3~",   TB_KEY_F6,          TB_MOD_ALT                             },
   1294     {"\x1b[17;4~",   TB_KEY_F6,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1295     {"\x1b[17;5~",   TB_KEY_F6,          TB_MOD_CTRL                            },
   1296     {"\x1b[17;6~",   TB_KEY_F6,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1297     {"\x1b[17;7~",   TB_KEY_F6,          TB_MOD_CTRL | TB_MOD_ALT               },
   1298     {"\x1b[17;8~",   TB_KEY_F6,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1299 
   1300     {"\x1b[18;2~",   TB_KEY_F7,          TB_MOD_SHIFT                           },
   1301     {"\x1b[18;3~",   TB_KEY_F7,          TB_MOD_ALT                             },
   1302     {"\x1b[18;4~",   TB_KEY_F7,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1303     {"\x1b[18;5~",   TB_KEY_F7,          TB_MOD_CTRL                            },
   1304     {"\x1b[18;6~",   TB_KEY_F7,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1305     {"\x1b[18;7~",   TB_KEY_F7,          TB_MOD_CTRL | TB_MOD_ALT               },
   1306     {"\x1b[18;8~",   TB_KEY_F7,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1307 
   1308     {"\x1b[19;2~",   TB_KEY_F8,          TB_MOD_SHIFT                           },
   1309     {"\x1b[19;3~",   TB_KEY_F8,          TB_MOD_ALT                             },
   1310     {"\x1b[19;4~",   TB_KEY_F8,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1311     {"\x1b[19;5~",   TB_KEY_F8,          TB_MOD_CTRL                            },
   1312     {"\x1b[19;6~",   TB_KEY_F8,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1313     {"\x1b[19;7~",   TB_KEY_F8,          TB_MOD_CTRL | TB_MOD_ALT               },
   1314     {"\x1b[19;8~",   TB_KEY_F8,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1315 
   1316     {"\x1b[20;2~",   TB_KEY_F9,          TB_MOD_SHIFT                           },
   1317     {"\x1b[20;3~",   TB_KEY_F9,          TB_MOD_ALT                             },
   1318     {"\x1b[20;4~",   TB_KEY_F9,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1319     {"\x1b[20;5~",   TB_KEY_F9,          TB_MOD_CTRL                            },
   1320     {"\x1b[20;6~",   TB_KEY_F9,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1321     {"\x1b[20;7~",   TB_KEY_F9,          TB_MOD_CTRL | TB_MOD_ALT               },
   1322     {"\x1b[20;8~",   TB_KEY_F9,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1323 
   1324     {"\x1b[21;2~",   TB_KEY_F10,         TB_MOD_SHIFT                           },
   1325     {"\x1b[21;3~",   TB_KEY_F10,         TB_MOD_ALT                             },
   1326     {"\x1b[21;4~",   TB_KEY_F10,         TB_MOD_ALT | TB_MOD_SHIFT              },
   1327     {"\x1b[21;5~",   TB_KEY_F10,         TB_MOD_CTRL                            },
   1328     {"\x1b[21;6~",   TB_KEY_F10,         TB_MOD_CTRL | TB_MOD_SHIFT             },
   1329     {"\x1b[21;7~",   TB_KEY_F10,         TB_MOD_CTRL | TB_MOD_ALT               },
   1330     {"\x1b[21;8~",   TB_KEY_F10,         TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1331 
   1332     {"\x1b[23;2~",   TB_KEY_F11,         TB_MOD_SHIFT                           },
   1333     {"\x1b[23;3~",   TB_KEY_F11,         TB_MOD_ALT                             },
   1334     {"\x1b[23;4~",   TB_KEY_F11,         TB_MOD_ALT | TB_MOD_SHIFT              },
   1335     {"\x1b[23;5~",   TB_KEY_F11,         TB_MOD_CTRL                            },
   1336     {"\x1b[23;6~",   TB_KEY_F11,         TB_MOD_CTRL | TB_MOD_SHIFT             },
   1337     {"\x1b[23;7~",   TB_KEY_F11,         TB_MOD_CTRL | TB_MOD_ALT               },
   1338     {"\x1b[23;8~",   TB_KEY_F11,         TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1339 
   1340     {"\x1b[24;2~",   TB_KEY_F12,         TB_MOD_SHIFT                           },
   1341     {"\x1b[24;3~",   TB_KEY_F12,         TB_MOD_ALT                             },
   1342     {"\x1b[24;4~",   TB_KEY_F12,         TB_MOD_ALT | TB_MOD_SHIFT              },
   1343     {"\x1b[24;5~",   TB_KEY_F12,         TB_MOD_CTRL                            },
   1344     {"\x1b[24;6~",   TB_KEY_F12,         TB_MOD_CTRL | TB_MOD_SHIFT             },
   1345     {"\x1b[24;7~",   TB_KEY_F12,         TB_MOD_CTRL | TB_MOD_ALT               },
   1346     {"\x1b[24;8~",   TB_KEY_F12,         TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1347 
   1348     // rxvt arrows
   1349     {"\x1b[a",       TB_KEY_ARROW_UP,    TB_MOD_SHIFT                           },
   1350     {"\x1b\x1b[A",   TB_KEY_ARROW_UP,    TB_MOD_ALT                             },
   1351     {"\x1b\x1b[a",   TB_KEY_ARROW_UP,    TB_MOD_ALT | TB_MOD_SHIFT              },
   1352     {"\x1bOa",       TB_KEY_ARROW_UP,    TB_MOD_CTRL                            },
   1353     {"\x1b\x1bOa",   TB_KEY_ARROW_UP,    TB_MOD_CTRL | TB_MOD_ALT               },
   1354 
   1355     {"\x1b[b",       TB_KEY_ARROW_DOWN,  TB_MOD_SHIFT                           },
   1356     {"\x1b\x1b[B",   TB_KEY_ARROW_DOWN,  TB_MOD_ALT                             },
   1357     {"\x1b\x1b[b",   TB_KEY_ARROW_DOWN,  TB_MOD_ALT | TB_MOD_SHIFT              },
   1358     {"\x1bOb",       TB_KEY_ARROW_DOWN,  TB_MOD_CTRL                            },
   1359     {"\x1b\x1bOb",   TB_KEY_ARROW_DOWN,  TB_MOD_CTRL | TB_MOD_ALT               },
   1360 
   1361     {"\x1b[c",       TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT                           },
   1362     {"\x1b\x1b[C",   TB_KEY_ARROW_RIGHT, TB_MOD_ALT                             },
   1363     {"\x1b\x1b[c",   TB_KEY_ARROW_RIGHT, TB_MOD_ALT | TB_MOD_SHIFT              },
   1364     {"\x1bOc",       TB_KEY_ARROW_RIGHT, TB_MOD_CTRL                            },
   1365     {"\x1b\x1bOc",   TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT               },
   1366 
   1367     {"\x1b[d",       TB_KEY_ARROW_LEFT,  TB_MOD_SHIFT                           },
   1368     {"\x1b\x1b[D",   TB_KEY_ARROW_LEFT,  TB_MOD_ALT                             },
   1369     {"\x1b\x1b[d",   TB_KEY_ARROW_LEFT,  TB_MOD_ALT | TB_MOD_SHIFT              },
   1370     {"\x1bOd",       TB_KEY_ARROW_LEFT,  TB_MOD_CTRL                            },
   1371     {"\x1b\x1bOd",   TB_KEY_ARROW_LEFT,  TB_MOD_CTRL | TB_MOD_ALT               },
   1372 
   1373     // rxvt keys
   1374     {"\x1b[7$",      TB_KEY_HOME,        TB_MOD_SHIFT                           },
   1375     {"\x1b\x1b[7~",  TB_KEY_HOME,        TB_MOD_ALT                             },
   1376     {"\x1b\x1b[7$",  TB_KEY_HOME,        TB_MOD_ALT | TB_MOD_SHIFT              },
   1377     {"\x1b[7^",      TB_KEY_HOME,        TB_MOD_CTRL                            },
   1378     {"\x1b[7@",      TB_KEY_HOME,        TB_MOD_CTRL | TB_MOD_SHIFT             },
   1379     {"\x1b\x1b[7^",  TB_KEY_HOME,        TB_MOD_CTRL | TB_MOD_ALT               },
   1380     {"\x1b\x1b[7@",  TB_KEY_HOME,        TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1381 
   1382     {"\x1b\x1b[8~",  TB_KEY_END,         TB_MOD_ALT                             },
   1383     {"\x1b\x1b[8$",  TB_KEY_END,         TB_MOD_ALT | TB_MOD_SHIFT              },
   1384     {"\x1b[8^",      TB_KEY_END,         TB_MOD_CTRL                            },
   1385     {"\x1b\x1b[8^",  TB_KEY_END,         TB_MOD_CTRL | TB_MOD_ALT               },
   1386     {"\x1b\x1b[8@",  TB_KEY_END,         TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1387     {"\x1b[8@",      TB_KEY_END,         TB_MOD_CTRL | TB_MOD_SHIFT             },
   1388     {"\x1b[8$",      TB_KEY_END,         TB_MOD_SHIFT                           },
   1389 
   1390     {"\x1b\x1b[2~",  TB_KEY_INSERT,      TB_MOD_ALT                             },
   1391     {"\x1b\x1b[2$",  TB_KEY_INSERT,      TB_MOD_ALT | TB_MOD_SHIFT              },
   1392     {"\x1b[2^",      TB_KEY_INSERT,      TB_MOD_CTRL                            },
   1393     {"\x1b\x1b[2^",  TB_KEY_INSERT,      TB_MOD_CTRL | TB_MOD_ALT               },
   1394     {"\x1b\x1b[2@",  TB_KEY_INSERT,      TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1395     {"\x1b[2@",      TB_KEY_INSERT,      TB_MOD_CTRL | TB_MOD_SHIFT             },
   1396     {"\x1b[2$",      TB_KEY_INSERT,      TB_MOD_SHIFT                           },
   1397 
   1398     {"\x1b\x1b[3~",  TB_KEY_DELETE,      TB_MOD_ALT                             },
   1399     {"\x1b\x1b[3$",  TB_KEY_DELETE,      TB_MOD_ALT | TB_MOD_SHIFT              },
   1400     {"\x1b[3^",      TB_KEY_DELETE,      TB_MOD_CTRL                            },
   1401     {"\x1b\x1b[3^",  TB_KEY_DELETE,      TB_MOD_CTRL | TB_MOD_ALT               },
   1402     {"\x1b\x1b[3@",  TB_KEY_DELETE,      TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1403     {"\x1b[3@",      TB_KEY_DELETE,      TB_MOD_CTRL | TB_MOD_SHIFT             },
   1404     {"\x1b[3$",      TB_KEY_DELETE,      TB_MOD_SHIFT                           },
   1405 
   1406     {"\x1b\x1b[5~",  TB_KEY_PGUP,        TB_MOD_ALT                             },
   1407     {"\x1b\x1b[5$",  TB_KEY_PGUP,        TB_MOD_ALT | TB_MOD_SHIFT              },
   1408     {"\x1b[5^",      TB_KEY_PGUP,        TB_MOD_CTRL                            },
   1409     {"\x1b\x1b[5^",  TB_KEY_PGUP,        TB_MOD_CTRL | TB_MOD_ALT               },
   1410     {"\x1b\x1b[5@",  TB_KEY_PGUP,        TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1411     {"\x1b[5@",      TB_KEY_PGUP,        TB_MOD_CTRL | TB_MOD_SHIFT             },
   1412     {"\x1b[5$",      TB_KEY_PGUP,        TB_MOD_SHIFT                           },
   1413 
   1414     {"\x1b\x1b[6~",  TB_KEY_PGDN,        TB_MOD_ALT                             },
   1415     {"\x1b\x1b[6$",  TB_KEY_PGDN,        TB_MOD_ALT | TB_MOD_SHIFT              },
   1416     {"\x1b[6^",      TB_KEY_PGDN,        TB_MOD_CTRL                            },
   1417     {"\x1b\x1b[6^",  TB_KEY_PGDN,        TB_MOD_CTRL | TB_MOD_ALT               },
   1418     {"\x1b\x1b[6@",  TB_KEY_PGDN,        TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1419     {"\x1b[6@",      TB_KEY_PGDN,        TB_MOD_CTRL | TB_MOD_SHIFT             },
   1420     {"\x1b[6$",      TB_KEY_PGDN,        TB_MOD_SHIFT                           },
   1421 
   1422     {"\x1b\x1b[11~", TB_KEY_F1,          TB_MOD_ALT                             },
   1423     {"\x1b\x1b[23~", TB_KEY_F1,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1424     {"\x1b[11^",     TB_KEY_F1,          TB_MOD_CTRL                            },
   1425     {"\x1b\x1b[11^", TB_KEY_F1,          TB_MOD_CTRL | TB_MOD_ALT               },
   1426     {"\x1b\x1b[23^", TB_KEY_F1,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1427     {"\x1b[23^",     TB_KEY_F1,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1428     {"\x1b[23~",     TB_KEY_F1,          TB_MOD_SHIFT                           },
   1429 
   1430     {"\x1b\x1b[12~", TB_KEY_F2,          TB_MOD_ALT                             },
   1431     {"\x1b\x1b[24~", TB_KEY_F2,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1432     {"\x1b[12^",     TB_KEY_F2,          TB_MOD_CTRL                            },
   1433     {"\x1b\x1b[12^", TB_KEY_F2,          TB_MOD_CTRL | TB_MOD_ALT               },
   1434     {"\x1b\x1b[24^", TB_KEY_F2,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1435     {"\x1b[24^",     TB_KEY_F2,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1436     {"\x1b[24~",     TB_KEY_F2,          TB_MOD_SHIFT                           },
   1437 
   1438     {"\x1b\x1b[13~", TB_KEY_F3,          TB_MOD_ALT                             },
   1439     {"\x1b\x1b[25~", TB_KEY_F3,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1440     {"\x1b[13^",     TB_KEY_F3,          TB_MOD_CTRL                            },
   1441     {"\x1b\x1b[13^", TB_KEY_F3,          TB_MOD_CTRL | TB_MOD_ALT               },
   1442     {"\x1b\x1b[25^", TB_KEY_F3,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1443     {"\x1b[25^",     TB_KEY_F3,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1444     {"\x1b[25~",     TB_KEY_F3,          TB_MOD_SHIFT                           },
   1445 
   1446     {"\x1b\x1b[14~", TB_KEY_F4,          TB_MOD_ALT                             },
   1447     {"\x1b\x1b[26~", TB_KEY_F4,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1448     {"\x1b[14^",     TB_KEY_F4,          TB_MOD_CTRL                            },
   1449     {"\x1b\x1b[14^", TB_KEY_F4,          TB_MOD_CTRL | TB_MOD_ALT               },
   1450     {"\x1b\x1b[26^", TB_KEY_F4,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1451     {"\x1b[26^",     TB_KEY_F4,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1452     {"\x1b[26~",     TB_KEY_F4,          TB_MOD_SHIFT                           },
   1453 
   1454     {"\x1b\x1b[15~", TB_KEY_F5,          TB_MOD_ALT                             },
   1455     {"\x1b\x1b[28~", TB_KEY_F5,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1456     {"\x1b[15^",     TB_KEY_F5,          TB_MOD_CTRL                            },
   1457     {"\x1b\x1b[15^", TB_KEY_F5,          TB_MOD_CTRL | TB_MOD_ALT               },
   1458     {"\x1b\x1b[28^", TB_KEY_F5,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1459     {"\x1b[28^",     TB_KEY_F5,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1460     {"\x1b[28~",     TB_KEY_F5,          TB_MOD_SHIFT                           },
   1461 
   1462     {"\x1b\x1b[17~", TB_KEY_F6,          TB_MOD_ALT                             },
   1463     {"\x1b\x1b[29~", TB_KEY_F6,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1464     {"\x1b[17^",     TB_KEY_F6,          TB_MOD_CTRL                            },
   1465     {"\x1b\x1b[17^", TB_KEY_F6,          TB_MOD_CTRL | TB_MOD_ALT               },
   1466     {"\x1b\x1b[29^", TB_KEY_F6,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1467     {"\x1b[29^",     TB_KEY_F6,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1468     {"\x1b[29~",     TB_KEY_F6,          TB_MOD_SHIFT                           },
   1469 
   1470     {"\x1b\x1b[18~", TB_KEY_F7,          TB_MOD_ALT                             },
   1471     {"\x1b\x1b[31~", TB_KEY_F7,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1472     {"\x1b[18^",     TB_KEY_F7,          TB_MOD_CTRL                            },
   1473     {"\x1b\x1b[18^", TB_KEY_F7,          TB_MOD_CTRL | TB_MOD_ALT               },
   1474     {"\x1b\x1b[31^", TB_KEY_F7,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1475     {"\x1b[31^",     TB_KEY_F7,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1476     {"\x1b[31~",     TB_KEY_F7,          TB_MOD_SHIFT                           },
   1477 
   1478     {"\x1b\x1b[19~", TB_KEY_F8,          TB_MOD_ALT                             },
   1479     {"\x1b\x1b[32~", TB_KEY_F8,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1480     {"\x1b[19^",     TB_KEY_F8,          TB_MOD_CTRL                            },
   1481     {"\x1b\x1b[19^", TB_KEY_F8,          TB_MOD_CTRL | TB_MOD_ALT               },
   1482     {"\x1b\x1b[32^", TB_KEY_F8,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1483     {"\x1b[32^",     TB_KEY_F8,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1484     {"\x1b[32~",     TB_KEY_F8,          TB_MOD_SHIFT                           },
   1485 
   1486     {"\x1b\x1b[20~", TB_KEY_F9,          TB_MOD_ALT                             },
   1487     {"\x1b\x1b[33~", TB_KEY_F9,          TB_MOD_ALT | TB_MOD_SHIFT              },
   1488     {"\x1b[20^",     TB_KEY_F9,          TB_MOD_CTRL                            },
   1489     {"\x1b\x1b[20^", TB_KEY_F9,          TB_MOD_CTRL | TB_MOD_ALT               },
   1490     {"\x1b\x1b[33^", TB_KEY_F9,          TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1491     {"\x1b[33^",     TB_KEY_F9,          TB_MOD_CTRL | TB_MOD_SHIFT             },
   1492     {"\x1b[33~",     TB_KEY_F9,          TB_MOD_SHIFT                           },
   1493 
   1494     {"\x1b\x1b[21~", TB_KEY_F10,         TB_MOD_ALT                             },
   1495     {"\x1b\x1b[34~", TB_KEY_F10,         TB_MOD_ALT | TB_MOD_SHIFT              },
   1496     {"\x1b[21^",     TB_KEY_F10,         TB_MOD_CTRL                            },
   1497     {"\x1b\x1b[21^", TB_KEY_F10,         TB_MOD_CTRL | TB_MOD_ALT               },
   1498     {"\x1b\x1b[34^", TB_KEY_F10,         TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1499     {"\x1b[34^",     TB_KEY_F10,         TB_MOD_CTRL | TB_MOD_SHIFT             },
   1500     {"\x1b[34~",     TB_KEY_F10,         TB_MOD_SHIFT                           },
   1501 
   1502     {"\x1b\x1b[23~", TB_KEY_F11,         TB_MOD_ALT                             },
   1503     {"\x1b\x1b[23$", TB_KEY_F11,         TB_MOD_ALT | TB_MOD_SHIFT              },
   1504     {"\x1b[23^",     TB_KEY_F11,         TB_MOD_CTRL                            },
   1505     {"\x1b\x1b[23^", TB_KEY_F11,         TB_MOD_CTRL | TB_MOD_ALT               },
   1506     {"\x1b\x1b[23@", TB_KEY_F11,         TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1507     {"\x1b[23@",     TB_KEY_F11,         TB_MOD_CTRL | TB_MOD_SHIFT             },
   1508     {"\x1b[23$",     TB_KEY_F11,         TB_MOD_SHIFT                           },
   1509 
   1510     {"\x1b\x1b[24~", TB_KEY_F12,         TB_MOD_ALT                             },
   1511     {"\x1b\x1b[24$", TB_KEY_F12,         TB_MOD_ALT | TB_MOD_SHIFT              },
   1512     {"\x1b[24^",     TB_KEY_F12,         TB_MOD_CTRL                            },
   1513     {"\x1b\x1b[24^", TB_KEY_F12,         TB_MOD_CTRL | TB_MOD_ALT               },
   1514     {"\x1b\x1b[24@", TB_KEY_F12,         TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
   1515     {"\x1b[24@",     TB_KEY_F12,         TB_MOD_CTRL | TB_MOD_SHIFT             },
   1516     {"\x1b[24$",     TB_KEY_F12,         TB_MOD_SHIFT                           },
   1517 
   1518     // linux console/putty arrows
   1519     {"\x1b[A",       TB_KEY_ARROW_UP,    TB_MOD_SHIFT                           },
   1520     {"\x1b[B",       TB_KEY_ARROW_DOWN,  TB_MOD_SHIFT                           },
   1521     {"\x1b[C",       TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT                           },
   1522     {"\x1b[D",       TB_KEY_ARROW_LEFT,  TB_MOD_SHIFT                           },
   1523 
   1524     // more putty arrows
   1525     {"\x1bOA",       TB_KEY_ARROW_UP,    TB_MOD_CTRL                            },
   1526     {"\x1b\x1bOA",   TB_KEY_ARROW_UP,    TB_MOD_CTRL | TB_MOD_ALT               },
   1527     {"\x1bOB",       TB_KEY_ARROW_DOWN,  TB_MOD_CTRL                            },
   1528     {"\x1b\x1bOB",   TB_KEY_ARROW_DOWN,  TB_MOD_CTRL | TB_MOD_ALT               },
   1529     {"\x1bOC",       TB_KEY_ARROW_RIGHT, TB_MOD_CTRL                            },
   1530     {"\x1b\x1bOC",   TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT               },
   1531     {"\x1bOD",       TB_KEY_ARROW_LEFT,  TB_MOD_CTRL                            },
   1532     {"\x1b\x1bOD",   TB_KEY_ARROW_LEFT,  TB_MOD_CTRL | TB_MOD_ALT               },
   1533 
   1534     {NULL,           0,                  0                                      },
   1535 };
   1536 
   1537 static const unsigned char utf8_length[256] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
   1538     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,
   1539     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,
   1540     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,
   1541     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,
   1542     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,
   1543     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,
   1544     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,
   1545     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,
   1546     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,
   1547     3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1};
   1548 
   1549 static const unsigned char utf8_mask[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
   1550 
   1551 #ifndef TB_OPT_LIBC_WCHAR
   1552 static struct {
   1553     uint32_t range_start;
   1554     uint32_t range_end;
   1555     int width; // -1 means iswprint==0, otherwise wcwidth value (0, 1, or 2)
   1556 } wcwidth_table[] = {
   1557     // clang-format off
   1558     {0x000001, 0x00001f, -1}, {0x000020, 0x00007e,  1}, {0x00007f, 0x00009f, -1},
   1559     {0x0000a0, 0x0002ff,  1}, {0x000300, 0x00036f,  0}, {0x000370, 0x000377,  1},
   1560     {0x000378, 0x000379, -1}, {0x00037a, 0x00037f,  1}, {0x000380, 0x000383, -1},
   1561     {0x000384, 0x00038a,  1}, {0x00038b, 0x00038b, -1}, {0x00038c, 0x00038c,  1},
   1562     {0x00038d, 0x00038d, -1}, {0x00038e, 0x0003a1,  1}, {0x0003a2, 0x0003a2, -1},
   1563     {0x0003a3, 0x000482,  1}, {0x000483, 0x000489,  0}, {0x00048a, 0x00052f,  1},
   1564     {0x000530, 0x000530, -1}, {0x000531, 0x000556,  1}, {0x000557, 0x000558, -1},
   1565     {0x000559, 0x00058a,  1}, {0x00058b, 0x00058c, -1}, {0x00058d, 0x00058f,  1},
   1566     {0x000590, 0x000590, -1}, {0x000591, 0x0005bd,  0}, {0x0005be, 0x0005be,  1},
   1567     {0x0005bf, 0x0005bf,  0}, {0x0005c0, 0x0005c0,  1}, {0x0005c1, 0x0005c2,  0},
   1568     {0x0005c3, 0x0005c3,  1}, {0x0005c4, 0x0005c5,  0}, {0x0005c6, 0x0005c6,  1},
   1569     {0x0005c7, 0x0005c7,  0}, {0x0005c8, 0x0005cf, -1}, {0x0005d0, 0x0005ea,  1},
   1570     {0x0005eb, 0x0005ee, -1}, {0x0005ef, 0x0005f4,  1}, {0x0005f5, 0x0005ff, -1},
   1571     {0x000600, 0x00060f,  1}, {0x000610, 0x00061a,  0}, {0x00061b, 0x00061b,  1},
   1572     {0x00061c, 0x00061c,  0}, {0x00061d, 0x00064a,  1}, {0x00064b, 0x00065f,  0},
   1573     {0x000660, 0x00066f,  1}, {0x000670, 0x000670,  0}, {0x000671, 0x0006d5,  1},
   1574     {0x0006d6, 0x0006dc,  0}, {0x0006dd, 0x0006de,  1}, {0x0006df, 0x0006e4,  0},
   1575     {0x0006e5, 0x0006e6,  1}, {0x0006e7, 0x0006e8,  0}, {0x0006e9, 0x0006e9,  1},
   1576     {0x0006ea, 0x0006ed,  0}, {0x0006ee, 0x00070d,  1}, {0x00070e, 0x00070e, -1},
   1577     {0x00070f, 0x000710,  1}, {0x000711, 0x000711,  0}, {0x000712, 0x00072f,  1},
   1578     {0x000730, 0x00074a,  0}, {0x00074b, 0x00074c, -1}, {0x00074d, 0x0007a5,  1},
   1579     {0x0007a6, 0x0007b0,  0}, {0x0007b1, 0x0007b1,  1}, {0x0007b2, 0x0007bf, -1},
   1580     {0x0007c0, 0x0007ea,  1}, {0x0007eb, 0x0007f3,  0}, {0x0007f4, 0x0007fa,  1},
   1581     {0x0007fb, 0x0007fc, -1}, {0x0007fd, 0x0007fd,  0}, {0x0007fe, 0x000815,  1},
   1582     {0x000816, 0x000819,  0}, {0x00081a, 0x00081a,  1}, {0x00081b, 0x000823,  0},
   1583     {0x000824, 0x000824,  1}, {0x000825, 0x000827,  0}, {0x000828, 0x000828,  1},
   1584     {0x000829, 0x00082d,  0}, {0x00082e, 0x00082f, -1}, {0x000830, 0x00083e,  1},
   1585     {0x00083f, 0x00083f, -1}, {0x000840, 0x000858,  1}, {0x000859, 0x00085b,  0},
   1586     {0x00085c, 0x00085d, -1}, {0x00085e, 0x00085e,  1}, {0x00085f, 0x00085f, -1},
   1587     {0x000860, 0x00086a,  1}, {0x00086b, 0x00086f, -1}, {0x000870, 0x00088e,  1},
   1588     {0x00088f, 0x00088f, -1}, {0x000890, 0x000891,  1}, {0x000892, 0x000896, -1},
   1589     {0x000897, 0x00089f,  0}, {0x0008a0, 0x0008c9,  1}, {0x0008ca, 0x0008e1,  0},
   1590     {0x0008e2, 0x0008e2,  1}, {0x0008e3, 0x000902,  0}, {0x000903, 0x000939,  1},
   1591     {0x00093a, 0x00093a,  0}, {0x00093b, 0x00093b,  1}, {0x00093c, 0x00093c,  0},
   1592     {0x00093d, 0x000940,  1}, {0x000941, 0x000948,  0}, {0x000949, 0x00094c,  1},
   1593     {0x00094d, 0x00094d,  0}, {0x00094e, 0x000950,  1}, {0x000951, 0x000957,  0},
   1594     {0x000958, 0x000961,  1}, {0x000962, 0x000963,  0}, {0x000964, 0x000980,  1},
   1595     {0x000981, 0x000981,  0}, {0x000982, 0x000983,  1}, {0x000984, 0x000984, -1},
   1596     {0x000985, 0x00098c,  1}, {0x00098d, 0x00098e, -1}, {0x00098f, 0x000990,  1},
   1597     {0x000991, 0x000992, -1}, {0x000993, 0x0009a8,  1}, {0x0009a9, 0x0009a9, -1},
   1598     {0x0009aa, 0x0009b0,  1}, {0x0009b1, 0x0009b1, -1}, {0x0009b2, 0x0009b2,  1},
   1599     {0x0009b3, 0x0009b5, -1}, {0x0009b6, 0x0009b9,  1}, {0x0009ba, 0x0009bb, -1},
   1600     {0x0009bc, 0x0009bc,  0}, {0x0009bd, 0x0009c0,  1}, {0x0009c1, 0x0009c4,  0},
   1601     {0x0009c5, 0x0009c6, -1}, {0x0009c7, 0x0009c8,  1}, {0x0009c9, 0x0009ca, -1},
   1602     {0x0009cb, 0x0009cc,  1}, {0x0009cd, 0x0009cd,  0}, {0x0009ce, 0x0009ce,  1},
   1603     {0x0009cf, 0x0009d6, -1}, {0x0009d7, 0x0009d7,  1}, {0x0009d8, 0x0009db, -1},
   1604     {0x0009dc, 0x0009dd,  1}, {0x0009de, 0x0009de, -1}, {0x0009df, 0x0009e1,  1},
   1605     {0x0009e2, 0x0009e3,  0}, {0x0009e4, 0x0009e5, -1}, {0x0009e6, 0x0009fd,  1},
   1606     {0x0009fe, 0x0009fe,  0}, {0x0009ff, 0x000a00, -1}, {0x000a01, 0x000a02,  0},
   1607     {0x000a03, 0x000a03,  1}, {0x000a04, 0x000a04, -1}, {0x000a05, 0x000a0a,  1},
   1608     {0x000a0b, 0x000a0e, -1}, {0x000a0f, 0x000a10,  1}, {0x000a11, 0x000a12, -1},
   1609     {0x000a13, 0x000a28,  1}, {0x000a29, 0x000a29, -1}, {0x000a2a, 0x000a30,  1},
   1610     {0x000a31, 0x000a31, -1}, {0x000a32, 0x000a33,  1}, {0x000a34, 0x000a34, -1},
   1611     {0x000a35, 0x000a36,  1}, {0x000a37, 0x000a37, -1}, {0x000a38, 0x000a39,  1},
   1612     {0x000a3a, 0x000a3b, -1}, {0x000a3c, 0x000a3c,  0}, {0x000a3d, 0x000a3d, -1},
   1613     {0x000a3e, 0x000a40,  1}, {0x000a41, 0x000a42,  0}, {0x000a43, 0x000a46, -1},
   1614     {0x000a47, 0x000a48,  0}, {0x000a49, 0x000a4a, -1}, {0x000a4b, 0x000a4d,  0},
   1615     {0x000a4e, 0x000a50, -1}, {0x000a51, 0x000a51,  0}, {0x000a52, 0x000a58, -1},
   1616     {0x000a59, 0x000a5c,  1}, {0x000a5d, 0x000a5d, -1}, {0x000a5e, 0x000a5e,  1},
   1617     {0x000a5f, 0x000a65, -1}, {0x000a66, 0x000a6f,  1}, {0x000a70, 0x000a71,  0},
   1618     {0x000a72, 0x000a74,  1}, {0x000a75, 0x000a75,  0}, {0x000a76, 0x000a76,  1},
   1619     {0x000a77, 0x000a80, -1}, {0x000a81, 0x000a82,  0}, {0x000a83, 0x000a83,  1},
   1620     {0x000a84, 0x000a84, -1}, {0x000a85, 0x000a8d,  1}, {0x000a8e, 0x000a8e, -1},
   1621     {0x000a8f, 0x000a91,  1}, {0x000a92, 0x000a92, -1}, {0x000a93, 0x000aa8,  1},
   1622     {0x000aa9, 0x000aa9, -1}, {0x000aaa, 0x000ab0,  1}, {0x000ab1, 0x000ab1, -1},
   1623     {0x000ab2, 0x000ab3,  1}, {0x000ab4, 0x000ab4, -1}, {0x000ab5, 0x000ab9,  1},
   1624     {0x000aba, 0x000abb, -1}, {0x000abc, 0x000abc,  0}, {0x000abd, 0x000ac0,  1},
   1625     {0x000ac1, 0x000ac5,  0}, {0x000ac6, 0x000ac6, -1}, {0x000ac7, 0x000ac8,  0},
   1626     {0x000ac9, 0x000ac9,  1}, {0x000aca, 0x000aca, -1}, {0x000acb, 0x000acc,  1},
   1627     {0x000acd, 0x000acd,  0}, {0x000ace, 0x000acf, -1}, {0x000ad0, 0x000ad0,  1},
   1628     {0x000ad1, 0x000adf, -1}, {0x000ae0, 0x000ae1,  1}, {0x000ae2, 0x000ae3,  0},
   1629     {0x000ae4, 0x000ae5, -1}, {0x000ae6, 0x000af1,  1}, {0x000af2, 0x000af8, -1},
   1630     {0x000af9, 0x000af9,  1}, {0x000afa, 0x000aff,  0}, {0x000b00, 0x000b00, -1},
   1631     {0x000b01, 0x000b01,  0}, {0x000b02, 0x000b03,  1}, {0x000b04, 0x000b04, -1},
   1632     {0x000b05, 0x000b0c,  1}, {0x000b0d, 0x000b0e, -1}, {0x000b0f, 0x000b10,  1},
   1633     {0x000b11, 0x000b12, -1}, {0x000b13, 0x000b28,  1}, {0x000b29, 0x000b29, -1},
   1634     {0x000b2a, 0x000b30,  1}, {0x000b31, 0x000b31, -1}, {0x000b32, 0x000b33,  1},
   1635     {0x000b34, 0x000b34, -1}, {0x000b35, 0x000b39,  1}, {0x000b3a, 0x000b3b, -1},
   1636     {0x000b3c, 0x000b3c,  0}, {0x000b3d, 0x000b3e,  1}, {0x000b3f, 0x000b3f,  0},
   1637     {0x000b40, 0x000b40,  1}, {0x000b41, 0x000b44,  0}, {0x000b45, 0x000b46, -1},
   1638     {0x000b47, 0x000b48,  1}, {0x000b49, 0x000b4a, -1}, {0x000b4b, 0x000b4c,  1},
   1639     {0x000b4d, 0x000b4d,  0}, {0x000b4e, 0x000b54, -1}, {0x000b55, 0x000b56,  0},
   1640     {0x000b57, 0x000b57,  1}, {0x000b58, 0x000b5b, -1}, {0x000b5c, 0x000b5d,  1},
   1641     {0x000b5e, 0x000b5e, -1}, {0x000b5f, 0x000b61,  1}, {0x000b62, 0x000b63,  0},
   1642     {0x000b64, 0x000b65, -1}, {0x000b66, 0x000b77,  1}, {0x000b78, 0x000b81, -1},
   1643     {0x000b82, 0x000b82,  0}, {0x000b83, 0x000b83,  1}, {0x000b84, 0x000b84, -1},
   1644     {0x000b85, 0x000b8a,  1}, {0x000b8b, 0x000b8d, -1}, {0x000b8e, 0x000b90,  1},
   1645     {0x000b91, 0x000b91, -1}, {0x000b92, 0x000b95,  1}, {0x000b96, 0x000b98, -1},
   1646     {0x000b99, 0x000b9a,  1}, {0x000b9b, 0x000b9b, -1}, {0x000b9c, 0x000b9c,  1},
   1647     {0x000b9d, 0x000b9d, -1}, {0x000b9e, 0x000b9f,  1}, {0x000ba0, 0x000ba2, -1},
   1648     {0x000ba3, 0x000ba4,  1}, {0x000ba5, 0x000ba7, -1}, {0x000ba8, 0x000baa,  1},
   1649     {0x000bab, 0x000bad, -1}, {0x000bae, 0x000bb9,  1}, {0x000bba, 0x000bbd, -1},
   1650     {0x000bbe, 0x000bbf,  1}, {0x000bc0, 0x000bc0,  0}, {0x000bc1, 0x000bc2,  1},
   1651     {0x000bc3, 0x000bc5, -1}, {0x000bc6, 0x000bc8,  1}, {0x000bc9, 0x000bc9, -1},
   1652     {0x000bca, 0x000bcc,  1}, {0x000bcd, 0x000bcd,  0}, {0x000bce, 0x000bcf, -1},
   1653     {0x000bd0, 0x000bd0,  1}, {0x000bd1, 0x000bd6, -1}, {0x000bd7, 0x000bd7,  1},
   1654     {0x000bd8, 0x000be5, -1}, {0x000be6, 0x000bfa,  1}, {0x000bfb, 0x000bff, -1},
   1655     {0x000c00, 0x000c00,  0}, {0x000c01, 0x000c03,  1}, {0x000c04, 0x000c04,  0},
   1656     {0x000c05, 0x000c0c,  1}, {0x000c0d, 0x000c0d, -1}, {0x000c0e, 0x000c10,  1},
   1657     {0x000c11, 0x000c11, -1}, {0x000c12, 0x000c28,  1}, {0x000c29, 0x000c29, -1},
   1658     {0x000c2a, 0x000c39,  1}, {0x000c3a, 0x000c3b, -1}, {0x000c3c, 0x000c3c,  0},
   1659     {0x000c3d, 0x000c3d,  1}, {0x000c3e, 0x000c40,  0}, {0x000c41, 0x000c44,  1},
   1660     {0x000c45, 0x000c45, -1}, {0x000c46, 0x000c48,  0}, {0x000c49, 0x000c49, -1},
   1661     {0x000c4a, 0x000c4d,  0}, {0x000c4e, 0x000c54, -1}, {0x000c55, 0x000c56,  0},
   1662     {0x000c57, 0x000c57, -1}, {0x000c58, 0x000c5a,  1}, {0x000c5b, 0x000c5c, -1},
   1663     {0x000c5d, 0x000c5d,  1}, {0x000c5e, 0x000c5f, -1}, {0x000c60, 0x000c61,  1},
   1664     {0x000c62, 0x000c63,  0}, {0x000c64, 0x000c65, -1}, {0x000c66, 0x000c6f,  1},
   1665     {0x000c70, 0x000c76, -1}, {0x000c77, 0x000c80,  1}, {0x000c81, 0x000c81,  0},
   1666     {0x000c82, 0x000c8c,  1}, {0x000c8d, 0x000c8d, -1}, {0x000c8e, 0x000c90,  1},
   1667     {0x000c91, 0x000c91, -1}, {0x000c92, 0x000ca8,  1}, {0x000ca9, 0x000ca9, -1},
   1668     {0x000caa, 0x000cb3,  1}, {0x000cb4, 0x000cb4, -1}, {0x000cb5, 0x000cb9,  1},
   1669     {0x000cba, 0x000cbb, -1}, {0x000cbc, 0x000cbc,  0}, {0x000cbd, 0x000cbe,  1},
   1670     {0x000cbf, 0x000cbf,  0}, {0x000cc0, 0x000cc4,  1}, {0x000cc5, 0x000cc5, -1},
   1671     {0x000cc6, 0x000cc6,  0}, {0x000cc7, 0x000cc8,  1}, {0x000cc9, 0x000cc9, -1},
   1672     {0x000cca, 0x000ccb,  1}, {0x000ccc, 0x000ccd,  0}, {0x000cce, 0x000cd4, -1},
   1673     {0x000cd5, 0x000cd6,  1}, {0x000cd7, 0x000cdc, -1}, {0x000cdd, 0x000cde,  1},
   1674     {0x000cdf, 0x000cdf, -1}, {0x000ce0, 0x000ce1,  1}, {0x000ce2, 0x000ce3,  0},
   1675     {0x000ce4, 0x000ce5, -1}, {0x000ce6, 0x000cef,  1}, {0x000cf0, 0x000cf0, -1},
   1676     {0x000cf1, 0x000cf3,  1}, {0x000cf4, 0x000cff, -1}, {0x000d00, 0x000d01,  0},
   1677     {0x000d02, 0x000d0c,  1}, {0x000d0d, 0x000d0d, -1}, {0x000d0e, 0x000d10,  1},
   1678     {0x000d11, 0x000d11, -1}, {0x000d12, 0x000d3a,  1}, {0x000d3b, 0x000d3c,  0},
   1679     {0x000d3d, 0x000d40,  1}, {0x000d41, 0x000d44,  0}, {0x000d45, 0x000d45, -1},
   1680     {0x000d46, 0x000d48,  1}, {0x000d49, 0x000d49, -1}, {0x000d4a, 0x000d4c,  1},
   1681     {0x000d4d, 0x000d4d,  0}, {0x000d4e, 0x000d4f,  1}, {0x000d50, 0x000d53, -1},
   1682     {0x000d54, 0x000d61,  1}, {0x000d62, 0x000d63,  0}, {0x000d64, 0x000d65, -1},
   1683     {0x000d66, 0x000d7f,  1}, {0x000d80, 0x000d80, -1}, {0x000d81, 0x000d81,  0},
   1684     {0x000d82, 0x000d83,  1}, {0x000d84, 0x000d84, -1}, {0x000d85, 0x000d96,  1},
   1685     {0x000d97, 0x000d99, -1}, {0x000d9a, 0x000db1,  1}, {0x000db2, 0x000db2, -1},
   1686     {0x000db3, 0x000dbb,  1}, {0x000dbc, 0x000dbc, -1}, {0x000dbd, 0x000dbd,  1},
   1687     {0x000dbe, 0x000dbf, -1}, {0x000dc0, 0x000dc6,  1}, {0x000dc7, 0x000dc9, -1},
   1688     {0x000dca, 0x000dca,  0}, {0x000dcb, 0x000dce, -1}, {0x000dcf, 0x000dd1,  1},
   1689     {0x000dd2, 0x000dd4,  0}, {0x000dd5, 0x000dd5, -1}, {0x000dd6, 0x000dd6,  0},
   1690     {0x000dd7, 0x000dd7, -1}, {0x000dd8, 0x000ddf,  1}, {0x000de0, 0x000de5, -1},
   1691     {0x000de6, 0x000def,  1}, {0x000df0, 0x000df1, -1}, {0x000df2, 0x000df4,  1},
   1692     {0x000df5, 0x000e00, -1}, {0x000e01, 0x000e30,  1}, {0x000e31, 0x000e31,  0},
   1693     {0x000e32, 0x000e33,  1}, {0x000e34, 0x000e3a,  0}, {0x000e3b, 0x000e3e, -1},
   1694     {0x000e3f, 0x000e46,  1}, {0x000e47, 0x000e4e,  0}, {0x000e4f, 0x000e5b,  1},
   1695     {0x000e5c, 0x000e80, -1}, {0x000e81, 0x000e82,  1}, {0x000e83, 0x000e83, -1},
   1696     {0x000e84, 0x000e84,  1}, {0x000e85, 0x000e85, -1}, {0x000e86, 0x000e8a,  1},
   1697     {0x000e8b, 0x000e8b, -1}, {0x000e8c, 0x000ea3,  1}, {0x000ea4, 0x000ea4, -1},
   1698     {0x000ea5, 0x000ea5,  1}, {0x000ea6, 0x000ea6, -1}, {0x000ea7, 0x000eb0,  1},
   1699     {0x000eb1, 0x000eb1,  0}, {0x000eb2, 0x000eb3,  1}, {0x000eb4, 0x000ebc,  0},
   1700     {0x000ebd, 0x000ebd,  1}, {0x000ebe, 0x000ebf, -1}, {0x000ec0, 0x000ec4,  1},
   1701     {0x000ec5, 0x000ec5, -1}, {0x000ec6, 0x000ec6,  1}, {0x000ec7, 0x000ec7, -1},
   1702     {0x000ec8, 0x000ece,  0}, {0x000ecf, 0x000ecf, -1}, {0x000ed0, 0x000ed9,  1},
   1703     {0x000eda, 0x000edb, -1}, {0x000edc, 0x000edf,  1}, {0x000ee0, 0x000eff, -1},
   1704     {0x000f00, 0x000f17,  1}, {0x000f18, 0x000f19,  0}, {0x000f1a, 0x000f34,  1},
   1705     {0x000f35, 0x000f35,  0}, {0x000f36, 0x000f36,  1}, {0x000f37, 0x000f37,  0},
   1706     {0x000f38, 0x000f38,  1}, {0x000f39, 0x000f39,  0}, {0x000f3a, 0x000f47,  1},
   1707     {0x000f48, 0x000f48, -1}, {0x000f49, 0x000f6c,  1}, {0x000f6d, 0x000f70, -1},
   1708     {0x000f71, 0x000f7e,  0}, {0x000f7f, 0x000f7f,  1}, {0x000f80, 0x000f84,  0},
   1709     {0x000f85, 0x000f85,  1}, {0x000f86, 0x000f87,  0}, {0x000f88, 0x000f8c,  1},
   1710     {0x000f8d, 0x000f97,  0}, {0x000f98, 0x000f98, -1}, {0x000f99, 0x000fbc,  0},
   1711     {0x000fbd, 0x000fbd, -1}, {0x000fbe, 0x000fc5,  1}, {0x000fc6, 0x000fc6,  0},
   1712     {0x000fc7, 0x000fcc,  1}, {0x000fcd, 0x000fcd, -1}, {0x000fce, 0x000fda,  1},
   1713     {0x000fdb, 0x000fff, -1}, {0x001000, 0x00102c,  1}, {0x00102d, 0x001030,  0},
   1714     {0x001031, 0x001031,  1}, {0x001032, 0x001037,  0}, {0x001038, 0x001038,  1},
   1715     {0x001039, 0x00103a,  0}, {0x00103b, 0x00103c,  1}, {0x00103d, 0x00103e,  0},
   1716     {0x00103f, 0x001057,  1}, {0x001058, 0x001059,  0}, {0x00105a, 0x00105d,  1},
   1717     {0x00105e, 0x001060,  0}, {0x001061, 0x001070,  1}, {0x001071, 0x001074,  0},
   1718     {0x001075, 0x001081,  1}, {0x001082, 0x001082,  0}, {0x001083, 0x001084,  1},
   1719     {0x001085, 0x001086,  0}, {0x001087, 0x00108c,  1}, {0x00108d, 0x00108d,  0},
   1720     {0x00108e, 0x00109c,  1}, {0x00109d, 0x00109d,  0}, {0x00109e, 0x0010c5,  1},
   1721     {0x0010c6, 0x0010c6, -1}, {0x0010c7, 0x0010c7,  1}, {0x0010c8, 0x0010cc, -1},
   1722     {0x0010cd, 0x0010cd,  1}, {0x0010ce, 0x0010cf, -1}, {0x0010d0, 0x0010ff,  1},
   1723     {0x001100, 0x00115f,  2}, {0x001160, 0x0011ff,  0}, {0x001200, 0x001248,  1},
   1724     {0x001249, 0x001249, -1}, {0x00124a, 0x00124d,  1}, {0x00124e, 0x00124f, -1},
   1725     {0x001250, 0x001256,  1}, {0x001257, 0x001257, -1}, {0x001258, 0x001258,  1},
   1726     {0x001259, 0x001259, -1}, {0x00125a, 0x00125d,  1}, {0x00125e, 0x00125f, -1},
   1727     {0x001260, 0x001288,  1}, {0x001289, 0x001289, -1}, {0x00128a, 0x00128d,  1},
   1728     {0x00128e, 0x00128f, -1}, {0x001290, 0x0012b0,  1}, {0x0012b1, 0x0012b1, -1},
   1729     {0x0012b2, 0x0012b5,  1}, {0x0012b6, 0x0012b7, -1}, {0x0012b8, 0x0012be,  1},
   1730     {0x0012bf, 0x0012bf, -1}, {0x0012c0, 0x0012c0,  1}, {0x0012c1, 0x0012c1, -1},
   1731     {0x0012c2, 0x0012c5,  1}, {0x0012c6, 0x0012c7, -1}, {0x0012c8, 0x0012d6,  1},
   1732     {0x0012d7, 0x0012d7, -1}, {0x0012d8, 0x001310,  1}, {0x001311, 0x001311, -1},
   1733     {0x001312, 0x001315,  1}, {0x001316, 0x001317, -1}, {0x001318, 0x00135a,  1},
   1734     {0x00135b, 0x00135c, -1}, {0x00135d, 0x00135f,  0}, {0x001360, 0x00137c,  1},
   1735     {0x00137d, 0x00137f, -1}, {0x001380, 0x001399,  1}, {0x00139a, 0x00139f, -1},
   1736     {0x0013a0, 0x0013f5,  1}, {0x0013f6, 0x0013f7, -1}, {0x0013f8, 0x0013fd,  1},
   1737     {0x0013fe, 0x0013ff, -1}, {0x001400, 0x00169c,  1}, {0x00169d, 0x00169f, -1},
   1738     {0x0016a0, 0x0016f8,  1}, {0x0016f9, 0x0016ff, -1}, {0x001700, 0x001711,  1},
   1739     {0x001712, 0x001714,  0}, {0x001715, 0x001715,  1}, {0x001716, 0x00171e, -1},
   1740     {0x00171f, 0x001731,  1}, {0x001732, 0x001733,  0}, {0x001734, 0x001736,  1},
   1741     {0x001737, 0x00173f, -1}, {0x001740, 0x001751,  1}, {0x001752, 0x001753,  0},
   1742     {0x001754, 0x00175f, -1}, {0x001760, 0x00176c,  1}, {0x00176d, 0x00176d, -1},
   1743     {0x00176e, 0x001770,  1}, {0x001771, 0x001771, -1}, {0x001772, 0x001773,  0},
   1744     {0x001774, 0x00177f, -1}, {0x001780, 0x0017b3,  1}, {0x0017b4, 0x0017b5,  0},
   1745     {0x0017b6, 0x0017b6,  1}, {0x0017b7, 0x0017bd,  0}, {0x0017be, 0x0017c5,  1},
   1746     {0x0017c6, 0x0017c6,  0}, {0x0017c7, 0x0017c8,  1}, {0x0017c9, 0x0017d3,  0},
   1747     {0x0017d4, 0x0017dc,  1}, {0x0017dd, 0x0017dd,  0}, {0x0017de, 0x0017df, -1},
   1748     {0x0017e0, 0x0017e9,  1}, {0x0017ea, 0x0017ef, -1}, {0x0017f0, 0x0017f9,  1},
   1749     {0x0017fa, 0x0017ff, -1}, {0x001800, 0x00180a,  1}, {0x00180b, 0x00180f,  0},
   1750     {0x001810, 0x001819,  1}, {0x00181a, 0x00181f, -1}, {0x001820, 0x001878,  1},
   1751     {0x001879, 0x00187f, -1}, {0x001880, 0x001884,  1}, {0x001885, 0x001886,  0},
   1752     {0x001887, 0x0018a8,  1}, {0x0018a9, 0x0018a9,  0}, {0x0018aa, 0x0018aa,  1},
   1753     {0x0018ab, 0x0018af, -1}, {0x0018b0, 0x0018f5,  1}, {0x0018f6, 0x0018ff, -1},
   1754     {0x001900, 0x00191e,  1}, {0x00191f, 0x00191f, -1}, {0x001920, 0x001922,  0},
   1755     {0x001923, 0x001926,  1}, {0x001927, 0x001928,  0}, {0x001929, 0x00192b,  1},
   1756     {0x00192c, 0x00192f, -1}, {0x001930, 0x001931,  1}, {0x001932, 0x001932,  0},
   1757     {0x001933, 0x001938,  1}, {0x001939, 0x00193b,  0}, {0x00193c, 0x00193f, -1},
   1758     {0x001940, 0x001940,  1}, {0x001941, 0x001943, -1}, {0x001944, 0x00196d,  1},
   1759     {0x00196e, 0x00196f, -1}, {0x001970, 0x001974,  1}, {0x001975, 0x00197f, -1},
   1760     {0x001980, 0x0019ab,  1}, {0x0019ac, 0x0019af, -1}, {0x0019b0, 0x0019c9,  1},
   1761     {0x0019ca, 0x0019cf, -1}, {0x0019d0, 0x0019da,  1}, {0x0019db, 0x0019dd, -1},
   1762     {0x0019de, 0x001a16,  1}, {0x001a17, 0x001a18,  0}, {0x001a19, 0x001a1a,  1},
   1763     {0x001a1b, 0x001a1b,  0}, {0x001a1c, 0x001a1d, -1}, {0x001a1e, 0x001a55,  1},
   1764     {0x001a56, 0x001a56,  0}, {0x001a57, 0x001a57,  1}, {0x001a58, 0x001a5e,  0},
   1765     {0x001a5f, 0x001a5f, -1}, {0x001a60, 0x001a60,  0}, {0x001a61, 0x001a61,  1},
   1766     {0x001a62, 0x001a62,  0}, {0x001a63, 0x001a64,  1}, {0x001a65, 0x001a6c,  0},
   1767     {0x001a6d, 0x001a72,  1}, {0x001a73, 0x001a7c,  0}, {0x001a7d, 0x001a7e, -1},
   1768     {0x001a7f, 0x001a7f,  0}, {0x001a80, 0x001a89,  1}, {0x001a8a, 0x001a8f, -1},
   1769     {0x001a90, 0x001a99,  1}, {0x001a9a, 0x001a9f, -1}, {0x001aa0, 0x001aad,  1},
   1770     {0x001aae, 0x001aaf, -1}, {0x001ab0, 0x001ace,  0}, {0x001acf, 0x001aff, -1},
   1771     {0x001b00, 0x001b03,  0}, {0x001b04, 0x001b33,  1}, {0x001b34, 0x001b34,  0},
   1772     {0x001b35, 0x001b35,  1}, {0x001b36, 0x001b3a,  0}, {0x001b3b, 0x001b3b,  1},
   1773     {0x001b3c, 0x001b3c,  0}, {0x001b3d, 0x001b41,  1}, {0x001b42, 0x001b42,  0},
   1774     {0x001b43, 0x001b4c,  1}, {0x001b4d, 0x001b4d, -1}, {0x001b4e, 0x001b6a,  1},
   1775     {0x001b6b, 0x001b73,  0}, {0x001b74, 0x001b7f,  1}, {0x001b80, 0x001b81,  0},
   1776     {0x001b82, 0x001ba1,  1}, {0x001ba2, 0x001ba5,  0}, {0x001ba6, 0x001ba7,  1},
   1777     {0x001ba8, 0x001ba9,  0}, {0x001baa, 0x001baa,  1}, {0x001bab, 0x001bad,  0},
   1778     {0x001bae, 0x001be5,  1}, {0x001be6, 0x001be6,  0}, {0x001be7, 0x001be7,  1},
   1779     {0x001be8, 0x001be9,  0}, {0x001bea, 0x001bec,  1}, {0x001bed, 0x001bed,  0},
   1780     {0x001bee, 0x001bee,  1}, {0x001bef, 0x001bf1,  0}, {0x001bf2, 0x001bf3,  1},
   1781     {0x001bf4, 0x001bfb, -1}, {0x001bfc, 0x001c2b,  1}, {0x001c2c, 0x001c33,  0},
   1782     {0x001c34, 0x001c35,  1}, {0x001c36, 0x001c37,  0}, {0x001c38, 0x001c3a, -1},
   1783     {0x001c3b, 0x001c49,  1}, {0x001c4a, 0x001c4c, -1}, {0x001c4d, 0x001c8a,  1},
   1784     {0x001c8b, 0x001c8f, -1}, {0x001c90, 0x001cba,  1}, {0x001cbb, 0x001cbc, -1},
   1785     {0x001cbd, 0x001cc7,  1}, {0x001cc8, 0x001ccf, -1}, {0x001cd0, 0x001cd2,  0},
   1786     {0x001cd3, 0x001cd3,  1}, {0x001cd4, 0x001ce0,  0}, {0x001ce1, 0x001ce1,  1},
   1787     {0x001ce2, 0x001ce8,  0}, {0x001ce9, 0x001cec,  1}, {0x001ced, 0x001ced,  0},
   1788     {0x001cee, 0x001cf3,  1}, {0x001cf4, 0x001cf4,  0}, {0x001cf5, 0x001cf7,  1},
   1789     {0x001cf8, 0x001cf9,  0}, {0x001cfa, 0x001cfa,  1}, {0x001cfb, 0x001cff, -1},
   1790     {0x001d00, 0x001dbf,  1}, {0x001dc0, 0x001dff,  0}, {0x001e00, 0x001f15,  1},
   1791     {0x001f16, 0x001f17, -1}, {0x001f18, 0x001f1d,  1}, {0x001f1e, 0x001f1f, -1},
   1792     {0x001f20, 0x001f45,  1}, {0x001f46, 0x001f47, -1}, {0x001f48, 0x001f4d,  1},
   1793     {0x001f4e, 0x001f4f, -1}, {0x001f50, 0x001f57,  1}, {0x001f58, 0x001f58, -1},
   1794     {0x001f59, 0x001f59,  1}, {0x001f5a, 0x001f5a, -1}, {0x001f5b, 0x001f5b,  1},
   1795     {0x001f5c, 0x001f5c, -1}, {0x001f5d, 0x001f5d,  1}, {0x001f5e, 0x001f5e, -1},
   1796     {0x001f5f, 0x001f7d,  1}, {0x001f7e, 0x001f7f, -1}, {0x001f80, 0x001fb4,  1},
   1797     {0x001fb5, 0x001fb5, -1}, {0x001fb6, 0x001fc4,  1}, {0x001fc5, 0x001fc5, -1},
   1798     {0x001fc6, 0x001fd3,  1}, {0x001fd4, 0x001fd5, -1}, {0x001fd6, 0x001fdb,  1},
   1799     {0x001fdc, 0x001fdc, -1}, {0x001fdd, 0x001fef,  1}, {0x001ff0, 0x001ff1, -1},
   1800     {0x001ff2, 0x001ff4,  1}, {0x001ff5, 0x001ff5, -1}, {0x001ff6, 0x001ffe,  1},
   1801     {0x001fff, 0x001fff, -1}, {0x002000, 0x00200a,  1}, {0x00200b, 0x00200f,  0},
   1802     {0x002010, 0x002027,  1}, {0x002028, 0x002029, -1}, {0x00202a, 0x00202e,  0},
   1803     {0x00202f, 0x00205f,  1}, {0x002060, 0x002064,  0}, {0x002065, 0x002065, -1},
   1804     {0x002066, 0x00206f,  0}, {0x002070, 0x002071,  1}, {0x002072, 0x002073, -1},
   1805     {0x002074, 0x00208e,  1}, {0x00208f, 0x00208f, -1}, {0x002090, 0x00209c,  1},
   1806     {0x00209d, 0x00209f, -1}, {0x0020a0, 0x0020c0,  1}, {0x0020c1, 0x0020cf, -1},
   1807     {0x0020d0, 0x0020f0,  0}, {0x0020f1, 0x0020ff, -1}, {0x002100, 0x00218b,  1},
   1808     {0x00218c, 0x00218f, -1}, {0x002190, 0x002319,  1}, {0x00231a, 0x00231b,  2},
   1809     {0x00231c, 0x002328,  1}, {0x002329, 0x00232a,  2}, {0x00232b, 0x0023e8,  1},
   1810     {0x0023e9, 0x0023ec,  2}, {0x0023ed, 0x0023ef,  1}, {0x0023f0, 0x0023f0,  2},
   1811     {0x0023f1, 0x0023f2,  1}, {0x0023f3, 0x0023f3,  2}, {0x0023f4, 0x002429,  1},
   1812     {0x00242a, 0x00243f, -1}, {0x002440, 0x00244a,  1}, {0x00244b, 0x00245f, -1},
   1813     {0x002460, 0x0025fc,  1}, {0x0025fd, 0x0025fe,  2}, {0x0025ff, 0x002613,  1},
   1814     {0x002614, 0x002615,  2}, {0x002616, 0x00262f,  1}, {0x002630, 0x002637,  2},
   1815     {0x002638, 0x002647,  1}, {0x002648, 0x002653,  2}, {0x002654, 0x00267e,  1},
   1816     {0x00267f, 0x00267f,  2}, {0x002680, 0x002689,  1}, {0x00268a, 0x00268f,  2},
   1817     {0x002690, 0x002692,  1}, {0x002693, 0x002693,  2}, {0x002694, 0x0026a0,  1},
   1818     {0x0026a1, 0x0026a1,  2}, {0x0026a2, 0x0026a9,  1}, {0x0026aa, 0x0026ab,  2},
   1819     {0x0026ac, 0x0026bc,  1}, {0x0026bd, 0x0026be,  2}, {0x0026bf, 0x0026c3,  1},
   1820     {0x0026c4, 0x0026c5,  2}, {0x0026c6, 0x0026cd,  1}, {0x0026ce, 0x0026ce,  2},
   1821     {0x0026cf, 0x0026d3,  1}, {0x0026d4, 0x0026d4,  2}, {0x0026d5, 0x0026e9,  1},
   1822     {0x0026ea, 0x0026ea,  2}, {0x0026eb, 0x0026f1,  1}, {0x0026f2, 0x0026f3,  2},
   1823     {0x0026f4, 0x0026f4,  1}, {0x0026f5, 0x0026f5,  2}, {0x0026f6, 0x0026f9,  1},
   1824     {0x0026fa, 0x0026fa,  2}, {0x0026fb, 0x0026fc,  1}, {0x0026fd, 0x0026fd,  2},
   1825     {0x0026fe, 0x002704,  1}, {0x002705, 0x002705,  2}, {0x002706, 0x002709,  1},
   1826     {0x00270a, 0x00270b,  2}, {0x00270c, 0x002727,  1}, {0x002728, 0x002728,  2},
   1827     {0x002729, 0x00274b,  1}, {0x00274c, 0x00274c,  2}, {0x00274d, 0x00274d,  1},
   1828     {0x00274e, 0x00274e,  2}, {0x00274f, 0x002752,  1}, {0x002753, 0x002755,  2},
   1829     {0x002756, 0x002756,  1}, {0x002757, 0x002757,  2}, {0x002758, 0x002794,  1},
   1830     {0x002795, 0x002797,  2}, {0x002798, 0x0027af,  1}, {0x0027b0, 0x0027b0,  2},
   1831     {0x0027b1, 0x0027be,  1}, {0x0027bf, 0x0027bf,  2}, {0x0027c0, 0x002b1a,  1},
   1832     {0x002b1b, 0x002b1c,  2}, {0x002b1d, 0x002b4f,  1}, {0x002b50, 0x002b50,  2},
   1833     {0x002b51, 0x002b54,  1}, {0x002b55, 0x002b55,  2}, {0x002b56, 0x002b73,  1},
   1834     {0x002b74, 0x002b75, -1}, {0x002b76, 0x002b95,  1}, {0x002b96, 0x002b96, -1},
   1835     {0x002b97, 0x002cee,  1}, {0x002cef, 0x002cf1,  0}, {0x002cf2, 0x002cf3,  1},
   1836     {0x002cf4, 0x002cf8, -1}, {0x002cf9, 0x002d25,  1}, {0x002d26, 0x002d26, -1},
   1837     {0x002d27, 0x002d27,  1}, {0x002d28, 0x002d2c, -1}, {0x002d2d, 0x002d2d,  1},
   1838     {0x002d2e, 0x002d2f, -1}, {0x002d30, 0x002d67,  1}, {0x002d68, 0x002d6e, -1},
   1839     {0x002d6f, 0x002d70,  1}, {0x002d71, 0x002d7e, -1}, {0x002d7f, 0x002d7f,  0},
   1840     {0x002d80, 0x002d96,  1}, {0x002d97, 0x002d9f, -1}, {0x002da0, 0x002da6,  1},
   1841     {0x002da7, 0x002da7, -1}, {0x002da8, 0x002dae,  1}, {0x002daf, 0x002daf, -1},
   1842     {0x002db0, 0x002db6,  1}, {0x002db7, 0x002db7, -1}, {0x002db8, 0x002dbe,  1},
   1843     {0x002dbf, 0x002dbf, -1}, {0x002dc0, 0x002dc6,  1}, {0x002dc7, 0x002dc7, -1},
   1844     {0x002dc8, 0x002dce,  1}, {0x002dcf, 0x002dcf, -1}, {0x002dd0, 0x002dd6,  1},
   1845     {0x002dd7, 0x002dd7, -1}, {0x002dd8, 0x002dde,  1}, {0x002ddf, 0x002ddf, -1},
   1846     {0x002de0, 0x002dff,  0}, {0x002e00, 0x002e5d,  1}, {0x002e5e, 0x002e7f, -1},
   1847     {0x002e80, 0x002e99,  2}, {0x002e9a, 0x002e9a, -1}, {0x002e9b, 0x002ef3,  2},
   1848     {0x002ef4, 0x002eff, -1}, {0x002f00, 0x002fd5,  2}, {0x002fd6, 0x002fef, -1},
   1849     {0x002ff0, 0x003029,  2}, {0x00302a, 0x00302d,  0}, {0x00302e, 0x00303e,  2},
   1850     {0x00303f, 0x00303f,  1}, {0x003040, 0x003040, -1}, {0x003041, 0x003096,  2},
   1851     {0x003097, 0x003098, -1}, {0x003099, 0x00309a,  0}, {0x00309b, 0x0030ff,  2},
   1852     {0x003100, 0x003104, -1}, {0x003105, 0x00312f,  2}, {0x003130, 0x003130, -1},
   1853     {0x003131, 0x003163,  2}, {0x003164, 0x003164,  0}, {0x003165, 0x00318e,  2},
   1854     {0x00318f, 0x00318f, -1}, {0x003190, 0x0031e5,  2}, {0x0031e6, 0x0031ee, -1},
   1855     {0x0031ef, 0x00321e,  2}, {0x00321f, 0x00321f, -1}, {0x003220, 0x00a48c,  2},
   1856     {0x00a48d, 0x00a48f, -1}, {0x00a490, 0x00a4c6,  2}, {0x00a4c7, 0x00a4cf, -1},
   1857     {0x00a4d0, 0x00a62b,  1}, {0x00a62c, 0x00a63f, -1}, {0x00a640, 0x00a66e,  1},
   1858     {0x00a66f, 0x00a672,  0}, {0x00a673, 0x00a673,  1}, {0x00a674, 0x00a67d,  0},
   1859     {0x00a67e, 0x00a69d,  1}, {0x00a69e, 0x00a69f,  0}, {0x00a6a0, 0x00a6ef,  1},
   1860     {0x00a6f0, 0x00a6f1,  0}, {0x00a6f2, 0x00a6f7,  1}, {0x00a6f8, 0x00a6ff, -1},
   1861     {0x00a700, 0x00a7cd,  1}, {0x00a7ce, 0x00a7cf, -1}, {0x00a7d0, 0x00a7d1,  1},
   1862     {0x00a7d2, 0x00a7d2, -1}, {0x00a7d3, 0x00a7d3,  1}, {0x00a7d4, 0x00a7d4, -1},
   1863     {0x00a7d5, 0x00a7dc,  1}, {0x00a7dd, 0x00a7f1, -1}, {0x00a7f2, 0x00a801,  1},
   1864     {0x00a802, 0x00a802,  0}, {0x00a803, 0x00a805,  1}, {0x00a806, 0x00a806,  0},
   1865     {0x00a807, 0x00a80a,  1}, {0x00a80b, 0x00a80b,  0}, {0x00a80c, 0x00a824,  1},
   1866     {0x00a825, 0x00a826,  0}, {0x00a827, 0x00a82b,  1}, {0x00a82c, 0x00a82c,  0},
   1867     {0x00a82d, 0x00a82f, -1}, {0x00a830, 0x00a839,  1}, {0x00a83a, 0x00a83f, -1},
   1868     {0x00a840, 0x00a877,  1}, {0x00a878, 0x00a87f, -1}, {0x00a880, 0x00a8c3,  1},
   1869     {0x00a8c4, 0x00a8c5,  0}, {0x00a8c6, 0x00a8cd, -1}, {0x00a8ce, 0x00a8d9,  1},
   1870     {0x00a8da, 0x00a8df, -1}, {0x00a8e0, 0x00a8f1,  0}, {0x00a8f2, 0x00a8fe,  1},
   1871     {0x00a8ff, 0x00a8ff,  0}, {0x00a900, 0x00a925,  1}, {0x00a926, 0x00a92d,  0},
   1872     {0x00a92e, 0x00a946,  1}, {0x00a947, 0x00a951,  0}, {0x00a952, 0x00a953,  1},
   1873     {0x00a954, 0x00a95e, -1}, {0x00a95f, 0x00a95f,  1}, {0x00a960, 0x00a97c,  2},
   1874     {0x00a97d, 0x00a97f, -1}, {0x00a980, 0x00a982,  0}, {0x00a983, 0x00a9b2,  1},
   1875     {0x00a9b3, 0x00a9b3,  0}, {0x00a9b4, 0x00a9b5,  1}, {0x00a9b6, 0x00a9b9,  0},
   1876     {0x00a9ba, 0x00a9bb,  1}, {0x00a9bc, 0x00a9bd,  0}, {0x00a9be, 0x00a9cd,  1},
   1877     {0x00a9ce, 0x00a9ce, -1}, {0x00a9cf, 0x00a9d9,  1}, {0x00a9da, 0x00a9dd, -1},
   1878     {0x00a9de, 0x00a9e4,  1}, {0x00a9e5, 0x00a9e5,  0}, {0x00a9e6, 0x00a9fe,  1},
   1879     {0x00a9ff, 0x00a9ff, -1}, {0x00aa00, 0x00aa28,  1}, {0x00aa29, 0x00aa2e,  0},
   1880     {0x00aa2f, 0x00aa30,  1}, {0x00aa31, 0x00aa32,  0}, {0x00aa33, 0x00aa34,  1},
   1881     {0x00aa35, 0x00aa36,  0}, {0x00aa37, 0x00aa3f, -1}, {0x00aa40, 0x00aa42,  1},
   1882     {0x00aa43, 0x00aa43,  0}, {0x00aa44, 0x00aa4b,  1}, {0x00aa4c, 0x00aa4c,  0},
   1883     {0x00aa4d, 0x00aa4d,  1}, {0x00aa4e, 0x00aa4f, -1}, {0x00aa50, 0x00aa59,  1},
   1884     {0x00aa5a, 0x00aa5b, -1}, {0x00aa5c, 0x00aa7b,  1}, {0x00aa7c, 0x00aa7c,  0},
   1885     {0x00aa7d, 0x00aaaf,  1}, {0x00aab0, 0x00aab0,  0}, {0x00aab1, 0x00aab1,  1},
   1886     {0x00aab2, 0x00aab4,  0}, {0x00aab5, 0x00aab6,  1}, {0x00aab7, 0x00aab8,  0},
   1887     {0x00aab9, 0x00aabd,  1}, {0x00aabe, 0x00aabf,  0}, {0x00aac0, 0x00aac0,  1},
   1888     {0x00aac1, 0x00aac1,  0}, {0x00aac2, 0x00aac2,  1}, {0x00aac3, 0x00aada, -1},
   1889     {0x00aadb, 0x00aaeb,  1}, {0x00aaec, 0x00aaed,  0}, {0x00aaee, 0x00aaf5,  1},
   1890     {0x00aaf6, 0x00aaf6,  0}, {0x00aaf7, 0x00ab00, -1}, {0x00ab01, 0x00ab06,  1},
   1891     {0x00ab07, 0x00ab08, -1}, {0x00ab09, 0x00ab0e,  1}, {0x00ab0f, 0x00ab10, -1},
   1892     {0x00ab11, 0x00ab16,  1}, {0x00ab17, 0x00ab1f, -1}, {0x00ab20, 0x00ab26,  1},
   1893     {0x00ab27, 0x00ab27, -1}, {0x00ab28, 0x00ab2e,  1}, {0x00ab2f, 0x00ab2f, -1},
   1894     {0x00ab30, 0x00ab6b,  1}, {0x00ab6c, 0x00ab6f, -1}, {0x00ab70, 0x00abe4,  1},
   1895     {0x00abe5, 0x00abe5,  0}, {0x00abe6, 0x00abe7,  1}, {0x00abe8, 0x00abe8,  0},
   1896     {0x00abe9, 0x00abec,  1}, {0x00abed, 0x00abed,  0}, {0x00abee, 0x00abef, -1},
   1897     {0x00abf0, 0x00abf9,  1}, {0x00abfa, 0x00abff, -1}, {0x00ac00, 0x00d7a3,  2},
   1898     {0x00d7a4, 0x00d7af, -1}, {0x00d7b0, 0x00d7c6,  0}, {0x00d7c7, 0x00d7ca, -1},
   1899     {0x00d7cb, 0x00d7fb,  0}, {0x00d7fc, 0x00dfff, -1}, {0x00e000, 0x00f8ff,  1},
   1900     {0x00f900, 0x00fa6d,  2}, {0x00fa6e, 0x00fa6f, -1}, {0x00fa70, 0x00fad9,  2},
   1901     {0x00fada, 0x00faff, -1}, {0x00fb00, 0x00fb06,  1}, {0x00fb07, 0x00fb12, -1},
   1902     {0x00fb13, 0x00fb17,  1}, {0x00fb18, 0x00fb1c, -1}, {0x00fb1d, 0x00fb1d,  1},
   1903     {0x00fb1e, 0x00fb1e,  0}, {0x00fb1f, 0x00fb36,  1}, {0x00fb37, 0x00fb37, -1},
   1904     {0x00fb38, 0x00fb3c,  1}, {0x00fb3d, 0x00fb3d, -1}, {0x00fb3e, 0x00fb3e,  1},
   1905     {0x00fb3f, 0x00fb3f, -1}, {0x00fb40, 0x00fb41,  1}, {0x00fb42, 0x00fb42, -1},
   1906     {0x00fb43, 0x00fb44,  1}, {0x00fb45, 0x00fb45, -1}, {0x00fb46, 0x00fbc2,  1},
   1907     {0x00fbc3, 0x00fbd2, -1}, {0x00fbd3, 0x00fd8f,  1}, {0x00fd90, 0x00fd91, -1},
   1908     {0x00fd92, 0x00fdc7,  1}, {0x00fdc8, 0x00fdce, -1}, {0x00fdcf, 0x00fdcf,  1},
   1909     {0x00fdd0, 0x00fdef, -1}, {0x00fdf0, 0x00fdff,  1}, {0x00fe00, 0x00fe0f,  0},
   1910     {0x00fe10, 0x00fe19,  2}, {0x00fe1a, 0x00fe1f, -1}, {0x00fe20, 0x00fe2f,  0},
   1911     {0x00fe30, 0x00fe52,  2}, {0x00fe53, 0x00fe53, -1}, {0x00fe54, 0x00fe66,  2},
   1912     {0x00fe67, 0x00fe67, -1}, {0x00fe68, 0x00fe6b,  2}, {0x00fe6c, 0x00fe6f, -1},
   1913     {0x00fe70, 0x00fe74,  1}, {0x00fe75, 0x00fe75, -1}, {0x00fe76, 0x00fefc,  1},
   1914     {0x00fefd, 0x00fefe, -1}, {0x00feff, 0x00feff,  0}, {0x00ff00, 0x00ff00, -1},
   1915     {0x00ff01, 0x00ff60,  2}, {0x00ff61, 0x00ff9f,  1}, {0x00ffa0, 0x00ffa0,  0},
   1916     {0x00ffa1, 0x00ffbe,  1}, {0x00ffbf, 0x00ffc1, -1}, {0x00ffc2, 0x00ffc7,  1},
   1917     {0x00ffc8, 0x00ffc9, -1}, {0x00ffca, 0x00ffcf,  1}, {0x00ffd0, 0x00ffd1, -1},
   1918     {0x00ffd2, 0x00ffd7,  1}, {0x00ffd8, 0x00ffd9, -1}, {0x00ffda, 0x00ffdc,  1},
   1919     {0x00ffdd, 0x00ffdf, -1}, {0x00ffe0, 0x00ffe6,  2}, {0x00ffe7, 0x00ffe7, -1},
   1920     {0x00ffe8, 0x00ffee,  1}, {0x00ffef, 0x00fff8, -1}, {0x00fff9, 0x00fffd,  1},
   1921     {0x00fffe, 0x00ffff, -1}, {0x010000, 0x01000b,  1}, {0x01000c, 0x01000c, -1},
   1922     {0x01000d, 0x010026,  1}, {0x010027, 0x010027, -1}, {0x010028, 0x01003a,  1},
   1923     {0x01003b, 0x01003b, -1}, {0x01003c, 0x01003d,  1}, {0x01003e, 0x01003e, -1},
   1924     {0x01003f, 0x01004d,  1}, {0x01004e, 0x01004f, -1}, {0x010050, 0x01005d,  1},
   1925     {0x01005e, 0x01007f, -1}, {0x010080, 0x0100fa,  1}, {0x0100fb, 0x0100ff, -1},
   1926     {0x010100, 0x010102,  1}, {0x010103, 0x010106, -1}, {0x010107, 0x010133,  1},
   1927     {0x010134, 0x010136, -1}, {0x010137, 0x01018e,  1}, {0x01018f, 0x01018f, -1},
   1928     {0x010190, 0x01019c,  1}, {0x01019d, 0x01019f, -1}, {0x0101a0, 0x0101a0,  1},
   1929     {0x0101a1, 0x0101cf, -1}, {0x0101d0, 0x0101fc,  1}, {0x0101fd, 0x0101fd,  0},
   1930     {0x0101fe, 0x01027f, -1}, {0x010280, 0x01029c,  1}, {0x01029d, 0x01029f, -1},
   1931     {0x0102a0, 0x0102d0,  1}, {0x0102d1, 0x0102df, -1}, {0x0102e0, 0x0102e0,  0},
   1932     {0x0102e1, 0x0102fb,  1}, {0x0102fc, 0x0102ff, -1}, {0x010300, 0x010323,  1},
   1933     {0x010324, 0x01032c, -1}, {0x01032d, 0x01034a,  1}, {0x01034b, 0x01034f, -1},
   1934     {0x010350, 0x010375,  1}, {0x010376, 0x01037a,  0}, {0x01037b, 0x01037f, -1},
   1935     {0x010380, 0x01039d,  1}, {0x01039e, 0x01039e, -1}, {0x01039f, 0x0103c3,  1},
   1936     {0x0103c4, 0x0103c7, -1}, {0x0103c8, 0x0103d5,  1}, {0x0103d6, 0x0103ff, -1},
   1937     {0x010400, 0x01049d,  1}, {0x01049e, 0x01049f, -1}, {0x0104a0, 0x0104a9,  1},
   1938     {0x0104aa, 0x0104af, -1}, {0x0104b0, 0x0104d3,  1}, {0x0104d4, 0x0104d7, -1},
   1939     {0x0104d8, 0x0104fb,  1}, {0x0104fc, 0x0104ff, -1}, {0x010500, 0x010527,  1},
   1940     {0x010528, 0x01052f, -1}, {0x010530, 0x010563,  1}, {0x010564, 0x01056e, -1},
   1941     {0x01056f, 0x01057a,  1}, {0x01057b, 0x01057b, -1}, {0x01057c, 0x01058a,  1},
   1942     {0x01058b, 0x01058b, -1}, {0x01058c, 0x010592,  1}, {0x010593, 0x010593, -1},
   1943     {0x010594, 0x010595,  1}, {0x010596, 0x010596, -1}, {0x010597, 0x0105a1,  1},
   1944     {0x0105a2, 0x0105a2, -1}, {0x0105a3, 0x0105b1,  1}, {0x0105b2, 0x0105b2, -1},
   1945     {0x0105b3, 0x0105b9,  1}, {0x0105ba, 0x0105ba, -1}, {0x0105bb, 0x0105bc,  1},
   1946     {0x0105bd, 0x0105bf, -1}, {0x0105c0, 0x0105f3,  1}, {0x0105f4, 0x0105ff, -1},
   1947     {0x010600, 0x010736,  1}, {0x010737, 0x01073f, -1}, {0x010740, 0x010755,  1},
   1948     {0x010756, 0x01075f, -1}, {0x010760, 0x010767,  1}, {0x010768, 0x01077f, -1},
   1949     {0x010780, 0x010785,  1}, {0x010786, 0x010786, -1}, {0x010787, 0x0107b0,  1},
   1950     {0x0107b1, 0x0107b1, -1}, {0x0107b2, 0x0107ba,  1}, {0x0107bb, 0x0107ff, -1},
   1951     {0x010800, 0x010805,  1}, {0x010806, 0x010807, -1}, {0x010808, 0x010808,  1},
   1952     {0x010809, 0x010809, -1}, {0x01080a, 0x010835,  1}, {0x010836, 0x010836, -1},
   1953     {0x010837, 0x010838,  1}, {0x010839, 0x01083b, -1}, {0x01083c, 0x01083c,  1},
   1954     {0x01083d, 0x01083e, -1}, {0x01083f, 0x010855,  1}, {0x010856, 0x010856, -1},
   1955     {0x010857, 0x01089e,  1}, {0x01089f, 0x0108a6, -1}, {0x0108a7, 0x0108af,  1},
   1956     {0x0108b0, 0x0108df, -1}, {0x0108e0, 0x0108f2,  1}, {0x0108f3, 0x0108f3, -1},
   1957     {0x0108f4, 0x0108f5,  1}, {0x0108f6, 0x0108fa, -1}, {0x0108fb, 0x01091b,  1},
   1958     {0x01091c, 0x01091e, -1}, {0x01091f, 0x010939,  1}, {0x01093a, 0x01093e, -1},
   1959     {0x01093f, 0x01093f,  1}, {0x010940, 0x01097f, -1}, {0x010980, 0x0109b7,  1},
   1960     {0x0109b8, 0x0109bb, -1}, {0x0109bc, 0x0109cf,  1}, {0x0109d0, 0x0109d1, -1},
   1961     {0x0109d2, 0x010a00,  1}, {0x010a01, 0x010a03,  0}, {0x010a04, 0x010a04, -1},
   1962     {0x010a05, 0x010a06,  0}, {0x010a07, 0x010a0b, -1}, {0x010a0c, 0x010a0f,  0},
   1963     {0x010a10, 0x010a13,  1}, {0x010a14, 0x010a14, -1}, {0x010a15, 0x010a17,  1},
   1964     {0x010a18, 0x010a18, -1}, {0x010a19, 0x010a35,  1}, {0x010a36, 0x010a37, -1},
   1965     {0x010a38, 0x010a3a,  0}, {0x010a3b, 0x010a3e, -1}, {0x010a3f, 0x010a3f,  0},
   1966     {0x010a40, 0x010a48,  1}, {0x010a49, 0x010a4f, -1}, {0x010a50, 0x010a58,  1},
   1967     {0x010a59, 0x010a5f, -1}, {0x010a60, 0x010a9f,  1}, {0x010aa0, 0x010abf, -1},
   1968     {0x010ac0, 0x010ae4,  1}, {0x010ae5, 0x010ae6,  0}, {0x010ae7, 0x010aea, -1},
   1969     {0x010aeb, 0x010af6,  1}, {0x010af7, 0x010aff, -1}, {0x010b00, 0x010b35,  1},
   1970     {0x010b36, 0x010b38, -1}, {0x010b39, 0x010b55,  1}, {0x010b56, 0x010b57, -1},
   1971     {0x010b58, 0x010b72,  1}, {0x010b73, 0x010b77, -1}, {0x010b78, 0x010b91,  1},
   1972     {0x010b92, 0x010b98, -1}, {0x010b99, 0x010b9c,  1}, {0x010b9d, 0x010ba8, -1},
   1973     {0x010ba9, 0x010baf,  1}, {0x010bb0, 0x010bff, -1}, {0x010c00, 0x010c48,  1},
   1974     {0x010c49, 0x010c7f, -1}, {0x010c80, 0x010cb2,  1}, {0x010cb3, 0x010cbf, -1},
   1975     {0x010cc0, 0x010cf2,  1}, {0x010cf3, 0x010cf9, -1}, {0x010cfa, 0x010d23,  1},
   1976     {0x010d24, 0x010d27,  0}, {0x010d28, 0x010d2f, -1}, {0x010d30, 0x010d39,  1},
   1977     {0x010d3a, 0x010d3f, -1}, {0x010d40, 0x010d65,  1}, {0x010d66, 0x010d68, -1},
   1978     {0x010d69, 0x010d6d,  0}, {0x010d6e, 0x010d85,  1}, {0x010d86, 0x010d8d, -1},
   1979     {0x010d8e, 0x010d8f,  1}, {0x010d90, 0x010e5f, -1}, {0x010e60, 0x010e7e,  1},
   1980     {0x010e7f, 0x010e7f, -1}, {0x010e80, 0x010ea9,  1}, {0x010eaa, 0x010eaa, -1},
   1981     {0x010eab, 0x010eac,  0}, {0x010ead, 0x010ead,  1}, {0x010eae, 0x010eaf, -1},
   1982     {0x010eb0, 0x010eb1,  1}, {0x010eb2, 0x010ec1, -1}, {0x010ec2, 0x010ec4,  1},
   1983     {0x010ec5, 0x010efb, -1}, {0x010efc, 0x010eff,  0}, {0x010f00, 0x010f27,  1},
   1984     {0x010f28, 0x010f2f, -1}, {0x010f30, 0x010f45,  1}, {0x010f46, 0x010f50,  0},
   1985     {0x010f51, 0x010f59,  1}, {0x010f5a, 0x010f6f, -1}, {0x010f70, 0x010f81,  1},
   1986     {0x010f82, 0x010f85,  0}, {0x010f86, 0x010f89,  1}, {0x010f8a, 0x010faf, -1},
   1987     {0x010fb0, 0x010fcb,  1}, {0x010fcc, 0x010fdf, -1}, {0x010fe0, 0x010ff6,  1},
   1988     {0x010ff7, 0x010fff, -1}, {0x011000, 0x011000,  1}, {0x011001, 0x011001,  0},
   1989     {0x011002, 0x011037,  1}, {0x011038, 0x011046,  0}, {0x011047, 0x01104d,  1},
   1990     {0x01104e, 0x011051, -1}, {0x011052, 0x01106f,  1}, {0x011070, 0x011070,  0},
   1991     {0x011071, 0x011072,  1}, {0x011073, 0x011074,  0}, {0x011075, 0x011075,  1},
   1992     {0x011076, 0x01107e, -1}, {0x01107f, 0x011081,  0}, {0x011082, 0x0110b2,  1},
   1993     {0x0110b3, 0x0110b6,  0}, {0x0110b7, 0x0110b8,  1}, {0x0110b9, 0x0110ba,  0},
   1994     {0x0110bb, 0x0110c1,  1}, {0x0110c2, 0x0110c2,  0}, {0x0110c3, 0x0110cc, -1},
   1995     {0x0110cd, 0x0110cd,  1}, {0x0110ce, 0x0110cf, -1}, {0x0110d0, 0x0110e8,  1},
   1996     {0x0110e9, 0x0110ef, -1}, {0x0110f0, 0x0110f9,  1}, {0x0110fa, 0x0110ff, -1},
   1997     {0x011100, 0x011102,  0}, {0x011103, 0x011126,  1}, {0x011127, 0x01112b,  0},
   1998     {0x01112c, 0x01112c,  1}, {0x01112d, 0x011134,  0}, {0x011135, 0x011135, -1},
   1999     {0x011136, 0x011147,  1}, {0x011148, 0x01114f, -1}, {0x011150, 0x011172,  1},
   2000     {0x011173, 0x011173,  0}, {0x011174, 0x011176,  1}, {0x011177, 0x01117f, -1},
   2001     {0x011180, 0x011181,  0}, {0x011182, 0x0111b5,  1}, {0x0111b6, 0x0111be,  0},
   2002     {0x0111bf, 0x0111c8,  1}, {0x0111c9, 0x0111cc,  0}, {0x0111cd, 0x0111ce,  1},
   2003     {0x0111cf, 0x0111cf,  0}, {0x0111d0, 0x0111df,  1}, {0x0111e0, 0x0111e0, -1},
   2004     {0x0111e1, 0x0111f4,  1}, {0x0111f5, 0x0111ff, -1}, {0x011200, 0x011211,  1},
   2005     {0x011212, 0x011212, -1}, {0x011213, 0x01122e,  1}, {0x01122f, 0x011231,  0},
   2006     {0x011232, 0x011233,  1}, {0x011234, 0x011234,  0}, {0x011235, 0x011235,  1},
   2007     {0x011236, 0x011237,  0}, {0x011238, 0x01123d,  1}, {0x01123e, 0x01123e,  0},
   2008     {0x01123f, 0x011240,  1}, {0x011241, 0x011241,  0}, {0x011242, 0x01127f, -1},
   2009     {0x011280, 0x011286,  1}, {0x011287, 0x011287, -1}, {0x011288, 0x011288,  1},
   2010     {0x011289, 0x011289, -1}, {0x01128a, 0x01128d,  1}, {0x01128e, 0x01128e, -1},
   2011     {0x01128f, 0x01129d,  1}, {0x01129e, 0x01129e, -1}, {0x01129f, 0x0112a9,  1},
   2012     {0x0112aa, 0x0112af, -1}, {0x0112b0, 0x0112de,  1}, {0x0112df, 0x0112df,  0},
   2013     {0x0112e0, 0x0112e2,  1}, {0x0112e3, 0x0112ea,  0}, {0x0112eb, 0x0112ef, -1},
   2014     {0x0112f0, 0x0112f9,  1}, {0x0112fa, 0x0112ff, -1}, {0x011300, 0x011301,  0},
   2015     {0x011302, 0x011303,  1}, {0x011304, 0x011304, -1}, {0x011305, 0x01130c,  1},
   2016     {0x01130d, 0x01130e, -1}, {0x01130f, 0x011310,  1}, {0x011311, 0x011312, -1},
   2017     {0x011313, 0x011328,  1}, {0x011329, 0x011329, -1}, {0x01132a, 0x011330,  1},
   2018     {0x011331, 0x011331, -1}, {0x011332, 0x011333,  1}, {0x011334, 0x011334, -1},
   2019     {0x011335, 0x011339,  1}, {0x01133a, 0x01133a, -1}, {0x01133b, 0x01133c,  0},
   2020     {0x01133d, 0x01133f,  1}, {0x011340, 0x011340,  0}, {0x011341, 0x011344,  1},
   2021     {0x011345, 0x011346, -1}, {0x011347, 0x011348,  1}, {0x011349, 0x01134a, -1},
   2022     {0x01134b, 0x01134d,  1}, {0x01134e, 0x01134f, -1}, {0x011350, 0x011350,  1},
   2023     {0x011351, 0x011356, -1}, {0x011357, 0x011357,  1}, {0x011358, 0x01135c, -1},
   2024     {0x01135d, 0x011363,  1}, {0x011364, 0x011365, -1}, {0x011366, 0x01136c,  0},
   2025     {0x01136d, 0x01136f, -1}, {0x011370, 0x011374,  0}, {0x011375, 0x01137f, -1},
   2026     {0x011380, 0x011389,  1}, {0x01138a, 0x01138a, -1}, {0x01138b, 0x01138b,  1},
   2027     {0x01138c, 0x01138d, -1}, {0x01138e, 0x01138e,  1}, {0x01138f, 0x01138f, -1},
   2028     {0x011390, 0x0113b5,  1}, {0x0113b6, 0x0113b6, -1}, {0x0113b7, 0x0113ba,  1},
   2029     {0x0113bb, 0x0113c0,  0}, {0x0113c1, 0x0113c1, -1}, {0x0113c2, 0x0113c2,  1},
   2030     {0x0113c3, 0x0113c4, -1}, {0x0113c5, 0x0113c5,  1}, {0x0113c6, 0x0113c6, -1},
   2031     {0x0113c7, 0x0113ca,  1}, {0x0113cb, 0x0113cb, -1}, {0x0113cc, 0x0113cd,  1},
   2032     {0x0113ce, 0x0113ce,  0}, {0x0113cf, 0x0113cf,  1}, {0x0113d0, 0x0113d0,  0},
   2033     {0x0113d1, 0x0113d1,  1}, {0x0113d2, 0x0113d2,  0}, {0x0113d3, 0x0113d5,  1},
   2034     {0x0113d6, 0x0113d6, -1}, {0x0113d7, 0x0113d8,  1}, {0x0113d9, 0x0113e0, -1},
   2035     {0x0113e1, 0x0113e2,  0}, {0x0113e3, 0x0113ff, -1}, {0x011400, 0x011437,  1},
   2036     {0x011438, 0x01143f,  0}, {0x011440, 0x011441,  1}, {0x011442, 0x011444,  0},
   2037     {0x011445, 0x011445,  1}, {0x011446, 0x011446,  0}, {0x011447, 0x01145b,  1},
   2038     {0x01145c, 0x01145c, -1}, {0x01145d, 0x01145d,  1}, {0x01145e, 0x01145e,  0},
   2039     {0x01145f, 0x011461,  1}, {0x011462, 0x01147f, -1}, {0x011480, 0x0114b2,  1},
   2040     {0x0114b3, 0x0114b8,  0}, {0x0114b9, 0x0114b9,  1}, {0x0114ba, 0x0114ba,  0},
   2041     {0x0114bb, 0x0114be,  1}, {0x0114bf, 0x0114c0,  0}, {0x0114c1, 0x0114c1,  1},
   2042     {0x0114c2, 0x0114c3,  0}, {0x0114c4, 0x0114c7,  1}, {0x0114c8, 0x0114cf, -1},
   2043     {0x0114d0, 0x0114d9,  1}, {0x0114da, 0x01157f, -1}, {0x011580, 0x0115b1,  1},
   2044     {0x0115b2, 0x0115b5,  0}, {0x0115b6, 0x0115b7, -1}, {0x0115b8, 0x0115bb,  1},
   2045     {0x0115bc, 0x0115bd,  0}, {0x0115be, 0x0115be,  1}, {0x0115bf, 0x0115c0,  0},
   2046     {0x0115c1, 0x0115db,  1}, {0x0115dc, 0x0115dd,  0}, {0x0115de, 0x0115ff, -1},
   2047     {0x011600, 0x011632,  1}, {0x011633, 0x01163a,  0}, {0x01163b, 0x01163c,  1},
   2048     {0x01163d, 0x01163d,  0}, {0x01163e, 0x01163e,  1}, {0x01163f, 0x011640,  0},
   2049     {0x011641, 0x011644,  1}, {0x011645, 0x01164f, -1}, {0x011650, 0x011659,  1},
   2050     {0x01165a, 0x01165f, -1}, {0x011660, 0x01166c,  1}, {0x01166d, 0x01167f, -1},
   2051     {0x011680, 0x0116aa,  1}, {0x0116ab, 0x0116ab,  0}, {0x0116ac, 0x0116ac,  1},
   2052     {0x0116ad, 0x0116ad,  0}, {0x0116ae, 0x0116af,  1}, {0x0116b0, 0x0116b5,  0},
   2053     {0x0116b6, 0x0116b6,  1}, {0x0116b7, 0x0116b7,  0}, {0x0116b8, 0x0116b9,  1},
   2054     {0x0116ba, 0x0116bf, -1}, {0x0116c0, 0x0116c9,  1}, {0x0116ca, 0x0116cf, -1},
   2055     {0x0116d0, 0x0116e3,  1}, {0x0116e4, 0x0116ff, -1}, {0x011700, 0x01171a,  1},
   2056     {0x01171b, 0x01171c, -1}, {0x01171d, 0x01171d,  0}, {0x01171e, 0x01171e,  1},
   2057     {0x01171f, 0x01171f,  0}, {0x011720, 0x011721,  1}, {0x011722, 0x011725,  0},
   2058     {0x011726, 0x011726,  1}, {0x011727, 0x01172b,  0}, {0x01172c, 0x01172f, -1},
   2059     {0x011730, 0x011746,  1}, {0x011747, 0x0117ff, -1}, {0x011800, 0x01182e,  1},
   2060     {0x01182f, 0x011837,  0}, {0x011838, 0x011838,  1}, {0x011839, 0x01183a,  0},
   2061     {0x01183b, 0x01183b,  1}, {0x01183c, 0x01189f, -1}, {0x0118a0, 0x0118f2,  1},
   2062     {0x0118f3, 0x0118fe, -1}, {0x0118ff, 0x011906,  1}, {0x011907, 0x011908, -1},
   2063     {0x011909, 0x011909,  1}, {0x01190a, 0x01190b, -1}, {0x01190c, 0x011913,  1},
   2064     {0x011914, 0x011914, -1}, {0x011915, 0x011916,  1}, {0x011917, 0x011917, -1},
   2065     {0x011918, 0x011935,  1}, {0x011936, 0x011936, -1}, {0x011937, 0x011938,  1},
   2066     {0x011939, 0x01193a, -1}, {0x01193b, 0x01193c,  0}, {0x01193d, 0x01193d,  1},
   2067     {0x01193e, 0x01193e,  0}, {0x01193f, 0x011942,  1}, {0x011943, 0x011943,  0},
   2068     {0x011944, 0x011946,  1}, {0x011947, 0x01194f, -1}, {0x011950, 0x011959,  1},
   2069     {0x01195a, 0x01199f, -1}, {0x0119a0, 0x0119a7,  1}, {0x0119a8, 0x0119a9, -1},
   2070     {0x0119aa, 0x0119d3,  1}, {0x0119d4, 0x0119d7,  0}, {0x0119d8, 0x0119d9, -1},
   2071     {0x0119da, 0x0119db,  0}, {0x0119dc, 0x0119df,  1}, {0x0119e0, 0x0119e0,  0},
   2072     {0x0119e1, 0x0119e4,  1}, {0x0119e5, 0x0119ff, -1}, {0x011a00, 0x011a00,  1},
   2073     {0x011a01, 0x011a0a,  0}, {0x011a0b, 0x011a32,  1}, {0x011a33, 0x011a38,  0},
   2074     {0x011a39, 0x011a3a,  1}, {0x011a3b, 0x011a3e,  0}, {0x011a3f, 0x011a46,  1},
   2075     {0x011a47, 0x011a47,  0}, {0x011a48, 0x011a4f, -1}, {0x011a50, 0x011a50,  1},
   2076     {0x011a51, 0x011a56,  0}, {0x011a57, 0x011a58,  1}, {0x011a59, 0x011a5b,  0},
   2077     {0x011a5c, 0x011a89,  1}, {0x011a8a, 0x011a96,  0}, {0x011a97, 0x011a97,  1},
   2078     {0x011a98, 0x011a99,  0}, {0x011a9a, 0x011aa2,  1}, {0x011aa3, 0x011aaf, -1},
   2079     {0x011ab0, 0x011af8,  1}, {0x011af9, 0x011aff, -1}, {0x011b00, 0x011b09,  1},
   2080     {0x011b0a, 0x011bbf, -1}, {0x011bc0, 0x011be1,  1}, {0x011be2, 0x011bef, -1},
   2081     {0x011bf0, 0x011bf9,  1}, {0x011bfa, 0x011bff, -1}, {0x011c00, 0x011c08,  1},
   2082     {0x011c09, 0x011c09, -1}, {0x011c0a, 0x011c2f,  1}, {0x011c30, 0x011c36,  0},
   2083     {0x011c37, 0x011c37, -1}, {0x011c38, 0x011c3d,  0}, {0x011c3e, 0x011c3e,  1},
   2084     {0x011c3f, 0x011c3f,  0}, {0x011c40, 0x011c45,  1}, {0x011c46, 0x011c4f, -1},
   2085     {0x011c50, 0x011c6c,  1}, {0x011c6d, 0x011c6f, -1}, {0x011c70, 0x011c8f,  1},
   2086     {0x011c90, 0x011c91, -1}, {0x011c92, 0x011ca7,  0}, {0x011ca8, 0x011ca8, -1},
   2087     {0x011ca9, 0x011ca9,  1}, {0x011caa, 0x011cb0,  0}, {0x011cb1, 0x011cb1,  1},
   2088     {0x011cb2, 0x011cb3,  0}, {0x011cb4, 0x011cb4,  1}, {0x011cb5, 0x011cb6,  0},
   2089     {0x011cb7, 0x011cff, -1}, {0x011d00, 0x011d06,  1}, {0x011d07, 0x011d07, -1},
   2090     {0x011d08, 0x011d09,  1}, {0x011d0a, 0x011d0a, -1}, {0x011d0b, 0x011d30,  1},
   2091     {0x011d31, 0x011d36,  0}, {0x011d37, 0x011d39, -1}, {0x011d3a, 0x011d3a,  0},
   2092     {0x011d3b, 0x011d3b, -1}, {0x011d3c, 0x011d3d,  0}, {0x011d3e, 0x011d3e, -1},
   2093     {0x011d3f, 0x011d45,  0}, {0x011d46, 0x011d46,  1}, {0x011d47, 0x011d47,  0},
   2094     {0x011d48, 0x011d4f, -1}, {0x011d50, 0x011d59,  1}, {0x011d5a, 0x011d5f, -1},
   2095     {0x011d60, 0x011d65,  1}, {0x011d66, 0x011d66, -1}, {0x011d67, 0x011d68,  1},
   2096     {0x011d69, 0x011d69, -1}, {0x011d6a, 0x011d8e,  1}, {0x011d8f, 0x011d8f, -1},
   2097     {0x011d90, 0x011d91,  0}, {0x011d92, 0x011d92, -1}, {0x011d93, 0x011d94,  1},
   2098     {0x011d95, 0x011d95,  0}, {0x011d96, 0x011d96,  1}, {0x011d97, 0x011d97,  0},
   2099     {0x011d98, 0x011d98,  1}, {0x011d99, 0x011d9f, -1}, {0x011da0, 0x011da9,  1},
   2100     {0x011daa, 0x011edf, -1}, {0x011ee0, 0x011ef2,  1}, {0x011ef3, 0x011ef4,  0},
   2101     {0x011ef5, 0x011ef8,  1}, {0x011ef9, 0x011eff, -1}, {0x011f00, 0x011f01,  0},
   2102     {0x011f02, 0x011f10,  1}, {0x011f11, 0x011f11, -1}, {0x011f12, 0x011f35,  1},
   2103     {0x011f36, 0x011f3a,  0}, {0x011f3b, 0x011f3d, -1}, {0x011f3e, 0x011f3f,  1},
   2104     {0x011f40, 0x011f40,  0}, {0x011f41, 0x011f41,  1}, {0x011f42, 0x011f42,  0},
   2105     {0x011f43, 0x011f59,  1}, {0x011f5a, 0x011f5a,  0}, {0x011f5b, 0x011faf, -1},
   2106     {0x011fb0, 0x011fb0,  1}, {0x011fb1, 0x011fbf, -1}, {0x011fc0, 0x011ff1,  1},
   2107     {0x011ff2, 0x011ffe, -1}, {0x011fff, 0x012399,  1}, {0x01239a, 0x0123ff, -1},
   2108     {0x012400, 0x01246e,  1}, {0x01246f, 0x01246f, -1}, {0x012470, 0x012474,  1},
   2109     {0x012475, 0x01247f, -1}, {0x012480, 0x012543,  1}, {0x012544, 0x012f8f, -1},
   2110     {0x012f90, 0x012ff2,  1}, {0x012ff3, 0x012fff, -1}, {0x013000, 0x01343f,  1},
   2111     {0x013440, 0x013440,  0}, {0x013441, 0x013446,  1}, {0x013447, 0x013455,  0},
   2112     {0x013456, 0x01345f, -1}, {0x013460, 0x0143fa,  1}, {0x0143fb, 0x0143ff, -1},
   2113     {0x014400, 0x014646,  1}, {0x014647, 0x0160ff, -1}, {0x016100, 0x01611d,  1},
   2114     {0x01611e, 0x016129,  0}, {0x01612a, 0x01612c,  1}, {0x01612d, 0x01612f,  0},
   2115     {0x016130, 0x016139,  1}, {0x01613a, 0x0167ff, -1}, {0x016800, 0x016a38,  1},
   2116     {0x016a39, 0x016a3f, -1}, {0x016a40, 0x016a5e,  1}, {0x016a5f, 0x016a5f, -1},
   2117     {0x016a60, 0x016a69,  1}, {0x016a6a, 0x016a6d, -1}, {0x016a6e, 0x016abe,  1},
   2118     {0x016abf, 0x016abf, -1}, {0x016ac0, 0x016ac9,  1}, {0x016aca, 0x016acf, -1},
   2119     {0x016ad0, 0x016aed,  1}, {0x016aee, 0x016aef, -1}, {0x016af0, 0x016af4,  0},
   2120     {0x016af5, 0x016af5,  1}, {0x016af6, 0x016aff, -1}, {0x016b00, 0x016b2f,  1},
   2121     {0x016b30, 0x016b36,  0}, {0x016b37, 0x016b45,  1}, {0x016b46, 0x016b4f, -1},
   2122     {0x016b50, 0x016b59,  1}, {0x016b5a, 0x016b5a, -1}, {0x016b5b, 0x016b61,  1},
   2123     {0x016b62, 0x016b62, -1}, {0x016b63, 0x016b77,  1}, {0x016b78, 0x016b7c, -1},
   2124     {0x016b7d, 0x016b8f,  1}, {0x016b90, 0x016d3f, -1}, {0x016d40, 0x016d79,  1},
   2125     {0x016d7a, 0x016e3f, -1}, {0x016e40, 0x016e9a,  1}, {0x016e9b, 0x016eff, -1},
   2126     {0x016f00, 0x016f4a,  1}, {0x016f4b, 0x016f4e, -1}, {0x016f4f, 0x016f4f,  0},
   2127     {0x016f50, 0x016f87,  1}, {0x016f88, 0x016f8e, -1}, {0x016f8f, 0x016f92,  0},
   2128     {0x016f93, 0x016f9f,  1}, {0x016fa0, 0x016fdf, -1}, {0x016fe0, 0x016fe3,  2},
   2129     {0x016fe4, 0x016fe4,  0}, {0x016fe5, 0x016fef, -1}, {0x016ff0, 0x016ff1,  2},
   2130     {0x016ff2, 0x016fff, -1}, {0x017000, 0x0187f7,  2}, {0x0187f8, 0x0187ff, -1},
   2131     {0x018800, 0x018cd5,  2}, {0x018cd6, 0x018cfe, -1}, {0x018cff, 0x018d08,  2},
   2132     {0x018d09, 0x01afef, -1}, {0x01aff0, 0x01aff3,  2}, {0x01aff4, 0x01aff4, -1},
   2133     {0x01aff5, 0x01affb,  2}, {0x01affc, 0x01affc, -1}, {0x01affd, 0x01affe,  2},
   2134     {0x01afff, 0x01afff, -1}, {0x01b000, 0x01b122,  2}, {0x01b123, 0x01b131, -1},
   2135     {0x01b132, 0x01b132,  2}, {0x01b133, 0x01b14f, -1}, {0x01b150, 0x01b152,  2},
   2136     {0x01b153, 0x01b154, -1}, {0x01b155, 0x01b155,  2}, {0x01b156, 0x01b163, -1},
   2137     {0x01b164, 0x01b167,  2}, {0x01b168, 0x01b16f, -1}, {0x01b170, 0x01b2fb,  2},
   2138     {0x01b2fc, 0x01bbff, -1}, {0x01bc00, 0x01bc6a,  1}, {0x01bc6b, 0x01bc6f, -1},
   2139     {0x01bc70, 0x01bc7c,  1}, {0x01bc7d, 0x01bc7f, -1}, {0x01bc80, 0x01bc88,  1},
   2140     {0x01bc89, 0x01bc8f, -1}, {0x01bc90, 0x01bc99,  1}, {0x01bc9a, 0x01bc9b, -1},
   2141     {0x01bc9c, 0x01bc9c,  1}, {0x01bc9d, 0x01bc9e,  0}, {0x01bc9f, 0x01bc9f,  1},
   2142     {0x01bca0, 0x01bca3,  0}, {0x01bca4, 0x01cbff, -1}, {0x01cc00, 0x01ccf9,  1},
   2143     {0x01ccfa, 0x01ccff, -1}, {0x01cd00, 0x01ceb3,  1}, {0x01ceb4, 0x01ceff, -1},
   2144     {0x01cf00, 0x01cf2d,  0}, {0x01cf2e, 0x01cf2f, -1}, {0x01cf30, 0x01cf46,  0},
   2145     {0x01cf47, 0x01cf4f, -1}, {0x01cf50, 0x01cfc3,  1}, {0x01cfc4, 0x01cfff, -1},
   2146     {0x01d000, 0x01d0f5,  1}, {0x01d0f6, 0x01d0ff, -1}, {0x01d100, 0x01d126,  1},
   2147     {0x01d127, 0x01d128, -1}, {0x01d129, 0x01d166,  1}, {0x01d167, 0x01d169,  0},
   2148     {0x01d16a, 0x01d172,  1}, {0x01d173, 0x01d182,  0}, {0x01d183, 0x01d184,  1},
   2149     {0x01d185, 0x01d18b,  0}, {0x01d18c, 0x01d1a9,  1}, {0x01d1aa, 0x01d1ad,  0},
   2150     {0x01d1ae, 0x01d1ea,  1}, {0x01d1eb, 0x01d1ff, -1}, {0x01d200, 0x01d241,  1},
   2151     {0x01d242, 0x01d244,  0}, {0x01d245, 0x01d245,  1}, {0x01d246, 0x01d2bf, -1},
   2152     {0x01d2c0, 0x01d2d3,  1}, {0x01d2d4, 0x01d2df, -1}, {0x01d2e0, 0x01d2f3,  1},
   2153     {0x01d2f4, 0x01d2ff, -1}, {0x01d300, 0x01d356,  2}, {0x01d357, 0x01d35f, -1},
   2154     {0x01d360, 0x01d376,  2}, {0x01d377, 0x01d378,  1}, {0x01d379, 0x01d3ff, -1},
   2155     {0x01d400, 0x01d454,  1}, {0x01d455, 0x01d455, -1}, {0x01d456, 0x01d49c,  1},
   2156     {0x01d49d, 0x01d49d, -1}, {0x01d49e, 0x01d49f,  1}, {0x01d4a0, 0x01d4a1, -1},
   2157     {0x01d4a2, 0x01d4a2,  1}, {0x01d4a3, 0x01d4a4, -1}, {0x01d4a5, 0x01d4a6,  1},
   2158     {0x01d4a7, 0x01d4a8, -1}, {0x01d4a9, 0x01d4ac,  1}, {0x01d4ad, 0x01d4ad, -1},
   2159     {0x01d4ae, 0x01d4b9,  1}, {0x01d4ba, 0x01d4ba, -1}, {0x01d4bb, 0x01d4bb,  1},
   2160     {0x01d4bc, 0x01d4bc, -1}, {0x01d4bd, 0x01d4c3,  1}, {0x01d4c4, 0x01d4c4, -1},
   2161     {0x01d4c5, 0x01d505,  1}, {0x01d506, 0x01d506, -1}, {0x01d507, 0x01d50a,  1},
   2162     {0x01d50b, 0x01d50c, -1}, {0x01d50d, 0x01d514,  1}, {0x01d515, 0x01d515, -1},
   2163     {0x01d516, 0x01d51c,  1}, {0x01d51d, 0x01d51d, -1}, {0x01d51e, 0x01d539,  1},
   2164     {0x01d53a, 0x01d53a, -1}, {0x01d53b, 0x01d53e,  1}, {0x01d53f, 0x01d53f, -1},
   2165     {0x01d540, 0x01d544,  1}, {0x01d545, 0x01d545, -1}, {0x01d546, 0x01d546,  1},
   2166     {0x01d547, 0x01d549, -1}, {0x01d54a, 0x01d550,  1}, {0x01d551, 0x01d551, -1},
   2167     {0x01d552, 0x01d6a5,  1}, {0x01d6a6, 0x01d6a7, -1}, {0x01d6a8, 0x01d7cb,  1},
   2168     {0x01d7cc, 0x01d7cd, -1}, {0x01d7ce, 0x01d9ff,  1}, {0x01da00, 0x01da36,  0},
   2169     {0x01da37, 0x01da3a,  1}, {0x01da3b, 0x01da6c,  0}, {0x01da6d, 0x01da74,  1},
   2170     {0x01da75, 0x01da75,  0}, {0x01da76, 0x01da83,  1}, {0x01da84, 0x01da84,  0},
   2171     {0x01da85, 0x01da8b,  1}, {0x01da8c, 0x01da9a, -1}, {0x01da9b, 0x01da9f,  0},
   2172     {0x01daa0, 0x01daa0, -1}, {0x01daa1, 0x01daaf,  0}, {0x01dab0, 0x01deff, -1},
   2173     {0x01df00, 0x01df1e,  1}, {0x01df1f, 0x01df24, -1}, {0x01df25, 0x01df2a,  1},
   2174     {0x01df2b, 0x01dfff, -1}, {0x01e000, 0x01e006,  0}, {0x01e007, 0x01e007, -1},
   2175     {0x01e008, 0x01e018,  0}, {0x01e019, 0x01e01a, -1}, {0x01e01b, 0x01e021,  0},
   2176     {0x01e022, 0x01e022, -1}, {0x01e023, 0x01e024,  0}, {0x01e025, 0x01e025, -1},
   2177     {0x01e026, 0x01e02a,  0}, {0x01e02b, 0x01e02f, -1}, {0x01e030, 0x01e06d,  1},
   2178     {0x01e06e, 0x01e08e, -1}, {0x01e08f, 0x01e08f,  0}, {0x01e090, 0x01e0ff, -1},
   2179     {0x01e100, 0x01e12c,  1}, {0x01e12d, 0x01e12f, -1}, {0x01e130, 0x01e136,  0},
   2180     {0x01e137, 0x01e13d,  1}, {0x01e13e, 0x01e13f, -1}, {0x01e140, 0x01e149,  1},
   2181     {0x01e14a, 0x01e14d, -1}, {0x01e14e, 0x01e14f,  1}, {0x01e150, 0x01e28f, -1},
   2182     {0x01e290, 0x01e2ad,  1}, {0x01e2ae, 0x01e2ae,  0}, {0x01e2af, 0x01e2bf, -1},
   2183     {0x01e2c0, 0x01e2eb,  1}, {0x01e2ec, 0x01e2ef,  0}, {0x01e2f0, 0x01e2f9,  1},
   2184     {0x01e2fa, 0x01e2fe, -1}, {0x01e2ff, 0x01e2ff,  1}, {0x01e300, 0x01e4cf, -1},
   2185     {0x01e4d0, 0x01e4eb,  1}, {0x01e4ec, 0x01e4ef,  0}, {0x01e4f0, 0x01e4f9,  1},
   2186     {0x01e4fa, 0x01e5cf, -1}, {0x01e5d0, 0x01e5ed,  1}, {0x01e5ee, 0x01e5ef,  0},
   2187     {0x01e5f0, 0x01e5fa,  1}, {0x01e5fb, 0x01e5fe, -1}, {0x01e5ff, 0x01e5ff,  1},
   2188     {0x01e600, 0x01e7df, -1}, {0x01e7e0, 0x01e7e6,  1}, {0x01e7e7, 0x01e7e7, -1},
   2189     {0x01e7e8, 0x01e7eb,  1}, {0x01e7ec, 0x01e7ec, -1}, {0x01e7ed, 0x01e7ee,  1},
   2190     {0x01e7ef, 0x01e7ef, -1}, {0x01e7f0, 0x01e7fe,  1}, {0x01e7ff, 0x01e7ff, -1},
   2191     {0x01e800, 0x01e8c4,  1}, {0x01e8c5, 0x01e8c6, -1}, {0x01e8c7, 0x01e8cf,  1},
   2192     {0x01e8d0, 0x01e8d6,  0}, {0x01e8d7, 0x01e8ff, -1}, {0x01e900, 0x01e943,  1},
   2193     {0x01e944, 0x01e94a,  0}, {0x01e94b, 0x01e94b,  1}, {0x01e94c, 0x01e94f, -1},
   2194     {0x01e950, 0x01e959,  1}, {0x01e95a, 0x01e95d, -1}, {0x01e95e, 0x01e95f,  1},
   2195     {0x01e960, 0x01ec70, -1}, {0x01ec71, 0x01ecb4,  1}, {0x01ecb5, 0x01ed00, -1},
   2196     {0x01ed01, 0x01ed3d,  1}, {0x01ed3e, 0x01edff, -1}, {0x01ee00, 0x01ee03,  1},
   2197     {0x01ee04, 0x01ee04, -1}, {0x01ee05, 0x01ee1f,  1}, {0x01ee20, 0x01ee20, -1},
   2198     {0x01ee21, 0x01ee22,  1}, {0x01ee23, 0x01ee23, -1}, {0x01ee24, 0x01ee24,  1},
   2199     {0x01ee25, 0x01ee26, -1}, {0x01ee27, 0x01ee27,  1}, {0x01ee28, 0x01ee28, -1},
   2200     {0x01ee29, 0x01ee32,  1}, {0x01ee33, 0x01ee33, -1}, {0x01ee34, 0x01ee37,  1},
   2201     {0x01ee38, 0x01ee38, -1}, {0x01ee39, 0x01ee39,  1}, {0x01ee3a, 0x01ee3a, -1},
   2202     {0x01ee3b, 0x01ee3b,  1}, {0x01ee3c, 0x01ee41, -1}, {0x01ee42, 0x01ee42,  1},
   2203     {0x01ee43, 0x01ee46, -1}, {0x01ee47, 0x01ee47,  1}, {0x01ee48, 0x01ee48, -1},
   2204     {0x01ee49, 0x01ee49,  1}, {0x01ee4a, 0x01ee4a, -1}, {0x01ee4b, 0x01ee4b,  1},
   2205     {0x01ee4c, 0x01ee4c, -1}, {0x01ee4d, 0x01ee4f,  1}, {0x01ee50, 0x01ee50, -1},
   2206     {0x01ee51, 0x01ee52,  1}, {0x01ee53, 0x01ee53, -1}, {0x01ee54, 0x01ee54,  1},
   2207     {0x01ee55, 0x01ee56, -1}, {0x01ee57, 0x01ee57,  1}, {0x01ee58, 0x01ee58, -1},
   2208     {0x01ee59, 0x01ee59,  1}, {0x01ee5a, 0x01ee5a, -1}, {0x01ee5b, 0x01ee5b,  1},
   2209     {0x01ee5c, 0x01ee5c, -1}, {0x01ee5d, 0x01ee5d,  1}, {0x01ee5e, 0x01ee5e, -1},
   2210     {0x01ee5f, 0x01ee5f,  1}, {0x01ee60, 0x01ee60, -1}, {0x01ee61, 0x01ee62,  1},
   2211     {0x01ee63, 0x01ee63, -1}, {0x01ee64, 0x01ee64,  1}, {0x01ee65, 0x01ee66, -1},
   2212     {0x01ee67, 0x01ee6a,  1}, {0x01ee6b, 0x01ee6b, -1}, {0x01ee6c, 0x01ee72,  1},
   2213     {0x01ee73, 0x01ee73, -1}, {0x01ee74, 0x01ee77,  1}, {0x01ee78, 0x01ee78, -1},
   2214     {0x01ee79, 0x01ee7c,  1}, {0x01ee7d, 0x01ee7d, -1}, {0x01ee7e, 0x01ee7e,  1},
   2215     {0x01ee7f, 0x01ee7f, -1}, {0x01ee80, 0x01ee89,  1}, {0x01ee8a, 0x01ee8a, -1},
   2216     {0x01ee8b, 0x01ee9b,  1}, {0x01ee9c, 0x01eea0, -1}, {0x01eea1, 0x01eea3,  1},
   2217     {0x01eea4, 0x01eea4, -1}, {0x01eea5, 0x01eea9,  1}, {0x01eeaa, 0x01eeaa, -1},
   2218     {0x01eeab, 0x01eebb,  1}, {0x01eebc, 0x01eeef, -1}, {0x01eef0, 0x01eef1,  1},
   2219     {0x01eef2, 0x01efff, -1}, {0x01f000, 0x01f003,  1}, {0x01f004, 0x01f004,  2},
   2220     {0x01f005, 0x01f02b,  1}, {0x01f02c, 0x01f02f, -1}, {0x01f030, 0x01f093,  1},
   2221     {0x01f094, 0x01f09f, -1}, {0x01f0a0, 0x01f0ae,  1}, {0x01f0af, 0x01f0b0, -1},
   2222     {0x01f0b1, 0x01f0bf,  1}, {0x01f0c0, 0x01f0c0, -1}, {0x01f0c1, 0x01f0ce,  1},
   2223     {0x01f0cf, 0x01f0cf,  2}, {0x01f0d0, 0x01f0d0, -1}, {0x01f0d1, 0x01f0f5,  1},
   2224     {0x01f0f6, 0x01f0ff, -1}, {0x01f100, 0x01f18d,  1}, {0x01f18e, 0x01f18e,  2},
   2225     {0x01f18f, 0x01f190,  1}, {0x01f191, 0x01f19a,  2}, {0x01f19b, 0x01f1ad,  1},
   2226     {0x01f1ae, 0x01f1e5, -1}, {0x01f1e6, 0x01f1ff,  1}, {0x01f200, 0x01f202,  2},
   2227     {0x01f203, 0x01f20f, -1}, {0x01f210, 0x01f23b,  2}, {0x01f23c, 0x01f23f, -1},
   2228     {0x01f240, 0x01f248,  2}, {0x01f249, 0x01f24f, -1}, {0x01f250, 0x01f251,  2},
   2229     {0x01f252, 0x01f25f, -1}, {0x01f260, 0x01f265,  2}, {0x01f266, 0x01f2ff, -1},
   2230     {0x01f300, 0x01f320,  2}, {0x01f321, 0x01f32c,  1}, {0x01f32d, 0x01f335,  2},
   2231     {0x01f336, 0x01f336,  1}, {0x01f337, 0x01f37c,  2}, {0x01f37d, 0x01f37d,  1},
   2232     {0x01f37e, 0x01f393,  2}, {0x01f394, 0x01f39f,  1}, {0x01f3a0, 0x01f3ca,  2},
   2233     {0x01f3cb, 0x01f3ce,  1}, {0x01f3cf, 0x01f3d3,  2}, {0x01f3d4, 0x01f3df,  1},
   2234     {0x01f3e0, 0x01f3f0,  2}, {0x01f3f1, 0x01f3f3,  1}, {0x01f3f4, 0x01f3f4,  2},
   2235     {0x01f3f5, 0x01f3f7,  1}, {0x01f3f8, 0x01f43e,  2}, {0x01f43f, 0x01f43f,  1},
   2236     {0x01f440, 0x01f440,  2}, {0x01f441, 0x01f441,  1}, {0x01f442, 0x01f4fc,  2},
   2237     {0x01f4fd, 0x01f4fe,  1}, {0x01f4ff, 0x01f53d,  2}, {0x01f53e, 0x01f54a,  1},
   2238     {0x01f54b, 0x01f54e,  2}, {0x01f54f, 0x01f54f,  1}, {0x01f550, 0x01f567,  2},
   2239     {0x01f568, 0x01f579,  1}, {0x01f57a, 0x01f57a,  2}, {0x01f57b, 0x01f594,  1},
   2240     {0x01f595, 0x01f596,  2}, {0x01f597, 0x01f5a3,  1}, {0x01f5a4, 0x01f5a4,  2},
   2241     {0x01f5a5, 0x01f5fa,  1}, {0x01f5fb, 0x01f64f,  2}, {0x01f650, 0x01f67f,  1},
   2242     {0x01f680, 0x01f6c5,  2}, {0x01f6c6, 0x01f6cb,  1}, {0x01f6cc, 0x01f6cc,  2},
   2243     {0x01f6cd, 0x01f6cf,  1}, {0x01f6d0, 0x01f6d2,  2}, {0x01f6d3, 0x01f6d4,  1},
   2244     {0x01f6d5, 0x01f6d7,  2}, {0x01f6d8, 0x01f6db, -1}, {0x01f6dc, 0x01f6df,  2},
   2245     {0x01f6e0, 0x01f6ea,  1}, {0x01f6eb, 0x01f6ec,  2}, {0x01f6ed, 0x01f6ef, -1},
   2246     {0x01f6f0, 0x01f6f3,  1}, {0x01f6f4, 0x01f6fc,  2}, {0x01f6fd, 0x01f6ff, -1},
   2247     {0x01f700, 0x01f776,  1}, {0x01f777, 0x01f77a, -1}, {0x01f77b, 0x01f7d9,  1},
   2248     {0x01f7da, 0x01f7df, -1}, {0x01f7e0, 0x01f7eb,  2}, {0x01f7ec, 0x01f7ef, -1},
   2249     {0x01f7f0, 0x01f7f0,  2}, {0x01f7f1, 0x01f7ff, -1}, {0x01f800, 0x01f80b,  1},
   2250     {0x01f80c, 0x01f80f, -1}, {0x01f810, 0x01f847,  1}, {0x01f848, 0x01f84f, -1},
   2251     {0x01f850, 0x01f859,  1}, {0x01f85a, 0x01f85f, -1}, {0x01f860, 0x01f887,  1},
   2252     {0x01f888, 0x01f88f, -1}, {0x01f890, 0x01f8ad,  1}, {0x01f8ae, 0x01f8af, -1},
   2253     {0x01f8b0, 0x01f8bb,  1}, {0x01f8bc, 0x01f8bf, -1}, {0x01f8c0, 0x01f8c1,  1},
   2254     {0x01f8c2, 0x01f8ff, -1}, {0x01f900, 0x01f90b,  1}, {0x01f90c, 0x01f93a,  2},
   2255     {0x01f93b, 0x01f93b,  1}, {0x01f93c, 0x01f945,  2}, {0x01f946, 0x01f946,  1},
   2256     {0x01f947, 0x01f9ff,  2}, {0x01fa00, 0x01fa53,  1}, {0x01fa54, 0x01fa5f, -1},
   2257     {0x01fa60, 0x01fa6d,  1}, {0x01fa6e, 0x01fa6f, -1}, {0x01fa70, 0x01fa7c,  2},
   2258     {0x01fa7d, 0x01fa7f, -1}, {0x01fa80, 0x01fa89,  2}, {0x01fa8a, 0x01fa8e, -1},
   2259     {0x01fa8f, 0x01fac6,  2}, {0x01fac7, 0x01facd, -1}, {0x01face, 0x01fadc,  2},
   2260     {0x01fadd, 0x01fade, -1}, {0x01fadf, 0x01fae9,  2}, {0x01faea, 0x01faef, -1},
   2261     {0x01faf0, 0x01faf8,  2}, {0x01faf9, 0x01faff, -1}, {0x01fb00, 0x01fb92,  1},
   2262     {0x01fb93, 0x01fb93, -1}, {0x01fb94, 0x01fbf9,  1}, {0x01fbfa, 0x01ffff, -1},
   2263     {0x020000, 0x02a6df,  2}, {0x02a6e0, 0x02a6ff, -1}, {0x02a700, 0x02b739,  2},
   2264     {0x02b73a, 0x02b73f, -1}, {0x02b740, 0x02b81d,  2}, {0x02b81e, 0x02b81f, -1},
   2265     {0x02b820, 0x02cea1,  2}, {0x02cea2, 0x02ceaf, -1}, {0x02ceb0, 0x02ebe0,  2},
   2266     {0x02ebe1, 0x02ebef, -1}, {0x02ebf0, 0x02ee5d,  2}, {0x02ee5e, 0x02f7ff, -1},
   2267     {0x02f800, 0x02fa1d,  2}, {0x02fa1e, 0x02ffff, -1}, {0x030000, 0x03134a,  2},
   2268     {0x03134b, 0x03134f, -1}, {0x031350, 0x0323af,  2}, {0x0323b0, 0x0e0000, -1},
   2269     {0x0e0001, 0x0e0001,  0}, {0x0e0002, 0x0e001f, -1}, {0x0e0020, 0x0e007f,  0},
   2270     {0x0e0080, 0x0e00ff, -1}, {0x0e0100, 0x0e01ef,  0}, {0x0e01f0, 0x0effff, -1},
   2271     {0x0f0000, 0x0ffffd,  1}, {0x0ffffe, 0x0fffff, -1}, {0x100000, 0x10fffd,  1},
   2272     {0x10fffe, 0x10ffff, -1},
   2273     // clang-format on
   2274 };
   2275 #define WCWIDTH_TABLE_LENGTH 2143
   2276 #endif // ifndef TB_OPT_LIBC_WCHAR
   2277 
   2278 static int tb_reset(void);
   2279 static int tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg,
   2280     size_t *out_w, const char *fmt, va_list vl);
   2281 static int init_term_attrs(void);
   2282 static int init_term_caps(void);
   2283 static int init_cap_trie(void);
   2284 static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod);
   2285 static int cap_trie_find(const char *buf, size_t nbuf, struct cap_trie **last,
   2286     size_t *depth);
   2287 static int cap_trie_deinit(struct cap_trie *node);
   2288 static int init_resize_handler(void);
   2289 static int send_init_escape_codes(void);
   2290 static int send_clear(void);
   2291 static int update_term_size(void);
   2292 static int update_term_size_via_esc(void);
   2293 static int init_cellbuf(void);
   2294 static int tb_deinit(void);
   2295 static int load_terminfo(void);
   2296 static int load_terminfo_from_path(const char *path, const char *term);
   2297 static int read_terminfo_path(const char *path);
   2298 static int parse_terminfo_caps(void);
   2299 static int load_builtin_caps(void);
   2300 static const char *get_terminfo_string(int16_t offsets_pos, int16_t offsets_len,
   2301     int16_t table_pos, int16_t table_size, int16_t index);
   2302 static int get_terminfo_int16(int offset, int16_t *val);
   2303 static int wait_event(struct tb_event *event, int timeout);
   2304 static int extract_event(struct tb_event *event);
   2305 static int extract_esc(struct tb_event *event);
   2306 static int extract_esc_user(struct tb_event *event, int is_post);
   2307 static int extract_esc_cap(struct tb_event *event);
   2308 static int extract_esc_mouse(struct tb_event *event);
   2309 static int resize_cellbufs(void);
   2310 static void handle_resize(int sig);
   2311 static int send_attr(uintattr_t fg, uintattr_t bg);
   2312 static int send_sgr(uint32_t fg, uint32_t bg, int fg_is_default,
   2313     int bg_is_default);
   2314 static int send_cursor_if(int x, int y);
   2315 static int send_char(int x, int y, uint32_t ch);
   2316 static int send_cluster(int x, int y, uint32_t *ch, size_t nch);
   2317 static int convert_num(uint32_t num, char *buf);
   2318 static int cell_cmp(struct tb_cell *a, struct tb_cell *b);
   2319 static int cell_copy(struct tb_cell *dst, struct tb_cell *src);
   2320 static int cell_set(struct tb_cell *cell, uint32_t *ch, size_t nch,
   2321     uintattr_t fg, uintattr_t bg);
   2322 static int cell_reserve_ech(struct tb_cell *cell, size_t n);
   2323 static int cell_free(struct tb_cell *cell);
   2324 static int cellbuf_init(struct cellbuf *c, int w, int h);
   2325 static int cellbuf_free(struct cellbuf *c);
   2326 static int cellbuf_clear(struct cellbuf *c);
   2327 static int cellbuf_get(struct cellbuf *c, int x, int y, struct tb_cell **out);
   2328 static int cellbuf_in_bounds(struct cellbuf *c, int x, int y);
   2329 static int cellbuf_resize(struct cellbuf *c, int w, int h);
   2330 static int bytebuf_puts(struct bytebuf *b, const char *str);
   2331 static int bytebuf_nputs(struct bytebuf *b, const char *str, size_t nstr);
   2332 static int bytebuf_shift(struct bytebuf *b, size_t n);
   2333 static int bytebuf_flush(struct bytebuf *b, int fd);
   2334 static int bytebuf_reserve(struct bytebuf *b, size_t sz);
   2335 static int bytebuf_free(struct bytebuf *b);
   2336 static int tb_iswprint_ex(uint32_t ch, int *width);
   2337 static int tb_cluster_width(uint32_t *ch, size_t nch);
   2338 
   2339 int tb_init(void) {
   2340     return tb_init_file("/dev/tty");
   2341 }
   2342 
   2343 int tb_init_file(const char *path) {
   2344     if (global.initialized) return TB_ERR_INIT_ALREADY;
   2345     int ttyfd = open(path, O_RDWR);
   2346     if (ttyfd < 0) {
   2347         global.last_errno = errno;
   2348         return TB_ERR_INIT_OPEN;
   2349     }
   2350     global.ttyfd_open = 1;
   2351     return tb_init_fd(ttyfd);
   2352 }
   2353 
   2354 int tb_init_fd(int ttyfd) {
   2355     return tb_init_rwfd(ttyfd, ttyfd);
   2356 }
   2357 
   2358 int tb_init_rwfd(int rfd, int wfd) {
   2359     int rv;
   2360 
   2361     tb_reset();
   2362     global.ttyfd = isatty(rfd) ? rfd : (isatty(wfd) ? wfd : -1);
   2363     global.rfd = rfd;
   2364     global.wfd = wfd;
   2365 
   2366     do {
   2367         if_err_break(rv, init_term_attrs());
   2368         if_err_break(rv, init_term_caps());
   2369         if_err_break(rv, init_cap_trie());
   2370         if_err_break(rv, init_resize_handler());
   2371         if_err_break(rv, send_init_escape_codes());
   2372         if_err_break(rv, send_clear());
   2373         if_err_break(rv, update_term_size());
   2374         if_err_break(rv, init_cellbuf());
   2375         global.initialized = 1;
   2376     } while (0);
   2377 
   2378     if (rv != TB_OK) tb_deinit();
   2379 
   2380     return rv;
   2381 }
   2382 
   2383 int tb_shutdown(void) {
   2384     if_not_init_return();
   2385     tb_deinit();
   2386     return TB_OK;
   2387 }
   2388 
   2389 int tb_width(void) {
   2390     if_not_init_return();
   2391     return global.width;
   2392 }
   2393 
   2394 int tb_height(void) {
   2395     if_not_init_return();
   2396     return global.height;
   2397 }
   2398 
   2399 int tb_clear(void) {
   2400     if_not_init_return();
   2401     return cellbuf_clear(&global.back);
   2402 }
   2403 
   2404 int tb_set_clear_attrs(uintattr_t fg, uintattr_t bg) {
   2405     if_not_init_return();
   2406     global.fg = fg;
   2407     global.bg = bg;
   2408     return TB_OK;
   2409 }
   2410 
   2411 int tb_present(void) {
   2412     if_not_init_return();
   2413 
   2414     int rv;
   2415 
   2416     // TODO: Assert global.back.(width,height) == global.front.(width,height)
   2417 
   2418     global.last_x = -1;
   2419     global.last_y = -1;
   2420 
   2421     int x, y, i;
   2422     for (y = 0; y < global.front.height; y++) {
   2423         for (x = 0; x < global.front.width;) {
   2424             struct tb_cell *back, *front;
   2425             if_err_return(rv, cellbuf_get(&global.back, x, y, &back));
   2426             if_err_return(rv, cellbuf_get(&global.front, x, y, &front));
   2427 
   2428             int w;
   2429             {
   2430 #ifdef TB_OPT_EGC
   2431                 if (back->nech > 0)
   2432                     w = tb_cluster_width(back->ech, back->nech);
   2433                 else
   2434 #endif
   2435                     w = tb_wcwidth((wchar_t)back->ch);
   2436             }
   2437             if (w < 1) w = 1; // wcwidth returns -1 for invalid codepoints
   2438 
   2439             if (cell_cmp(back, front) != 0) {
   2440                 cell_copy(front, back);
   2441 
   2442                 send_attr(back->fg, back->bg);
   2443                 if (w > 1 && x >= global.front.width - (w - 1)) {
   2444                     // Not enough room for wide char, send spaces
   2445                     for (i = x; i < global.front.width; i++) {
   2446                         send_char(i, y, ' ');
   2447                     }
   2448                 } else {
   2449                     {
   2450 #ifdef TB_OPT_EGC
   2451                         if (back->nech > 0)
   2452                             send_cluster(x, y, back->ech, back->nech);
   2453                         else
   2454 #endif
   2455                             send_char(x, y, back->ch);
   2456                     }
   2457 
   2458                     // When wcwidth>1, we need to advance the cursor by more
   2459                     // than 1, thereby skipping some cells. Set these skipped
   2460                     // cells to an invalid codepoint in the front buffer, so
   2461                     // that if this cell is later replaced by a wcwidth==1
   2462                     // char, we'll get a cell_cmp diff for the skipped cells
   2463                     // and properly re-render.
   2464                     for (i = 1; i < w; i++) {
   2465                         struct tb_cell *front_wide;
   2466                         uint32_t invalid = -1;
   2467                         if_err_return(rv,
   2468                             cellbuf_get(&global.front, x + i, y, &front_wide));
   2469                         if_err_return(rv,
   2470                             cell_set(front_wide, &invalid, 1, -1, -1));
   2471                     }
   2472                 }
   2473             }
   2474             x += w;
   2475         }
   2476     }
   2477 
   2478     if_err_return(rv, send_cursor_if(global.cursor_x, global.cursor_y));
   2479     if_err_return(rv, bytebuf_flush(&global.out, global.wfd));
   2480 
   2481     return TB_OK;
   2482 }
   2483 
   2484 int tb_invalidate(void) {
   2485     int rv;
   2486     if_not_init_return();
   2487     if_err_return(rv, resize_cellbufs());
   2488     return TB_OK;
   2489 }
   2490 
   2491 int tb_set_cursor(int cx, int cy) {
   2492     if_not_init_return();
   2493     int rv;
   2494     if (cx < 0) cx = 0;
   2495     if (cy < 0) cy = 0;
   2496     if (global.cursor_x == -1) {
   2497         if_err_return(rv,
   2498             bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]));
   2499     }
   2500     if_err_return(rv, send_cursor_if(cx, cy));
   2501     global.cursor_x = cx;
   2502     global.cursor_y = cy;
   2503     return TB_OK;
   2504 }
   2505 
   2506 int tb_hide_cursor(void) {
   2507     if_not_init_return();
   2508     int rv;
   2509     if (global.cursor_x >= 0) {
   2510         if_err_return(rv,
   2511             bytebuf_puts(&global.out, global.caps[TB_CAP_HIDE_CURSOR]));
   2512     }
   2513     global.cursor_x = -1;
   2514     global.cursor_y = -1;
   2515     return TB_OK;
   2516 }
   2517 
   2518 int tb_set_cell(int x, int y, uint32_t ch, uintattr_t fg, uintattr_t bg) {
   2519     return tb_set_cell_ex(x, y, &ch, 1, fg, bg);
   2520 }
   2521 
   2522 int tb_set_cell_ex(int x, int y, uint32_t *ch, size_t nch, uintattr_t fg,
   2523     uintattr_t bg) {
   2524     if_not_init_return();
   2525     int rv;
   2526     struct tb_cell *cell;
   2527     if_err_return(rv, cellbuf_get(&global.back, x, y, &cell));
   2528     if_err_return(rv, cell_set(cell, ch, nch, fg, bg));
   2529     return TB_OK;
   2530 }
   2531 
   2532 int tb_get_cell(int x, int y, int back, struct tb_cell **cell) {
   2533     if_not_init_return();
   2534     return cellbuf_get(back ? &global.back : &global.front, x, y, cell);
   2535 }
   2536 
   2537 int tb_extend_cell(int x, int y, uint32_t ch) {
   2538     if_not_init_return();
   2539 #ifdef TB_OPT_EGC
   2540     // TODO: iswprint ch?
   2541     int rv;
   2542     struct tb_cell *cell;
   2543     size_t nech;
   2544     if_err_return(rv, cellbuf_get(&global.back, x, y, &cell));
   2545     if (cell->nech > 0) { // append to ech
   2546         nech = cell->nech + 1;
   2547         if_err_return(rv, cell_reserve_ech(cell, nech + 1));
   2548         cell->ech[nech - 1] = ch;
   2549     } else { // make new ech
   2550         nech = 2;
   2551         if_err_return(rv, cell_reserve_ech(cell, nech + 1));
   2552         cell->ech[0] = cell->ch;
   2553         cell->ech[1] = ch;
   2554     }
   2555     cell->ech[nech] = '\0';
   2556     cell->nech = nech;
   2557     return TB_OK;
   2558 #else
   2559     (void)x;
   2560     (void)y;
   2561     (void)ch;
   2562     return TB_ERR;
   2563 #endif
   2564 }
   2565 
   2566 int tb_set_input_mode(int mode) {
   2567     if_not_init_return();
   2568 
   2569     if (mode == TB_INPUT_CURRENT) return global.input_mode;
   2570 
   2571     int esc_or_alt = TB_INPUT_ESC | TB_INPUT_ALT;
   2572     if ((mode & esc_or_alt) == 0) {
   2573         // neither specified; flip on ESC
   2574         mode |= TB_INPUT_ESC;
   2575     } else if ((mode & esc_or_alt) == esc_or_alt) {
   2576         // both specified; flip off ALT
   2577         mode &= ~TB_INPUT_ALT;
   2578     }
   2579 
   2580     if (mode & TB_INPUT_MOUSE) {
   2581         bytebuf_puts(&global.out, TB_HARDCAP_ENTER_MOUSE);
   2582         bytebuf_flush(&global.out, global.wfd);
   2583     } else {
   2584         bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE);
   2585         bytebuf_flush(&global.out, global.wfd);
   2586     }
   2587 
   2588     global.input_mode = mode;
   2589     return TB_OK;
   2590 }
   2591 
   2592 int tb_set_output_mode(int mode) {
   2593     if_not_init_return();
   2594     switch (mode) {
   2595         case TB_OUTPUT_CURRENT:
   2596             return global.output_mode;
   2597         case TB_OUTPUT_NORMAL:
   2598         case TB_OUTPUT_256:
   2599         case TB_OUTPUT_216:
   2600         case TB_OUTPUT_GRAYSCALE:
   2601 #if TB_OPT_ATTR_W >= 32
   2602         case TB_OUTPUT_TRUECOLOR:
   2603 #endif
   2604             global.last_fg = ~global.fg;
   2605             global.last_bg = ~global.bg;
   2606             global.output_mode = mode;
   2607             return TB_OK;
   2608     }
   2609     return TB_ERR;
   2610 }
   2611 
   2612 int tb_peek_event(struct tb_event *event, int timeout_ms) {
   2613     if_not_init_return();
   2614     return wait_event(event, timeout_ms);
   2615 }
   2616 
   2617 int tb_poll_event(struct tb_event *event) {
   2618     if_not_init_return();
   2619     return wait_event(event, -1);
   2620 }
   2621 
   2622 int tb_get_fds(int *ttyfd, int *resizefd) {
   2623     if_not_init_return();
   2624 
   2625     *ttyfd = global.rfd;
   2626     *resizefd = global.resize_pipefd[0];
   2627 
   2628     return TB_OK;
   2629 }
   2630 
   2631 int tb_print(int x, int y, uintattr_t fg, uintattr_t bg, const char *str) {
   2632     return tb_print_ex(x, y, fg, bg, NULL, str);
   2633 }
   2634 
   2635 int tb_print_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
   2636     const char *str) {
   2637     int rv, w, ix, x_prev;
   2638     uint32_t uni;
   2639 
   2640     if_not_init_return();
   2641 
   2642     if (!cellbuf_in_bounds(&global.back, x, y)) {
   2643         return TB_ERR_OUT_OF_BOUNDS;
   2644     }
   2645 
   2646     ix = x;
   2647     x_prev = x;
   2648     if (out_w) *out_w = 0;
   2649 
   2650     while (*str) {
   2651         rv = tb_utf8_char_to_unicode(&uni, str);
   2652 
   2653         if (rv < 0) {
   2654             uni = 0xfffd; // replace invalid UTF-8 char with U+FFFD
   2655             str += rv * -1;
   2656         } else if (rv > 0) {
   2657             str += rv;
   2658         } else {
   2659             break; // shouldn't get here
   2660         }
   2661 
   2662         if (uni == '\n') { // TODO: \r, \t, \v, \f, etc?
   2663             x = ix;
   2664             x_prev = x;
   2665             y += 1;
   2666             continue;
   2667         } else if (!tb_iswprint_ex(uni, &w)) {
   2668             uni = 0xfffd; // replace non-printable with U+FFFD
   2669             w = 1;
   2670         }
   2671 
   2672         if (w < 0) {
   2673             return TB_ERR;   // shouldn't happen if iswprint
   2674         } else if (w == 0) { // combining character
   2675             if (cellbuf_in_bounds(&global.back, x_prev, y)) {
   2676                 if_err_return(rv, tb_extend_cell(x_prev, y, uni));
   2677             }
   2678         } else {
   2679             if (cellbuf_in_bounds(&global.back, x, y)) {
   2680                 if_err_return(rv, tb_set_cell(x, y, uni, fg, bg));
   2681             }
   2682             x_prev = x;
   2683             x += w;
   2684             if (out_w) *out_w += w;
   2685         }
   2686     }
   2687 
   2688     return TB_OK;
   2689 }
   2690 
   2691 int tb_printf(int x, int y, uintattr_t fg, uintattr_t bg, const char *fmt,
   2692     ...) {
   2693     int rv;
   2694     va_list vl;
   2695     va_start(vl, fmt);
   2696     rv = tb_printf_inner(x, y, fg, bg, NULL, fmt, vl);
   2697     va_end(vl);
   2698     return rv;
   2699 }
   2700 
   2701 int tb_printf_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
   2702     const char *fmt, ...) {
   2703     int rv;
   2704     va_list vl;
   2705     va_start(vl, fmt);
   2706     rv = tb_printf_inner(x, y, fg, bg, out_w, fmt, vl);
   2707     va_end(vl);
   2708     return rv;
   2709 }
   2710 
   2711 int tb_send(const char *buf, size_t nbuf) {
   2712     return bytebuf_nputs(&global.out, buf, nbuf);
   2713 }
   2714 
   2715 int tb_sendf(const char *fmt, ...) {
   2716     int rv;
   2717     char buf[TB_OPT_PRINTF_BUF];
   2718     va_list vl;
   2719     va_start(vl, fmt);
   2720     rv = vsnprintf(buf, sizeof(buf), fmt, vl);
   2721     va_end(vl);
   2722     if (rv < 0 || rv >= (int)sizeof(buf)) {
   2723         return TB_ERR;
   2724     }
   2725     return tb_send(buf, (size_t)rv);
   2726 }
   2727 
   2728 int tb_set_func(int fn_type, int (*fn)(struct tb_event *, size_t *)) {
   2729     switch (fn_type) {
   2730         case TB_FUNC_EXTRACT_PRE:
   2731             global.fn_extract_esc_pre = fn;
   2732             return TB_OK;
   2733         case TB_FUNC_EXTRACT_POST:
   2734             global.fn_extract_esc_post = fn;
   2735             return TB_OK;
   2736     }
   2737     return TB_ERR;
   2738 }
   2739 
   2740 struct tb_cell *tb_cell_buffer(void) {
   2741     if (!global.initialized) return NULL;
   2742     return global.back.cells;
   2743 }
   2744 
   2745 int tb_utf8_char_length(char c) {
   2746     return utf8_length[(unsigned char)c];
   2747 }
   2748 
   2749 int tb_utf8_char_to_unicode(uint32_t *out, const char *c) {
   2750     if (*c == '\0') return 0;
   2751 
   2752     int i;
   2753     unsigned char len = tb_utf8_char_length(*c);
   2754     unsigned char mask = utf8_mask[len - 1];
   2755     uint32_t result = c[0] & mask;
   2756     for (i = 1; i < len && c[i] != '\0'; ++i) {
   2757         result <<= 6;
   2758         result |= c[i] & 0x3f;
   2759     }
   2760 
   2761     if (i != len) return i * -1;
   2762 
   2763     *out = result;
   2764     return (int)len;
   2765 }
   2766 
   2767 int tb_utf8_unicode_to_char(char *out, uint32_t c) {
   2768     int len = 0;
   2769     int first;
   2770     int i;
   2771 
   2772     if (c < 0x80) {
   2773         first = 0;
   2774         len = 1;
   2775     } else if (c < 0x800) {
   2776         first = 0xc0;
   2777         len = 2;
   2778     } else if (c < 0x10000) {
   2779         first = 0xe0;
   2780         len = 3;
   2781     } else if (c < 0x200000) {
   2782         first = 0xf0;
   2783         len = 4;
   2784     } else if (c < 0x4000000) {
   2785         first = 0xf8;
   2786         len = 5;
   2787     } else {
   2788         first = 0xfc;
   2789         len = 6;
   2790     }
   2791 
   2792     for (i = len - 1; i > 0; --i) {
   2793         out[i] = (c & 0x3f) | 0x80;
   2794         c >>= 6;
   2795     }
   2796     out[0] = c | first;
   2797     out[len] = '\0';
   2798 
   2799     return len;
   2800 }
   2801 
   2802 int tb_last_errno(void) {
   2803     return global.last_errno;
   2804 }
   2805 
   2806 const char *tb_strerror(int err) {
   2807     switch (err) {
   2808         case TB_OK:
   2809             return "Success";
   2810         case TB_ERR_NEED_MORE:
   2811             return "Not enough input";
   2812         case TB_ERR_INIT_ALREADY:
   2813             return "Termbox initialized already";
   2814         case TB_ERR_MEM:
   2815             return "Out of memory";
   2816         case TB_ERR_NO_EVENT:
   2817             return "No event";
   2818         case TB_ERR_NO_TERM:
   2819             return "No TERM in environment";
   2820         case TB_ERR_NOT_INIT:
   2821             return "Termbox not initialized";
   2822         case TB_ERR_OUT_OF_BOUNDS:
   2823             return "Out of bounds";
   2824         case TB_ERR_UNSUPPORTED_TERM:
   2825             return "Unsupported terminal";
   2826         case TB_ERR_CAP_COLLISION:
   2827             return "Termcaps collision";
   2828         case TB_ERR_RESIZE_SSCANF:
   2829             return "Terminal width/height not received by sscanf() after "
   2830                    "resize";
   2831         case TB_ERR:
   2832         case TB_ERR_INIT_OPEN:
   2833         case TB_ERR_READ:
   2834         case TB_ERR_RESIZE_IOCTL:
   2835         case TB_ERR_RESIZE_PIPE:
   2836         case TB_ERR_RESIZE_SIGACTION:
   2837         case TB_ERR_POLL:
   2838         case TB_ERR_TCGETATTR:
   2839         case TB_ERR_TCSETATTR:
   2840         case TB_ERR_RESIZE_WRITE:
   2841         case TB_ERR_RESIZE_POLL:
   2842         case TB_ERR_RESIZE_READ:
   2843         default:
   2844             strerror_r(global.last_errno, global.errbuf, sizeof(global.errbuf));
   2845             return (const char *)global.errbuf;
   2846     }
   2847 }
   2848 
   2849 int tb_has_truecolor(void) {
   2850 #if TB_OPT_ATTR_W >= 32
   2851     return 1;
   2852 #else
   2853     return 0;
   2854 #endif
   2855 }
   2856 
   2857 int tb_has_egc(void) {
   2858 #ifdef TB_OPT_EGC
   2859     return 1;
   2860 #else
   2861     return 0;
   2862 #endif
   2863 }
   2864 
   2865 int tb_attr_width(void) {
   2866     return TB_OPT_ATTR_W;
   2867 }
   2868 
   2869 const char *tb_version(void) {
   2870     return TB_VERSION_STR;
   2871 }
   2872 
   2873 static int tb_reset(void) {
   2874     int ttyfd_open = global.ttyfd_open;
   2875     memset(&global, 0, sizeof(global));
   2876     global.ttyfd = -1;
   2877     global.rfd = -1;
   2878     global.wfd = -1;
   2879     global.ttyfd_open = ttyfd_open;
   2880     global.resize_pipefd[0] = -1;
   2881     global.resize_pipefd[1] = -1;
   2882     global.width = -1;
   2883     global.height = -1;
   2884     global.cursor_x = -1;
   2885     global.cursor_y = -1;
   2886     global.last_x = -1;
   2887     global.last_y = -1;
   2888     global.fg = TB_DEFAULT;
   2889     global.bg = TB_DEFAULT;
   2890     global.last_fg = ~global.fg;
   2891     global.last_bg = ~global.bg;
   2892     global.input_mode = TB_INPUT_ESC;
   2893     global.output_mode = TB_OUTPUT_NORMAL;
   2894     return TB_OK;
   2895 }
   2896 
   2897 static int init_term_attrs(void) {
   2898     if (global.ttyfd < 0) return TB_OK;
   2899 
   2900     if (tcgetattr(global.ttyfd, &global.orig_tios) != 0) {
   2901         global.last_errno = errno;
   2902         return TB_ERR_TCGETATTR;
   2903     }
   2904 
   2905     struct termios tios;
   2906     memcpy(&tios, &global.orig_tios, sizeof(tios));
   2907     global.has_orig_tios = 1;
   2908 
   2909     cfmakeraw(&tios);
   2910     tios.c_cc[VMIN] = 1;
   2911     tios.c_cc[VTIME] = 0;
   2912 
   2913     if (tcsetattr(global.ttyfd, TCSAFLUSH, &tios) != 0) {
   2914         global.last_errno = errno;
   2915         return TB_ERR_TCSETATTR;
   2916     }
   2917 
   2918     return TB_OK;
   2919 }
   2920 
   2921 int tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
   2922     const char *fmt, va_list vl) {
   2923     int rv;
   2924     char buf[TB_OPT_PRINTF_BUF];
   2925     rv = vsnprintf(buf, sizeof(buf), fmt, vl);
   2926     if (rv < 0 || rv >= (int)sizeof(buf)) {
   2927         return TB_ERR;
   2928     }
   2929     return tb_print_ex(x, y, fg, bg, out_w, buf);
   2930 }
   2931 
   2932 static int init_term_caps(void) {
   2933     if (load_terminfo() == TB_OK) {
   2934         return parse_terminfo_caps();
   2935     }
   2936     return load_builtin_caps();
   2937 }
   2938 
   2939 static int init_cap_trie(void) {
   2940     int rv, i;
   2941 
   2942     // Add caps from terminfo or built-in
   2943     //
   2944     // Collisions are expected as some terminfo entries have dupes. (For
   2945     // example, att605-pc collides on TB_CAP_F4 and TB_CAP_DELETE.) First cap
   2946     // in TB_CAP_* index order will win.
   2947     //
   2948     // TODO: Reorder TB_CAP_* so more critical caps come first.
   2949     for (i = 0; i < TB_CAP__COUNT_KEYS; i++) {
   2950         rv = cap_trie_add(global.caps[i], tb_key_i(i), 0);
   2951         if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv;
   2952     }
   2953 
   2954     // Add built-in mod caps
   2955     //
   2956     // Collisions are OK here as well. This can happen if global.caps collides
   2957     // with builtin_mod_caps. It is desirable to give precedence to global.caps
   2958     // here.
   2959     for (i = 0; builtin_mod_caps[i].cap != NULL; i++) {
   2960         rv = cap_trie_add(builtin_mod_caps[i].cap, builtin_mod_caps[i].key,
   2961             builtin_mod_caps[i].mod);
   2962         if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv;
   2963     }
   2964 
   2965     return TB_OK;
   2966 }
   2967 
   2968 static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod) {
   2969     struct cap_trie *next, *node = &global.cap_trie;
   2970     size_t i, j;
   2971 
   2972     if (!cap || strlen(cap) <= 0) return TB_OK; // Nothing to do for empty caps
   2973 
   2974     for (i = 0; cap[i] != '\0'; i++) {
   2975         char c = cap[i];
   2976         next = NULL;
   2977 
   2978         // Check if c is already a child of node
   2979         for (j = 0; j < node->nchildren; j++) {
   2980             if (node->children[j].c == c) {
   2981                 next = &node->children[j];
   2982                 break;
   2983             }
   2984         }
   2985         if (!next) {
   2986             // We need to add a new child to node
   2987             node->nchildren += 1;
   2988             node->children = (struct cap_trie *)tb_realloc(node->children,
   2989                 sizeof(*node) * node->nchildren);
   2990             if (!node->children) {
   2991                 return TB_ERR_MEM;
   2992             }
   2993             next = &node->children[node->nchildren - 1];
   2994             memset(next, 0, sizeof(*next));
   2995             next->c = c;
   2996         }
   2997 
   2998         // Continue
   2999         node = next;
   3000     }
   3001 
   3002     if (node->is_leaf) {
   3003         // Already a leaf here
   3004         return TB_ERR_CAP_COLLISION;
   3005     }
   3006 
   3007     node->is_leaf = 1;
   3008     node->key = key;
   3009     node->mod = mod;
   3010     return TB_OK;
   3011 }
   3012 
   3013 static int cap_trie_find(const char *buf, size_t nbuf, struct cap_trie **last,
   3014     size_t *depth) {
   3015     struct cap_trie *next, *node = &global.cap_trie;
   3016     size_t i, j;
   3017     *last = node;
   3018     *depth = 0;
   3019     for (i = 0; i < nbuf; i++) {
   3020         char c = buf[i];
   3021         next = NULL;
   3022 
   3023         // Find c in node.children
   3024         for (j = 0; j < node->nchildren; j++) {
   3025             if (node->children[j].c == c) {
   3026                 next = &node->children[j];
   3027                 break;
   3028             }
   3029         }
   3030         if (!next) {
   3031             // Not found
   3032             return TB_OK;
   3033         }
   3034         node = next;
   3035         *last = node;
   3036         *depth += 1;
   3037         if (node->is_leaf && node->nchildren < 1) {
   3038             break;
   3039         }
   3040     }
   3041     return TB_OK;
   3042 }
   3043 
   3044 static int cap_trie_deinit(struct cap_trie *node) {
   3045     size_t j;
   3046     for (j = 0; j < node->nchildren; j++) {
   3047         cap_trie_deinit(&node->children[j]);
   3048     }
   3049     if (node->children) tb_free(node->children);
   3050     memset(node, 0, sizeof(*node));
   3051     return TB_OK;
   3052 }
   3053 
   3054 static int init_resize_handler(void) {
   3055     if (pipe(global.resize_pipefd) != 0) {
   3056         global.last_errno = errno;
   3057         return TB_ERR_RESIZE_PIPE;
   3058     }
   3059 
   3060     struct sigaction sa;
   3061     memset(&sa, 0, sizeof(sa));
   3062     sa.sa_handler = handle_resize;
   3063     if (sigaction(SIGWINCH, &sa, NULL) != 0) {
   3064         global.last_errno = errno;
   3065         return TB_ERR_RESIZE_SIGACTION;
   3066     }
   3067 
   3068     return TB_OK;
   3069 }
   3070 
   3071 static int send_init_escape_codes(void) {
   3072     int rv;
   3073     if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_CA]));
   3074     if_err_return(rv,
   3075         bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_KEYPAD]));
   3076     if_err_return(rv,
   3077         bytebuf_puts(&global.out, global.caps[TB_CAP_HIDE_CURSOR]));
   3078     return TB_OK;
   3079 }
   3080 
   3081 static int send_clear(void) {
   3082     int rv;
   3083 
   3084     if_err_return(rv, send_attr(global.fg, global.bg));
   3085     if_err_return(rv,
   3086         bytebuf_puts(&global.out, global.caps[TB_CAP_CLEAR_SCREEN]));
   3087 
   3088     if_err_return(rv, send_cursor_if(global.cursor_x, global.cursor_y));
   3089     if_err_return(rv, bytebuf_flush(&global.out, global.wfd));
   3090 
   3091     global.last_x = -1;
   3092     global.last_y = -1;
   3093 
   3094     return TB_OK;
   3095 }
   3096 
   3097 static int update_term_size(void) {
   3098     int rv, ioctl_errno;
   3099 
   3100     if (global.ttyfd < 0) return TB_OK;
   3101 
   3102     struct winsize sz;
   3103     memset(&sz, 0, sizeof(sz));
   3104 
   3105     // Try ioctl TIOCGWINSZ
   3106     if (ioctl(global.ttyfd, TIOCGWINSZ, &sz) == 0) {
   3107         global.width = sz.ws_col;
   3108         global.height = sz.ws_row;
   3109         return TB_OK;
   3110     }
   3111     ioctl_errno = errno;
   3112 
   3113     // Try >cursor(9999,9999), >u7, <u6
   3114     if_ok_return(rv, update_term_size_via_esc());
   3115 
   3116     global.last_errno = ioctl_errno;
   3117     return TB_ERR_RESIZE_IOCTL;
   3118 }
   3119 
   3120 static int update_term_size_via_esc(void) {
   3121 #ifndef TB_RESIZE_FALLBACK_MS
   3122 #define TB_RESIZE_FALLBACK_MS 1000
   3123 #endif
   3124 
   3125     char move_and_report[] = "\x1b[9999;9999H\x1b[6n";
   3126     ssize_t write_rv =
   3127         write(global.wfd, move_and_report, strlen(move_and_report));
   3128     if (write_rv != (ssize_t)strlen(move_and_report)) {
   3129         return TB_ERR_RESIZE_WRITE;
   3130     }
   3131 
   3132     fd_set fds;
   3133     FD_ZERO(&fds);
   3134     FD_SET(global.rfd, &fds);
   3135 
   3136     struct timeval timeout;
   3137     timeout.tv_sec = 0;
   3138     timeout.tv_usec = TB_RESIZE_FALLBACK_MS * 1000;
   3139 
   3140     int select_rv = select(global.rfd + 1, &fds, NULL, NULL, &timeout);
   3141 
   3142     if (select_rv != 1) {
   3143         global.last_errno = errno;
   3144         return TB_ERR_RESIZE_POLL;
   3145     }
   3146 
   3147     char buf[TB_OPT_READ_BUF];
   3148     ssize_t read_rv = read(global.rfd, buf, sizeof(buf) - 1);
   3149     if (read_rv < 1) {
   3150         global.last_errno = errno;
   3151         return TB_ERR_RESIZE_READ;
   3152     }
   3153     buf[read_rv] = '\0';
   3154 
   3155     int rw, rh;
   3156     if (sscanf(buf, "\x1b[%d;%dR", &rh, &rw) != 2) {
   3157         return TB_ERR_RESIZE_SSCANF;
   3158     }
   3159 
   3160     global.width = rw;
   3161     global.height = rh;
   3162     return TB_OK;
   3163 }
   3164 
   3165 static int init_cellbuf(void) {
   3166     int rv;
   3167     if_err_return(rv, cellbuf_init(&global.back, global.width, global.height));
   3168     if_err_return(rv, cellbuf_init(&global.front, global.width, global.height));
   3169     if_err_return(rv, cellbuf_clear(&global.back));
   3170     if_err_return(rv, cellbuf_clear(&global.front));
   3171     return TB_OK;
   3172 }
   3173 
   3174 static int tb_deinit(void) {
   3175     if (global.caps[0] != NULL && global.wfd >= 0) {
   3176         bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]);
   3177         bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]);
   3178         bytebuf_puts(&global.out, global.caps[TB_CAP_CLEAR_SCREEN]);
   3179         bytebuf_puts(&global.out, global.caps[TB_CAP_EXIT_CA]);
   3180         bytebuf_puts(&global.out, global.caps[TB_CAP_EXIT_KEYPAD]);
   3181         bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE);
   3182         bytebuf_flush(&global.out, global.wfd);
   3183     }
   3184     if (global.ttyfd >= 0) {
   3185         if (global.has_orig_tios) {
   3186             tcsetattr(global.ttyfd, TCSAFLUSH, &global.orig_tios);
   3187         }
   3188         if (global.ttyfd_open) {
   3189             close(global.ttyfd);
   3190             global.ttyfd_open = 0;
   3191         }
   3192     }
   3193 
   3194     struct sigaction sa;
   3195     memset(&sa, 0, sizeof(sa));
   3196     sa.sa_handler = SIG_DFL;
   3197     sigaction(SIGWINCH, &sa, NULL);
   3198     if (global.resize_pipefd[0] >= 0) close(global.resize_pipefd[0]);
   3199     if (global.resize_pipefd[1] >= 0) close(global.resize_pipefd[1]);
   3200 
   3201     cellbuf_free(&global.back);
   3202     cellbuf_free(&global.front);
   3203     bytebuf_free(&global.in);
   3204     bytebuf_free(&global.out);
   3205 
   3206     if (global.terminfo) tb_free(global.terminfo);
   3207 
   3208     cap_trie_deinit(&global.cap_trie);
   3209 
   3210     tb_reset();
   3211     return TB_OK;
   3212 }
   3213 
   3214 static int load_terminfo(void) {
   3215     int rv;
   3216     char tmp[TB_PATH_MAX];
   3217 
   3218     // See terminfo(5) "Fetching Compiled Descriptions" for a description of
   3219     // this behavior. Some of these paths are compile-time ncurses options, so
   3220     // best guesses are used here.
   3221     const char *term = getenv("TERM");
   3222     if (!term) return TB_ERR;
   3223 
   3224     // If TERMINFO is set, try that directory first
   3225     const char *terminfo = getenv("TERMINFO");
   3226     if (terminfo) if_ok_return(rv, load_terminfo_from_path(terminfo, term));
   3227 
   3228     // Next try ~/.terminfo
   3229     const char *home = getenv("HOME");
   3230     if (home) {
   3231         snprintf_or_return(rv, tmp, sizeof(tmp), "%s/.terminfo", home);
   3232         if_ok_return(rv, load_terminfo_from_path(tmp, term));
   3233     }
   3234 
   3235     // Next try TERMINFO_DIRS
   3236     //
   3237     // Note, empty entries are supposed to be interpretted as the "compiled-in
   3238     // default", which is of course system-dependent. Previously /etc/terminfo
   3239     // was used here. Let's skip empty entries altogether rather than give
   3240     // precedence to a guess, and check common paths after this loop.
   3241     const char *dirs = getenv("TERMINFO_DIRS");
   3242     if (dirs) {
   3243         snprintf_or_return(rv, tmp, sizeof(tmp), "%s", dirs);
   3244         char *dir = strtok(tmp, ":");
   3245         while (dir) {
   3246             const char *cdir = dir;
   3247             if (*cdir != '\0') {
   3248                 if_ok_return(rv, load_terminfo_from_path(cdir, term));
   3249             }
   3250             dir = strtok(NULL, ":");
   3251         }
   3252     }
   3253 
   3254 #ifdef TB_TERMINFO_DIR
   3255     if_ok_return(rv, load_terminfo_from_path(TB_TERMINFO_DIR, term));
   3256 #endif
   3257     if_ok_return(rv, load_terminfo_from_path("/usr/local/etc/terminfo", term));
   3258     if_ok_return(rv,
   3259         load_terminfo_from_path("/usr/local/share/terminfo", term));
   3260     if_ok_return(rv, load_terminfo_from_path("/usr/local/lib/terminfo", term));
   3261     if_ok_return(rv, load_terminfo_from_path("/etc/terminfo", term));
   3262     if_ok_return(rv, load_terminfo_from_path("/usr/share/terminfo", term));
   3263     if_ok_return(rv, load_terminfo_from_path("/usr/lib/terminfo", term));
   3264     if_ok_return(rv, load_terminfo_from_path("/usr/share/lib/terminfo", term));
   3265     if_ok_return(rv, load_terminfo_from_path("/lib/terminfo", term));
   3266 
   3267     return TB_ERR;
   3268 }
   3269 
   3270 static int load_terminfo_from_path(const char *path, const char *term) {
   3271     int rv;
   3272     char tmp[TB_PATH_MAX];
   3273 
   3274     // Look for term at this terminfo location, e.g., <terminfo>/x/xterm
   3275     snprintf_or_return(rv, tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term);
   3276     if_ok_return(rv, read_terminfo_path(tmp));
   3277 
   3278 #ifdef __APPLE__
   3279     // Try the Darwin equivalent path, e.g., <terminfo>/78/xterm
   3280     snprintf_or_return(rv, tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term);
   3281     return read_terminfo_path(tmp);
   3282 #endif
   3283 
   3284     return TB_ERR;
   3285 }
   3286 
   3287 static int read_terminfo_path(const char *path) {
   3288     FILE *fp = fopen(path, "rb");
   3289     if (!fp) return TB_ERR;
   3290 
   3291     struct stat st;
   3292     if (fstat(fileno(fp), &st) != 0) {
   3293         fclose(fp);
   3294         return TB_ERR;
   3295     }
   3296 
   3297     size_t fsize = st.st_size;
   3298     char *data = (char *)tb_malloc(fsize);
   3299     if (!data) {
   3300         fclose(fp);
   3301         return TB_ERR;
   3302     }
   3303 
   3304     if (fread(data, 1, fsize, fp) != fsize) {
   3305         fclose(fp);
   3306         tb_free(data);
   3307         return TB_ERR;
   3308     }
   3309 
   3310     global.terminfo = data;
   3311     global.nterminfo = fsize;
   3312 
   3313     fclose(fp);
   3314     return TB_OK;
   3315 }
   3316 
   3317 static int parse_terminfo_caps(void) {
   3318     // See term(5) "LEGACY STORAGE FORMAT" and "EXTENDED STORAGE FORMAT" for a
   3319     // description of this behavior.
   3320 
   3321     // Ensure there's at least a header's worth of data
   3322     if (global.nterminfo < 6 * (int)sizeof(int16_t)) return TB_ERR;
   3323 
   3324     int16_t magic_number, nbytes_names, nbytes_bools, num_ints, num_offsets,
   3325         nbytes_strings;
   3326     size_t nbytes_header = 6 * sizeof(int16_t);
   3327     // header[0] the magic number (octal 0432 or 01036)
   3328     // header[1] the size, in bytes, of the names section
   3329     // header[2] the number of bytes in the boolean section
   3330     // header[3] the number of short integers in the numbers section
   3331     // header[4] the number of offsets (short integers) in the strings section
   3332     // header[5] the size, in bytes, of the string table
   3333     get_terminfo_int16(0 * sizeof(int16_t), &magic_number);
   3334     get_terminfo_int16(1 * sizeof(int16_t), &nbytes_names);
   3335     get_terminfo_int16(2 * sizeof(int16_t), &nbytes_bools);
   3336     get_terminfo_int16(3 * sizeof(int16_t), &num_ints);
   3337     get_terminfo_int16(4 * sizeof(int16_t), &num_offsets);
   3338     get_terminfo_int16(5 * sizeof(int16_t), &nbytes_strings);
   3339 
   3340     // Legacy ints are 16-bit, extended ints are 32-bit
   3341     const int bytes_per_int = magic_number == 01036 ? 4  // 32-bit
   3342                                                     : 2; // 16-bit
   3343 
   3344     // > Between the boolean section and the number section, a null byte will
   3345     // > be inserted, if necessary, to ensure that the number section begins on
   3346     // > an even byte
   3347     const int align_offset = (nbytes_names + nbytes_bools) % 2 != 0 ? 1 : 0;
   3348 
   3349     const int pos_str_offsets =
   3350         nbytes_header  // header (12 bytes)
   3351         + nbytes_names // length of names section
   3352         + nbytes_bools // length of boolean section
   3353         + align_offset +
   3354         (num_ints * bytes_per_int); // length of numbers section
   3355 
   3356     const int pos_str_table =
   3357         pos_str_offsets +
   3358         (num_offsets * sizeof(int16_t)); // length of string offsets table
   3359 
   3360     // Load caps
   3361     int i;
   3362     for (i = 0; i < TB_CAP__COUNT; i++) {
   3363         const char *cap = get_terminfo_string(pos_str_offsets, num_offsets,
   3364             pos_str_table, nbytes_strings, terminfo_cap_indexes[i]);
   3365         if (!cap) {
   3366             // Something is not right
   3367             return TB_ERR;
   3368         }
   3369         global.caps[i] = cap;
   3370     }
   3371 
   3372     return TB_OK;
   3373 }
   3374 
   3375 static int load_builtin_caps(void) {
   3376     int i, j;
   3377     const char *term = getenv("TERM");
   3378 
   3379     if (!term) return TB_ERR_NO_TERM;
   3380 
   3381     // Check for exact TERM match
   3382     for (i = 0; builtin_terms[i].name != NULL; i++) {
   3383         if (strcmp(term, builtin_terms[i].name) == 0) {
   3384             for (j = 0; j < TB_CAP__COUNT; j++) {
   3385                 global.caps[j] = builtin_terms[i].caps[j];
   3386             }
   3387             return TB_OK;
   3388         }
   3389     }
   3390 
   3391     // Check for partial TERM or alias match
   3392     for (i = 0; builtin_terms[i].name != NULL; i++) {
   3393         if (strstr(term, builtin_terms[i].name) != NULL ||
   3394             (*(builtin_terms[i].alias) != '\0' &&
   3395                 strstr(term, builtin_terms[i].alias) != NULL))
   3396         {
   3397             for (j = 0; j < TB_CAP__COUNT; j++) {
   3398                 global.caps[j] = builtin_terms[i].caps[j];
   3399             }
   3400             return TB_OK;
   3401         }
   3402     }
   3403 
   3404     return TB_ERR_UNSUPPORTED_TERM;
   3405 }
   3406 
   3407 static const char *get_terminfo_string(int16_t offsets_pos, int16_t offsets_len,
   3408     int16_t table_pos, int16_t table_size, int16_t index) {
   3409     if (index >= offsets_len) {
   3410         // An index beyond the offset table indicates absent
   3411         // See `convert_strings` in tinfo `read_entry.c`
   3412         return "";
   3413     }
   3414 
   3415     int16_t table_offset;
   3416     int table_offset_offset = (int)offsets_pos + (index * (int)sizeof(int16_t));
   3417     if (get_terminfo_int16(table_offset_offset, &table_offset) != TB_OK) {
   3418         // offset beyond end of terminfo entry
   3419         // Truncated/corrupt terminfo entry?
   3420         return NULL;
   3421     }
   3422 
   3423     if (table_offset < 0 || table_offset >= table_size) {
   3424         // A negative offset indicates absent
   3425         // An offset beyond the string table indicates absent
   3426         // See `convert_strings` in tinfo `read_entry.c`
   3427         return "";
   3428     }
   3429 
   3430     int str_offset = (int)table_pos + (int)table_offset;
   3431     if (str_offset >= (int)global.nterminfo) {
   3432         // string beyond end of terminfo entry
   3433         // Truncated/corrupt terminfo entry?
   3434         return NULL;
   3435     }
   3436 
   3437     return (const char *)(global.terminfo + str_offset);
   3438 }
   3439 
   3440 static int get_terminfo_int16(int offset, int16_t *val) {
   3441     if (offset < 0 || offset + sizeof(int16_t) > global.nterminfo) {
   3442         *val = -1;
   3443         return TB_ERR;
   3444     }
   3445     memcpy(val, global.terminfo + offset, sizeof(int16_t));
   3446     return TB_OK;
   3447 }
   3448 
   3449 static int wait_event(struct tb_event *event, int timeout) {
   3450     int rv;
   3451     char buf[TB_OPT_READ_BUF];
   3452 
   3453     memset(event, 0, sizeof(*event));
   3454     if_ok_return(rv, extract_event(event));
   3455 
   3456     fd_set fds;
   3457     struct timeval tv;
   3458     tv.tv_sec = timeout / 1000;
   3459     tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
   3460 
   3461     do {
   3462         FD_ZERO(&fds);
   3463         FD_SET(global.rfd, &fds);
   3464         FD_SET(global.resize_pipefd[0], &fds);
   3465 
   3466         int maxfd = global.resize_pipefd[0] > global.rfd
   3467                         ? global.resize_pipefd[0]
   3468                         : global.rfd;
   3469 
   3470         int select_rv =
   3471             select(maxfd + 1, &fds, NULL, NULL, (timeout < 0) ? NULL : &tv);
   3472 
   3473         if (select_rv < 0) {
   3474             // Let EINTR/EAGAIN bubble up
   3475             global.last_errno = errno;
   3476             return TB_ERR_POLL;
   3477         } else if (select_rv == 0) {
   3478             return TB_ERR_NO_EVENT;
   3479         }
   3480 
   3481         int tty_has_events = (FD_ISSET(global.rfd, &fds));
   3482         int resize_has_events = (FD_ISSET(global.resize_pipefd[0], &fds));
   3483 
   3484         if (tty_has_events) {
   3485             ssize_t read_rv = read(global.rfd, buf, sizeof(buf));
   3486             if (read_rv < 0) {
   3487                 global.last_errno = errno;
   3488                 return TB_ERR_READ;
   3489             } else if (read_rv > 0) {
   3490                 bytebuf_nputs(&global.in, buf, read_rv);
   3491             }
   3492         }
   3493 
   3494         if (resize_has_events) {
   3495             int ignore = 0;
   3496             read(global.resize_pipefd[0], &ignore, sizeof(ignore));
   3497             // TODO: Harden against errors encountered mid-resize
   3498             if_err_return(rv, update_term_size());
   3499             if_err_return(rv, resize_cellbufs());
   3500             event->type = TB_EVENT_RESIZE;
   3501             event->w = global.width;
   3502             event->h = global.height;
   3503             return TB_OK;
   3504         }
   3505 
   3506         memset(event, 0, sizeof(*event));
   3507         if_ok_return(rv, extract_event(event));
   3508     } while (timeout == -1);
   3509 
   3510     return rv;
   3511 }
   3512 
   3513 static int extract_event(struct tb_event *event) {
   3514     int rv;
   3515     struct bytebuf *in = &global.in;
   3516 
   3517     if (in->len == 0) return TB_ERR;
   3518 
   3519     if (in->buf[0] == '\x1b') {
   3520         // Escape sequence?
   3521         // In TB_INPUT_ESC, skip if the buffer is a single escape char
   3522         if (!((global.input_mode & TB_INPUT_ESC) && in->len == 1)) {
   3523             if_ok_or_need_more_return(rv, extract_esc(event));
   3524         }
   3525 
   3526         // Escape key?
   3527         if (global.input_mode & TB_INPUT_ESC) {
   3528             event->type = TB_EVENT_KEY;
   3529             event->ch = 0;
   3530             event->key = TB_KEY_ESC;
   3531             event->mod = 0;
   3532             bytebuf_shift(in, 1);
   3533             return TB_OK;
   3534         }
   3535 
   3536         // Recurse for alt key
   3537         event->mod |= TB_MOD_ALT;
   3538         bytebuf_shift(in, 1);
   3539         return extract_event(event);
   3540     }
   3541 
   3542     // ASCII control key?
   3543     int is_ctrl =
   3544         (uint16_t)in->buf[0] < TB_KEY_SPACE || in->buf[0] == TB_KEY_BACKSPACE2;
   3545     if (is_ctrl) {
   3546         event->type = TB_EVENT_KEY;
   3547         event->ch = 0;
   3548         event->key = (uint16_t)in->buf[0];
   3549         event->mod |= TB_MOD_CTRL;
   3550         bytebuf_shift(in, 1);
   3551         return TB_OK;
   3552     }
   3553 
   3554     // UTF-8?
   3555     if (in->len >= (size_t)tb_utf8_char_length(in->buf[0])) {
   3556         event->type = TB_EVENT_KEY;
   3557         tb_utf8_char_to_unicode(&event->ch, in->buf);
   3558         event->key = 0;
   3559         bytebuf_shift(in, tb_utf8_char_length(in->buf[0]));
   3560         return TB_OK;
   3561     }
   3562 
   3563     // Need more input
   3564     return TB_ERR;
   3565 }
   3566 
   3567 static int extract_esc(struct tb_event *event) {
   3568     int rv;
   3569     if_ok_or_need_more_return(rv, extract_esc_user(event, 0));
   3570     if_ok_or_need_more_return(rv, extract_esc_cap(event));
   3571     if_ok_or_need_more_return(rv, extract_esc_mouse(event));
   3572     if_ok_or_need_more_return(rv, extract_esc_user(event, 1));
   3573     return TB_ERR;
   3574 }
   3575 
   3576 static int extract_esc_user(struct tb_event *event, int is_post) {
   3577     int rv;
   3578     size_t consumed = 0;
   3579     struct bytebuf *in = &global.in;
   3580     int (*fn)(struct tb_event *, size_t *);
   3581 
   3582     fn = is_post ? global.fn_extract_esc_post : global.fn_extract_esc_pre;
   3583 
   3584     if (!fn) return TB_ERR;
   3585 
   3586     rv = fn(event, &consumed);
   3587     if (rv == TB_OK) bytebuf_shift(in, consumed);
   3588 
   3589     if_ok_or_need_more_return(rv, rv);
   3590     return TB_ERR;
   3591 }
   3592 
   3593 static int extract_esc_cap(struct tb_event *event) {
   3594     int rv;
   3595     struct bytebuf *in = &global.in;
   3596     struct cap_trie *node;
   3597     size_t depth;
   3598 
   3599     if_err_return(rv, cap_trie_find(in->buf, in->len, &node, &depth));
   3600     if (node->is_leaf) {
   3601         // Found a leaf node
   3602         event->type = TB_EVENT_KEY;
   3603         event->ch = 0;
   3604         event->key = node->key;
   3605         event->mod = node->mod;
   3606         bytebuf_shift(in, depth);
   3607         return TB_OK;
   3608     } else if (node->nchildren > 0 && in->len <= depth) {
   3609         // Found a branch node (not enough input)
   3610         return TB_ERR_NEED_MORE;
   3611     }
   3612 
   3613     return TB_ERR;
   3614 }
   3615 
   3616 static int extract_esc_mouse(struct tb_event *event) {
   3617     struct bytebuf *in = &global.in;
   3618     size_t buf_shift = 0;
   3619 
   3620     // Bail if not enough to determine type
   3621     if (in->len < 2) {
   3622         return TB_ERR_NEED_MORE;
   3623     } else if (in->buf[1] != '[') {
   3624         return TB_ERR;
   3625     } else if (in->len < 3) {
   3626         return TB_ERR_NEED_MORE;
   3627     }
   3628 
   3629     // Discern type of mouse event from 3rd byte
   3630     int type = 0;
   3631     enum { TYPE_VT200 = 0, TYPE_1006, TYPE_1015, TYPE_MAX };
   3632     if (in->buf[2] == 'M') {
   3633         // X10 mouse encoding, the simplest one
   3634         // \x1b [ M Cb Cx Cy
   3635         type = TYPE_VT200;
   3636     } else if (in->buf[2] == '<') {
   3637         // xterm 1006 extended mode or urxvt 1015 extended mode
   3638         // xterm: \x1b [ < Cb ; Cx ; Cy (M or m)
   3639         type = TYPE_1006;
   3640     } else {
   3641         // urxvt: \x1b [ Cb ; Cx ; Cy M
   3642         type = TYPE_1015;
   3643     }
   3644 
   3645     switch (type) {
   3646         case TYPE_VT200: {
   3647             // In this mode, we need 6 bytes
   3648             if (in->len < 6) return TB_ERR_NEED_MORE;
   3649 
   3650             int b = in->buf[3] - 0x20;
   3651 
   3652             switch (b & 3) {
   3653                 case 0:
   3654                     event->key = ((b & 64) != 0) ? TB_KEY_MOUSE_WHEEL_UP
   3655                                                  : TB_KEY_MOUSE_LEFT;
   3656                     break;
   3657                 case 1:
   3658                     event->key = ((b & 64) != 0) ? TB_KEY_MOUSE_WHEEL_DOWN
   3659                                                  : TB_KEY_MOUSE_MIDDLE;
   3660                     break;
   3661                 case 2:
   3662                     event->key = TB_KEY_MOUSE_RIGHT;
   3663                     break;
   3664                 case 3:
   3665                     event->key = TB_KEY_MOUSE_RELEASE;
   3666                     break;
   3667                 default:
   3668                     return TB_ERR;
   3669             }
   3670 
   3671             if ((b & 32) != 0) event->mod |= TB_MOD_MOTION;
   3672 
   3673             // the coord is 1,1 for upper left
   3674             event->x = ((uint8_t)in->buf[4]) - 0x21;
   3675             event->y = ((uint8_t)in->buf[5]) - 0x21;
   3676 
   3677             // Eat 6 bytes
   3678             buf_shift = 6;
   3679             break;
   3680         }
   3681 
   3682         case TYPE_1006:
   3683             // fallthrough
   3684 
   3685         case TYPE_1015: {
   3686             int num[3] = {-1, -1, -1};
   3687             int num_i = 0;
   3688             int cur_num = -1;
   3689             char trail = ' ';
   3690 
   3691             size_t i = 2;
   3692             if (type == TYPE_1006) ++i; // skip '<'
   3693 
   3694             // Parse %d;%d;%d[mM] into `num`
   3695             while (i < in->len && num_i < 3) {
   3696                 char c = in->buf[i];
   3697                 if (c >= '0' && c <= '9') {
   3698                     // Digit
   3699                     if (cur_num == -1) cur_num = 0;
   3700                     cur_num *= 10;
   3701                     cur_num += (int)(c - '0');
   3702                 } else if (cur_num != -1 &&
   3703                            ((num_i < 2 && c == ';') ||
   3704                                (num_i == 2 && (c == 'm' || c == 'M'))))
   3705                 {
   3706                     // We're at a semi-colon, 'm', or 'M'
   3707                     // and we have a number
   3708                     num[num_i] = cur_num;
   3709                     ++num_i;
   3710                     cur_num = -1;
   3711                     trail = c;
   3712                 } else {
   3713                     // Something else; not a mouse event
   3714                     return TB_ERR;
   3715                 }
   3716                 ++i;
   3717             }
   3718 
   3719             // If we didn't get to the 3rd number, we need more
   3720             if (num[2] == -1) return TB_ERR_NEED_MORE;
   3721 
   3722             // We have a valid mouse event, eat `i` bytes from the buffer
   3723             buf_shift = i;
   3724 
   3725             if (type == TYPE_1015) num[0] -= 0x20;
   3726 
   3727             switch (num[0] & 3) {
   3728                 case 0:
   3729                     event->key = ((num[0] & 64) != 0) ? TB_KEY_MOUSE_WHEEL_UP
   3730                                                       : TB_KEY_MOUSE_LEFT;
   3731                     break;
   3732                 case 1:
   3733                     event->key = ((num[0] & 64) != 0) ? TB_KEY_MOUSE_WHEEL_DOWN
   3734                                                       : TB_KEY_MOUSE_MIDDLE;
   3735                     break;
   3736                 case 2:
   3737                     event->key = TB_KEY_MOUSE_RIGHT;
   3738                     break;
   3739                 case 3:
   3740                     event->key = TB_KEY_MOUSE_RELEASE;
   3741                     break;
   3742                 default:
   3743                     return TB_ERR;
   3744             }
   3745 
   3746             // on xterm mouse release is signaled by lowercase m
   3747             if (trail == 'm') event->key = TB_KEY_MOUSE_RELEASE;
   3748 
   3749             if ((num[0] & 32) != 0) event->mod |= TB_MOD_MOTION;
   3750 
   3751             event->x = (num[1] - 1 < 0) ? 0 : num[1] - 1;
   3752             event->y = (num[2] - 1 < 0) ? 0 : num[2] - 1;
   3753 
   3754             break;
   3755         }
   3756     }
   3757 
   3758     if (buf_shift > 0) bytebuf_shift(in, buf_shift);
   3759 
   3760     event->type = TB_EVENT_MOUSE;
   3761 
   3762     return TB_OK;
   3763 }
   3764 
   3765 static int resize_cellbufs(void) {
   3766     int rv;
   3767     if_err_return(rv,
   3768         cellbuf_resize(&global.back, global.width, global.height));
   3769     if_err_return(rv,
   3770         cellbuf_resize(&global.front, global.width, global.height));
   3771     if_err_return(rv, cellbuf_clear(&global.front));
   3772     if_err_return(rv, send_clear());
   3773     return TB_OK;
   3774 }
   3775 
   3776 static void handle_resize(int sig) {
   3777     int errno_copy = errno;
   3778     write(global.resize_pipefd[1], &sig, sizeof(sig));
   3779     errno = errno_copy;
   3780 }
   3781 
   3782 static int send_attr(uintattr_t fg, uintattr_t bg) {
   3783     int rv;
   3784 
   3785     if (fg == global.last_fg && bg == global.last_bg) {
   3786         return TB_OK;
   3787     }
   3788 
   3789     if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]));
   3790 
   3791     uint32_t cfg, cbg;
   3792     switch (global.output_mode) {
   3793         default:
   3794         case TB_OUTPUT_NORMAL:
   3795             // The minus 1 below is because our colors are 1-indexed starting
   3796             // from black. Black is represented by a 30, 40, 90, or 100 for fg,
   3797             // bg, bright fg, or bright bg respectively. Red is 31, 41, 91,
   3798             // 101, etc.
   3799             cfg = (fg & TB_BRIGHT ? 90 : 30) + (fg & 0x0f) - 1;
   3800             cbg = (bg & TB_BRIGHT ? 100 : 40) + (bg & 0x0f) - 1;
   3801             break;
   3802 
   3803         case TB_OUTPUT_256:
   3804             cfg = fg & 0xff;
   3805             cbg = bg & 0xff;
   3806             if (fg & TB_HI_BLACK) cfg = 0;
   3807             if (bg & TB_HI_BLACK) cbg = 0;
   3808             break;
   3809 
   3810         case TB_OUTPUT_216:
   3811             cfg = fg & 0xff;
   3812             cbg = bg & 0xff;
   3813             if (cfg > 216) cfg = 216;
   3814             if (cbg > 216) cbg = 216;
   3815             cfg += 0x0f;
   3816             cbg += 0x0f;
   3817             break;
   3818 
   3819         case TB_OUTPUT_GRAYSCALE:
   3820             cfg = fg & 0xff;
   3821             cbg = bg & 0xff;
   3822             if (cfg > 24) cfg = 24;
   3823             if (cbg > 24) cbg = 24;
   3824             cfg += 0xe7;
   3825             cbg += 0xe7;
   3826             break;
   3827 
   3828 #if TB_OPT_ATTR_W >= 32
   3829         case TB_OUTPUT_TRUECOLOR:
   3830             cfg = fg & 0xffffff;
   3831             cbg = bg & 0xffffff;
   3832             if (fg & TB_HI_BLACK) cfg = 0;
   3833             if (bg & TB_HI_BLACK) cbg = 0;
   3834             break;
   3835 #endif
   3836     }
   3837 
   3838     if (fg & TB_BOLD)
   3839         if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BOLD]));
   3840 
   3841     if (fg & TB_BLINK)
   3842         if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BLINK]));
   3843 
   3844     if (fg & TB_UNDERLINE)
   3845         if_err_return(rv,
   3846             bytebuf_puts(&global.out, global.caps[TB_CAP_UNDERLINE]));
   3847 
   3848     if (fg & TB_ITALIC)
   3849         if_err_return(rv,
   3850             bytebuf_puts(&global.out, global.caps[TB_CAP_ITALIC]));
   3851 
   3852     if (fg & TB_DIM)
   3853         if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_DIM]));
   3854 
   3855 #if TB_OPT_ATTR_W == 64
   3856     if (fg & TB_STRIKEOUT)
   3857         if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_STRIKEOUT));
   3858 
   3859     if (fg & TB_UNDERLINE_2)
   3860         if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_UNDERLINE_2));
   3861 
   3862     if (fg & TB_OVERLINE)
   3863         if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_OVERLINE));
   3864 
   3865     if (fg & TB_INVISIBLE)
   3866         if_err_return(rv,
   3867             bytebuf_puts(&global.out, global.caps[TB_CAP_INVISIBLE]));
   3868 #endif
   3869 
   3870     if ((fg & TB_REVERSE) || (bg & TB_REVERSE))
   3871         if_err_return(rv,
   3872             bytebuf_puts(&global.out, global.caps[TB_CAP_REVERSE]));
   3873 
   3874     int fg_is_default = (fg & 0xff) == 0;
   3875     int bg_is_default = (bg & 0xff) == 0;
   3876     if (global.output_mode == TB_OUTPUT_256) {
   3877         if (fg & TB_HI_BLACK) fg_is_default = 0;
   3878         if (bg & TB_HI_BLACK) bg_is_default = 0;
   3879     }
   3880 #if TB_OPT_ATTR_W >= 32
   3881     if (global.output_mode == TB_OUTPUT_TRUECOLOR) {
   3882         fg_is_default = ((fg & 0xffffff) == 0) && ((fg & TB_HI_BLACK) == 0);
   3883         bg_is_default = ((bg & 0xffffff) == 0) && ((bg & TB_HI_BLACK) == 0);
   3884     }
   3885 #endif
   3886 
   3887     if_err_return(rv, send_sgr(cfg, cbg, fg_is_default, bg_is_default));
   3888 
   3889     global.last_fg = fg;
   3890     global.last_bg = bg;
   3891 
   3892     return TB_OK;
   3893 }
   3894 
   3895 static int send_sgr(uint32_t cfg, uint32_t cbg, int fg_is_default,
   3896     int bg_is_default) {
   3897     int rv;
   3898     char nbuf[32];
   3899 
   3900     if (fg_is_default && bg_is_default) {
   3901         return TB_OK;
   3902     }
   3903 
   3904     switch (global.output_mode) {
   3905         default:
   3906         case TB_OUTPUT_NORMAL:
   3907             send_literal(rv, "\x1b[");
   3908             if (!fg_is_default) {
   3909                 send_num(rv, nbuf, cfg);
   3910                 if (!bg_is_default) {
   3911                     send_literal(rv, ";");
   3912                 }
   3913             }
   3914             if (!bg_is_default) {
   3915                 send_num(rv, nbuf, cbg);
   3916             }
   3917             send_literal(rv, "m");
   3918             break;
   3919 
   3920         case TB_OUTPUT_256:
   3921         case TB_OUTPUT_216:
   3922         case TB_OUTPUT_GRAYSCALE:
   3923             send_literal(rv, "\x1b[");
   3924             if (!fg_is_default) {
   3925                 send_literal(rv, "38;5;");
   3926                 send_num(rv, nbuf, cfg);
   3927                 if (!bg_is_default) {
   3928                     send_literal(rv, ";");
   3929                 }
   3930             }
   3931             if (!bg_is_default) {
   3932                 send_literal(rv, "48;5;");
   3933                 send_num(rv, nbuf, cbg);
   3934             }
   3935             send_literal(rv, "m");
   3936             break;
   3937 
   3938 #if TB_OPT_ATTR_W >= 32
   3939         case TB_OUTPUT_TRUECOLOR:
   3940             send_literal(rv, "\x1b[");
   3941             if (!fg_is_default) {
   3942                 send_literal(rv, "38;2;");
   3943                 send_num(rv, nbuf, (cfg >> 16) & 0xff);
   3944                 send_literal(rv, ";");
   3945                 send_num(rv, nbuf, (cfg >> 8) & 0xff);
   3946                 send_literal(rv, ";");
   3947                 send_num(rv, nbuf, cfg & 0xff);
   3948                 if (!bg_is_default) {
   3949                     send_literal(rv, ";");
   3950                 }
   3951             }
   3952             if (!bg_is_default) {
   3953                 send_literal(rv, "48;2;");
   3954                 send_num(rv, nbuf, (cbg >> 16) & 0xff);
   3955                 send_literal(rv, ";");
   3956                 send_num(rv, nbuf, (cbg >> 8) & 0xff);
   3957                 send_literal(rv, ";");
   3958                 send_num(rv, nbuf, cbg & 0xff);
   3959             }
   3960             send_literal(rv, "m");
   3961             break;
   3962 #endif
   3963     }
   3964     return TB_OK;
   3965 }
   3966 
   3967 static int send_cursor_if(int x, int y) {
   3968     int rv;
   3969     char nbuf[32];
   3970     if (x < 0 || y < 0) {
   3971         return TB_OK;
   3972     }
   3973     send_literal(rv, "\x1b[");
   3974     send_num(rv, nbuf, y + 1);
   3975     send_literal(rv, ";");
   3976     send_num(rv, nbuf, x + 1);
   3977     send_literal(rv, "H");
   3978     return TB_OK;
   3979 }
   3980 
   3981 static int send_char(int x, int y, uint32_t ch) {
   3982     return send_cluster(x, y, &ch, 1);
   3983 }
   3984 
   3985 static int send_cluster(int x, int y, uint32_t *ch, size_t nch) {
   3986     int rv;
   3987     char chu8[8];
   3988 
   3989     if (global.last_x != x - 1 || global.last_y != y) {
   3990         if_err_return(rv, send_cursor_if(x, y));
   3991     }
   3992     global.last_x = x;
   3993     global.last_y = y;
   3994 
   3995     int i;
   3996     for (i = 0; i < (int)nch; i++) {
   3997         uint32_t ch32 = *(ch + i);
   3998         if (!tb_iswprint(ch32)) {
   3999             ch32 = 0xfffd; // replace non-printable codepoints with U+FFFD
   4000         }
   4001         int chu8_len = tb_utf8_unicode_to_char(chu8, ch32);
   4002         if_err_return(rv, bytebuf_nputs(&global.out, chu8, (size_t)chu8_len));
   4003     }
   4004 
   4005     return TB_OK;
   4006 }
   4007 
   4008 static int convert_num(uint32_t num, char *buf) {
   4009     int i, l = 0;
   4010     char ch;
   4011     do {
   4012         buf[l++] = (char)('0' + (num % 10));
   4013         num /= 10;
   4014     } while (num);
   4015     for (i = 0; i < l / 2; i++) {
   4016         ch = buf[i];
   4017         buf[i] = buf[l - 1 - i];
   4018         buf[l - 1 - i] = ch;
   4019     }
   4020     return l;
   4021 }
   4022 
   4023 static int cell_cmp(struct tb_cell *a, struct tb_cell *b) {
   4024     if (a->ch != b->ch || a->fg != b->fg || a->bg != b->bg) {
   4025         return 1;
   4026     }
   4027 #ifdef TB_OPT_EGC
   4028     if (a->nech != b->nech) {
   4029         return 1;
   4030     } else if (a->nech > 0) { // a->nech == b->nech
   4031         return memcmp(a->ech, b->ech, a->nech);
   4032     }
   4033 #endif
   4034     return 0;
   4035 }
   4036 
   4037 static int cell_copy(struct tb_cell *dst, struct tb_cell *src) {
   4038 #ifdef TB_OPT_EGC
   4039     if (src->nech > 0) {
   4040         return cell_set(dst, src->ech, src->nech, src->fg, src->bg);
   4041     }
   4042 #endif
   4043     return cell_set(dst, &src->ch, 1, src->fg, src->bg);
   4044 }
   4045 
   4046 static int cell_set(struct tb_cell *cell, uint32_t *ch, size_t nch,
   4047     uintattr_t fg, uintattr_t bg) {
   4048     // TODO: iswprint ch?
   4049     cell->ch = ch ? *ch : 0;
   4050     cell->fg = fg;
   4051     cell->bg = bg;
   4052 #ifdef TB_OPT_EGC
   4053     if (nch <= 1) {
   4054         cell->nech = 0;
   4055     } else {
   4056         int rv;
   4057         if_err_return(rv, cell_reserve_ech(cell, nch + 1));
   4058         memcpy(cell->ech, ch, sizeof(*ch) * nch);
   4059         cell->ech[nch] = '\0';
   4060         cell->nech = nch;
   4061     }
   4062 #else
   4063     (void)nch;
   4064     (void)cell_reserve_ech;
   4065 #endif
   4066     return TB_OK;
   4067 }
   4068 
   4069 static int cell_reserve_ech(struct tb_cell *cell, size_t n) {
   4070 #ifdef TB_OPT_EGC
   4071     if (cell->cech >= n) return TB_OK;
   4072     cell->ech = (uint32_t *)tb_realloc(cell->ech, n * sizeof(cell->ch));
   4073     if (!cell->ech) return TB_ERR_MEM;
   4074     cell->cech = n;
   4075     return TB_OK;
   4076 #else
   4077     (void)cell;
   4078     (void)n;
   4079     return TB_ERR;
   4080 #endif
   4081 }
   4082 
   4083 static int cell_free(struct tb_cell *cell) {
   4084 #ifdef TB_OPT_EGC
   4085     if (cell->ech) tb_free(cell->ech);
   4086 #endif
   4087     memset(cell, 0, sizeof(*cell));
   4088     return TB_OK;
   4089 }
   4090 
   4091 static int cellbuf_init(struct cellbuf *c, int w, int h) {
   4092     c->cells = (struct tb_cell *)tb_malloc(sizeof(struct tb_cell) * w * h);
   4093     if (!c->cells) return TB_ERR_MEM;
   4094     memset(c->cells, 0, sizeof(struct tb_cell) * w * h);
   4095     c->width = w;
   4096     c->height = h;
   4097     return TB_OK;
   4098 }
   4099 
   4100 static int cellbuf_free(struct cellbuf *c) {
   4101     if (c->cells) {
   4102         int i;
   4103         for (i = 0; i < c->width * c->height; i++) {
   4104             cell_free(&c->cells[i]);
   4105         }
   4106         tb_free(c->cells);
   4107     }
   4108     memset(c, 0, sizeof(*c));
   4109     return TB_OK;
   4110 }
   4111 
   4112 static int cellbuf_clear(struct cellbuf *c) {
   4113     int rv, i;
   4114     uint32_t space = (uint32_t)' ';
   4115     for (i = 0; i < c->width * c->height; i++) {
   4116         if_err_return(rv,
   4117             cell_set(&c->cells[i], &space, 1, global.fg, global.bg));
   4118     }
   4119     return TB_OK;
   4120 }
   4121 
   4122 static int cellbuf_get(struct cellbuf *c, int x, int y,
   4123     struct tb_cell **out) {
   4124     if (!cellbuf_in_bounds(c, x, y)) {
   4125         *out = NULL;
   4126         return TB_ERR_OUT_OF_BOUNDS;
   4127     }
   4128     *out = &c->cells[(y * c->width) + x];
   4129     return TB_OK;
   4130 }
   4131 
   4132 static int cellbuf_in_bounds(struct cellbuf *c, int x, int y) {
   4133     if (x < 0 || x >= c->width || y < 0 || y >= c->height) {
   4134         return 0;
   4135     }
   4136     return 1;
   4137 }
   4138 
   4139 static int cellbuf_resize(struct cellbuf *c, int w, int h) {
   4140     int rv;
   4141 
   4142     int ow = c->width;
   4143     int oh = c->height;
   4144 
   4145     if (ow == w && oh == h) {
   4146         return TB_OK;
   4147     }
   4148 
   4149     w = w < 1 ? 1 : w;
   4150     h = h < 1 ? 1 : h;
   4151 
   4152     int minw = (w < ow) ? w : ow;
   4153     int minh = (h < oh) ? h : oh;
   4154 
   4155     struct tb_cell *prev = c->cells;
   4156 
   4157     if_err_return(rv, cellbuf_init(c, w, h));
   4158     if_err_return(rv, cellbuf_clear(c));
   4159 
   4160     int x, y;
   4161     for (x = 0; x < minw; x++) {
   4162         for (y = 0; y < minh; y++) {
   4163             struct tb_cell *src, *dst;
   4164             src = &prev[(y * ow) + x];
   4165             if_err_return(rv, cellbuf_get(c, x, y, &dst));
   4166             if_err_return(rv, cell_copy(dst, src));
   4167         }
   4168     }
   4169 
   4170     tb_free(prev);
   4171 
   4172     return TB_OK;
   4173 }
   4174 
   4175 static int bytebuf_puts(struct bytebuf *b, const char *str) {
   4176     if (!str || strlen(str) <= 0) return TB_OK; // Nothing to do for empty caps
   4177     return bytebuf_nputs(b, str, (size_t)strlen(str));
   4178 }
   4179 
   4180 static int bytebuf_nputs(struct bytebuf *b, const char *str, size_t nstr) {
   4181     int rv;
   4182     if_err_return(rv, bytebuf_reserve(b, b->len + nstr + 1));
   4183     memcpy(b->buf + b->len, str, nstr);
   4184     b->len += nstr;
   4185     b->buf[b->len] = '\0';
   4186     return TB_OK;
   4187 }
   4188 
   4189 static int bytebuf_shift(struct bytebuf *b, size_t n) {
   4190     if (n > b->len) n = b->len;
   4191     size_t nmove = b->len - n;
   4192     memmove(b->buf, b->buf + n, nmove);
   4193     b->len -= n;
   4194     return TB_OK;
   4195 }
   4196 
   4197 static int bytebuf_flush(struct bytebuf *b, int fd) {
   4198     if (b->len <= 0) return TB_OK;
   4199     ssize_t write_rv = write(fd, b->buf, b->len);
   4200     if (write_rv < 0 || (size_t)write_rv != b->len) {
   4201         // Note, errno will be 0 on partial write
   4202         global.last_errno = errno;
   4203         return TB_ERR;
   4204     }
   4205     b->len = 0;
   4206     return TB_OK;
   4207 }
   4208 
   4209 static int bytebuf_reserve(struct bytebuf *b, size_t sz) {
   4210     if (b->cap >= sz) return TB_OK;
   4211 
   4212     size_t newcap = b->cap > 0 ? b->cap : 1;
   4213     while (newcap < sz) {
   4214         newcap *= 2;
   4215     }
   4216 
   4217     char *newbuf;
   4218     if (b->buf) {
   4219         newbuf = (char *)tb_realloc(b->buf, newcap);
   4220     } else {
   4221         newbuf = (char *)tb_malloc(newcap);
   4222     }
   4223     if (!newbuf) return TB_ERR_MEM;
   4224 
   4225     b->buf = newbuf;
   4226     b->cap = newcap;
   4227     return TB_OK;
   4228 }
   4229 
   4230 static int bytebuf_free(struct bytebuf *b) {
   4231     if (b->buf) tb_free(b->buf);
   4232     memset(b, 0, sizeof(*b));
   4233     return TB_OK;
   4234 }
   4235 
   4236 int tb_iswprint(uint32_t ch) {
   4237 #ifdef TB_OPT_LIBC_WCHAR
   4238     return iswprint((wint_t)ch);
   4239 #else
   4240     return tb_iswprint_ex(ch, NULL);
   4241 #endif
   4242 }
   4243 
   4244 int tb_wcwidth(uint32_t ch) {
   4245     int w;
   4246 #ifdef TB_OPT_LIBC_WCHAR
   4247     w = wcwidth((wchar_t)ch);
   4248 #else
   4249     tb_iswprint_ex(ch, &w);
   4250 #endif
   4251     return w;
   4252 }
   4253 
   4254 static int tb_cluster_width(uint32_t *ch, size_t nch) {
   4255     int wmax = -1;
   4256     int vs15 = 0, vs16 = 0, ri = 0, zwj = 0;
   4257     size_t i = 0;
   4258     for (i = 0; i < nch; i++) {
   4259         uint32_t c = ch[i];
   4260         switch (c) {
   4261             case 0xfe0e: ++vs15; break;
   4262             case 0xfe0f: ++vs16; break;
   4263             case 0x200d: ++zwj; break;
   4264             default: if (c >= 0x1f1e6 && c <= 0x1f1ff) ++ri;
   4265         }
   4266         int w = tb_wcwidth(c);
   4267         if (w > wmax) wmax = w;
   4268     }
   4269     if (wmax >= 1) {
   4270         if (vs15) return 1;
   4271         else if (vs16 || zwj || ri >= 2) return 2;
   4272     }
   4273     return wmax;
   4274 }
   4275 
   4276 static int tb_iswprint_ex(uint32_t ch, int *w) {
   4277 #ifdef TB_OPT_LIBC_WCHAR
   4278     if (w) *w = wcwidth((wint_t)ch);
   4279     return iswprint(ch);
   4280 #else
   4281     // Fast path for 1-byte codepoints
   4282     if ((ch >= 0x20 && ch <= 0x7e) || (ch >= 0xa0 && ch <= 0xff)) {
   4283         if (w) *w = 1;
   4284         return 1;
   4285     } else if (ch <= 0xff) {
   4286         if (w) *w = ch == 0 ? 0 : -1;
   4287         return 0;
   4288     }
   4289 
   4290     int lo = 0, hi = WCWIDTH_TABLE_LENGTH - 1;
   4291     while (lo <= hi) {
   4292         int i = (lo + hi) / 2;
   4293         if (ch < wcwidth_table[i].range_start) {
   4294             hi = i - 1;
   4295         } else if (ch > wcwidth_table[i].range_end) {
   4296             lo = i + 1;
   4297         } else {
   4298             if (w) *w = wcwidth_table[i].width;
   4299             return wcwidth_table[i].width >= 0 ? 1 : 0;
   4300         }
   4301     }
   4302     if (w) *w = -1; // Invalid codepoint
   4303     return 0;
   4304 #endif
   4305 }
   4306 
   4307 #endif // TB_IMPL