poe

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

termbox.h (191308B)


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