Un outil pour réarranger les lignes de stdin - retour accueil
git clone git://bebou.netlib.re/reorder
Log | Files | Refs | README |
termbox2.h (126816B)
1 /* 2 MIT License 3 4 Copyright (c) 2010-2020 nsf <no.smile.face@gmail.com> 5 2015-2024 Adam Saponara <as@php.net> 6 7 Permission is hereby granted, free of charge, to any person obtaining a copy 8 of this software and associated documentation files (the "Software"), to deal 9 in the Software without restriction, including without limitation the rights 10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 copies of the Software, and to permit persons to whom the Software is 12 furnished to do so, subject to the following conditions: 13 14 The above copyright notice and this permission notice shall be included in all 15 copies or substantial portions of the Software. 16 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 SOFTWARE. 24 */ 25 26 #ifndef TERMBOX_H_INCL 27 #define TERMBOX_H_INCL 28 29 #ifndef _XOPEN_SOURCE 30 #define _XOPEN_SOURCE 31 #endif 32 33 #ifndef _DEFAULT_SOURCE 34 #define _DEFAULT_SOURCE 35 #endif 36 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <limits.h> 40 #include <signal.h> 41 #include <stdarg.h> 42 #include <stdint.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <sys/ioctl.h> 47 #include <sys/select.h> 48 #include <sys/stat.h> 49 #include <sys/time.h> 50 #include <sys/types.h> 51 #include <termios.h> 52 #include <unistd.h> 53 #include <wchar.h> 54 #include <wctype.h> 55 56 #ifdef PATH_MAX 57 #define TB_PATH_MAX PATH_MAX 58 #else 59 #define TB_PATH_MAX 4096 60 #endif 61 62 #ifdef __cplusplus 63 extern "C" { 64 #endif 65 66 // __ffi_start 67 68 #define TB_VERSION_STR "2.5.0-dev" 69 70 /* The following compile-time options are supported: 71 * 72 * TB_OPT_ATTR_W: Integer width of fg and bg attributes. Valid values 73 * (assuming system support) are 16, 32, and 64. (See 74 * uintattr_t). 32 or 64 enables output mode 75 * TB_OUTPUT_TRUECOLOR. 64 enables additional style 76 * attributes. (See tb_set_output_mode.) Larger values 77 * consume more memory in exchange for more features. 78 * Defaults to 16. 79 * 80 * TB_OPT_EGC: If set, enable extended grapheme cluster support 81 * (tb_extend_cell, tb_set_cell_ex). Consumes more memory. 82 * Defaults off. 83 * 84 * TB_OPT_PRINTF_BUF: Write buffer size for printf operations. Represents the 85 * largest string that can be sent in one call to tb_print* 86 * and tb_send* functions. Defaults to 4096. 87 * 88 * TB_OPT_READ_BUF: Read buffer size for tty reads. Defaults to 64. 89 * 90 * TB_OPT_TRUECOLOR: Deprecated. Sets TB_OPT_ATTR_W to 32 if not already set. 91 */ 92 93 #if defined(TB_LIB_OPTS) || 0 // __tb_lib_opts 94 /* Ensure consistent compile-time options when using as a shared library */ 95 #undef TB_OPT_ATTR_W 96 #undef TB_OPT_EGC 97 #undef TB_OPT_PRINTF_BUF 98 #undef TB_OPT_READ_BUF 99 #define TB_OPT_ATTR_W 64 100 #define TB_OPT_EGC 101 #endif 102 103 /* Ensure sane `TB_OPT_ATTR_W` (16, 32, or 64) */ 104 #if defined TB_OPT_ATTR_W && TB_OPT_ATTR_W == 16 105 #elif defined TB_OPT_ATTR_W && TB_OPT_ATTR_W == 32 106 #elif defined TB_OPT_ATTR_W && TB_OPT_ATTR_W == 64 107 #else 108 #undef TB_OPT_ATTR_W 109 #if defined TB_OPT_TRUECOLOR // Deprecated. Back-compat for old flag. 110 #define TB_OPT_ATTR_W 32 111 #else 112 #define TB_OPT_ATTR_W 16 113 #endif 114 #endif 115 116 /* ASCII key constants (`tb_event.key`) */ 117 #define TB_KEY_CTRL_TILDE 0x00 118 #define TB_KEY_CTRL_2 0x00 // clash with `CTRL_TILDE` 119 #define TB_KEY_CTRL_A 0x01 120 #define TB_KEY_CTRL_B 0x02 121 #define TB_KEY_CTRL_C 0x03 122 #define TB_KEY_CTRL_D 0x04 123 #define TB_KEY_CTRL_E 0x05 124 #define TB_KEY_CTRL_F 0x06 125 #define TB_KEY_CTRL_G 0x07 126 #define TB_KEY_BACKSPACE 0x08 127 #define TB_KEY_CTRL_H 0x08 // clash with `CTRL_BACKSPACE` 128 #define TB_KEY_TAB 0x09 129 #define TB_KEY_CTRL_I 0x09 // clash with `TAB` 130 #define TB_KEY_CTRL_J 0x0a 131 #define TB_KEY_CTRL_K 0x0b 132 #define TB_KEY_CTRL_L 0x0c 133 #define TB_KEY_ENTER 0x0d 134 #define TB_KEY_CTRL_M 0x0d // clash with `ENTER` 135 #define TB_KEY_CTRL_N 0x0e 136 #define TB_KEY_CTRL_O 0x0f 137 #define TB_KEY_CTRL_P 0x10 138 #define TB_KEY_CTRL_Q 0x11 139 #define TB_KEY_CTRL_R 0x12 140 #define TB_KEY_CTRL_S 0x13 141 #define TB_KEY_CTRL_T 0x14 142 #define TB_KEY_CTRL_U 0x15 143 #define TB_KEY_CTRL_V 0x16 144 #define TB_KEY_CTRL_W 0x17 145 #define TB_KEY_CTRL_X 0x18 146 #define TB_KEY_CTRL_Y 0x19 147 #define TB_KEY_CTRL_Z 0x1a 148 #define TB_KEY_ESC 0x1b 149 #define TB_KEY_CTRL_LSQ_BRACKET 0x1b // clash with 'ESC' 150 #define TB_KEY_CTRL_3 0x1b // clash with 'ESC' 151 #define TB_KEY_CTRL_4 0x1c 152 #define TB_KEY_CTRL_BACKSLASH 0x1c // clash with 'CTRL_4' 153 #define TB_KEY_CTRL_5 0x1d 154 #define TB_KEY_CTRL_RSQ_BRACKET 0x1d // clash with 'CTRL_5' 155 #define TB_KEY_CTRL_6 0x1e 156 #define TB_KEY_CTRL_7 0x1f 157 #define TB_KEY_CTRL_SLASH 0x1f // clash with 'CTRL_7' 158 #define TB_KEY_CTRL_UNDERSCORE 0x1f // clash with 'CTRL_7' 159 #define TB_KEY_SPACE 0x20 160 #define TB_KEY_BACKSPACE2 0x7f 161 #define TB_KEY_CTRL_8 0x7f // clash with 'BACKSPACE2' 162 163 #define tb_key_i(i) 0xffff - (i) 164 /* Terminal-dependent key constants (`tb_event.key`) and terminfo caps */ 165 /* BEGIN codegen h */ 166 /* Produced by ./codegen.sh on Tue, 03 Sep 2024 04:17:47 +0000 */ 167 #define TB_KEY_F1 (0xffff - 0) 168 #define TB_KEY_F2 (0xffff - 1) 169 #define TB_KEY_F3 (0xffff - 2) 170 #define TB_KEY_F4 (0xffff - 3) 171 #define TB_KEY_F5 (0xffff - 4) 172 #define TB_KEY_F6 (0xffff - 5) 173 #define TB_KEY_F7 (0xffff - 6) 174 #define TB_KEY_F8 (0xffff - 7) 175 #define TB_KEY_F9 (0xffff - 8) 176 #define TB_KEY_F10 (0xffff - 9) 177 #define TB_KEY_F11 (0xffff - 10) 178 #define TB_KEY_F12 (0xffff - 11) 179 #define TB_KEY_INSERT (0xffff - 12) 180 #define TB_KEY_DELETE (0xffff - 13) 181 #define TB_KEY_HOME (0xffff - 14) 182 #define TB_KEY_END (0xffff - 15) 183 #define TB_KEY_PGUP (0xffff - 16) 184 #define TB_KEY_PGDN (0xffff - 17) 185 #define TB_KEY_ARROW_UP (0xffff - 18) 186 #define TB_KEY_ARROW_DOWN (0xffff - 19) 187 #define TB_KEY_ARROW_LEFT (0xffff - 20) 188 #define TB_KEY_ARROW_RIGHT (0xffff - 21) 189 #define TB_KEY_BACK_TAB (0xffff - 22) 190 #define TB_KEY_MOUSE_LEFT (0xffff - 23) 191 #define TB_KEY_MOUSE_RIGHT (0xffff - 24) 192 #define TB_KEY_MOUSE_MIDDLE (0xffff - 25) 193 #define TB_KEY_MOUSE_RELEASE (0xffff - 26) 194 #define TB_KEY_MOUSE_WHEEL_UP (0xffff - 27) 195 #define TB_KEY_MOUSE_WHEEL_DOWN (0xffff - 28) 196 197 #define TB_CAP_F1 0 198 #define TB_CAP_F2 1 199 #define TB_CAP_F3 2 200 #define TB_CAP_F4 3 201 #define TB_CAP_F5 4 202 #define TB_CAP_F6 5 203 #define TB_CAP_F7 6 204 #define TB_CAP_F8 7 205 #define TB_CAP_F9 8 206 #define TB_CAP_F10 9 207 #define TB_CAP_F11 10 208 #define TB_CAP_F12 11 209 #define TB_CAP_INSERT 12 210 #define TB_CAP_DELETE 13 211 #define TB_CAP_HOME 14 212 #define TB_CAP_END 15 213 #define TB_CAP_PGUP 16 214 #define TB_CAP_PGDN 17 215 #define TB_CAP_ARROW_UP 18 216 #define TB_CAP_ARROW_DOWN 19 217 #define TB_CAP_ARROW_LEFT 20 218 #define TB_CAP_ARROW_RIGHT 21 219 #define TB_CAP_BACK_TAB 22 220 #define TB_CAP__COUNT_KEYS 23 221 #define TB_CAP_ENTER_CA 23 222 #define TB_CAP_EXIT_CA 24 223 #define TB_CAP_SHOW_CURSOR 25 224 #define TB_CAP_HIDE_CURSOR 26 225 #define TB_CAP_CLEAR_SCREEN 27 226 #define TB_CAP_SGR0 28 227 #define TB_CAP_UNDERLINE 29 228 #define TB_CAP_BOLD 30 229 #define TB_CAP_BLINK 31 230 #define TB_CAP_ITALIC 32 231 #define TB_CAP_REVERSE 33 232 #define TB_CAP_ENTER_KEYPAD 34 233 #define TB_CAP_EXIT_KEYPAD 35 234 #define TB_CAP_DIM 36 235 #define TB_CAP_INVISIBLE 37 236 #define TB_CAP__COUNT 38 237 /* END codegen h */ 238 239 /* Some hard-coded caps */ 240 #define TB_HARDCAP_ENTER_MOUSE "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h" 241 #define TB_HARDCAP_EXIT_MOUSE "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l" 242 #define TB_HARDCAP_STRIKEOUT "\x1b[9m" 243 #define TB_HARDCAP_UNDERLINE_2 "\x1b[21m" 244 #define TB_HARDCAP_OVERLINE "\x1b[53m" 245 246 /* Colors (numeric) and attributes (bitwise) (`tb_cell.fg`, `tb_cell.bg`) */ 247 #define TB_DEFAULT 0x0000 248 #define TB_BLACK 0x0001 249 #define TB_RED 0x0002 250 #define TB_GREEN 0x0003 251 #define TB_YELLOW 0x0004 252 #define TB_BLUE 0x0005 253 #define TB_MAGENTA 0x0006 254 #define TB_CYAN 0x0007 255 #define TB_WHITE 0x0008 256 257 #if TB_OPT_ATTR_W == 16 258 #define TB_BOLD 0x0100 259 #define TB_UNDERLINE 0x0200 260 #define TB_REVERSE 0x0400 261 #define TB_ITALIC 0x0800 262 #define TB_BLINK 0x1000 263 #define TB_HI_BLACK 0x2000 264 #define TB_BRIGHT 0x4000 265 #define TB_DIM 0x8000 266 #define TB_256_BLACK TB_HI_BLACK // `TB_256_BLACK` is deprecated 267 #else 268 // `TB_OPT_ATTR_W` is 32 or 64 269 #define TB_BOLD 0x01000000 270 #define TB_UNDERLINE 0x02000000 271 #define TB_REVERSE 0x04000000 272 #define TB_ITALIC 0x08000000 273 #define TB_BLINK 0x10000000 274 #define TB_HI_BLACK 0x20000000 275 #define TB_BRIGHT 0x40000000 276 #define TB_DIM 0x80000000 277 #define TB_TRUECOLOR_BOLD TB_BOLD // `TB_TRUECOLOR_*` is deprecated 278 #define TB_TRUECOLOR_UNDERLINE TB_UNDERLINE 279 #define TB_TRUECOLOR_REVERSE TB_REVERSE 280 #define TB_TRUECOLOR_ITALIC TB_ITALIC 281 #define TB_TRUECOLOR_BLINK TB_BLINK 282 #define TB_TRUECOLOR_BLACK TB_HI_BLACK 283 #endif 284 285 #if TB_OPT_ATTR_W == 64 286 #define TB_STRIKEOUT 0x0000000100000000 287 #define TB_UNDERLINE_2 0x0000000200000000 288 #define TB_OVERLINE 0x0000000400000000 289 #define TB_INVISIBLE 0x0000000800000000 290 #endif 291 292 /* Event types (`tb_event.type`) */ 293 #define TB_EVENT_KEY 1 294 #define TB_EVENT_RESIZE 2 295 #define TB_EVENT_MOUSE 3 296 297 /* Key modifiers (bitwise) (`tb_event.mod`) */ 298 #define TB_MOD_ALT 1 299 #define TB_MOD_CTRL 2 300 #define TB_MOD_SHIFT 4 301 #define TB_MOD_MOTION 8 302 303 /* Input modes (bitwise) (`tb_set_input_mode`) */ 304 #define TB_INPUT_CURRENT 0 305 #define TB_INPUT_ESC 1 306 #define TB_INPUT_ALT 2 307 #define TB_INPUT_MOUSE 4 308 309 /* Output modes (`tb_set_output_mode`) */ 310 #define TB_OUTPUT_CURRENT 0 311 #define TB_OUTPUT_NORMAL 1 312 #define TB_OUTPUT_256 2 313 #define TB_OUTPUT_216 3 314 #define TB_OUTPUT_GRAYSCALE 4 315 #if TB_OPT_ATTR_W >= 32 316 #define TB_OUTPUT_TRUECOLOR 5 317 #endif 318 319 /* Common function return values unless otherwise noted. 320 * 321 * Library behavior is undefined after receiving `TB_ERR_MEM`. Callers may 322 * attempt reinitializing by freeing memory, invoking `tb_shutdown`, then 323 * `tb_init`. 324 */ 325 #define TB_OK 0 326 #define TB_ERR -1 327 #define TB_ERR_NEED_MORE -2 328 #define TB_ERR_INIT_ALREADY -3 329 #define TB_ERR_INIT_OPEN -4 330 #define TB_ERR_MEM -5 331 #define TB_ERR_NO_EVENT -6 332 #define TB_ERR_NO_TERM -7 333 #define TB_ERR_NOT_INIT -8 334 #define TB_ERR_OUT_OF_BOUNDS -9 335 #define TB_ERR_READ -10 336 #define TB_ERR_RESIZE_IOCTL -11 337 #define TB_ERR_RESIZE_PIPE -12 338 #define TB_ERR_RESIZE_SIGACTION -13 339 #define TB_ERR_POLL -14 340 #define TB_ERR_TCGETATTR -15 341 #define TB_ERR_TCSETATTR -16 342 #define TB_ERR_UNSUPPORTED_TERM -17 343 #define TB_ERR_RESIZE_WRITE -18 344 #define TB_ERR_RESIZE_POLL -19 345 #define TB_ERR_RESIZE_READ -20 346 #define TB_ERR_RESIZE_SSCANF -21 347 #define TB_ERR_CAP_COLLISION -22 348 349 #define TB_ERR_SELECT TB_ERR_POLL 350 #define TB_ERR_RESIZE_SELECT TB_ERR_RESIZE_POLL 351 352 /* Deprecated. Function types to be used with `tb_set_func`. */ 353 #define TB_FUNC_EXTRACT_PRE 0 354 #define TB_FUNC_EXTRACT_POST 1 355 356 /* Define this to set the size of the buffer used in `tb_printf` 357 * and `tb_sendf` 358 */ 359 #ifndef TB_OPT_PRINTF_BUF 360 #define TB_OPT_PRINTF_BUF 4096 361 #endif 362 363 /* Define this to set the size of the read buffer used when reading 364 * from the tty 365 */ 366 #ifndef TB_OPT_READ_BUF 367 #define TB_OPT_READ_BUF 64 368 #endif 369 370 /* Define this for limited back compat with termbox v1 */ 371 #ifdef TB_OPT_V1_COMPAT 372 #define tb_change_cell tb_set_cell 373 #define tb_put_cell(x, y, c) tb_set_cell((x), (y), (c)->ch, (c)->fg, (c)->bg) 374 #define tb_set_clear_attributes tb_set_clear_attrs 375 #define tb_select_input_mode tb_set_input_mode 376 #define tb_select_output_mode tb_set_output_mode 377 #endif 378 379 /* Define these to swap in a different allocator */ 380 #ifndef tb_malloc 381 #define tb_malloc malloc 382 #define tb_realloc realloc 383 #define tb_free free 384 #endif 385 386 #if TB_OPT_ATTR_W == 64 387 typedef uint64_t uintattr_t; 388 #elif TB_OPT_ATTR_W == 32 389 typedef uint32_t uintattr_t; 390 #else // 16 391 typedef uint16_t uintattr_t; 392 #endif 393 394 /* A cell in a 2d grid representing the terminal screen. 395 * 396 * The terminal screen is represented as 2d array of cells. The structure is 397 * optimized for dealing with single-width (`wcwidth==1`) Unicode codepoints, 398 * however some support for grapheme clusters (e.g., combining diacritical 399 * marks) and wide codepoints (e.g., Hiragana) is provided through `ech`, 400 * `nech`, and `cech` via `tb_set_cell_ex`. `ech` is only valid when `nech>0`, 401 * otherwise `ch` is used. 402 * 403 * For non-single-width codepoints, given `N=wcwidth(ch)/wcswidth(ech)`: 404 * 405 * when `N==0`: termbox forces a single-width cell. Callers should avoid this 406 * if aiming to render text accurately. Callers may use 407 * `tb_set_cell_ex` or `tb_print*` to render `N==0` combining 408 * characters. 409 * 410 * when `N>1`: termbox zeroes out the following `N-1` cells and skips sending 411 * them to the tty. So, e.g., if the caller sets `x=0,y=0` to an 412 * `N==2` codepoint, the caller's next set should be at `x=2,y=0`. 413 * Anything set at `x=1,y=0` will be ignored. If there are not 414 * enough columns remaining on the line to render `N` width, spaces 415 * are sent instead. 416 * 417 * See `tb_present` for implementation. 418 */ 419 struct tb_cell { 420 uint32_t ch; // a Unicode codepoint 421 uintattr_t fg; // bitwise foreground attributes 422 uintattr_t bg; // bitwise background attributes 423 #ifdef TB_OPT_EGC 424 uint32_t *ech; // a grapheme cluster of Unicode codepoints, 0-terminated 425 size_t nech; // num elements in ech, 0 means use ch instead of ech 426 size_t cech; // num elements allocated for ech 427 #endif 428 }; 429 430 /* An incoming event from the tty. 431 * 432 * Given the event type, the following fields are relevant: 433 * 434 * when `TB_EVENT_KEY`: `key` xor `ch` (one will be zero) and `mod`. Note 435 * there is overlap between `TB_MOD_CTRL` and 436 * `TB_KEY_CTRL_*`. `TB_MOD_CTRL` and `TB_MOD_SHIFT` are 437 * only set as modifiers to `TB_KEY_ARROW_*`. 438 * 439 * when `TB_EVENT_RESIZE`: `w` and `h` 440 * 441 * when `TB_EVENT_MOUSE`: `key` (`TB_KEY_MOUSE_*`), `x`, and `y` 442 */ 443 struct tb_event { 444 uint8_t type; // one of `TB_EVENT_*` constants 445 uint8_t mod; // bitwise `TB_MOD_*` constants 446 uint16_t key; // one of `TB_KEY_*` constants 447 uint32_t ch; // a Unicode codepoint 448 int32_t w; // resize width 449 int32_t h; // resize height 450 int32_t x; // mouse x 451 int32_t y; // mouse y 452 }; 453 454 /* Initialize the termbox library. This function should be called before any 455 * other functions. `tb_init` is equivalent to `tb_init_file("/dev/tty")`. After 456 * successful initialization, the library must be finalized using `tb_shutdown`. 457 */ 458 int tb_init(void); 459 int tb_init_file(const char *path); 460 int tb_init_fd(int ttyfd); 461 int tb_init_rwfd(int rfd, int wfd); 462 int tb_shutdown(void); 463 464 /* Return the size of the internal back buffer (which is the same as terminal's 465 * window size in rows and columns). The internal buffer can be resized after 466 * `tb_clear` or `tb_present` calls. Both dimensions have an unspecified 467 * negative value when called before `tb_init` or after `tb_shutdown`. 468 */ 469 int tb_width(void); 470 int tb_height(void); 471 472 /* Clear the internal back buffer using `TB_DEFAULT` or the attributes set by 473 * `tb_set_clear_attrs`. 474 */ 475 int tb_clear(void); 476 int tb_set_clear_attrs(uintattr_t fg, uintattr_t bg); 477 478 /* Synchronize the internal back buffer with the terminal by writing to tty. */ 479 int tb_present(void); 480 481 /* Clear the internal front buffer effectively forcing a complete re-render of 482 * the back buffer to the tty. It is not necessary to call this under normal 483 * circumstances. */ 484 int tb_invalidate(void); 485 486 /* Set the position of the cursor. Upper-left cell is (0, 0). */ 487 int tb_set_cursor(int cx, int cy); 488 int tb_hide_cursor(void); 489 490 /* Set cell contents in the internal back buffer at the specified position. 491 * 492 * Use `tb_set_cell_ex` for rendering grapheme clusters (e.g., combining 493 * diacritical marks). 494 * 495 * Calling `tb_set_cell(x, y, ch, fg, bg)` is equivalent to 496 * `tb_set_cell_ex(x, y, &ch, 1, fg, bg)`. 497 * 498 * `tb_extend_cell` is a shortcut for appending 1 codepoint to `tb_cell.ech`. 499 * 500 * Non-printable (`iswprint(3)`) codepoints are replaced with `U+FFFD` at render 501 * time. 502 */ 503 int tb_set_cell(int x, int y, uint32_t ch, uintattr_t fg, uintattr_t bg); 504 int tb_set_cell_ex(int x, int y, uint32_t *ch, size_t nch, uintattr_t fg, 505 uintattr_t bg); 506 int tb_extend_cell(int x, int y, uint32_t ch); 507 508 /* Set the input mode. Termbox has two input modes: 509 * 510 * 1. `TB_INPUT_ESC` 511 * When escape (`\x1b`) is in the buffer and there's no match for an escape 512 * sequence, a key event for `TB_KEY_ESC` is returned. 513 * 514 * 2. `TB_INPUT_ALT` 515 * When escape (`\x1b`) is in the buffer and there's no match for an escape 516 * sequence, the next keyboard event is returned with a `TB_MOD_ALT` 517 * modifier. 518 * 519 * You can also apply `TB_INPUT_MOUSE` via bitwise OR operation to either of the 520 * modes (e.g., `TB_INPUT_ESC | TB_INPUT_MOUSE`) to receive `TB_EVENT_MOUSE` 521 * events. If none of the main two modes were set, but the mouse mode was, 522 * `TB_INPUT_ESC` is used. If for some reason you've decided to use 523 * `TB_INPUT_ESC | TB_INPUT_ALT`, it will behave as if only `TB_INPUT_ESC` was 524 * selected. 525 * 526 * If mode is `TB_INPUT_CURRENT`, return the current input mode. 527 * 528 * The default input mode is `TB_INPUT_ESC`. 529 */ 530 int tb_set_input_mode(int mode); 531 532 /* Set the output mode. Termbox has multiple output modes: 533 * 534 * 1. `TB_OUTPUT_NORMAL` => [0..8] 535 * 536 * This mode provides 8 different colors: 537 * `TB_BLACK`, `TB_RED`, `TB_GREEN`, `TB_YELLOW`, 538 * `TB_BLUE`, `TB_MAGENTA`, `TB_CYAN`, `TB_WHITE` 539 * 540 * Plus `TB_DEFAULT` which skips sending a color code (i.e., uses the 541 * terminal's default color). 542 * 543 * Colors (including `TB_DEFAULT`) may be bitwise OR'd with attributes: 544 * `TB_BOLD`, `TB_UNDERLINE`, `TB_REVERSE`, `TB_ITALIC`, `TB_BLINK`, 545 * `TB_BRIGHT`, `TB_DIM` 546 * 547 * The following style attributes are also available if compiled with 548 * `TB_OPT_ATTR_W` set to 64: 549 * `TB_STRIKEOUT`, `TB_UNDERLINE_2`, `TB_OVERLINE`, `TB_INVISIBLE` 550 * 551 * As in all modes, the value 0 is interpreted as `TB_DEFAULT` for 552 * convenience. 553 * 554 * Some notes: `TB_REVERSE` and `TB_BRIGHT` can be applied as either `fg` or 555 * `bg` attributes for the same effect. The rest of the attributes apply to 556 * `fg` only and are ignored as `bg` attributes. 557 * 558 * Example usage: `tb_set_cell(x, y, '@', TB_BLACK | TB_BOLD, TB_RED)` 559 * 560 * 2. `TB_OUTPUT_256` => [0..255] + `TB_HI_BLACK` 561 * 562 * In this mode you get 256 distinct colors (plus default): 563 * 0x00 (1): `TB_DEFAULT` 564 * `TB_HI_BLACK` (1): `TB_BLACK` in `TB_OUTPUT_NORMAL` 565 * 0x01..0x07 (7): the next 7 colors as in `TB_OUTPUT_NORMAL` 566 * 0x08..0x0f (8): bright versions of the above 567 * 0x10..0xe7 (216): 216 different colors 568 * 0xe8..0xff (24): 24 different shades of gray 569 * 570 * All `TB_*` style attributes except `TB_BRIGHT` may be bitwise OR'd as in 571 * `TB_OUTPUT_NORMAL`. 572 * 573 * Note `TB_HI_BLACK` must be used for black, as 0x00 represents default. 574 * 575 * 3. `TB_OUTPUT_216` => [0..216] 576 * 577 * This mode supports the 216-color range of `TB_OUTPUT_256` only, but you 578 * don't need to provide an offset: 579 * 0x00 (1): `TB_DEFAULT` 580 * 0x01..0xd8 (216): 216 different colors 581 * 582 * 4. `TB_OUTPUT_GRAYSCALE` => [0..24] 583 * 584 * This mode supports the 24-color range of `TB_OUTPUT_256` only, but you 585 * don't need to provide an offset: 586 * 0x00 (1): `TB_DEFAULT` 587 * 0x01..0x18 (24): 24 different shades of gray 588 * 589 * 5. `TB_OUTPUT_TRUECOLOR` => [0x000000..0xffffff] + `TB_HI_BLACK` 590 * 591 * This mode provides 24-bit color on supported terminals. The format is 592 * 0xRRGGBB. 593 * 594 * All `TB_*` style attributes except `TB_BRIGHT` may be bitwise OR'd as in 595 * `TB_OUTPUT_NORMAL`. 596 * 597 * Note `TB_HI_BLACK` must be used for black, as 0x000000 represents default. 598 * 599 * To use the terminal default color (i.e., to not send an escape code), pass 600 * `TB_DEFAULT`. For convenience, the value 0 is interpreted as `TB_DEFAULT` in 601 * all modes. 602 * 603 * Note, cell attributes persist after switching output modes. Any translation 604 * between, for example, `TB_OUTPUT_NORMAL`'s `TB_RED` and 605 * `TB_OUTPUT_TRUECOLOR`'s 0xff0000 must be performed by the caller. Also note 606 * that cells previously rendered in one mode may persist unchanged until the 607 * front buffer is cleared (such as after a resize event) at which point it will 608 * be re-interpreted and flushed according to the current mode. Callers may 609 * invoke `tb_invalidate` if it is desirable to immediately re-interpret and 610 * flush the entire screen according to the current mode. 611 * 612 * Note, not all terminals support all output modes, especially beyond 613 * `TB_OUTPUT_NORMAL`. There is also no very reliable way to determine color 614 * support dynamically. If portability is desired, callers are recommended to 615 * use `TB_OUTPUT_NORMAL` or make output mode end-user configurable. The same 616 * advice applies to style attributes. 617 * 618 * If mode is `TB_OUTPUT_CURRENT`, return the current output mode. 619 * 620 * The default output mode is `TB_OUTPUT_NORMAL`. 621 */ 622 int tb_set_output_mode(int mode); 623 624 /* Wait for an event up to `timeout_ms` milliseconds and populate `event` with 625 * it. If no event is available within the timeout period, `TB_ERR_NO_EVENT` 626 * is returned. On a resize event, the underlying `select(2)` call may be 627 * interrupted, yielding a return code of `TB_ERR_POLL`. In this case, you may 628 * check `errno` via `tb_last_errno`. If it's `EINTR`, you may elect to ignore 629 * that and call `tb_peek_event` again. 630 */ 631 int tb_peek_event(struct tb_event *event, int timeout_ms); 632 633 /* Same as `tb_peek_event` except no timeout. */ 634 int tb_poll_event(struct tb_event *event); 635 636 /* Internal termbox fds that can be used with `poll(2)`, `select(2)`, etc. 637 * externally. Callers must invoke `tb_poll_event` or `tb_peek_event` if 638 * fds become readable. */ 639 int tb_get_fds(int *ttyfd, int *resizefd); 640 641 /* Print and printf functions. Specify param `out_w` to determine width of 642 * printed string. Strings are interpreted as UTF-8. 643 * 644 * Non-printable characters (`iswprint(3)`) and truncated UTF-8 byte sequences 645 * are replaced with U+FFFD. 646 * 647 * Newlines (`\n`) are supported with the caveat that `out_w` will return the 648 * width of the string as if it were on a single line. 649 * 650 * If the starting coordinate is out of bounds, `TB_ERR_OUT_OF_BOUNDS` is 651 * returned. If the starting coordinate is in bounds, but goes out of bounds, 652 * then the out-of-bounds portions of the string are ignored. 653 * 654 * For finer control, use `tb_set_cell`. 655 */ 656 int tb_print(int x, int y, uintattr_t fg, uintattr_t bg, const char *str); 657 int tb_printf(int x, int y, uintattr_t fg, uintattr_t bg, const char *fmt, ...); 658 int tb_print_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w, 659 const char *str); 660 int tb_printf_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w, 661 const char *fmt, ...); 662 663 /* Send raw bytes to terminal. */ 664 int tb_send(const char *buf, size_t nbuf); 665 int tb_sendf(const char *fmt, ...); 666 667 /* Deprecated. Set custom callbacks. `fn_type` is one of `TB_FUNC_*` constants, 668 * `fn` is a compatible function pointer, or NULL to clear. 669 * 670 * `TB_FUNC_EXTRACT_PRE`: 671 * If specified, invoke this function BEFORE termbox tries to extract any 672 * escape sequences from the input buffer. 673 * 674 * `TB_FUNC_EXTRACT_POST`: 675 * If specified, invoke this function AFTER termbox tries (and fails) to 676 * extract any escape sequences from the input buffer. 677 */ 678 int tb_set_func(int fn_type, int (*fn)(struct tb_event *, size_t *)); 679 680 /* Return byte length of codepoint given first byte of UTF-8 sequence (1-6). */ 681 int tb_utf8_char_length(char c); 682 683 /* Convert UTF-8 null-terminated byte sequence to UTF-32 codepoint. 684 * 685 * If `c` is an empty C string, return 0. `out` is left unchanged. 686 * 687 * If a null byte is encountered in the middle of the codepoint, return a 688 * negative number indicating how many bytes were processed. `out` is left 689 * unchanged. 690 * 691 * Otherwise, return byte length of codepoint (1-6). 692 */ 693 int tb_utf8_char_to_unicode(uint32_t *out, const char *c); 694 695 /* Convert UTF-32 codepoint to UTF-8 null-terminated byte sequence. 696 * 697 * `out` must be char[7] or greater. Return byte length of codepoint (1-6). 698 */ 699 int tb_utf8_unicode_to_char(char *out, uint32_t c); 700 701 /* Library utility functions */ 702 int tb_last_errno(void); 703 const char *tb_strerror(int err); 704 struct tb_cell *tb_cell_buffer(void); // Deprecated 705 int tb_has_truecolor(void); 706 int tb_has_egc(void); 707 int tb_attr_width(void); 708 const char *tb_version(void); 709 710 /* Deprecation notice! 711 * 712 * The following will be removed in version 3.x (ABI version 3): 713 * 714 * TB_256_BLACK (use TB_HI_BLACK) 715 * TB_OPT_TRUECOLOR (use TB_OPT_ATTR_W) 716 * TB_TRUECOLOR_BOLD (use TB_BOLD) 717 * TB_TRUECOLOR_UNDERLINE (use TB_UNDERLINE) 718 * TB_TRUECOLOR_REVERSE (use TB_REVERSE) 719 * TB_TRUECOLOR_ITALIC (use TB_ITALICe) 720 * TB_TRUECOLOR_BLINK (use TB_BLINK) 721 * TB_TRUECOLOR_BLACK (use TB_HI_BLACK) 722 * tb_cell_buffer 723 * tb_set_func 724 * TB_FUNC_EXTRACT_PRE 725 * TB_FUNC_EXTRACT_POST 726 */ 727 728 #ifdef __cplusplus 729 } 730 #endif 731 732 #endif // TERMBOX_H_INCL 733 734 #ifdef TB_IMPL 735 736 #define if_err_return(rv, expr) \ 737 if (((rv) = (expr)) != TB_OK) return (rv) 738 #define if_err_break(rv, expr) \ 739 if (((rv) = (expr)) != TB_OK) break 740 #define if_ok_return(rv, expr) \ 741 if (((rv) = (expr)) == TB_OK) return (rv) 742 #define if_ok_or_need_more_return(rv, expr) \ 743 if (((rv) = (expr)) == TB_OK || (rv) == TB_ERR_NEED_MORE) return (rv) 744 745 #define send_literal(rv, a) \ 746 if_err_return((rv), bytebuf_nputs(&global.out, (a), sizeof(a) - 1)) 747 748 #define send_num(rv, nbuf, n) \ 749 if_err_return((rv), \ 750 bytebuf_nputs(&global.out, (nbuf), convert_num((n), (nbuf)))) 751 752 #define snprintf_or_return(rv, str, sz, fmt, ...) \ 753 do { \ 754 (rv) = snprintf((str), (sz), (fmt), __VA_ARGS__); \ 755 if ((rv) < 0 || (rv) >= (int)(sz)) return TB_ERR; \ 756 } while (0) 757 758 #define if_not_init_return() \ 759 if (!global.initialized) return TB_ERR_NOT_INIT 760 761 struct bytebuf_t { 762 char *buf; 763 size_t len; 764 size_t cap; 765 }; 766 767 struct cellbuf_t { 768 int width; 769 int height; 770 struct tb_cell *cells; 771 }; 772 773 struct cap_trie_t { 774 char c; 775 struct cap_trie_t *children; 776 size_t nchildren; 777 int is_leaf; 778 uint16_t key; 779 uint8_t mod; 780 }; 781 782 struct tb_global_t { 783 int ttyfd; 784 int rfd; 785 int wfd; 786 int ttyfd_open; 787 int resize_pipefd[2]; 788 int width; 789 int height; 790 int cursor_x; 791 int cursor_y; 792 int last_x; 793 int last_y; 794 uintattr_t fg; 795 uintattr_t bg; 796 uintattr_t last_fg; 797 uintattr_t last_bg; 798 int input_mode; 799 int output_mode; 800 char *terminfo; 801 size_t nterminfo; 802 const char *caps[TB_CAP__COUNT]; 803 struct cap_trie_t cap_trie; 804 struct bytebuf_t in; 805 struct bytebuf_t out; 806 struct cellbuf_t back; 807 struct cellbuf_t front; 808 struct termios orig_tios; 809 int has_orig_tios; 810 int last_errno; 811 int initialized; 812 int (*fn_extract_esc_pre)(struct tb_event *, size_t *); 813 int (*fn_extract_esc_post)(struct tb_event *, size_t *); 814 char errbuf[1024]; 815 }; 816 817 static struct tb_global_t global = {0}; 818 819 /* BEGIN codegen c */ 820 /* Produced by ./codegen.sh on Tue, 03 Sep 2024 04:17:48 +0000 */ 821 822 static const int16_t terminfo_cap_indexes[] = { 823 66, // kf1 (TB_CAP_F1) 824 68, // kf2 (TB_CAP_F2) 825 69, // kf3 (TB_CAP_F3) 826 70, // kf4 (TB_CAP_F4) 827 71, // kf5 (TB_CAP_F5) 828 72, // kf6 (TB_CAP_F6) 829 73, // kf7 (TB_CAP_F7) 830 74, // kf8 (TB_CAP_F8) 831 75, // kf9 (TB_CAP_F9) 832 67, // kf10 (TB_CAP_F10) 833 216, // kf11 (TB_CAP_F11) 834 217, // kf12 (TB_CAP_F12) 835 77, // kich1 (TB_CAP_INSERT) 836 59, // kdch1 (TB_CAP_DELETE) 837 76, // khome (TB_CAP_HOME) 838 164, // kend (TB_CAP_END) 839 82, // kpp (TB_CAP_PGUP) 840 81, // knp (TB_CAP_PGDN) 841 87, // kcuu1 (TB_CAP_ARROW_UP) 842 61, // kcud1 (TB_CAP_ARROW_DOWN) 843 79, // kcub1 (TB_CAP_ARROW_LEFT) 844 83, // kcuf1 (TB_CAP_ARROW_RIGHT) 845 148, // kcbt (TB_CAP_BACK_TAB) 846 28, // smcup (TB_CAP_ENTER_CA) 847 40, // rmcup (TB_CAP_EXIT_CA) 848 16, // cnorm (TB_CAP_SHOW_CURSOR) 849 13, // civis (TB_CAP_HIDE_CURSOR) 850 5, // clear (TB_CAP_CLEAR_SCREEN) 851 39, // sgr0 (TB_CAP_SGR0) 852 36, // smul (TB_CAP_UNDERLINE) 853 27, // bold (TB_CAP_BOLD) 854 26, // blink (TB_CAP_BLINK) 855 311, // sitm (TB_CAP_ITALIC) 856 34, // rev (TB_CAP_REVERSE) 857 89, // smkx (TB_CAP_ENTER_KEYPAD) 858 88, // rmkx (TB_CAP_EXIT_KEYPAD) 859 30, // dim (TB_CAP_DIM) 860 32, // invis (TB_CAP_INVISIBLE) 861 }; 862 863 // xterm 864 static const char *xterm_caps[] = { 865 "\033OP", // kf1 (TB_CAP_F1) 866 "\033OQ", // kf2 (TB_CAP_F2) 867 "\033OR", // kf3 (TB_CAP_F3) 868 "\033OS", // kf4 (TB_CAP_F4) 869 "\033[15~", // kf5 (TB_CAP_F5) 870 "\033[17~", // kf6 (TB_CAP_F6) 871 "\033[18~", // kf7 (TB_CAP_F7) 872 "\033[19~", // kf8 (TB_CAP_F8) 873 "\033[20~", // kf9 (TB_CAP_F9) 874 "\033[21~", // kf10 (TB_CAP_F10) 875 "\033[23~", // kf11 (TB_CAP_F11) 876 "\033[24~", // kf12 (TB_CAP_F12) 877 "\033[2~", // kich1 (TB_CAP_INSERT) 878 "\033[3~", // kdch1 (TB_CAP_DELETE) 879 "\033OH", // khome (TB_CAP_HOME) 880 "\033OF", // kend (TB_CAP_END) 881 "\033[5~", // kpp (TB_CAP_PGUP) 882 "\033[6~", // knp (TB_CAP_PGDN) 883 "\033OA", // kcuu1 (TB_CAP_ARROW_UP) 884 "\033OB", // kcud1 (TB_CAP_ARROW_DOWN) 885 "\033OD", // kcub1 (TB_CAP_ARROW_LEFT) 886 "\033OC", // kcuf1 (TB_CAP_ARROW_RIGHT) 887 "\033[Z", // kcbt (TB_CAP_BACK_TAB) 888 "\033[?1049h\033[22;0;0t", // smcup (TB_CAP_ENTER_CA) 889 "\033[?1049l\033[23;0;0t", // rmcup (TB_CAP_EXIT_CA) 890 "\033[?12l\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR) 891 "\033[?25l", // civis (TB_CAP_HIDE_CURSOR) 892 "\033[H\033[2J", // clear (TB_CAP_CLEAR_SCREEN) 893 "\033(B\033[m", // sgr0 (TB_CAP_SGR0) 894 "\033[4m", // smul (TB_CAP_UNDERLINE) 895 "\033[1m", // bold (TB_CAP_BOLD) 896 "\033[5m", // blink (TB_CAP_BLINK) 897 "\033[3m", // sitm (TB_CAP_ITALIC) 898 "\033[7m", // rev (TB_CAP_REVERSE) 899 "\033[?1h\033=", // smkx (TB_CAP_ENTER_KEYPAD) 900 "\033[?1l\033>", // rmkx (TB_CAP_EXIT_KEYPAD) 901 "\033[2m", // dim (TB_CAP_DIM) 902 "\033[8m", // invis (TB_CAP_INVISIBLE) 903 }; 904 905 // linux 906 static const char *linux_caps[] = { 907 "\033[[A", // kf1 (TB_CAP_F1) 908 "\033[[B", // kf2 (TB_CAP_F2) 909 "\033[[C", // kf3 (TB_CAP_F3) 910 "\033[[D", // kf4 (TB_CAP_F4) 911 "\033[[E", // kf5 (TB_CAP_F5) 912 "\033[17~", // kf6 (TB_CAP_F6) 913 "\033[18~", // kf7 (TB_CAP_F7) 914 "\033[19~", // kf8 (TB_CAP_F8) 915 "\033[20~", // kf9 (TB_CAP_F9) 916 "\033[21~", // kf10 (TB_CAP_F10) 917 "\033[23~", // kf11 (TB_CAP_F11) 918 "\033[24~", // kf12 (TB_CAP_F12) 919 "\033[2~", // kich1 (TB_CAP_INSERT) 920 "\033[3~", // kdch1 (TB_CAP_DELETE) 921 "\033[1~", // khome (TB_CAP_HOME) 922 "\033[4~", // kend (TB_CAP_END) 923 "\033[5~", // kpp (TB_CAP_PGUP) 924 "\033[6~", // knp (TB_CAP_PGDN) 925 "\033[A", // kcuu1 (TB_CAP_ARROW_UP) 926 "\033[B", // kcud1 (TB_CAP_ARROW_DOWN) 927 "\033[D", // kcub1 (TB_CAP_ARROW_LEFT) 928 "\033[C", // kcuf1 (TB_CAP_ARROW_RIGHT) 929 "\033\011", // kcbt (TB_CAP_BACK_TAB) 930 "", // smcup (TB_CAP_ENTER_CA) 931 "", // rmcup (TB_CAP_EXIT_CA) 932 "\033[?25h\033[?0c", // cnorm (TB_CAP_SHOW_CURSOR) 933 "\033[?25l\033[?1c", // civis (TB_CAP_HIDE_CURSOR) 934 "\033[H\033[J", // clear (TB_CAP_CLEAR_SCREEN) 935 "\033[m\017", // sgr0 (TB_CAP_SGR0) 936 "\033[4m", // smul (TB_CAP_UNDERLINE) 937 "\033[1m", // bold (TB_CAP_BOLD) 938 "\033[5m", // blink (TB_CAP_BLINK) 939 "", // sitm (TB_CAP_ITALIC) 940 "\033[7m", // rev (TB_CAP_REVERSE) 941 "", // smkx (TB_CAP_ENTER_KEYPAD) 942 "", // rmkx (TB_CAP_EXIT_KEYPAD) 943 "\033[2m", // dim (TB_CAP_DIM) 944 "", // invis (TB_CAP_INVISIBLE) 945 }; 946 947 // screen 948 static const char *screen_caps[] = { 949 "\033OP", // kf1 (TB_CAP_F1) 950 "\033OQ", // kf2 (TB_CAP_F2) 951 "\033OR", // kf3 (TB_CAP_F3) 952 "\033OS", // kf4 (TB_CAP_F4) 953 "\033[15~", // kf5 (TB_CAP_F5) 954 "\033[17~", // kf6 (TB_CAP_F6) 955 "\033[18~", // kf7 (TB_CAP_F7) 956 "\033[19~", // kf8 (TB_CAP_F8) 957 "\033[20~", // kf9 (TB_CAP_F9) 958 "\033[21~", // kf10 (TB_CAP_F10) 959 "\033[23~", // kf11 (TB_CAP_F11) 960 "\033[24~", // kf12 (TB_CAP_F12) 961 "\033[2~", // kich1 (TB_CAP_INSERT) 962 "\033[3~", // kdch1 (TB_CAP_DELETE) 963 "\033[1~", // khome (TB_CAP_HOME) 964 "\033[4~", // kend (TB_CAP_END) 965 "\033[5~", // kpp (TB_CAP_PGUP) 966 "\033[6~", // knp (TB_CAP_PGDN) 967 "\033OA", // kcuu1 (TB_CAP_ARROW_UP) 968 "\033OB", // kcud1 (TB_CAP_ARROW_DOWN) 969 "\033OD", // kcub1 (TB_CAP_ARROW_LEFT) 970 "\033OC", // kcuf1 (TB_CAP_ARROW_RIGHT) 971 "\033[Z", // kcbt (TB_CAP_BACK_TAB) 972 "\033[?1049h", // smcup (TB_CAP_ENTER_CA) 973 "\033[?1049l", // rmcup (TB_CAP_EXIT_CA) 974 "\033[34h\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR) 975 "\033[?25l", // civis (TB_CAP_HIDE_CURSOR) 976 "\033[H\033[J", // clear (TB_CAP_CLEAR_SCREEN) 977 "\033[m\017", // sgr0 (TB_CAP_SGR0) 978 "\033[4m", // smul (TB_CAP_UNDERLINE) 979 "\033[1m", // bold (TB_CAP_BOLD) 980 "\033[5m", // blink (TB_CAP_BLINK) 981 "", // sitm (TB_CAP_ITALIC) 982 "\033[7m", // rev (TB_CAP_REVERSE) 983 "\033[?1h\033=", // smkx (TB_CAP_ENTER_KEYPAD) 984 "\033[?1l\033>", // rmkx (TB_CAP_EXIT_KEYPAD) 985 "\033[2m", // dim (TB_CAP_DIM) 986 "", // invis (TB_CAP_INVISIBLE) 987 }; 988 989 // rxvt-256color 990 static const char *rxvt_256color_caps[] = { 991 "\033[11~", // kf1 (TB_CAP_F1) 992 "\033[12~", // kf2 (TB_CAP_F2) 993 "\033[13~", // kf3 (TB_CAP_F3) 994 "\033[14~", // kf4 (TB_CAP_F4) 995 "\033[15~", // kf5 (TB_CAP_F5) 996 "\033[17~", // kf6 (TB_CAP_F6) 997 "\033[18~", // kf7 (TB_CAP_F7) 998 "\033[19~", // kf8 (TB_CAP_F8) 999 "\033[20~", // kf9 (TB_CAP_F9) 1000 "\033[21~", // kf10 (TB_CAP_F10) 1001 "\033[23~", // kf11 (TB_CAP_F11) 1002 "\033[24~", // kf12 (TB_CAP_F12) 1003 "\033[2~", // kich1 (TB_CAP_INSERT) 1004 "\033[3~", // kdch1 (TB_CAP_DELETE) 1005 "\033[7~", // khome (TB_CAP_HOME) 1006 "\033[8~", // kend (TB_CAP_END) 1007 "\033[5~", // kpp (TB_CAP_PGUP) 1008 "\033[6~", // knp (TB_CAP_PGDN) 1009 "\033[A", // kcuu1 (TB_CAP_ARROW_UP) 1010 "\033[B", // kcud1 (TB_CAP_ARROW_DOWN) 1011 "\033[D", // kcub1 (TB_CAP_ARROW_LEFT) 1012 "\033[C", // kcuf1 (TB_CAP_ARROW_RIGHT) 1013 "\033[Z", // kcbt (TB_CAP_BACK_TAB) 1014 "\0337\033[?47h", // smcup (TB_CAP_ENTER_CA) 1015 "\033[2J\033[?47l\0338", // rmcup (TB_CAP_EXIT_CA) 1016 "\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR) 1017 "\033[?25l", // civis (TB_CAP_HIDE_CURSOR) 1018 "\033[H\033[2J", // clear (TB_CAP_CLEAR_SCREEN) 1019 "\033[m\017", // sgr0 (TB_CAP_SGR0) 1020 "\033[4m", // smul (TB_CAP_UNDERLINE) 1021 "\033[1m", // bold (TB_CAP_BOLD) 1022 "\033[5m", // blink (TB_CAP_BLINK) 1023 "", // sitm (TB_CAP_ITALIC) 1024 "\033[7m", // rev (TB_CAP_REVERSE) 1025 "\033=", // smkx (TB_CAP_ENTER_KEYPAD) 1026 "\033>", // rmkx (TB_CAP_EXIT_KEYPAD) 1027 "", // dim (TB_CAP_DIM) 1028 "", // invis (TB_CAP_INVISIBLE) 1029 }; 1030 1031 // rxvt-unicode 1032 static const char *rxvt_unicode_caps[] = { 1033 "\033[11~", // kf1 (TB_CAP_F1) 1034 "\033[12~", // kf2 (TB_CAP_F2) 1035 "\033[13~", // kf3 (TB_CAP_F3) 1036 "\033[14~", // kf4 (TB_CAP_F4) 1037 "\033[15~", // kf5 (TB_CAP_F5) 1038 "\033[17~", // kf6 (TB_CAP_F6) 1039 "\033[18~", // kf7 (TB_CAP_F7) 1040 "\033[19~", // kf8 (TB_CAP_F8) 1041 "\033[20~", // kf9 (TB_CAP_F9) 1042 "\033[21~", // kf10 (TB_CAP_F10) 1043 "\033[23~", // kf11 (TB_CAP_F11) 1044 "\033[24~", // kf12 (TB_CAP_F12) 1045 "\033[2~", // kich1 (TB_CAP_INSERT) 1046 "\033[3~", // kdch1 (TB_CAP_DELETE) 1047 "\033[7~", // khome (TB_CAP_HOME) 1048 "\033[8~", // kend (TB_CAP_END) 1049 "\033[5~", // kpp (TB_CAP_PGUP) 1050 "\033[6~", // knp (TB_CAP_PGDN) 1051 "\033[A", // kcuu1 (TB_CAP_ARROW_UP) 1052 "\033[B", // kcud1 (TB_CAP_ARROW_DOWN) 1053 "\033[D", // kcub1 (TB_CAP_ARROW_LEFT) 1054 "\033[C", // kcuf1 (TB_CAP_ARROW_RIGHT) 1055 "\033[Z", // kcbt (TB_CAP_BACK_TAB) 1056 "\033[?1049h", // smcup (TB_CAP_ENTER_CA) 1057 "\033[r\033[?1049l", // rmcup (TB_CAP_EXIT_CA) 1058 "\033[?12l\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR) 1059 "\033[?25l", // civis (TB_CAP_HIDE_CURSOR) 1060 "\033[H\033[2J", // clear (TB_CAP_CLEAR_SCREEN) 1061 "\033[m\033(B", // sgr0 (TB_CAP_SGR0) 1062 "\033[4m", // smul (TB_CAP_UNDERLINE) 1063 "\033[1m", // bold (TB_CAP_BOLD) 1064 "\033[5m", // blink (TB_CAP_BLINK) 1065 "\033[3m", // sitm (TB_CAP_ITALIC) 1066 "\033[7m", // rev (TB_CAP_REVERSE) 1067 "\033=", // smkx (TB_CAP_ENTER_KEYPAD) 1068 "\033>", // rmkx (TB_CAP_EXIT_KEYPAD) 1069 "", // dim (TB_CAP_DIM) 1070 "", // invis (TB_CAP_INVISIBLE) 1071 }; 1072 1073 // Eterm 1074 static const char *eterm_caps[] = { 1075 "\033[11~", // kf1 (TB_CAP_F1) 1076 "\033[12~", // kf2 (TB_CAP_F2) 1077 "\033[13~", // kf3 (TB_CAP_F3) 1078 "\033[14~", // kf4 (TB_CAP_F4) 1079 "\033[15~", // kf5 (TB_CAP_F5) 1080 "\033[17~", // kf6 (TB_CAP_F6) 1081 "\033[18~", // kf7 (TB_CAP_F7) 1082 "\033[19~", // kf8 (TB_CAP_F8) 1083 "\033[20~", // kf9 (TB_CAP_F9) 1084 "\033[21~", // kf10 (TB_CAP_F10) 1085 "\033[23~", // kf11 (TB_CAP_F11) 1086 "\033[24~", // kf12 (TB_CAP_F12) 1087 "\033[2~", // kich1 (TB_CAP_INSERT) 1088 "\033[3~", // kdch1 (TB_CAP_DELETE) 1089 "\033[7~", // khome (TB_CAP_HOME) 1090 "\033[8~", // kend (TB_CAP_END) 1091 "\033[5~", // kpp (TB_CAP_PGUP) 1092 "\033[6~", // knp (TB_CAP_PGDN) 1093 "\033[A", // kcuu1 (TB_CAP_ARROW_UP) 1094 "\033[B", // kcud1 (TB_CAP_ARROW_DOWN) 1095 "\033[D", // kcub1 (TB_CAP_ARROW_LEFT) 1096 "\033[C", // kcuf1 (TB_CAP_ARROW_RIGHT) 1097 "", // kcbt (TB_CAP_BACK_TAB) 1098 "\0337\033[?47h", // smcup (TB_CAP_ENTER_CA) 1099 "\033[2J\033[?47l\0338", // rmcup (TB_CAP_EXIT_CA) 1100 "\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR) 1101 "\033[?25l", // civis (TB_CAP_HIDE_CURSOR) 1102 "\033[H\033[2J", // clear (TB_CAP_CLEAR_SCREEN) 1103 "\033[m\017", // sgr0 (TB_CAP_SGR0) 1104 "\033[4m", // smul (TB_CAP_UNDERLINE) 1105 "\033[1m", // bold (TB_CAP_BOLD) 1106 "\033[5m", // blink (TB_CAP_BLINK) 1107 "", // sitm (TB_CAP_ITALIC) 1108 "\033[7m", // rev (TB_CAP_REVERSE) 1109 "", // smkx (TB_CAP_ENTER_KEYPAD) 1110 "", // rmkx (TB_CAP_EXIT_KEYPAD) 1111 "", // dim (TB_CAP_DIM) 1112 "", // invis (TB_CAP_INVISIBLE) 1113 }; 1114 1115 static struct { 1116 const char *name; 1117 const char **caps; 1118 const char *alias; 1119 } builtin_terms[] = { 1120 {"xterm", xterm_caps, "" }, 1121 {"linux", linux_caps, "" }, 1122 {"screen", screen_caps, "tmux"}, 1123 {"rxvt-256color", rxvt_256color_caps, "" }, 1124 {"rxvt-unicode", rxvt_unicode_caps, "rxvt"}, 1125 {"Eterm", eterm_caps, "" }, 1126 {NULL, NULL, NULL }, 1127 }; 1128 1129 /* END codegen c */ 1130 1131 static struct { 1132 const char *cap; 1133 const uint16_t key; 1134 const uint8_t mod; 1135 } builtin_mod_caps[] = { 1136 // xterm arrows 1137 {"\x1b[1;2A", TB_KEY_ARROW_UP, TB_MOD_SHIFT }, 1138 {"\x1b[1;3A", TB_KEY_ARROW_UP, TB_MOD_ALT }, 1139 {"\x1b[1;4A", TB_KEY_ARROW_UP, TB_MOD_ALT | TB_MOD_SHIFT }, 1140 {"\x1b[1;5A", TB_KEY_ARROW_UP, TB_MOD_CTRL }, 1141 {"\x1b[1;6A", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_SHIFT }, 1142 {"\x1b[1;7A", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT }, 1143 {"\x1b[1;8A", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1144 1145 {"\x1b[1;2B", TB_KEY_ARROW_DOWN, TB_MOD_SHIFT }, 1146 {"\x1b[1;3B", TB_KEY_ARROW_DOWN, TB_MOD_ALT }, 1147 {"\x1b[1;4B", TB_KEY_ARROW_DOWN, TB_MOD_ALT | TB_MOD_SHIFT }, 1148 {"\x1b[1;5B", TB_KEY_ARROW_DOWN, TB_MOD_CTRL }, 1149 {"\x1b[1;6B", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_SHIFT }, 1150 {"\x1b[1;7B", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_ALT }, 1151 {"\x1b[1;8B", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1152 1153 {"\x1b[1;2C", TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT }, 1154 {"\x1b[1;3C", TB_KEY_ARROW_RIGHT, TB_MOD_ALT }, 1155 {"\x1b[1;4C", TB_KEY_ARROW_RIGHT, TB_MOD_ALT | TB_MOD_SHIFT }, 1156 {"\x1b[1;5C", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL }, 1157 {"\x1b[1;6C", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_SHIFT }, 1158 {"\x1b[1;7C", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT }, 1159 {"\x1b[1;8C", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1160 1161 {"\x1b[1;2D", TB_KEY_ARROW_LEFT, TB_MOD_SHIFT }, 1162 {"\x1b[1;3D", TB_KEY_ARROW_LEFT, TB_MOD_ALT }, 1163 {"\x1b[1;4D", TB_KEY_ARROW_LEFT, TB_MOD_ALT | TB_MOD_SHIFT }, 1164 {"\x1b[1;5D", TB_KEY_ARROW_LEFT, TB_MOD_CTRL }, 1165 {"\x1b[1;6D", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_SHIFT }, 1166 {"\x1b[1;7D", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_ALT }, 1167 {"\x1b[1;8D", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1168 1169 // xterm keys 1170 {"\x1b[1;2H", TB_KEY_HOME, TB_MOD_SHIFT }, 1171 {"\x1b[1;3H", TB_KEY_HOME, TB_MOD_ALT }, 1172 {"\x1b[1;4H", TB_KEY_HOME, TB_MOD_ALT | TB_MOD_SHIFT }, 1173 {"\x1b[1;5H", TB_KEY_HOME, TB_MOD_CTRL }, 1174 {"\x1b[1;6H", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_SHIFT }, 1175 {"\x1b[1;7H", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_ALT }, 1176 {"\x1b[1;8H", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1177 1178 {"\x1b[1;2F", TB_KEY_END, TB_MOD_SHIFT }, 1179 {"\x1b[1;3F", TB_KEY_END, TB_MOD_ALT }, 1180 {"\x1b[1;4F", TB_KEY_END, TB_MOD_ALT | TB_MOD_SHIFT }, 1181 {"\x1b[1;5F", TB_KEY_END, TB_MOD_CTRL }, 1182 {"\x1b[1;6F", TB_KEY_END, TB_MOD_CTRL | TB_MOD_SHIFT }, 1183 {"\x1b[1;7F", TB_KEY_END, TB_MOD_CTRL | TB_MOD_ALT }, 1184 {"\x1b[1;8F", TB_KEY_END, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1185 1186 {"\x1b[2;2~", TB_KEY_INSERT, TB_MOD_SHIFT }, 1187 {"\x1b[2;3~", TB_KEY_INSERT, TB_MOD_ALT }, 1188 {"\x1b[2;4~", TB_KEY_INSERT, TB_MOD_ALT | TB_MOD_SHIFT }, 1189 {"\x1b[2;5~", TB_KEY_INSERT, TB_MOD_CTRL }, 1190 {"\x1b[2;6~", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_SHIFT }, 1191 {"\x1b[2;7~", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_ALT }, 1192 {"\x1b[2;8~", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1193 1194 {"\x1b[3;2~", TB_KEY_DELETE, TB_MOD_SHIFT }, 1195 {"\x1b[3;3~", TB_KEY_DELETE, TB_MOD_ALT }, 1196 {"\x1b[3;4~", TB_KEY_DELETE, TB_MOD_ALT | TB_MOD_SHIFT }, 1197 {"\x1b[3;5~", TB_KEY_DELETE, TB_MOD_CTRL }, 1198 {"\x1b[3;6~", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_SHIFT }, 1199 {"\x1b[3;7~", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_ALT }, 1200 {"\x1b[3;8~", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1201 1202 {"\x1b[5;2~", TB_KEY_PGUP, TB_MOD_SHIFT }, 1203 {"\x1b[5;3~", TB_KEY_PGUP, TB_MOD_ALT }, 1204 {"\x1b[5;4~", TB_KEY_PGUP, TB_MOD_ALT | TB_MOD_SHIFT }, 1205 {"\x1b[5;5~", TB_KEY_PGUP, TB_MOD_CTRL }, 1206 {"\x1b[5;6~", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_SHIFT }, 1207 {"\x1b[5;7~", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_ALT }, 1208 {"\x1b[5;8~", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1209 1210 {"\x1b[6;2~", TB_KEY_PGDN, TB_MOD_SHIFT }, 1211 {"\x1b[6;3~", TB_KEY_PGDN, TB_MOD_ALT }, 1212 {"\x1b[6;4~", TB_KEY_PGDN, TB_MOD_ALT | TB_MOD_SHIFT }, 1213 {"\x1b[6;5~", TB_KEY_PGDN, TB_MOD_CTRL }, 1214 {"\x1b[6;6~", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_SHIFT }, 1215 {"\x1b[6;7~", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_ALT }, 1216 {"\x1b[6;8~", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1217 1218 {"\x1b[1;2P", TB_KEY_F1, TB_MOD_SHIFT }, 1219 {"\x1b[1;3P", TB_KEY_F1, TB_MOD_ALT }, 1220 {"\x1b[1;4P", TB_KEY_F1, TB_MOD_ALT | TB_MOD_SHIFT }, 1221 {"\x1b[1;5P", TB_KEY_F1, TB_MOD_CTRL }, 1222 {"\x1b[1;6P", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_SHIFT }, 1223 {"\x1b[1;7P", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_ALT }, 1224 {"\x1b[1;8P", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1225 1226 {"\x1b[1;2Q", TB_KEY_F2, TB_MOD_SHIFT }, 1227 {"\x1b[1;3Q", TB_KEY_F2, TB_MOD_ALT }, 1228 {"\x1b[1;4Q", TB_KEY_F2, TB_MOD_ALT | TB_MOD_SHIFT }, 1229 {"\x1b[1;5Q", TB_KEY_F2, TB_MOD_CTRL }, 1230 {"\x1b[1;6Q", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_SHIFT }, 1231 {"\x1b[1;7Q", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_ALT }, 1232 {"\x1b[1;8Q", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1233 1234 {"\x1b[1;2R", TB_KEY_F3, TB_MOD_SHIFT }, 1235 {"\x1b[1;3R", TB_KEY_F3, TB_MOD_ALT }, 1236 {"\x1b[1;4R", TB_KEY_F3, TB_MOD_ALT | TB_MOD_SHIFT }, 1237 {"\x1b[1;5R", TB_KEY_F3, TB_MOD_CTRL }, 1238 {"\x1b[1;6R", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_SHIFT }, 1239 {"\x1b[1;7R", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_ALT }, 1240 {"\x1b[1;8R", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1241 1242 {"\x1b[1;2S", TB_KEY_F4, TB_MOD_SHIFT }, 1243 {"\x1b[1;3S", TB_KEY_F4, TB_MOD_ALT }, 1244 {"\x1b[1;4S", TB_KEY_F4, TB_MOD_ALT | TB_MOD_SHIFT }, 1245 {"\x1b[1;5S", TB_KEY_F4, TB_MOD_CTRL }, 1246 {"\x1b[1;6S", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_SHIFT }, 1247 {"\x1b[1;7S", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_ALT }, 1248 {"\x1b[1;8S", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1249 1250 {"\x1b[15;2~", TB_KEY_F5, TB_MOD_SHIFT }, 1251 {"\x1b[15;3~", TB_KEY_F5, TB_MOD_ALT }, 1252 {"\x1b[15;4~", TB_KEY_F5, TB_MOD_ALT | TB_MOD_SHIFT }, 1253 {"\x1b[15;5~", TB_KEY_F5, TB_MOD_CTRL }, 1254 {"\x1b[15;6~", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_SHIFT }, 1255 {"\x1b[15;7~", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_ALT }, 1256 {"\x1b[15;8~", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1257 1258 {"\x1b[17;2~", TB_KEY_F6, TB_MOD_SHIFT }, 1259 {"\x1b[17;3~", TB_KEY_F6, TB_MOD_ALT }, 1260 {"\x1b[17;4~", TB_KEY_F6, TB_MOD_ALT | TB_MOD_SHIFT }, 1261 {"\x1b[17;5~", TB_KEY_F6, TB_MOD_CTRL }, 1262 {"\x1b[17;6~", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_SHIFT }, 1263 {"\x1b[17;7~", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_ALT }, 1264 {"\x1b[17;8~", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1265 1266 {"\x1b[18;2~", TB_KEY_F7, TB_MOD_SHIFT }, 1267 {"\x1b[18;3~", TB_KEY_F7, TB_MOD_ALT }, 1268 {"\x1b[18;4~", TB_KEY_F7, TB_MOD_ALT | TB_MOD_SHIFT }, 1269 {"\x1b[18;5~", TB_KEY_F7, TB_MOD_CTRL }, 1270 {"\x1b[18;6~", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_SHIFT }, 1271 {"\x1b[18;7~", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_ALT }, 1272 {"\x1b[18;8~", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1273 1274 {"\x1b[19;2~", TB_KEY_F8, TB_MOD_SHIFT }, 1275 {"\x1b[19;3~", TB_KEY_F8, TB_MOD_ALT }, 1276 {"\x1b[19;4~", TB_KEY_F8, TB_MOD_ALT | TB_MOD_SHIFT }, 1277 {"\x1b[19;5~", TB_KEY_F8, TB_MOD_CTRL }, 1278 {"\x1b[19;6~", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_SHIFT }, 1279 {"\x1b[19;7~", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_ALT }, 1280 {"\x1b[19;8~", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1281 1282 {"\x1b[20;2~", TB_KEY_F9, TB_MOD_SHIFT }, 1283 {"\x1b[20;3~", TB_KEY_F9, TB_MOD_ALT }, 1284 {"\x1b[20;4~", TB_KEY_F9, TB_MOD_ALT | TB_MOD_SHIFT }, 1285 {"\x1b[20;5~", TB_KEY_F9, TB_MOD_CTRL }, 1286 {"\x1b[20;6~", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_SHIFT }, 1287 {"\x1b[20;7~", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_ALT }, 1288 {"\x1b[20;8~", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1289 1290 {"\x1b[21;2~", TB_KEY_F10, TB_MOD_SHIFT }, 1291 {"\x1b[21;3~", TB_KEY_F10, TB_MOD_ALT }, 1292 {"\x1b[21;4~", TB_KEY_F10, TB_MOD_ALT | TB_MOD_SHIFT }, 1293 {"\x1b[21;5~", TB_KEY_F10, TB_MOD_CTRL }, 1294 {"\x1b[21;6~", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_SHIFT }, 1295 {"\x1b[21;7~", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_ALT }, 1296 {"\x1b[21;8~", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1297 1298 {"\x1b[23;2~", TB_KEY_F11, TB_MOD_SHIFT }, 1299 {"\x1b[23;3~", TB_KEY_F11, TB_MOD_ALT }, 1300 {"\x1b[23;4~", TB_KEY_F11, TB_MOD_ALT | TB_MOD_SHIFT }, 1301 {"\x1b[23;5~", TB_KEY_F11, TB_MOD_CTRL }, 1302 {"\x1b[23;6~", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_SHIFT }, 1303 {"\x1b[23;7~", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_ALT }, 1304 {"\x1b[23;8~", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1305 1306 {"\x1b[24;2~", TB_KEY_F12, TB_MOD_SHIFT }, 1307 {"\x1b[24;3~", TB_KEY_F12, TB_MOD_ALT }, 1308 {"\x1b[24;4~", TB_KEY_F12, TB_MOD_ALT | TB_MOD_SHIFT }, 1309 {"\x1b[24;5~", TB_KEY_F12, TB_MOD_CTRL }, 1310 {"\x1b[24;6~", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_SHIFT }, 1311 {"\x1b[24;7~", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_ALT }, 1312 {"\x1b[24;8~", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1313 1314 // rxvt arrows 1315 {"\x1b[a", TB_KEY_ARROW_UP, TB_MOD_SHIFT }, 1316 {"\x1b\x1b[A", TB_KEY_ARROW_UP, TB_MOD_ALT }, 1317 {"\x1b\x1b[a", TB_KEY_ARROW_UP, TB_MOD_ALT | TB_MOD_SHIFT }, 1318 {"\x1bOa", TB_KEY_ARROW_UP, TB_MOD_CTRL }, 1319 {"\x1b\x1bOa", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT }, 1320 1321 {"\x1b[b", TB_KEY_ARROW_DOWN, TB_MOD_SHIFT }, 1322 {"\x1b\x1b[B", TB_KEY_ARROW_DOWN, TB_MOD_ALT }, 1323 {"\x1b\x1b[b", TB_KEY_ARROW_DOWN, TB_MOD_ALT | TB_MOD_SHIFT }, 1324 {"\x1bOb", TB_KEY_ARROW_DOWN, TB_MOD_CTRL }, 1325 {"\x1b\x1bOb", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_ALT }, 1326 1327 {"\x1b[c", TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT }, 1328 {"\x1b\x1b[C", TB_KEY_ARROW_RIGHT, TB_MOD_ALT }, 1329 {"\x1b\x1b[c", TB_KEY_ARROW_RIGHT, TB_MOD_ALT | TB_MOD_SHIFT }, 1330 {"\x1bOc", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL }, 1331 {"\x1b\x1bOc", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT }, 1332 1333 {"\x1b[d", TB_KEY_ARROW_LEFT, TB_MOD_SHIFT }, 1334 {"\x1b\x1b[D", TB_KEY_ARROW_LEFT, TB_MOD_ALT }, 1335 {"\x1b\x1b[d", TB_KEY_ARROW_LEFT, TB_MOD_ALT | TB_MOD_SHIFT }, 1336 {"\x1bOd", TB_KEY_ARROW_LEFT, TB_MOD_CTRL }, 1337 {"\x1b\x1bOd", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_ALT }, 1338 1339 // rxvt keys 1340 {"\x1b[7$", TB_KEY_HOME, TB_MOD_SHIFT }, 1341 {"\x1b\x1b[7~", TB_KEY_HOME, TB_MOD_ALT }, 1342 {"\x1b\x1b[7$", TB_KEY_HOME, TB_MOD_ALT | TB_MOD_SHIFT }, 1343 {"\x1b[7^", TB_KEY_HOME, TB_MOD_CTRL }, 1344 {"\x1b[7@", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_SHIFT }, 1345 {"\x1b\x1b[7^", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_ALT }, 1346 {"\x1b\x1b[7@", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1347 1348 {"\x1b\x1b[8~", TB_KEY_END, TB_MOD_ALT }, 1349 {"\x1b\x1b[8$", TB_KEY_END, TB_MOD_ALT | TB_MOD_SHIFT }, 1350 {"\x1b[8^", TB_KEY_END, TB_MOD_CTRL }, 1351 {"\x1b\x1b[8^", TB_KEY_END, TB_MOD_CTRL | TB_MOD_ALT }, 1352 {"\x1b\x1b[8@", TB_KEY_END, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1353 {"\x1b[8@", TB_KEY_END, TB_MOD_CTRL | TB_MOD_SHIFT }, 1354 {"\x1b[8$", TB_KEY_END, TB_MOD_SHIFT }, 1355 1356 {"\x1b\x1b[2~", TB_KEY_INSERT, TB_MOD_ALT }, 1357 {"\x1b\x1b[2$", TB_KEY_INSERT, TB_MOD_ALT | TB_MOD_SHIFT }, 1358 {"\x1b[2^", TB_KEY_INSERT, TB_MOD_CTRL }, 1359 {"\x1b\x1b[2^", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_ALT }, 1360 {"\x1b\x1b[2@", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1361 {"\x1b[2@", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_SHIFT }, 1362 {"\x1b[2$", TB_KEY_INSERT, TB_MOD_SHIFT }, 1363 1364 {"\x1b\x1b[3~", TB_KEY_DELETE, TB_MOD_ALT }, 1365 {"\x1b\x1b[3$", TB_KEY_DELETE, TB_MOD_ALT | TB_MOD_SHIFT }, 1366 {"\x1b[3^", TB_KEY_DELETE, TB_MOD_CTRL }, 1367 {"\x1b\x1b[3^", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_ALT }, 1368 {"\x1b\x1b[3@", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1369 {"\x1b[3@", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_SHIFT }, 1370 {"\x1b[3$", TB_KEY_DELETE, TB_MOD_SHIFT }, 1371 1372 {"\x1b\x1b[5~", TB_KEY_PGUP, TB_MOD_ALT }, 1373 {"\x1b\x1b[5$", TB_KEY_PGUP, TB_MOD_ALT | TB_MOD_SHIFT }, 1374 {"\x1b[5^", TB_KEY_PGUP, TB_MOD_CTRL }, 1375 {"\x1b\x1b[5^", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_ALT }, 1376 {"\x1b\x1b[5@", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1377 {"\x1b[5@", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_SHIFT }, 1378 {"\x1b[5$", TB_KEY_PGUP, TB_MOD_SHIFT }, 1379 1380 {"\x1b\x1b[6~", TB_KEY_PGDN, TB_MOD_ALT }, 1381 {"\x1b\x1b[6$", TB_KEY_PGDN, TB_MOD_ALT | TB_MOD_SHIFT }, 1382 {"\x1b[6^", TB_KEY_PGDN, TB_MOD_CTRL }, 1383 {"\x1b\x1b[6^", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_ALT }, 1384 {"\x1b\x1b[6@", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1385 {"\x1b[6@", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_SHIFT }, 1386 {"\x1b[6$", TB_KEY_PGDN, TB_MOD_SHIFT }, 1387 1388 {"\x1b\x1b[11~", TB_KEY_F1, TB_MOD_ALT }, 1389 {"\x1b\x1b[23~", TB_KEY_F1, TB_MOD_ALT | TB_MOD_SHIFT }, 1390 {"\x1b[11^", TB_KEY_F1, TB_MOD_CTRL }, 1391 {"\x1b\x1b[11^", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_ALT }, 1392 {"\x1b\x1b[23^", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1393 {"\x1b[23^", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_SHIFT }, 1394 {"\x1b[23~", TB_KEY_F1, TB_MOD_SHIFT }, 1395 1396 {"\x1b\x1b[12~", TB_KEY_F2, TB_MOD_ALT }, 1397 {"\x1b\x1b[24~", TB_KEY_F2, TB_MOD_ALT | TB_MOD_SHIFT }, 1398 {"\x1b[12^", TB_KEY_F2, TB_MOD_CTRL }, 1399 {"\x1b\x1b[12^", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_ALT }, 1400 {"\x1b\x1b[24^", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1401 {"\x1b[24^", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_SHIFT }, 1402 {"\x1b[24~", TB_KEY_F2, TB_MOD_SHIFT }, 1403 1404 {"\x1b\x1b[13~", TB_KEY_F3, TB_MOD_ALT }, 1405 {"\x1b\x1b[25~", TB_KEY_F3, TB_MOD_ALT | TB_MOD_SHIFT }, 1406 {"\x1b[13^", TB_KEY_F3, TB_MOD_CTRL }, 1407 {"\x1b\x1b[13^", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_ALT }, 1408 {"\x1b\x1b[25^", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1409 {"\x1b[25^", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_SHIFT }, 1410 {"\x1b[25~", TB_KEY_F3, TB_MOD_SHIFT }, 1411 1412 {"\x1b\x1b[14~", TB_KEY_F4, TB_MOD_ALT }, 1413 {"\x1b\x1b[26~", TB_KEY_F4, TB_MOD_ALT | TB_MOD_SHIFT }, 1414 {"\x1b[14^", TB_KEY_F4, TB_MOD_CTRL }, 1415 {"\x1b\x1b[14^", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_ALT }, 1416 {"\x1b\x1b[26^", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1417 {"\x1b[26^", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_SHIFT }, 1418 {"\x1b[26~", TB_KEY_F4, TB_MOD_SHIFT }, 1419 1420 {"\x1b\x1b[15~", TB_KEY_F5, TB_MOD_ALT }, 1421 {"\x1b\x1b[28~", TB_KEY_F5, TB_MOD_ALT | TB_MOD_SHIFT }, 1422 {"\x1b[15^", TB_KEY_F5, TB_MOD_CTRL }, 1423 {"\x1b\x1b[15^", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_ALT }, 1424 {"\x1b\x1b[28^", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1425 {"\x1b[28^", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_SHIFT }, 1426 {"\x1b[28~", TB_KEY_F5, TB_MOD_SHIFT }, 1427 1428 {"\x1b\x1b[17~", TB_KEY_F6, TB_MOD_ALT }, 1429 {"\x1b\x1b[29~", TB_KEY_F6, TB_MOD_ALT | TB_MOD_SHIFT }, 1430 {"\x1b[17^", TB_KEY_F6, TB_MOD_CTRL }, 1431 {"\x1b\x1b[17^", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_ALT }, 1432 {"\x1b\x1b[29^", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1433 {"\x1b[29^", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_SHIFT }, 1434 {"\x1b[29~", TB_KEY_F6, TB_MOD_SHIFT }, 1435 1436 {"\x1b\x1b[18~", TB_KEY_F7, TB_MOD_ALT }, 1437 {"\x1b\x1b[31~", TB_KEY_F7, TB_MOD_ALT | TB_MOD_SHIFT }, 1438 {"\x1b[18^", TB_KEY_F7, TB_MOD_CTRL }, 1439 {"\x1b\x1b[18^", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_ALT }, 1440 {"\x1b\x1b[31^", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1441 {"\x1b[31^", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_SHIFT }, 1442 {"\x1b[31~", TB_KEY_F7, TB_MOD_SHIFT }, 1443 1444 {"\x1b\x1b[19~", TB_KEY_F8, TB_MOD_ALT }, 1445 {"\x1b\x1b[32~", TB_KEY_F8, TB_MOD_ALT | TB_MOD_SHIFT }, 1446 {"\x1b[19^", TB_KEY_F8, TB_MOD_CTRL }, 1447 {"\x1b\x1b[19^", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_ALT }, 1448 {"\x1b\x1b[32^", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1449 {"\x1b[32^", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_SHIFT }, 1450 {"\x1b[32~", TB_KEY_F8, TB_MOD_SHIFT }, 1451 1452 {"\x1b\x1b[20~", TB_KEY_F9, TB_MOD_ALT }, 1453 {"\x1b\x1b[33~", TB_KEY_F9, TB_MOD_ALT | TB_MOD_SHIFT }, 1454 {"\x1b[20^", TB_KEY_F9, TB_MOD_CTRL }, 1455 {"\x1b\x1b[20^", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_ALT }, 1456 {"\x1b\x1b[33^", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1457 {"\x1b[33^", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_SHIFT }, 1458 {"\x1b[33~", TB_KEY_F9, TB_MOD_SHIFT }, 1459 1460 {"\x1b\x1b[21~", TB_KEY_F10, TB_MOD_ALT }, 1461 {"\x1b\x1b[34~", TB_KEY_F10, TB_MOD_ALT | TB_MOD_SHIFT }, 1462 {"\x1b[21^", TB_KEY_F10, TB_MOD_CTRL }, 1463 {"\x1b\x1b[21^", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_ALT }, 1464 {"\x1b\x1b[34^", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1465 {"\x1b[34^", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_SHIFT }, 1466 {"\x1b[34~", TB_KEY_F10, TB_MOD_SHIFT }, 1467 1468 {"\x1b\x1b[23~", TB_KEY_F11, TB_MOD_ALT }, 1469 {"\x1b\x1b[23$", TB_KEY_F11, TB_MOD_ALT | TB_MOD_SHIFT }, 1470 {"\x1b[23^", TB_KEY_F11, TB_MOD_CTRL }, 1471 {"\x1b\x1b[23^", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_ALT }, 1472 {"\x1b\x1b[23@", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1473 {"\x1b[23@", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_SHIFT }, 1474 {"\x1b[23$", TB_KEY_F11, TB_MOD_SHIFT }, 1475 1476 {"\x1b\x1b[24~", TB_KEY_F12, TB_MOD_ALT }, 1477 {"\x1b\x1b[24$", TB_KEY_F12, TB_MOD_ALT | TB_MOD_SHIFT }, 1478 {"\x1b[24^", TB_KEY_F12, TB_MOD_CTRL }, 1479 {"\x1b\x1b[24^", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_ALT }, 1480 {"\x1b\x1b[24@", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT}, 1481 {"\x1b[24@", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_SHIFT }, 1482 {"\x1b[24$", TB_KEY_F12, TB_MOD_SHIFT }, 1483 1484 // linux console/putty arrows 1485 {"\x1b[A", TB_KEY_ARROW_UP, TB_MOD_SHIFT }, 1486 {"\x1b[B", TB_KEY_ARROW_DOWN, TB_MOD_SHIFT }, 1487 {"\x1b[C", TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT }, 1488 {"\x1b[D", TB_KEY_ARROW_LEFT, TB_MOD_SHIFT }, 1489 1490 // more putty arrows 1491 {"\x1bOA", TB_KEY_ARROW_UP, TB_MOD_CTRL }, 1492 {"\x1b\x1bOA", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT }, 1493 {"\x1bOB", TB_KEY_ARROW_DOWN, TB_MOD_CTRL }, 1494 {"\x1b\x1bOB", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_ALT }, 1495 {"\x1bOC", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL }, 1496 {"\x1b\x1bOC", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT }, 1497 {"\x1bOD", TB_KEY_ARROW_LEFT, TB_MOD_CTRL }, 1498 {"\x1b\x1bOD", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_ALT }, 1499 1500 {NULL, 0, 0 }, 1501 }; 1502 1503 static const unsigned char utf8_length[256] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1504 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, 1505 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, 1506 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, 1507 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, 1508 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, 1509 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, 1510 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, 1511 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, 1512 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, 1513 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1}; 1514 1515 static const unsigned char utf8_mask[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01}; 1516 1517 static int tb_reset(void); 1518 static int tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg, 1519 size_t *out_w, const char *fmt, va_list vl); 1520 static int init_term_attrs(void); 1521 static int init_term_caps(void); 1522 static int init_cap_trie(void); 1523 static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod); 1524 static int cap_trie_find(const char *buf, size_t nbuf, struct cap_trie_t **last, 1525 size_t *depth); 1526 static int cap_trie_deinit(struct cap_trie_t *node); 1527 static int init_resize_handler(void); 1528 static int send_init_escape_codes(void); 1529 static int send_clear(void); 1530 static int update_term_size(void); 1531 static int update_term_size_via_esc(void); 1532 static int init_cellbuf(void); 1533 static int tb_deinit(void); 1534 static int load_terminfo(void); 1535 static int load_terminfo_from_path(const char *path, const char *term); 1536 static int read_terminfo_path(const char *path); 1537 static int parse_terminfo_caps(void); 1538 static int load_builtin_caps(void); 1539 static const char *get_terminfo_string(int16_t str_offsets_pos, 1540 int16_t str_offsets_len, int16_t str_table_pos, int16_t str_table_len, 1541 int16_t str_index); 1542 static int wait_event(struct tb_event *event, int timeout); 1543 static int extract_event(struct tb_event *event); 1544 static int extract_esc(struct tb_event *event); 1545 static int extract_esc_user(struct tb_event *event, int is_post); 1546 static int extract_esc_cap(struct tb_event *event); 1547 static int extract_esc_mouse(struct tb_event *event); 1548 static int resize_cellbufs(void); 1549 static void handle_resize(int sig); 1550 static int send_attr(uintattr_t fg, uintattr_t bg); 1551 static int send_sgr(uint32_t fg, uint32_t bg, int fg_is_default, 1552 int bg_is_default); 1553 static int send_cursor_if(int x, int y); 1554 static int send_char(int x, int y, uint32_t ch); 1555 static int send_cluster(int x, int y, uint32_t *ch, size_t nch); 1556 static int convert_num(uint32_t num, char *buf); 1557 static int cell_cmp(struct tb_cell *a, struct tb_cell *b); 1558 static int cell_copy(struct tb_cell *dst, struct tb_cell *src); 1559 static int cell_set(struct tb_cell *cell, uint32_t *ch, size_t nch, 1560 uintattr_t fg, uintattr_t bg); 1561 static int cell_reserve_ech(struct tb_cell *cell, size_t n); 1562 static int cell_free(struct tb_cell *cell); 1563 static int cellbuf_init(struct cellbuf_t *c, int w, int h); 1564 static int cellbuf_free(struct cellbuf_t *c); 1565 static int cellbuf_clear(struct cellbuf_t *c); 1566 static int cellbuf_get(struct cellbuf_t *c, int x, int y, struct tb_cell **out); 1567 static int cellbuf_in_bounds(struct cellbuf_t *c, int x, int y); 1568 static int cellbuf_resize(struct cellbuf_t *c, int w, int h); 1569 static int bytebuf_puts(struct bytebuf_t *b, const char *str); 1570 static int bytebuf_nputs(struct bytebuf_t *b, const char *str, size_t nstr); 1571 static int bytebuf_shift(struct bytebuf_t *b, size_t n); 1572 static int bytebuf_flush(struct bytebuf_t *b, int fd); 1573 static int bytebuf_reserve(struct bytebuf_t *b, size_t sz); 1574 static int bytebuf_free(struct bytebuf_t *b); 1575 1576 int tb_init(void) { 1577 return tb_init_file("/dev/tty"); 1578 } 1579 1580 int tb_init_file(const char *path) { 1581 if (global.initialized) return TB_ERR_INIT_ALREADY; 1582 int ttyfd = open(path, O_RDWR); 1583 if (ttyfd < 0) { 1584 global.last_errno = errno; 1585 return TB_ERR_INIT_OPEN; 1586 } 1587 global.ttyfd_open = 1; 1588 return tb_init_fd(ttyfd); 1589 } 1590 1591 int tb_init_fd(int ttyfd) { 1592 return tb_init_rwfd(ttyfd, ttyfd); 1593 } 1594 1595 int tb_init_rwfd(int rfd, int wfd) { 1596 int rv; 1597 1598 tb_reset(); 1599 global.ttyfd = rfd == wfd && isatty(rfd) ? rfd : -1; 1600 global.rfd = rfd; 1601 global.wfd = wfd; 1602 1603 do { 1604 if_err_break(rv, init_term_attrs()); 1605 if_err_break(rv, init_term_caps()); 1606 if_err_break(rv, init_cap_trie()); 1607 if_err_break(rv, init_resize_handler()); 1608 if_err_break(rv, send_init_escape_codes()); 1609 if_err_break(rv, send_clear()); 1610 if_err_break(rv, update_term_size()); 1611 if_err_break(rv, init_cellbuf()); 1612 global.initialized = 1; 1613 } while (0); 1614 1615 if (rv != TB_OK) { 1616 tb_deinit(); 1617 } 1618 1619 return rv; 1620 } 1621 1622 int tb_shutdown(void) { 1623 if_not_init_return(); 1624 tb_deinit(); 1625 return TB_OK; 1626 } 1627 1628 int tb_width(void) { 1629 if_not_init_return(); 1630 return global.width; 1631 } 1632 1633 int tb_height(void) { 1634 if_not_init_return(); 1635 return global.height; 1636 } 1637 1638 int tb_clear(void) { 1639 if_not_init_return(); 1640 return cellbuf_clear(&global.back); 1641 } 1642 1643 int tb_set_clear_attrs(uintattr_t fg, uintattr_t bg) { 1644 if_not_init_return(); 1645 global.fg = fg; 1646 global.bg = bg; 1647 return TB_OK; 1648 } 1649 1650 int tb_present(void) { 1651 if_not_init_return(); 1652 1653 int rv; 1654 1655 // TODO: Assert global.back.(width,height) == global.front.(width,height) 1656 1657 global.last_x = -1; 1658 global.last_y = -1; 1659 1660 int x, y, i; 1661 for (y = 0; y < global.front.height; y++) { 1662 for (x = 0; x < global.front.width;) { 1663 struct tb_cell *back, *front; 1664 if_err_return(rv, cellbuf_get(&global.back, x, y, &back)); 1665 if_err_return(rv, cellbuf_get(&global.front, x, y, &front)); 1666 1667 int w; 1668 { 1669 #ifdef TB_OPT_EGC 1670 if (back->nech > 0) 1671 w = wcswidth((wchar_t *)back->ech, back->nech); 1672 else 1673 #endif 1674 // wcwidth simply returns -1 on overflow of wchar_t 1675 w = wcwidth((wchar_t)back->ch); 1676 } 1677 if (w < 1) w = 1; 1678 1679 if (cell_cmp(back, front) != 0) { 1680 cell_copy(front, back); 1681 1682 send_attr(back->fg, back->bg); 1683 if (w > 1 && x >= global.front.width - (w - 1)) { 1684 // Not enough room for wide char, send spaces 1685 for (i = x; i < global.front.width; i++) { 1686 send_char(i, y, ' '); 1687 } 1688 } else { 1689 { 1690 #ifdef TB_OPT_EGC 1691 if (back->nech > 0) 1692 send_cluster(x, y, back->ech, back->nech); 1693 else 1694 #endif 1695 send_char(x, y, back->ch); 1696 } 1697 1698 // When wcwidth>1, we need to advance the cursor by more 1699 // than 1, thereby skipping some cells. Set these skipped 1700 // cells to an invalid codepoint in the front buffer, so 1701 // that if this cell is later replaced by a wcwidth==1 char, 1702 // we'll get a cell_cmp diff for the skipped cells and 1703 // properly re-render. 1704 for (i = 1; i < w; i++) { 1705 struct tb_cell *front_wide; 1706 uint32_t invalid = -1; 1707 if_err_return(rv, 1708 cellbuf_get(&global.front, x + i, y, &front_wide)); 1709 if_err_return(rv, 1710 cell_set(front_wide, &invalid, 1, -1, -1)); 1711 } 1712 } 1713 } 1714 x += w; 1715 } 1716 } 1717 1718 if_err_return(rv, send_cursor_if(global.cursor_x, global.cursor_y)); 1719 if_err_return(rv, bytebuf_flush(&global.out, global.wfd)); 1720 1721 return TB_OK; 1722 } 1723 1724 int tb_invalidate(void) { 1725 int rv; 1726 if_not_init_return(); 1727 if_err_return(rv, resize_cellbufs()); 1728 return TB_OK; 1729 } 1730 1731 int tb_set_cursor(int cx, int cy) { 1732 if_not_init_return(); 1733 int rv; 1734 if (cx < 0) cx = 0; 1735 if (cy < 0) cy = 0; 1736 if (global.cursor_x == -1) { 1737 if_err_return(rv, 1738 bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR])); 1739 } 1740 if_err_return(rv, send_cursor_if(cx, cy)); 1741 global.cursor_x = cx; 1742 global.cursor_y = cy; 1743 return TB_OK; 1744 } 1745 1746 int tb_hide_cursor(void) { 1747 if_not_init_return(); 1748 int rv; 1749 if (global.cursor_x >= 0) { 1750 if_err_return(rv, 1751 bytebuf_puts(&global.out, global.caps[TB_CAP_HIDE_CURSOR])); 1752 } 1753 global.cursor_x = -1; 1754 global.cursor_y = -1; 1755 return TB_OK; 1756 } 1757 1758 int tb_set_cell(int x, int y, uint32_t ch, uintattr_t fg, uintattr_t bg) { 1759 return tb_set_cell_ex(x, y, &ch, 1, fg, bg); 1760 } 1761 1762 int tb_set_cell_ex(int x, int y, uint32_t *ch, size_t nch, uintattr_t fg, 1763 uintattr_t bg) { 1764 if_not_init_return(); 1765 int rv; 1766 struct tb_cell *cell; 1767 if_err_return(rv, cellbuf_get(&global.back, x, y, &cell)); 1768 if_err_return(rv, cell_set(cell, ch, nch, fg, bg)); 1769 return TB_OK; 1770 } 1771 1772 int tb_extend_cell(int x, int y, uint32_t ch) { 1773 if_not_init_return(); 1774 #ifdef TB_OPT_EGC 1775 int rv; 1776 struct tb_cell *cell; 1777 size_t nech; 1778 if_err_return(rv, cellbuf_get(&global.back, x, y, &cell)); 1779 if (cell->nech > 0) { // append to ech 1780 nech = cell->nech + 1; 1781 if_err_return(rv, cell_reserve_ech(cell, nech)); 1782 cell->ech[nech - 1] = ch; 1783 } else { // make new ech 1784 nech = 2; 1785 if_err_return(rv, cell_reserve_ech(cell, nech)); 1786 cell->ech[0] = cell->ch; 1787 cell->ech[1] = ch; 1788 } 1789 cell->ech[nech] = '\0'; 1790 cell->nech = nech; 1791 return TB_OK; 1792 #else 1793 (void)x; 1794 (void)y; 1795 (void)ch; 1796 return TB_ERR; 1797 #endif 1798 } 1799 1800 int tb_set_input_mode(int mode) { 1801 if_not_init_return(); 1802 if (mode == TB_INPUT_CURRENT) { 1803 return global.input_mode; 1804 } 1805 1806 if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == 0) { 1807 mode |= TB_INPUT_ESC; 1808 } 1809 1810 if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == (TB_INPUT_ESC | TB_INPUT_ALT)) 1811 { 1812 mode &= ~TB_INPUT_ALT; 1813 } 1814 1815 if (mode & TB_INPUT_MOUSE) { 1816 bytebuf_puts(&global.out, TB_HARDCAP_ENTER_MOUSE); 1817 bytebuf_flush(&global.out, global.wfd); 1818 } else { 1819 bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE); 1820 bytebuf_flush(&global.out, global.wfd); 1821 } 1822 1823 global.input_mode = mode; 1824 return TB_OK; 1825 } 1826 1827 int tb_set_output_mode(int mode) { 1828 if_not_init_return(); 1829 switch (mode) { 1830 case TB_OUTPUT_CURRENT: 1831 return global.output_mode; 1832 case TB_OUTPUT_NORMAL: 1833 case TB_OUTPUT_256: 1834 case TB_OUTPUT_216: 1835 case TB_OUTPUT_GRAYSCALE: 1836 #if TB_OPT_ATTR_W >= 32 1837 case TB_OUTPUT_TRUECOLOR: 1838 #endif 1839 global.last_fg = ~global.fg; 1840 global.last_bg = ~global.bg; 1841 global.output_mode = mode; 1842 return TB_OK; 1843 } 1844 return TB_ERR; 1845 } 1846 1847 int tb_peek_event(struct tb_event *event, int timeout_ms) { 1848 if_not_init_return(); 1849 return wait_event(event, timeout_ms); 1850 } 1851 1852 int tb_poll_event(struct tb_event *event) { 1853 if_not_init_return(); 1854 return wait_event(event, -1); 1855 } 1856 1857 int tb_get_fds(int *ttyfd, int *resizefd) { 1858 if_not_init_return(); 1859 1860 *ttyfd = global.rfd; 1861 *resizefd = global.resize_pipefd[0]; 1862 1863 return TB_OK; 1864 } 1865 1866 int tb_print(int x, int y, uintattr_t fg, uintattr_t bg, const char *str) { 1867 return tb_print_ex(x, y, fg, bg, NULL, str); 1868 } 1869 1870 int tb_print_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w, 1871 const char *str) { 1872 int rv, w, ix; 1873 uint32_t uni; 1874 1875 if_not_init_return(); 1876 1877 if (!cellbuf_in_bounds(&global.back, x, y)) { 1878 return TB_ERR_OUT_OF_BOUNDS; 1879 } 1880 1881 ix = x; 1882 if (out_w) *out_w = 0; 1883 1884 while (*str) { 1885 rv = tb_utf8_char_to_unicode(&uni, str); 1886 1887 if (rv < 0) { 1888 uni = 0xfffd; // replace invalid UTF-8 char with U+FFFD 1889 str += rv * -1; 1890 } else if (rv > 0) { 1891 str += rv; 1892 } else { 1893 break; // shouldn't get here 1894 } 1895 1896 if (uni == '\n') { // TODO: \r, \t, \v, \f, etc? 1897 x = ix; 1898 y += 1; 1899 continue; 1900 } else if (!iswprint((wint_t)uni)) { 1901 uni = 0xfffd; // replace non-printable with U+FFFD 1902 } 1903 1904 w = wcwidth((wchar_t)uni); 1905 if (w < 0) { 1906 return TB_ERR; // shouldn't happen if iswprint 1907 } else if (w == 0) { // combining character 1908 if (cellbuf_in_bounds(&global.back, x - 1, y)) { 1909 if_err_return(rv, tb_extend_cell(x - 1, y, uni)); 1910 } 1911 } else { 1912 if (cellbuf_in_bounds(&global.back, x, y)) { 1913 if_err_return(rv, tb_set_cell(x, y, uni, fg, bg)); 1914 } 1915 } 1916 1917 x += w; 1918 if (out_w) *out_w += w; 1919 } 1920 1921 return TB_OK; 1922 } 1923 1924 int tb_printf(int x, int y, uintattr_t fg, uintattr_t bg, const char *fmt, 1925 ...) { 1926 int rv; 1927 va_list vl; 1928 va_start(vl, fmt); 1929 rv = tb_printf_inner(x, y, fg, bg, NULL, fmt, vl); 1930 va_end(vl); 1931 return rv; 1932 } 1933 1934 int tb_printf_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w, 1935 const char *fmt, ...) { 1936 int rv; 1937 va_list vl; 1938 va_start(vl, fmt); 1939 rv = tb_printf_inner(x, y, fg, bg, out_w, fmt, vl); 1940 va_end(vl); 1941 return rv; 1942 } 1943 1944 int tb_send(const char *buf, size_t nbuf) { 1945 return bytebuf_nputs(&global.out, buf, nbuf); 1946 } 1947 1948 int tb_sendf(const char *fmt, ...) { 1949 int rv; 1950 char buf[TB_OPT_PRINTF_BUF]; 1951 va_list vl; 1952 va_start(vl, fmt); 1953 rv = vsnprintf(buf, sizeof(buf), fmt, vl); 1954 va_end(vl); 1955 if (rv < 0 || rv >= (int)sizeof(buf)) { 1956 return TB_ERR; 1957 } 1958 return tb_send(buf, (size_t)rv); 1959 } 1960 1961 int tb_set_func(int fn_type, int (*fn)(struct tb_event *, size_t *)) { 1962 switch (fn_type) { 1963 case TB_FUNC_EXTRACT_PRE: 1964 global.fn_extract_esc_pre = fn; 1965 return TB_OK; 1966 case TB_FUNC_EXTRACT_POST: 1967 global.fn_extract_esc_post = fn; 1968 return TB_OK; 1969 } 1970 return TB_ERR; 1971 } 1972 1973 struct tb_cell *tb_cell_buffer(void) { 1974 if (!global.initialized) return NULL; 1975 return global.back.cells; 1976 } 1977 1978 int tb_utf8_char_length(char c) { 1979 return utf8_length[(unsigned char)c]; 1980 } 1981 1982 int tb_utf8_char_to_unicode(uint32_t *out, const char *c) { 1983 if (*c == '\0') return 0; 1984 1985 int i; 1986 unsigned char len = tb_utf8_char_length(*c); 1987 unsigned char mask = utf8_mask[len - 1]; 1988 uint32_t result = c[0] & mask; 1989 for (i = 1; i < len && c[i] != '\0'; ++i) { 1990 result <<= 6; 1991 result |= c[i] & 0x3f; 1992 } 1993 1994 if (i != len) return i * -1; 1995 1996 *out = result; 1997 return (int)len; 1998 } 1999 2000 int tb_utf8_unicode_to_char(char *out, uint32_t c) { 2001 int len = 0; 2002 int first; 2003 int i; 2004 2005 if (c < 0x80) { 2006 first = 0; 2007 len = 1; 2008 } else if (c < 0x800) { 2009 first = 0xc0; 2010 len = 2; 2011 } else if (c < 0x10000) { 2012 first = 0xe0; 2013 len = 3; 2014 } else if (c < 0x200000) { 2015 first = 0xf0; 2016 len = 4; 2017 } else if (c < 0x4000000) { 2018 first = 0xf8; 2019 len = 5; 2020 } else { 2021 first = 0xfc; 2022 len = 6; 2023 } 2024 2025 for (i = len - 1; i > 0; --i) { 2026 out[i] = (c & 0x3f) | 0x80; 2027 c >>= 6; 2028 } 2029 out[0] = c | first; 2030 out[len] = '\0'; 2031 2032 return len; 2033 } 2034 2035 int tb_last_errno(void) { 2036 return global.last_errno; 2037 } 2038 2039 const char *tb_strerror(int err) { 2040 switch (err) { 2041 case TB_OK: 2042 return "Success"; 2043 case TB_ERR_NEED_MORE: 2044 return "Not enough input"; 2045 case TB_ERR_INIT_ALREADY: 2046 return "Termbox initialized already"; 2047 case TB_ERR_MEM: 2048 return "Out of memory"; 2049 case TB_ERR_NO_EVENT: 2050 return "No event"; 2051 case TB_ERR_NO_TERM: 2052 return "No TERM in environment"; 2053 case TB_ERR_NOT_INIT: 2054 return "Termbox not initialized"; 2055 case TB_ERR_OUT_OF_BOUNDS: 2056 return "Out of bounds"; 2057 case TB_ERR_UNSUPPORTED_TERM: 2058 return "Unsupported terminal"; 2059 case TB_ERR_CAP_COLLISION: 2060 return "Termcaps collision"; 2061 case TB_ERR_RESIZE_SSCANF: 2062 return "Terminal width/height not received by sscanf() after " 2063 "resize"; 2064 case TB_ERR: 2065 case TB_ERR_INIT_OPEN: 2066 case TB_ERR_READ: 2067 case TB_ERR_RESIZE_IOCTL: 2068 case TB_ERR_RESIZE_PIPE: 2069 case TB_ERR_RESIZE_SIGACTION: 2070 case TB_ERR_POLL: 2071 case TB_ERR_TCGETATTR: 2072 case TB_ERR_TCSETATTR: 2073 case TB_ERR_RESIZE_WRITE: 2074 case TB_ERR_RESIZE_POLL: 2075 case TB_ERR_RESIZE_READ: 2076 default: 2077 strerror_r(global.last_errno, global.errbuf, sizeof(global.errbuf)); 2078 return (const char *)global.errbuf; 2079 } 2080 } 2081 2082 int tb_has_truecolor(void) { 2083 #if TB_OPT_ATTR_W >= 32 2084 return 1; 2085 #else 2086 return 0; 2087 #endif 2088 } 2089 2090 int tb_has_egc(void) { 2091 #ifdef TB_OPT_EGC 2092 return 1; 2093 #else 2094 return 0; 2095 #endif 2096 } 2097 2098 int tb_attr_width(void) { 2099 return TB_OPT_ATTR_W; 2100 } 2101 2102 const char *tb_version(void) { 2103 return TB_VERSION_STR; 2104 } 2105 2106 static int tb_reset(void) { 2107 int ttyfd_open = global.ttyfd_open; 2108 memset(&global, 0, sizeof(global)); 2109 global.ttyfd = -1; 2110 global.rfd = -1; 2111 global.wfd = -1; 2112 global.ttyfd_open = ttyfd_open; 2113 global.resize_pipefd[0] = -1; 2114 global.resize_pipefd[1] = -1; 2115 global.width = -1; 2116 global.height = -1; 2117 global.cursor_x = -1; 2118 global.cursor_y = -1; 2119 global.last_x = -1; 2120 global.last_y = -1; 2121 global.fg = TB_DEFAULT; 2122 global.bg = TB_DEFAULT; 2123 global.last_fg = ~global.fg; 2124 global.last_bg = ~global.bg; 2125 global.input_mode = TB_INPUT_ESC; 2126 global.output_mode = TB_OUTPUT_NORMAL; 2127 return TB_OK; 2128 } 2129 2130 static int init_term_attrs(void) { 2131 if (global.ttyfd < 0) { 2132 return TB_OK; 2133 } 2134 2135 if (tcgetattr(global.ttyfd, &global.orig_tios) != 0) { 2136 global.last_errno = errno; 2137 return TB_ERR_TCGETATTR; 2138 } 2139 2140 struct termios tios; 2141 memcpy(&tios, &global.orig_tios, sizeof(tios)); 2142 global.has_orig_tios = 1; 2143 2144 cfmakeraw(&tios); 2145 tios.c_cc[VMIN] = 1; 2146 tios.c_cc[VTIME] = 0; 2147 2148 if (tcsetattr(global.ttyfd, TCSAFLUSH, &tios) != 0) { 2149 global.last_errno = errno; 2150 return TB_ERR_TCSETATTR; 2151 } 2152 2153 return TB_OK; 2154 } 2155 2156 int tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w, 2157 const char *fmt, va_list vl) { 2158 int rv; 2159 char buf[TB_OPT_PRINTF_BUF]; 2160 rv = vsnprintf(buf, sizeof(buf), fmt, vl); 2161 if (rv < 0 || rv >= (int)sizeof(buf)) { 2162 return TB_ERR; 2163 } 2164 return tb_print_ex(x, y, fg, bg, out_w, buf); 2165 } 2166 2167 static int init_term_caps(void) { 2168 if (load_terminfo() == TB_OK) { 2169 return parse_terminfo_caps(); 2170 } 2171 return load_builtin_caps(); 2172 } 2173 2174 static int init_cap_trie(void) { 2175 int rv, i; 2176 2177 // Add caps from terminfo or built-in 2178 // 2179 // Collisions are expected as some terminfo entries have dupes. (For 2180 // example, att605-pc collides on TB_CAP_F4 and TB_CAP_DELETE.) First cap 2181 // in TB_CAP_* index order will win. 2182 // 2183 // TODO: Reorder TB_CAP_* so more critical caps come first. 2184 for (i = 0; i < TB_CAP__COUNT_KEYS; i++) { 2185 rv = cap_trie_add(global.caps[i], tb_key_i(i), 0); 2186 if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv; 2187 } 2188 2189 // Add built-in mod caps 2190 // 2191 // Collisions are OK here as well. This can happen if global.caps collides 2192 // with builtin_mod_caps. It is desirable to give precedence to global.caps 2193 // here. 2194 for (i = 0; builtin_mod_caps[i].cap != NULL; i++) { 2195 rv = cap_trie_add(builtin_mod_caps[i].cap, builtin_mod_caps[i].key, 2196 builtin_mod_caps[i].mod); 2197 if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv; 2198 } 2199 2200 return TB_OK; 2201 } 2202 2203 static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod) { 2204 struct cap_trie_t *next, *node = &global.cap_trie; 2205 size_t i, j; 2206 2207 if (!cap || strlen(cap) <= 0) return TB_OK; // Nothing to do for empty caps 2208 2209 for (i = 0; cap[i] != '\0'; i++) { 2210 char c = cap[i]; 2211 next = NULL; 2212 2213 // Check if c is already a child of node 2214 for (j = 0; j < node->nchildren; j++) { 2215 if (node->children[j].c == c) { 2216 next = &node->children[j]; 2217 break; 2218 } 2219 } 2220 if (!next) { 2221 // We need to add a new child to node 2222 node->nchildren += 1; 2223 node->children = 2224 tb_realloc(node->children, sizeof(*node) * node->nchildren); 2225 if (!node->children) { 2226 return TB_ERR_MEM; 2227 } 2228 next = &node->children[node->nchildren - 1]; 2229 memset(next, 0, sizeof(*next)); 2230 next->c = c; 2231 } 2232 2233 // Continue 2234 node = next; 2235 } 2236 2237 if (node->is_leaf) { 2238 // Already a leaf here 2239 return TB_ERR_CAP_COLLISION; 2240 } 2241 2242 node->is_leaf = 1; 2243 node->key = key; 2244 node->mod = mod; 2245 return TB_OK; 2246 } 2247 2248 static int cap_trie_find(const char *buf, size_t nbuf, struct cap_trie_t **last, 2249 size_t *depth) { 2250 struct cap_trie_t *next, *node = &global.cap_trie; 2251 size_t i, j; 2252 *last = node; 2253 *depth = 0; 2254 for (i = 0; i < nbuf; i++) { 2255 char c = buf[i]; 2256 next = NULL; 2257 2258 // Find c in node.children 2259 for (j = 0; j < node->nchildren; j++) { 2260 if (node->children[j].c == c) { 2261 next = &node->children[j]; 2262 break; 2263 } 2264 } 2265 if (!next) { 2266 // Not found 2267 return TB_OK; 2268 } 2269 node = next; 2270 *last = node; 2271 *depth += 1; 2272 if (node->is_leaf && node->nchildren < 1) { 2273 break; 2274 } 2275 } 2276 return TB_OK; 2277 } 2278 2279 static int cap_trie_deinit(struct cap_trie_t *node) { 2280 size_t j; 2281 for (j = 0; j < node->nchildren; j++) { 2282 cap_trie_deinit(&node->children[j]); 2283 } 2284 if (node->children) { 2285 tb_free(node->children); 2286 } 2287 memset(node, 0, sizeof(*node)); 2288 return TB_OK; 2289 } 2290 2291 static int init_resize_handler(void) { 2292 if (pipe(global.resize_pipefd) != 0) { 2293 global.last_errno = errno; 2294 return TB_ERR_RESIZE_PIPE; 2295 } 2296 2297 struct sigaction sa; 2298 memset(&sa, 0, sizeof(sa)); 2299 sa.sa_handler = handle_resize; 2300 if (sigaction(SIGWINCH, &sa, NULL) != 0) { 2301 global.last_errno = errno; 2302 return TB_ERR_RESIZE_SIGACTION; 2303 } 2304 2305 return TB_OK; 2306 } 2307 2308 static int send_init_escape_codes(void) { 2309 int rv; 2310 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_CA])); 2311 if_err_return(rv, 2312 bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_KEYPAD])); 2313 if_err_return(rv, 2314 bytebuf_puts(&global.out, global.caps[TB_CAP_HIDE_CURSOR])); 2315 return TB_OK; 2316 } 2317 2318 static int send_clear(void) { 2319 int rv; 2320 2321 if_err_return(rv, send_attr(global.fg, global.bg)); 2322 if_err_return(rv, 2323 bytebuf_puts(&global.out, global.caps[TB_CAP_CLEAR_SCREEN])); 2324 2325 if_err_return(rv, send_cursor_if(global.cursor_x, global.cursor_y)); 2326 if_err_return(rv, bytebuf_flush(&global.out, global.wfd)); 2327 2328 global.last_x = -1; 2329 global.last_y = -1; 2330 2331 return TB_OK; 2332 } 2333 2334 static int update_term_size(void) { 2335 int rv, ioctl_errno; 2336 2337 if (global.ttyfd < 0) { 2338 return TB_OK; 2339 } 2340 2341 struct winsize sz; 2342 memset(&sz, 0, sizeof(sz)); 2343 2344 // Try ioctl TIOCGWINSZ 2345 if (ioctl(global.ttyfd, TIOCGWINSZ, &sz) == 0) { 2346 global.width = sz.ws_col; 2347 global.height = sz.ws_row; 2348 return TB_OK; 2349 } 2350 ioctl_errno = errno; 2351 2352 // Try >cursor(9999,9999), >u7, <u6 2353 if_ok_return(rv, update_term_size_via_esc()); 2354 2355 global.last_errno = ioctl_errno; 2356 return TB_ERR_RESIZE_IOCTL; 2357 } 2358 2359 static int update_term_size_via_esc(void) { 2360 #ifndef TB_RESIZE_FALLBACK_MS 2361 #define TB_RESIZE_FALLBACK_MS 1000 2362 #endif 2363 2364 char *move_and_report = "\x1b[9999;9999H\x1b[6n"; 2365 ssize_t write_rv = 2366 write(global.wfd, move_and_report, strlen(move_and_report)); 2367 if (write_rv != (ssize_t)strlen(move_and_report)) { 2368 return TB_ERR_RESIZE_WRITE; 2369 } 2370 2371 fd_set fds; 2372 FD_ZERO(&fds); 2373 FD_SET(global.rfd, &fds); 2374 2375 struct timeval timeout; 2376 timeout.tv_sec = 0; 2377 timeout.tv_usec = TB_RESIZE_FALLBACK_MS * 1000; 2378 2379 int select_rv = select(global.rfd + 1, &fds, NULL, NULL, &timeout); 2380 2381 if (select_rv != 1) { 2382 global.last_errno = errno; 2383 return TB_ERR_RESIZE_POLL; 2384 } 2385 2386 char buf[TB_OPT_READ_BUF]; 2387 ssize_t read_rv = read(global.rfd, buf, sizeof(buf) - 1); 2388 if (read_rv < 1) { 2389 global.last_errno = errno; 2390 return TB_ERR_RESIZE_READ; 2391 } 2392 buf[read_rv] = '\0'; 2393 2394 int rw, rh; 2395 if (sscanf(buf, "\x1b[%d;%dR", &rh, &rw) != 2) { 2396 return TB_ERR_RESIZE_SSCANF; 2397 } 2398 2399 global.width = rw; 2400 global.height = rh; 2401 return TB_OK; 2402 } 2403 2404 static int init_cellbuf(void) { 2405 int rv; 2406 if_err_return(rv, cellbuf_init(&global.back, global.width, global.height)); 2407 if_err_return(rv, cellbuf_init(&global.front, global.width, global.height)); 2408 if_err_return(rv, cellbuf_clear(&global.back)); 2409 if_err_return(rv, cellbuf_clear(&global.front)); 2410 return TB_OK; 2411 } 2412 2413 static int tb_deinit(void) { 2414 if (global.caps[0] != NULL && global.wfd >= 0) { 2415 bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]); 2416 bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]); 2417 bytebuf_puts(&global.out, global.caps[TB_CAP_CLEAR_SCREEN]); 2418 bytebuf_puts(&global.out, global.caps[TB_CAP_EXIT_CA]); 2419 bytebuf_puts(&global.out, global.caps[TB_CAP_EXIT_KEYPAD]); 2420 bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE); 2421 bytebuf_flush(&global.out, global.wfd); 2422 } 2423 if (global.ttyfd >= 0) { 2424 if (global.has_orig_tios) { 2425 tcsetattr(global.ttyfd, TCSAFLUSH, &global.orig_tios); 2426 } 2427 if (global.ttyfd_open) { 2428 close(global.ttyfd); 2429 global.ttyfd_open = 0; 2430 } 2431 } 2432 2433 sigaction(SIGWINCH, &(struct sigaction){.sa_handler = SIG_DFL}, NULL); 2434 if (global.resize_pipefd[0] >= 0) close(global.resize_pipefd[0]); 2435 if (global.resize_pipefd[1] >= 0) close(global.resize_pipefd[1]); 2436 2437 cellbuf_free(&global.back); 2438 cellbuf_free(&global.front); 2439 bytebuf_free(&global.in); 2440 bytebuf_free(&global.out); 2441 2442 if (global.terminfo) tb_free(global.terminfo); 2443 2444 cap_trie_deinit(&global.cap_trie); 2445 2446 tb_reset(); 2447 return TB_OK; 2448 } 2449 2450 static int load_terminfo(void) { 2451 int rv; 2452 char tmp[TB_PATH_MAX]; 2453 2454 // See terminfo(5) "Fetching Compiled Descriptions" for a description of 2455 // this behavior. Some of these paths are compile-time ncurses options, so 2456 // best guesses are used here. 2457 const char *term = getenv("TERM"); 2458 if (!term) { 2459 return TB_ERR; 2460 } 2461 2462 // If TERMINFO is set, try that directory and stop 2463 const char *terminfo = getenv("TERMINFO"); 2464 if (terminfo) { 2465 return load_terminfo_from_path(terminfo, term); 2466 } 2467 2468 // Next try ~/.terminfo 2469 const char *home = getenv("HOME"); 2470 if (home) { 2471 snprintf_or_return(rv, tmp, sizeof(tmp), "%s/.terminfo", home); 2472 if_ok_return(rv, load_terminfo_from_path(tmp, term)); 2473 } 2474 2475 // Next try TERMINFO_DIRS 2476 // 2477 // Note, empty entries are supposed to be interpretted as the "compiled-in 2478 // default", which is of course system-dependent. Previously /etc/terminfo 2479 // was used here. Let's skip empty entries altogether rather than give 2480 // precedence to a guess, and check common paths after this loop. 2481 const char *dirs = getenv("TERMINFO_DIRS"); 2482 if (dirs) { 2483 snprintf_or_return(rv, tmp, sizeof(tmp), "%s", dirs); 2484 char *dir = strtok(tmp, ":"); 2485 while (dir) { 2486 const char *cdir = dir; 2487 if (*cdir != '\0') { 2488 if_ok_return(rv, load_terminfo_from_path(cdir, term)); 2489 } 2490 dir = strtok(NULL, ":"); 2491 } 2492 } 2493 2494 #ifdef TB_TERMINFO_DIR 2495 if_ok_return(rv, load_terminfo_from_path(TB_TERMINFO_DIR, term)); 2496 #endif 2497 if_ok_return(rv, load_terminfo_from_path("/usr/local/etc/terminfo", term)); 2498 if_ok_return(rv, 2499 load_terminfo_from_path("/usr/local/share/terminfo", term)); 2500 if_ok_return(rv, load_terminfo_from_path("/usr/local/lib/terminfo", term)); 2501 if_ok_return(rv, load_terminfo_from_path("/etc/terminfo", term)); 2502 if_ok_return(rv, load_terminfo_from_path("/usr/share/terminfo", term)); 2503 if_ok_return(rv, load_terminfo_from_path("/usr/lib/terminfo", term)); 2504 if_ok_return(rv, load_terminfo_from_path("/usr/share/lib/terminfo", term)); 2505 if_ok_return(rv, load_terminfo_from_path("/lib/terminfo", term)); 2506 2507 return TB_ERR; 2508 } 2509 2510 static int load_terminfo_from_path(const char *path, const char *term) { 2511 int rv; 2512 char tmp[TB_PATH_MAX]; 2513 2514 // Look for term at this terminfo location, e.g., <terminfo>/x/xterm 2515 snprintf_or_return(rv, tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term); 2516 if_ok_return(rv, read_terminfo_path(tmp)); 2517 2518 #ifdef __APPLE__ 2519 // Try the Darwin equivalent path, e.g., <terminfo>/78/xterm 2520 snprintf_or_return(rv, tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term); 2521 return read_terminfo_path(tmp); 2522 #endif 2523 2524 return TB_ERR; 2525 } 2526 2527 static int read_terminfo_path(const char *path) { 2528 FILE *fp = fopen(path, "rb"); 2529 if (!fp) { 2530 return TB_ERR; 2531 } 2532 2533 struct stat st; 2534 if (fstat(fileno(fp), &st) != 0) { 2535 fclose(fp); 2536 return TB_ERR; 2537 } 2538 2539 size_t fsize = st.st_size; 2540 char *data = tb_malloc(fsize); 2541 if (!data) { 2542 fclose(fp); 2543 return TB_ERR; 2544 } 2545 2546 if (fread(data, 1, fsize, fp) != fsize) { 2547 fclose(fp); 2548 tb_free(data); 2549 return TB_ERR; 2550 } 2551 2552 global.terminfo = data; 2553 global.nterminfo = fsize; 2554 2555 fclose(fp); 2556 return TB_OK; 2557 } 2558 2559 static int parse_terminfo_caps(void) { 2560 // See term(5) "LEGACY STORAGE FORMAT" and "EXTENDED STORAGE FORMAT" for a 2561 // description of this behavior. 2562 2563 // Ensure there's at least a header's worth of data 2564 if (global.nterminfo < 6) { 2565 return TB_ERR; 2566 } 2567 2568 int16_t *header = (int16_t *)global.terminfo; 2569 // header[0] the magic number (octal 0432 or 01036) 2570 // header[1] the size, in bytes, of the names section 2571 // header[2] the number of bytes in the boolean section 2572 // header[3] the number of short integers in the numbers section 2573 // header[4] the number of offsets (short integers) in the strings section 2574 // header[5] the size, in bytes, of the string table 2575 2576 // Legacy ints are 16-bit, extended ints are 32-bit 2577 const int bytes_per_int = header[0] == 01036 ? 4 // 32-bit 2578 : 2; // 16-bit 2579 2580 // > Between the boolean section and the number section, a null byte will be 2581 // > inserted, if necessary, to ensure that the number section begins on an 2582 // > even byte 2583 const int align_offset = (header[1] + header[2]) % 2 != 0 ? 1 : 0; 2584 2585 const int pos_str_offsets = 2586 (6 * sizeof(int16_t)) // header (12 bytes) 2587 + header[1] // length of names section 2588 + header[2] // length of boolean section 2589 + align_offset + 2590 (header[3] * bytes_per_int); // length of numbers section 2591 2592 const int pos_str_table = 2593 pos_str_offsets + 2594 (header[4] * sizeof(int16_t)); // length of string offsets table 2595 2596 // Load caps 2597 int i; 2598 for (i = 0; i < TB_CAP__COUNT; i++) { 2599 const char *cap = get_terminfo_string(pos_str_offsets, header[4], 2600 pos_str_table, header[5], terminfo_cap_indexes[i]); 2601 if (!cap) { 2602 // Something is not right 2603 return TB_ERR; 2604 } 2605 global.caps[i] = cap; 2606 } 2607 2608 return TB_OK; 2609 } 2610 2611 static int load_builtin_caps(void) { 2612 int i, j; 2613 const char *term = getenv("TERM"); 2614 2615 if (!term) { 2616 return TB_ERR_NO_TERM; 2617 } 2618 2619 // Check for exact TERM match 2620 for (i = 0; builtin_terms[i].name != NULL; i++) { 2621 if (strcmp(term, builtin_terms[i].name) == 0) { 2622 for (j = 0; j < TB_CAP__COUNT; j++) { 2623 global.caps[j] = builtin_terms[i].caps[j]; 2624 } 2625 return TB_OK; 2626 } 2627 } 2628 2629 // Check for partial TERM or alias match 2630 for (i = 0; builtin_terms[i].name != NULL; i++) { 2631 if (strstr(term, builtin_terms[i].name) != NULL || 2632 (*(builtin_terms[i].alias) != '\0' && 2633 strstr(term, builtin_terms[i].alias) != NULL)) 2634 { 2635 for (j = 0; j < TB_CAP__COUNT; j++) { 2636 global.caps[j] = builtin_terms[i].caps[j]; 2637 } 2638 return TB_OK; 2639 } 2640 } 2641 2642 return TB_ERR_UNSUPPORTED_TERM; 2643 } 2644 2645 static const char *get_terminfo_string(int16_t str_offsets_pos, 2646 int16_t str_offsets_len, int16_t str_table_pos, int16_t str_table_len, 2647 int16_t str_index) { 2648 const int str_byte_index = (int)str_index * (int)sizeof(int16_t); 2649 if (str_byte_index >= (int)str_offsets_len * (int)sizeof(int16_t)) { 2650 // An offset beyond the table indicates absent 2651 // See `convert_strings` in tinfo `read_entry.c` 2652 return ""; 2653 } 2654 const int16_t *str_offset = 2655 (int16_t *)(global.terminfo + (int)str_offsets_pos + str_byte_index); 2656 if ((char *)str_offset >= global.terminfo + global.nterminfo) { 2657 // str_offset points beyond end of entry 2658 // Truncated/corrupt terminfo entry? 2659 return NULL; 2660 } 2661 if (*str_offset < 0 || *str_offset >= str_table_len) { 2662 // A negative offset indicates absent 2663 // An offset beyond the table indicates absent 2664 // See `convert_strings` in tinfo `read_entry.c` 2665 return ""; 2666 } 2667 if (((size_t)((int)str_table_pos + (int)*str_offset)) >= global.nterminfo) { 2668 // string points beyond end of entry 2669 // Truncated/corrupt terminfo entry? 2670 return NULL; 2671 } 2672 return ( 2673 const char *)(global.terminfo + (int)str_table_pos + (int)*str_offset); 2674 } 2675 2676 static int wait_event(struct tb_event *event, int timeout) { 2677 int rv; 2678 char buf[TB_OPT_READ_BUF]; 2679 2680 memset(event, 0, sizeof(*event)); 2681 if_ok_return(rv, extract_event(event)); 2682 2683 fd_set fds; 2684 struct timeval tv; 2685 tv.tv_sec = timeout / 1000; 2686 tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000; 2687 2688 do { 2689 FD_ZERO(&fds); 2690 FD_SET(global.rfd, &fds); 2691 FD_SET(global.resize_pipefd[0], &fds); 2692 2693 int maxfd = global.resize_pipefd[0] > global.rfd 2694 ? global.resize_pipefd[0] 2695 : global.rfd; 2696 2697 int select_rv = 2698 select(maxfd + 1, &fds, NULL, NULL, (timeout < 0) ? NULL : &tv); 2699 2700 if (select_rv < 0) { 2701 // Let EINTR/EAGAIN bubble up 2702 global.last_errno = errno; 2703 return TB_ERR_POLL; 2704 } else if (select_rv == 0) { 2705 return TB_ERR_NO_EVENT; 2706 } 2707 2708 int tty_has_events = (FD_ISSET(global.rfd, &fds)); 2709 int resize_has_events = (FD_ISSET(global.resize_pipefd[0], &fds)); 2710 2711 if (tty_has_events) { 2712 ssize_t read_rv = read(global.rfd, buf, sizeof(buf)); 2713 if (read_rv < 0) { 2714 global.last_errno = errno; 2715 return TB_ERR_READ; 2716 } else if (read_rv > 0) { 2717 bytebuf_nputs(&global.in, buf, read_rv); 2718 } 2719 } 2720 2721 if (resize_has_events) { 2722 int ignore = 0; 2723 read(global.resize_pipefd[0], &ignore, sizeof(ignore)); 2724 // TODO: Harden against errors encountered mid-resize 2725 if_err_return(rv, update_term_size()); 2726 if_err_return(rv, resize_cellbufs()); 2727 event->type = TB_EVENT_RESIZE; 2728 event->w = global.width; 2729 event->h = global.height; 2730 return TB_OK; 2731 } 2732 2733 memset(event, 0, sizeof(*event)); 2734 if_ok_return(rv, extract_event(event)); 2735 } while (timeout == -1); 2736 2737 return rv; 2738 } 2739 2740 static int extract_event(struct tb_event *event) { 2741 int rv; 2742 struct bytebuf_t *in = &global.in; 2743 2744 if (in->len == 0) { 2745 return TB_ERR; 2746 } 2747 2748 if (in->buf[0] == '\x1b') { 2749 // Escape sequence? 2750 // In TB_INPUT_ESC, skip if the buffer is a single escape char 2751 if (!((global.input_mode & TB_INPUT_ESC) && in->len == 1)) { 2752 if_ok_or_need_more_return(rv, extract_esc(event)); 2753 } 2754 2755 // Escape key? 2756 if (global.input_mode & TB_INPUT_ESC) { 2757 event->type = TB_EVENT_KEY; 2758 event->ch = 0; 2759 event->key = TB_KEY_ESC; 2760 event->mod = 0; 2761 bytebuf_shift(in, 1); 2762 return TB_OK; 2763 } 2764 2765 // Recurse for alt key 2766 event->mod |= TB_MOD_ALT; 2767 bytebuf_shift(in, 1); 2768 return extract_event(event); 2769 } 2770 2771 // ASCII control key? 2772 if ((uint16_t)in->buf[0] < TB_KEY_SPACE || in->buf[0] == TB_KEY_BACKSPACE2) 2773 { 2774 event->type = TB_EVENT_KEY; 2775 event->ch = 0; 2776 event->key = (uint16_t)in->buf[0]; 2777 event->mod |= TB_MOD_CTRL; 2778 bytebuf_shift(in, 1); 2779 return TB_OK; 2780 } 2781 2782 // UTF-8? 2783 if (in->len >= (size_t)tb_utf8_char_length(in->buf[0])) { 2784 event->type = TB_EVENT_KEY; 2785 tb_utf8_char_to_unicode(&event->ch, in->buf); 2786 event->key = 0; 2787 bytebuf_shift(in, tb_utf8_char_length(in->buf[0])); 2788 return TB_OK; 2789 } 2790 2791 // Need more input 2792 return TB_ERR; 2793 } 2794 2795 static int extract_esc(struct tb_event *event) { 2796 int rv; 2797 if_ok_or_need_more_return(rv, extract_esc_user(event, 0)); 2798 if_ok_or_need_more_return(rv, extract_esc_cap(event)); 2799 if_ok_or_need_more_return(rv, extract_esc_mouse(event)); 2800 if_ok_or_need_more_return(rv, extract_esc_user(event, 1)); 2801 return TB_ERR; 2802 } 2803 2804 static int extract_esc_user(struct tb_event *event, int is_post) { 2805 int rv; 2806 size_t consumed = 0; 2807 struct bytebuf_t *in = &global.in; 2808 int (*fn)(struct tb_event *, size_t *); 2809 2810 fn = is_post ? global.fn_extract_esc_post : global.fn_extract_esc_pre; 2811 2812 if (!fn) { 2813 return TB_ERR; 2814 } 2815 2816 rv = fn(event, &consumed); 2817 if (rv == TB_OK) { 2818 bytebuf_shift(in, consumed); 2819 } 2820 2821 if_ok_or_need_more_return(rv, rv); 2822 return TB_ERR; 2823 } 2824 2825 static int extract_esc_cap(struct tb_event *event) { 2826 int rv; 2827 struct bytebuf_t *in = &global.in; 2828 struct cap_trie_t *node; 2829 size_t depth; 2830 2831 if_err_return(rv, cap_trie_find(in->buf, in->len, &node, &depth)); 2832 if (node->is_leaf) { 2833 // Found a leaf node 2834 event->type = TB_EVENT_KEY; 2835 event->ch = 0; 2836 event->key = node->key; 2837 event->mod = node->mod; 2838 bytebuf_shift(in, depth); 2839 return TB_OK; 2840 } else if (node->nchildren > 0 && in->len <= depth) { 2841 // Found a branch node (not enough input) 2842 return TB_ERR_NEED_MORE; 2843 } 2844 2845 return TB_ERR; 2846 } 2847 2848 static int extract_esc_mouse(struct tb_event *event) { 2849 struct bytebuf_t *in = &global.in; 2850 2851 enum type { TYPE_VT200 = 0, TYPE_1006, TYPE_1015, TYPE_MAX }; 2852 2853 const char *cmp[TYPE_MAX] = {// 2854 // X10 mouse encoding, the simplest one 2855 // \x1b [ M Cb Cx Cy 2856 [TYPE_VT200] = "\x1b[M", 2857 // xterm 1006 extended mode or urxvt 1015 extended mode 2858 // xterm: \x1b [ < Cb ; Cx ; Cy (M or m) 2859 [TYPE_1006] = "\x1b[<", 2860 // urxvt: \x1b [ Cb ; Cx ; Cy M 2861 [TYPE_1015] = "\x1b["}; 2862 2863 enum type type = 0; 2864 int ret = TB_ERR; 2865 2866 // Unrolled at compile-time (probably) 2867 for (; type < TYPE_MAX; type++) { 2868 size_t size = strlen(cmp[type]); 2869 2870 if (in->len >= size && (strncmp(cmp[type], in->buf, size)) == 0) { 2871 break; 2872 } 2873 } 2874 2875 if (type == TYPE_MAX) { 2876 ret = TB_ERR; // No match 2877 return ret; 2878 } 2879 2880 size_t buf_shift = 0; 2881 2882 switch (type) { 2883 case TYPE_VT200: 2884 if (in->len >= 6) { 2885 int b = in->buf[3] - 0x20; 2886 int fail = 0; 2887 2888 switch (b & 3) { 2889 case 0: 2890 event->key = ((b & 64) != 0) ? TB_KEY_MOUSE_WHEEL_UP 2891 : TB_KEY_MOUSE_LEFT; 2892 break; 2893 case 1: 2894 event->key = ((b & 64) != 0) ? TB_KEY_MOUSE_WHEEL_DOWN 2895 : TB_KEY_MOUSE_MIDDLE; 2896 break; 2897 case 2: 2898 event->key = TB_KEY_MOUSE_RIGHT; 2899 break; 2900 case 3: 2901 event->key = TB_KEY_MOUSE_RELEASE; 2902 break; 2903 default: 2904 ret = TB_ERR; 2905 fail = 1; 2906 break; 2907 } 2908 2909 if (!fail) { 2910 if ((b & 32) != 0) { 2911 event->mod |= TB_MOD_MOTION; 2912 } 2913 2914 // the coord is 1,1 for upper left 2915 event->x = ((uint8_t)in->buf[4]) - 0x21; 2916 event->y = ((uint8_t)in->buf[5]) - 0x21; 2917 2918 ret = TB_OK; 2919 } 2920 2921 buf_shift = 6; 2922 } 2923 break; 2924 case TYPE_1006: 2925 // fallthrough 2926 case TYPE_1015: { 2927 size_t index_fail = (size_t)-1; 2928 2929 enum { 2930 FIRST_M = 0, 2931 FIRST_SEMICOLON, 2932 LAST_SEMICOLON, 2933 FIRST_LAST_MAX 2934 }; 2935 2936 size_t indices[FIRST_LAST_MAX] = {index_fail, index_fail, 2937 index_fail}; 2938 int m_is_capital = 0; 2939 2940 for (size_t i = 0; i < in->len; i++) { 2941 if (in->buf[i] == ';') { 2942 if (indices[FIRST_SEMICOLON] == index_fail) { 2943 indices[FIRST_SEMICOLON] = i; 2944 } else { 2945 indices[LAST_SEMICOLON] = i; 2946 } 2947 } else if (indices[FIRST_M] == index_fail) { 2948 if (in->buf[i] == 'm' || in->buf[i] == 'M') { 2949 m_is_capital = (in->buf[i] == 'M'); 2950 indices[FIRST_M] = i; 2951 } 2952 } 2953 } 2954 2955 if (indices[FIRST_M] == index_fail || 2956 indices[FIRST_SEMICOLON] == index_fail || 2957 indices[LAST_SEMICOLON] == index_fail) 2958 { 2959 ret = TB_ERR; 2960 } else { 2961 int start = (type == TYPE_1015 ? 2 : 3); 2962 2963 unsigned n1 = strtoul(&in->buf[start], NULL, 10); 2964 unsigned n2 = 2965 strtoul(&in->buf[indices[FIRST_SEMICOLON] + 1], NULL, 10); 2966 unsigned n3 = 2967 strtoul(&in->buf[indices[LAST_SEMICOLON] + 1], NULL, 10); 2968 2969 if (type == TYPE_1015) { 2970 n1 -= 0x20; 2971 } 2972 2973 int fail = 0; 2974 2975 switch (n1 & 3) { 2976 case 0: 2977 event->key = ((n1 & 64) != 0) ? TB_KEY_MOUSE_WHEEL_UP 2978 : TB_KEY_MOUSE_LEFT; 2979 break; 2980 case 1: 2981 event->key = ((n1 & 64) != 0) ? TB_KEY_MOUSE_WHEEL_DOWN 2982 : TB_KEY_MOUSE_MIDDLE; 2983 break; 2984 case 2: 2985 event->key = TB_KEY_MOUSE_RIGHT; 2986 break; 2987 case 3: 2988 event->key = TB_KEY_MOUSE_RELEASE; 2989 break; 2990 default: 2991 ret = TB_ERR; 2992 fail = 1; 2993 break; 2994 } 2995 2996 buf_shift = in->len; 2997 2998 if (!fail) { 2999 if (!m_is_capital) { 3000 // on xterm mouse release is signaled by lowercase m 3001 event->key = TB_KEY_MOUSE_RELEASE; 3002 } 3003 3004 if ((n1 & 32) != 0) { 3005 event->mod |= TB_MOD_MOTION; 3006 } 3007 3008 event->x = ((uint8_t)n2) - 1; 3009 event->y = ((uint8_t)n3) - 1; 3010 3011 ret = TB_OK; 3012 } 3013 } 3014 } break; 3015 case TYPE_MAX: 3016 ret = TB_ERR; 3017 } 3018 3019 if (buf_shift > 0) { 3020 bytebuf_shift(in, buf_shift); 3021 } 3022 3023 if (ret == TB_OK) { 3024 event->type = TB_EVENT_MOUSE; 3025 } 3026 3027 return ret; 3028 } 3029 3030 static int resize_cellbufs(void) { 3031 int rv; 3032 if_err_return(rv, 3033 cellbuf_resize(&global.back, global.width, global.height)); 3034 if_err_return(rv, 3035 cellbuf_resize(&global.front, global.width, global.height)); 3036 if_err_return(rv, cellbuf_clear(&global.front)); 3037 if_err_return(rv, send_clear()); 3038 return TB_OK; 3039 } 3040 3041 static void handle_resize(int sig) { 3042 int errno_copy = errno; 3043 write(global.resize_pipefd[1], &sig, sizeof(sig)); 3044 errno = errno_copy; 3045 } 3046 3047 static int send_attr(uintattr_t fg, uintattr_t bg) { 3048 int rv; 3049 3050 if (fg == global.last_fg && bg == global.last_bg) { 3051 return TB_OK; 3052 } 3053 3054 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0])); 3055 3056 uint32_t cfg, cbg; 3057 switch (global.output_mode) { 3058 default: 3059 case TB_OUTPUT_NORMAL: 3060 // The minus 1 below is because our colors are 1-indexed starting 3061 // from black. Black is represented by a 30, 40, 90, or 100 for fg, 3062 // bg, bright fg, or bright bg respectively. Red is 31, 41, 91, 3063 // 101, etc. 3064 cfg = (fg & TB_BRIGHT ? 90 : 30) + (fg & 0x0f) - 1; 3065 cbg = (bg & TB_BRIGHT ? 100 : 40) + (bg & 0x0f) - 1; 3066 break; 3067 3068 case TB_OUTPUT_256: 3069 cfg = fg & 0xff; 3070 cbg = bg & 0xff; 3071 if (fg & TB_HI_BLACK) cfg = 0; 3072 if (bg & TB_HI_BLACK) cbg = 0; 3073 break; 3074 3075 case TB_OUTPUT_216: 3076 cfg = fg & 0xff; 3077 cbg = bg & 0xff; 3078 if (cfg > 216) cfg = 216; 3079 if (cbg > 216) cbg = 216; 3080 cfg += 0x0f; 3081 cbg += 0x0f; 3082 break; 3083 3084 case TB_OUTPUT_GRAYSCALE: 3085 cfg = fg & 0xff; 3086 cbg = bg & 0xff; 3087 if (cfg > 24) cfg = 24; 3088 if (cbg > 24) cbg = 24; 3089 cfg += 0xe7; 3090 cbg += 0xe7; 3091 break; 3092 3093 #if TB_OPT_ATTR_W >= 32 3094 case TB_OUTPUT_TRUECOLOR: 3095 cfg = fg & 0xffffff; 3096 cbg = bg & 0xffffff; 3097 if (fg & TB_HI_BLACK) cfg = 0; 3098 if (bg & TB_HI_BLACK) cbg = 0; 3099 break; 3100 #endif 3101 } 3102 3103 if (fg & TB_BOLD) 3104 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BOLD])); 3105 3106 if (fg & TB_BLINK) 3107 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BLINK])); 3108 3109 if (fg & TB_UNDERLINE) 3110 if_err_return(rv, 3111 bytebuf_puts(&global.out, global.caps[TB_CAP_UNDERLINE])); 3112 3113 if (fg & TB_ITALIC) 3114 if_err_return(rv, 3115 bytebuf_puts(&global.out, global.caps[TB_CAP_ITALIC])); 3116 3117 if (fg & TB_DIM) 3118 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_DIM])); 3119 3120 #if TB_OPT_ATTR_W == 64 3121 if (fg & TB_STRIKEOUT) 3122 if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_STRIKEOUT)); 3123 3124 if (fg & TB_UNDERLINE_2) 3125 if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_UNDERLINE_2)); 3126 3127 if (fg & TB_OVERLINE) 3128 if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_OVERLINE)); 3129 3130 if (fg & TB_INVISIBLE) 3131 if_err_return(rv, 3132 bytebuf_puts(&global.out, global.caps[TB_CAP_INVISIBLE])); 3133 #endif 3134 3135 if ((fg & TB_REVERSE) || (bg & TB_REVERSE)) 3136 if_err_return(rv, 3137 bytebuf_puts(&global.out, global.caps[TB_CAP_REVERSE])); 3138 3139 int fg_is_default = (fg & 0xff) == 0; 3140 int bg_is_default = (bg & 0xff) == 0; 3141 if (global.output_mode == TB_OUTPUT_256) { 3142 if (fg & TB_HI_BLACK) fg_is_default = 0; 3143 if (bg & TB_HI_BLACK) bg_is_default = 0; 3144 } 3145 #if TB_OPT_ATTR_W >= 32 3146 if (global.output_mode == TB_OUTPUT_TRUECOLOR) { 3147 fg_is_default = ((fg & 0xffffff) == 0) && ((fg & TB_HI_BLACK) == 0); 3148 bg_is_default = ((bg & 0xffffff) == 0) && ((bg & TB_HI_BLACK) == 0); 3149 } 3150 #endif 3151 3152 if_err_return(rv, send_sgr(cfg, cbg, fg_is_default, bg_is_default)); 3153 3154 global.last_fg = fg; 3155 global.last_bg = bg; 3156 3157 return TB_OK; 3158 } 3159 3160 static int send_sgr(uint32_t cfg, uint32_t cbg, int fg_is_default, 3161 int bg_is_default) { 3162 int rv; 3163 char nbuf[32]; 3164 3165 if (fg_is_default && bg_is_default) { 3166 return TB_OK; 3167 } 3168 3169 switch (global.output_mode) { 3170 default: 3171 case TB_OUTPUT_NORMAL: 3172 send_literal(rv, "\x1b["); 3173 if (!fg_is_default) { 3174 send_num(rv, nbuf, cfg); 3175 if (!bg_is_default) { 3176 send_literal(rv, ";"); 3177 } 3178 } 3179 if (!bg_is_default) { 3180 send_num(rv, nbuf, cbg); 3181 } 3182 send_literal(rv, "m"); 3183 break; 3184 3185 case TB_OUTPUT_256: 3186 case TB_OUTPUT_216: 3187 case TB_OUTPUT_GRAYSCALE: 3188 send_literal(rv, "\x1b["); 3189 if (!fg_is_default) { 3190 send_literal(rv, "38;5;"); 3191 send_num(rv, nbuf, cfg); 3192 if (!bg_is_default) { 3193 send_literal(rv, ";"); 3194 } 3195 } 3196 if (!bg_is_default) { 3197 send_literal(rv, "48;5;"); 3198 send_num(rv, nbuf, cbg); 3199 } 3200 send_literal(rv, "m"); 3201 break; 3202 3203 #if TB_OPT_ATTR_W >= 32 3204 case TB_OUTPUT_TRUECOLOR: 3205 send_literal(rv, "\x1b["); 3206 if (!fg_is_default) { 3207 send_literal(rv, "38;2;"); 3208 send_num(rv, nbuf, (cfg >> 16) & 0xff); 3209 send_literal(rv, ";"); 3210 send_num(rv, nbuf, (cfg >> 8) & 0xff); 3211 send_literal(rv, ";"); 3212 send_num(rv, nbuf, cfg & 0xff); 3213 if (!bg_is_default) { 3214 send_literal(rv, ";"); 3215 } 3216 } 3217 if (!bg_is_default) { 3218 send_literal(rv, "48;2;"); 3219 send_num(rv, nbuf, (cbg >> 16) & 0xff); 3220 send_literal(rv, ";"); 3221 send_num(rv, nbuf, (cbg >> 8) & 0xff); 3222 send_literal(rv, ";"); 3223 send_num(rv, nbuf, cbg & 0xff); 3224 } 3225 send_literal(rv, "m"); 3226 break; 3227 #endif 3228 } 3229 return TB_OK; 3230 } 3231 3232 static int send_cursor_if(int x, int y) { 3233 int rv; 3234 char nbuf[32]; 3235 if (x < 0 || y < 0) { 3236 return TB_OK; 3237 } 3238 send_literal(rv, "\x1b["); 3239 send_num(rv, nbuf, y + 1); 3240 send_literal(rv, ";"); 3241 send_num(rv, nbuf, x + 1); 3242 send_literal(rv, "H"); 3243 return TB_OK; 3244 } 3245 3246 static int send_char(int x, int y, uint32_t ch) { 3247 return send_cluster(x, y, &ch, 1); 3248 } 3249 3250 static int send_cluster(int x, int y, uint32_t *ch, size_t nch) { 3251 int rv; 3252 char chu8[8]; 3253 3254 if (global.last_x != x - 1 || global.last_y != y) { 3255 if_err_return(rv, send_cursor_if(x, y)); 3256 } 3257 global.last_x = x; 3258 global.last_y = y; 3259 3260 int i; 3261 for (i = 0; i < (int)nch; i++) { 3262 uint32_t ch32 = *(ch + i); 3263 if (!iswprint((wint_t)ch32)) { 3264 ch32 = 0xfffd; // replace non-printable codepoints with U+FFFD 3265 } 3266 int chu8_len = tb_utf8_unicode_to_char(chu8, ch32); 3267 if_err_return(rv, bytebuf_nputs(&global.out, chu8, (size_t)chu8_len)); 3268 } 3269 3270 return TB_OK; 3271 } 3272 3273 static int convert_num(uint32_t num, char *buf) { 3274 int i, l = 0; 3275 char ch; 3276 do { 3277 buf[l++] = (char)('0' + (num % 10)); 3278 num /= 10; 3279 } while (num); 3280 for (i = 0; i < l / 2; i++) { 3281 ch = buf[i]; 3282 buf[i] = buf[l - 1 - i]; 3283 buf[l - 1 - i] = ch; 3284 } 3285 return l; 3286 } 3287 3288 static int cell_cmp(struct tb_cell *a, struct tb_cell *b) { 3289 if (a->ch != b->ch || a->fg != b->fg || a->bg != b->bg) { 3290 return 1; 3291 } 3292 #ifdef TB_OPT_EGC 3293 if (a->nech != b->nech) { 3294 return 1; 3295 } else if (a->nech > 0) { // a->nech == b->nech 3296 return memcmp(a->ech, b->ech, a->nech); 3297 } 3298 #endif 3299 return 0; 3300 } 3301 3302 static int cell_copy(struct tb_cell *dst, struct tb_cell *src) { 3303 #ifdef TB_OPT_EGC 3304 if (src->nech > 0) { 3305 return cell_set(dst, src->ech, src->nech, src->fg, src->bg); 3306 } 3307 #endif 3308 return cell_set(dst, &src->ch, 1, src->fg, src->bg); 3309 } 3310 3311 static int cell_set(struct tb_cell *cell, uint32_t *ch, size_t nch, 3312 uintattr_t fg, uintattr_t bg) { 3313 cell->ch = ch ? *ch : 0; 3314 cell->fg = fg; 3315 cell->bg = bg; 3316 #ifdef TB_OPT_EGC 3317 if (nch <= 1) { 3318 cell->nech = 0; 3319 } else { 3320 int rv; 3321 if_err_return(rv, cell_reserve_ech(cell, nch + 1)); 3322 memcpy(cell->ech, ch, sizeof(ch) * nch); 3323 cell->ech[nch] = '\0'; 3324 cell->nech = nch; 3325 } 3326 #else 3327 (void)nch; 3328 (void)cell_reserve_ech; 3329 #endif 3330 return TB_OK; 3331 } 3332 3333 static int cell_reserve_ech(struct tb_cell *cell, size_t n) { 3334 #ifdef TB_OPT_EGC 3335 if (cell->cech >= n) { 3336 return TB_OK; 3337 } 3338 if (!(cell->ech = tb_realloc(cell->ech, n * sizeof(cell->ch)))) { 3339 return TB_ERR_MEM; 3340 } 3341 cell->cech = n; 3342 return TB_OK; 3343 #else 3344 (void)cell; 3345 (void)n; 3346 return TB_ERR; 3347 #endif 3348 } 3349 3350 static int cell_free(struct tb_cell *cell) { 3351 #ifdef TB_OPT_EGC 3352 if (cell->ech) { 3353 tb_free(cell->ech); 3354 } 3355 #endif 3356 memset(cell, 0, sizeof(*cell)); 3357 return TB_OK; 3358 } 3359 3360 static int cellbuf_init(struct cellbuf_t *c, int w, int h) { 3361 c->cells = tb_malloc(sizeof(struct tb_cell) * w * h); 3362 if (!c->cells) { 3363 return TB_ERR_MEM; 3364 } 3365 memset(c->cells, 0, sizeof(struct tb_cell) * w * h); 3366 c->width = w; 3367 c->height = h; 3368 return TB_OK; 3369 } 3370 3371 static int cellbuf_free(struct cellbuf_t *c) { 3372 if (c->cells) { 3373 int i; 3374 for (i = 0; i < c->width * c->height; i++) { 3375 cell_free(&c->cells[i]); 3376 } 3377 tb_free(c->cells); 3378 } 3379 memset(c, 0, sizeof(*c)); 3380 return TB_OK; 3381 } 3382 3383 static int cellbuf_clear(struct cellbuf_t *c) { 3384 int rv, i; 3385 uint32_t space = (uint32_t)' '; 3386 for (i = 0; i < c->width * c->height; i++) { 3387 if_err_return(rv, 3388 cell_set(&c->cells[i], &space, 1, global.fg, global.bg)); 3389 } 3390 return TB_OK; 3391 } 3392 3393 static int cellbuf_get(struct cellbuf_t *c, int x, int y, 3394 struct tb_cell **out) { 3395 if (!cellbuf_in_bounds(c, x, y)) { 3396 *out = NULL; 3397 return TB_ERR_OUT_OF_BOUNDS; 3398 } 3399 *out = &c->cells[(y * c->width) + x]; 3400 return TB_OK; 3401 } 3402 3403 static int cellbuf_in_bounds(struct cellbuf_t *c, int x, int y) { 3404 if (x < 0 || x >= c->width || y < 0 || y >= c->height) { 3405 return 0; 3406 } 3407 return 1; 3408 } 3409 3410 static int cellbuf_resize(struct cellbuf_t *c, int w, int h) { 3411 int rv; 3412 3413 int ow = c->width; 3414 int oh = c->height; 3415 3416 if (ow == w && oh == h) { 3417 return TB_OK; 3418 } 3419 3420 w = w < 1 ? 1 : w; 3421 h = h < 1 ? 1 : h; 3422 3423 int minw = (w < ow) ? w : ow; 3424 int minh = (h < oh) ? h : oh; 3425 3426 struct tb_cell *prev = c->cells; 3427 3428 if_err_return(rv, cellbuf_init(c, w, h)); 3429 if_err_return(rv, cellbuf_clear(c)); 3430 3431 int x, y; 3432 for (x = 0; x < minw; x++) { 3433 for (y = 0; y < minh; y++) { 3434 struct tb_cell *src, *dst; 3435 src = &prev[(y * ow) + x]; 3436 if_err_return(rv, cellbuf_get(c, x, y, &dst)); 3437 if_err_return(rv, cell_copy(dst, src)); 3438 } 3439 } 3440 3441 tb_free(prev); 3442 3443 return TB_OK; 3444 } 3445 3446 static int bytebuf_puts(struct bytebuf_t *b, const char *str) { 3447 if (!str || strlen(str) <= 0) return TB_OK; // Nothing to do for empty caps 3448 return bytebuf_nputs(b, str, (size_t)strlen(str)); 3449 } 3450 3451 static int bytebuf_nputs(struct bytebuf_t *b, const char *str, size_t nstr) { 3452 int rv; 3453 if_err_return(rv, bytebuf_reserve(b, b->len + nstr + 1)); 3454 memcpy(b->buf + b->len, str, nstr); 3455 b->len += nstr; 3456 b->buf[b->len] = '\0'; 3457 return TB_OK; 3458 } 3459 3460 static int bytebuf_shift(struct bytebuf_t *b, size_t n) { 3461 if (n > b->len) { 3462 n = b->len; 3463 } 3464 size_t nmove = b->len - n; 3465 memmove(b->buf, b->buf + n, nmove); 3466 b->len -= n; 3467 return TB_OK; 3468 } 3469 3470 static int bytebuf_flush(struct bytebuf_t *b, int fd) { 3471 if (b->len <= 0) { 3472 return TB_OK; 3473 } 3474 ssize_t write_rv = write(fd, b->buf, b->len); 3475 if (write_rv < 0 || (size_t)write_rv != b->len) { 3476 // Note, errno will be 0 on partial write 3477 global.last_errno = errno; 3478 return TB_ERR; 3479 } 3480 b->len = 0; 3481 return TB_OK; 3482 } 3483 3484 static int bytebuf_reserve(struct bytebuf_t *b, size_t sz) { 3485 if (b->cap >= sz) { 3486 return TB_OK; 3487 } 3488 size_t newcap = b->cap > 0 ? b->cap : 1; 3489 while (newcap < sz) { 3490 newcap *= 2; 3491 } 3492 char *newbuf; 3493 if (b->buf) { 3494 newbuf = tb_realloc(b->buf, newcap); 3495 } else { 3496 newbuf = tb_malloc(newcap); 3497 } 3498 if (!newbuf) { 3499 return TB_ERR_MEM; 3500 } 3501 b->buf = newbuf; 3502 b->cap = newcap; 3503 return TB_OK; 3504 } 3505 3506 static int bytebuf_free(struct bytebuf_t *b) { 3507 if (b->buf) { 3508 tb_free(b->buf); 3509 } 3510 memset(b, 0, sizeof(*b)); 3511 return TB_OK; 3512 } 3513 3514 #endif // TB_IMPL