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