go-tui

Une TUI pour jouer au go - retour accueil

git clone git://bebou.netlib.re/go-tui

Log | Files | Refs |

termbox2.h (188337B)


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