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