Un jeu de cartes à piper les une dans les autres - retour accueil
git clone git://bebou.netlib.re/pipe-game
Log | Files | Refs | README |
termbox2.h (126526B)
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" 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 + 1)); 1782 cell->ech[nech - 1] = ch; 1783 } else { // make new ech 1784 nech = 2; 1785 if_err_return(rv, cell_reserve_ech(cell, nech + 1)); 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, x_prev; 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 x_prev = x; 1883 if (out_w) *out_w = 0; 1884 1885 while (*str) { 1886 rv = tb_utf8_char_to_unicode(&uni, str); 1887 1888 if (rv < 0) { 1889 uni = 0xfffd; // replace invalid UTF-8 char with U+FFFD 1890 str += rv * -1; 1891 } else if (rv > 0) { 1892 str += rv; 1893 } else { 1894 break; // shouldn't get here 1895 } 1896 1897 if (uni == '\n') { // TODO: \r, \t, \v, \f, etc? 1898 x = ix; 1899 x_prev = x; 1900 y += 1; 1901 continue; 1902 } 1903 1904 w = wcwidth((wchar_t)uni); 1905 if (w<0) w=1; 1906 if(w==0 && x > ix) { 1907 if_err_return(rv, tb_extend_cell(x - 1, y, uni)); 1908 } else { 1909 if_err_return(rv, tb_set_cell(x, y, uni, fg, bg)); 1910 x_prev = x; 1911 x += w; 1912 if (out_w) *out_w += w; 1913 } 1914 } 1915 1916 return TB_OK; 1917 } 1918 1919 int tb_printf(int x, int y, uintattr_t fg, uintattr_t bg, const char *fmt, 1920 ...) { 1921 int rv; 1922 va_list vl; 1923 va_start(vl, fmt); 1924 rv = tb_printf_inner(x, y, fg, bg, NULL, fmt, vl); 1925 va_end(vl); 1926 return rv; 1927 } 1928 1929 int tb_printf_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w, 1930 const char *fmt, ...) { 1931 int rv; 1932 va_list vl; 1933 va_start(vl, fmt); 1934 rv = tb_printf_inner(x, y, fg, bg, out_w, fmt, vl); 1935 va_end(vl); 1936 return rv; 1937 } 1938 1939 int tb_send(const char *buf, size_t nbuf) { 1940 return bytebuf_nputs(&global.out, buf, nbuf); 1941 } 1942 1943 int tb_sendf(const char *fmt, ...) { 1944 int rv; 1945 char buf[TB_OPT_PRINTF_BUF]; 1946 va_list vl; 1947 va_start(vl, fmt); 1948 rv = vsnprintf(buf, sizeof(buf), fmt, vl); 1949 va_end(vl); 1950 if (rv < 0 || rv >= (int)sizeof(buf)) { 1951 return TB_ERR; 1952 } 1953 return tb_send(buf, (size_t)rv); 1954 } 1955 1956 int tb_set_func(int fn_type, int (*fn)(struct tb_event *, size_t *)) { 1957 switch (fn_type) { 1958 case TB_FUNC_EXTRACT_PRE: 1959 global.fn_extract_esc_pre = fn; 1960 return TB_OK; 1961 case TB_FUNC_EXTRACT_POST: 1962 global.fn_extract_esc_post = fn; 1963 return TB_OK; 1964 } 1965 return TB_ERR; 1966 } 1967 1968 struct tb_cell *tb_cell_buffer(void) { 1969 if (!global.initialized) return NULL; 1970 return global.back.cells; 1971 } 1972 1973 int tb_utf8_char_length(char c) { 1974 return utf8_length[(unsigned char)c]; 1975 } 1976 1977 int tb_utf8_char_to_unicode(uint32_t *out, const char *c) { 1978 if (*c == '\0') return 0; 1979 1980 int i; 1981 unsigned char len = tb_utf8_char_length(*c); 1982 unsigned char mask = utf8_mask[len - 1]; 1983 uint32_t result = c[0] & mask; 1984 for (i = 1; i < len && c[i] != '\0'; ++i) { 1985 result <<= 6; 1986 result |= c[i] & 0x3f; 1987 } 1988 1989 if (i != len) return i * -1; 1990 1991 *out = result; 1992 return (int)len; 1993 } 1994 1995 int tb_utf8_unicode_to_char(char *out, uint32_t c) { 1996 int len = 0; 1997 int first; 1998 int i; 1999 2000 if (c < 0x80) { 2001 first = 0; 2002 len = 1; 2003 } else if (c < 0x800) { 2004 first = 0xc0; 2005 len = 2; 2006 } else if (c < 0x10000) { 2007 first = 0xe0; 2008 len = 3; 2009 } else if (c < 0x200000) { 2010 first = 0xf0; 2011 len = 4; 2012 } else if (c < 0x4000000) { 2013 first = 0xf8; 2014 len = 5; 2015 } else { 2016 first = 0xfc; 2017 len = 6; 2018 } 2019 2020 for (i = len - 1; i > 0; --i) { 2021 out[i] = (c & 0x3f) | 0x80; 2022 c >>= 6; 2023 } 2024 out[0] = c | first; 2025 out[len] = '\0'; 2026 2027 return len; 2028 } 2029 2030 int tb_last_errno(void) { 2031 return global.last_errno; 2032 } 2033 2034 const char *tb_strerror(int err) { 2035 switch (err) { 2036 case TB_OK: 2037 return "Success"; 2038 case TB_ERR_NEED_MORE: 2039 return "Not enough input"; 2040 case TB_ERR_INIT_ALREADY: 2041 return "Termbox initialized already"; 2042 case TB_ERR_MEM: 2043 return "Out of memory"; 2044 case TB_ERR_NO_EVENT: 2045 return "No event"; 2046 case TB_ERR_NO_TERM: 2047 return "No TERM in environment"; 2048 case TB_ERR_NOT_INIT: 2049 return "Termbox not initialized"; 2050 case TB_ERR_OUT_OF_BOUNDS: 2051 return "Out of bounds"; 2052 case TB_ERR_UNSUPPORTED_TERM: 2053 return "Unsupported terminal"; 2054 case TB_ERR_CAP_COLLISION: 2055 return "Termcaps collision"; 2056 case TB_ERR_RESIZE_SSCANF: 2057 return "Terminal width/height not received by sscanf() after " 2058 "resize"; 2059 case TB_ERR: 2060 case TB_ERR_INIT_OPEN: 2061 case TB_ERR_READ: 2062 case TB_ERR_RESIZE_IOCTL: 2063 case TB_ERR_RESIZE_PIPE: 2064 case TB_ERR_RESIZE_SIGACTION: 2065 case TB_ERR_POLL: 2066 case TB_ERR_TCGETATTR: 2067 case TB_ERR_TCSETATTR: 2068 case TB_ERR_RESIZE_WRITE: 2069 case TB_ERR_RESIZE_POLL: 2070 case TB_ERR_RESIZE_READ: 2071 default: 2072 strerror_r(global.last_errno, global.errbuf, sizeof(global.errbuf)); 2073 return (const char *)global.errbuf; 2074 } 2075 } 2076 2077 int tb_has_truecolor(void) { 2078 #if TB_OPT_ATTR_W >= 32 2079 return 1; 2080 #else 2081 return 0; 2082 #endif 2083 } 2084 2085 int tb_has_egc(void) { 2086 #ifdef TB_OPT_EGC 2087 return 1; 2088 #else 2089 return 0; 2090 #endif 2091 } 2092 2093 int tb_attr_width(void) { 2094 return TB_OPT_ATTR_W; 2095 } 2096 2097 const char *tb_version(void) { 2098 return TB_VERSION_STR; 2099 } 2100 2101 static int tb_reset(void) { 2102 int ttyfd_open = global.ttyfd_open; 2103 memset(&global, 0, sizeof(global)); 2104 global.ttyfd = -1; 2105 global.rfd = -1; 2106 global.wfd = -1; 2107 global.ttyfd_open = ttyfd_open; 2108 global.resize_pipefd[0] = -1; 2109 global.resize_pipefd[1] = -1; 2110 global.width = -1; 2111 global.height = -1; 2112 global.cursor_x = -1; 2113 global.cursor_y = -1; 2114 global.last_x = -1; 2115 global.last_y = -1; 2116 global.fg = TB_DEFAULT; 2117 global.bg = TB_DEFAULT; 2118 global.last_fg = ~global.fg; 2119 global.last_bg = ~global.bg; 2120 global.input_mode = TB_INPUT_ESC; 2121 global.output_mode = TB_OUTPUT_NORMAL; 2122 return TB_OK; 2123 } 2124 2125 static int init_term_attrs(void) { 2126 if (global.ttyfd < 0) { 2127 return TB_OK; 2128 } 2129 2130 if (tcgetattr(global.ttyfd, &global.orig_tios) != 0) { 2131 global.last_errno = errno; 2132 return TB_ERR_TCGETATTR; 2133 } 2134 2135 struct termios tios; 2136 memcpy(&tios, &global.orig_tios, sizeof(tios)); 2137 global.has_orig_tios = 1; 2138 2139 cfmakeraw(&tios); 2140 tios.c_cc[VMIN] = 1; 2141 tios.c_cc[VTIME] = 0; 2142 2143 if (tcsetattr(global.ttyfd, TCSAFLUSH, &tios) != 0) { 2144 global.last_errno = errno; 2145 return TB_ERR_TCSETATTR; 2146 } 2147 2148 return TB_OK; 2149 } 2150 2151 int tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w, 2152 const char *fmt, va_list vl) { 2153 int rv; 2154 char buf[TB_OPT_PRINTF_BUF]; 2155 rv = vsnprintf(buf, sizeof(buf), fmt, vl); 2156 if (rv < 0 || rv >= (int)sizeof(buf)) { 2157 return TB_ERR; 2158 } 2159 return tb_print_ex(x, y, fg, bg, out_w, buf); 2160 } 2161 2162 static int init_term_caps(void) { 2163 if (load_terminfo() == TB_OK) { 2164 return parse_terminfo_caps(); 2165 } 2166 return load_builtin_caps(); 2167 } 2168 2169 static int init_cap_trie(void) { 2170 int rv, i; 2171 2172 // Add caps from terminfo or built-in 2173 // 2174 // Collisions are expected as some terminfo entries have dupes. (For 2175 // example, att605-pc collides on TB_CAP_F4 and TB_CAP_DELETE.) First cap 2176 // in TB_CAP_* index order will win. 2177 // 2178 // TODO: Reorder TB_CAP_* so more critical caps come first. 2179 for (i = 0; i < TB_CAP__COUNT_KEYS; i++) { 2180 rv = cap_trie_add(global.caps[i], tb_key_i(i), 0); 2181 if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv; 2182 } 2183 2184 // Add built-in mod caps 2185 // 2186 // Collisions are OK here as well. This can happen if global.caps collides 2187 // with builtin_mod_caps. It is desirable to give precedence to global.caps 2188 // here. 2189 for (i = 0; builtin_mod_caps[i].cap != NULL; i++) { 2190 rv = cap_trie_add(builtin_mod_caps[i].cap, builtin_mod_caps[i].key, 2191 builtin_mod_caps[i].mod); 2192 if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv; 2193 } 2194 2195 return TB_OK; 2196 } 2197 2198 static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod) { 2199 struct cap_trie_t *next, *node = &global.cap_trie; 2200 size_t i, j; 2201 2202 if (!cap || strlen(cap) <= 0) return TB_OK; // Nothing to do for empty caps 2203 2204 for (i = 0; cap[i] != '\0'; i++) { 2205 char c = cap[i]; 2206 next = NULL; 2207 2208 // Check if c is already a child of node 2209 for (j = 0; j < node->nchildren; j++) { 2210 if (node->children[j].c == c) { 2211 next = &node->children[j]; 2212 break; 2213 } 2214 } 2215 if (!next) { 2216 // We need to add a new child to node 2217 node->nchildren += 1; 2218 node->children = (struct cap_trie_t *)tb_realloc(node->children, 2219 sizeof(*node) * node->nchildren); 2220 if (!node->children) { 2221 return TB_ERR_MEM; 2222 } 2223 next = &node->children[node->nchildren - 1]; 2224 memset(next, 0, sizeof(*next)); 2225 next->c = c; 2226 } 2227 2228 // Continue 2229 node = next; 2230 } 2231 2232 if (node->is_leaf) { 2233 // Already a leaf here 2234 return TB_ERR_CAP_COLLISION; 2235 } 2236 2237 node->is_leaf = 1; 2238 node->key = key; 2239 node->mod = mod; 2240 return TB_OK; 2241 } 2242 2243 static int cap_trie_find(const char *buf, size_t nbuf, struct cap_trie_t **last, 2244 size_t *depth) { 2245 struct cap_trie_t *next, *node = &global.cap_trie; 2246 size_t i, j; 2247 *last = node; 2248 *depth = 0; 2249 for (i = 0; i < nbuf; i++) { 2250 char c = buf[i]; 2251 next = NULL; 2252 2253 // Find c in node.children 2254 for (j = 0; j < node->nchildren; j++) { 2255 if (node->children[j].c == c) { 2256 next = &node->children[j]; 2257 break; 2258 } 2259 } 2260 if (!next) { 2261 // Not found 2262 return TB_OK; 2263 } 2264 node = next; 2265 *last = node; 2266 *depth += 1; 2267 if (node->is_leaf && node->nchildren < 1) { 2268 break; 2269 } 2270 } 2271 return TB_OK; 2272 } 2273 2274 static int cap_trie_deinit(struct cap_trie_t *node) { 2275 size_t j; 2276 for (j = 0; j < node->nchildren; j++) { 2277 cap_trie_deinit(&node->children[j]); 2278 } 2279 if (node->children) { 2280 tb_free(node->children); 2281 } 2282 memset(node, 0, sizeof(*node)); 2283 return TB_OK; 2284 } 2285 2286 static int init_resize_handler(void) { 2287 if (pipe(global.resize_pipefd) != 0) { 2288 global.last_errno = errno; 2289 return TB_ERR_RESIZE_PIPE; 2290 } 2291 2292 struct sigaction sa; 2293 memset(&sa, 0, sizeof(sa)); 2294 sa.sa_handler = handle_resize; 2295 if (sigaction(SIGWINCH, &sa, NULL) != 0) { 2296 global.last_errno = errno; 2297 return TB_ERR_RESIZE_SIGACTION; 2298 } 2299 2300 return TB_OK; 2301 } 2302 2303 static int send_init_escape_codes(void) { 2304 int rv; 2305 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_CA])); 2306 if_err_return(rv, 2307 bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_KEYPAD])); 2308 if_err_return(rv, 2309 bytebuf_puts(&global.out, global.caps[TB_CAP_HIDE_CURSOR])); 2310 return TB_OK; 2311 } 2312 2313 static int send_clear(void) { 2314 int rv; 2315 2316 if_err_return(rv, send_attr(global.fg, global.bg)); 2317 if_err_return(rv, 2318 bytebuf_puts(&global.out, global.caps[TB_CAP_CLEAR_SCREEN])); 2319 2320 if_err_return(rv, send_cursor_if(global.cursor_x, global.cursor_y)); 2321 if_err_return(rv, bytebuf_flush(&global.out, global.wfd)); 2322 2323 global.last_x = -1; 2324 global.last_y = -1; 2325 2326 return TB_OK; 2327 } 2328 2329 static int update_term_size(void) { 2330 int rv, ioctl_errno; 2331 2332 if (global.ttyfd < 0) { 2333 return TB_OK; 2334 } 2335 2336 struct winsize sz; 2337 memset(&sz, 0, sizeof(sz)); 2338 2339 // Try ioctl TIOCGWINSZ 2340 if (ioctl(global.ttyfd, TIOCGWINSZ, &sz) == 0) { 2341 global.width = sz.ws_col; 2342 global.height = sz.ws_row; 2343 return TB_OK; 2344 } 2345 ioctl_errno = errno; 2346 2347 // Try >cursor(9999,9999), >u7, <u6 2348 if_ok_return(rv, update_term_size_via_esc()); 2349 2350 global.last_errno = ioctl_errno; 2351 return TB_ERR_RESIZE_IOCTL; 2352 } 2353 2354 static int update_term_size_via_esc(void) { 2355 #ifndef TB_RESIZE_FALLBACK_MS 2356 #define TB_RESIZE_FALLBACK_MS 1000 2357 #endif 2358 2359 char move_and_report[] = "\x1b[9999;9999H\x1b[6n"; 2360 ssize_t write_rv = 2361 write(global.wfd, move_and_report, strlen(move_and_report)); 2362 if (write_rv != (ssize_t)strlen(move_and_report)) { 2363 return TB_ERR_RESIZE_WRITE; 2364 } 2365 2366 fd_set fds; 2367 FD_ZERO(&fds); 2368 FD_SET(global.rfd, &fds); 2369 2370 struct timeval timeout; 2371 timeout.tv_sec = 0; 2372 timeout.tv_usec = TB_RESIZE_FALLBACK_MS * 1000; 2373 2374 int select_rv = select(global.rfd + 1, &fds, NULL, NULL, &timeout); 2375 2376 if (select_rv != 1) { 2377 global.last_errno = errno; 2378 return TB_ERR_RESIZE_POLL; 2379 } 2380 2381 char buf[TB_OPT_READ_BUF]; 2382 ssize_t read_rv = read(global.rfd, buf, sizeof(buf) - 1); 2383 if (read_rv < 1) { 2384 global.last_errno = errno; 2385 return TB_ERR_RESIZE_READ; 2386 } 2387 buf[read_rv] = '\0'; 2388 2389 int rw, rh; 2390 if (sscanf(buf, "\x1b[%d;%dR", &rh, &rw) != 2) { 2391 return TB_ERR_RESIZE_SSCANF; 2392 } 2393 2394 global.width = rw; 2395 global.height = rh; 2396 return TB_OK; 2397 } 2398 2399 static int init_cellbuf(void) { 2400 int rv; 2401 if_err_return(rv, cellbuf_init(&global.back, global.width, global.height)); 2402 if_err_return(rv, cellbuf_init(&global.front, global.width, global.height)); 2403 if_err_return(rv, cellbuf_clear(&global.back)); 2404 if_err_return(rv, cellbuf_clear(&global.front)); 2405 return TB_OK; 2406 } 2407 2408 static int tb_deinit(void) { 2409 if (global.caps[0] != NULL && global.wfd >= 0) { 2410 bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]); 2411 bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]); 2412 bytebuf_puts(&global.out, global.caps[TB_CAP_CLEAR_SCREEN]); 2413 bytebuf_puts(&global.out, global.caps[TB_CAP_EXIT_CA]); 2414 bytebuf_puts(&global.out, global.caps[TB_CAP_EXIT_KEYPAD]); 2415 bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE); 2416 bytebuf_flush(&global.out, global.wfd); 2417 } 2418 if (global.ttyfd >= 0) { 2419 if (global.has_orig_tios) { 2420 tcsetattr(global.ttyfd, TCSAFLUSH, &global.orig_tios); 2421 } 2422 if (global.ttyfd_open) { 2423 close(global.ttyfd); 2424 global.ttyfd_open = 0; 2425 } 2426 } 2427 2428 struct sigaction sa; 2429 memset(&sa, 0, sizeof(sa)); 2430 sa.sa_handler = SIG_DFL; 2431 sigaction(SIGWINCH, &sa, NULL); 2432 if (global.resize_pipefd[0] >= 0) close(global.resize_pipefd[0]); 2433 if (global.resize_pipefd[1] >= 0) close(global.resize_pipefd[1]); 2434 2435 cellbuf_free(&global.back); 2436 cellbuf_free(&global.front); 2437 bytebuf_free(&global.in); 2438 bytebuf_free(&global.out); 2439 2440 if (global.terminfo) tb_free(global.terminfo); 2441 2442 cap_trie_deinit(&global.cap_trie); 2443 2444 tb_reset(); 2445 return TB_OK; 2446 } 2447 2448 static int load_terminfo(void) { 2449 int rv; 2450 char tmp[TB_PATH_MAX]; 2451 2452 // See terminfo(5) "Fetching Compiled Descriptions" for a description of 2453 // this behavior. Some of these paths are compile-time ncurses options, so 2454 // best guesses are used here. 2455 const char *term = getenv("TERM"); 2456 if (!term) { 2457 return TB_ERR; 2458 } 2459 2460 // If TERMINFO is set, try that directory and stop 2461 const char *terminfo = getenv("TERMINFO"); 2462 if (terminfo) { 2463 return load_terminfo_from_path(terminfo, term); 2464 } 2465 2466 // Next try ~/.terminfo 2467 const char *home = getenv("HOME"); 2468 if (home) { 2469 snprintf_or_return(rv, tmp, sizeof(tmp), "%s/.terminfo", home); 2470 if_ok_return(rv, load_terminfo_from_path(tmp, term)); 2471 } 2472 2473 // Next try TERMINFO_DIRS 2474 // 2475 // Note, empty entries are supposed to be interpretted as the "compiled-in 2476 // default", which is of course system-dependent. Previously /etc/terminfo 2477 // was used here. Let's skip empty entries altogether rather than give 2478 // precedence to a guess, and check common paths after this loop. 2479 const char *dirs = getenv("TERMINFO_DIRS"); 2480 if (dirs) { 2481 snprintf_or_return(rv, tmp, sizeof(tmp), "%s", dirs); 2482 char *dir = strtok(tmp, ":"); 2483 while (dir) { 2484 const char *cdir = dir; 2485 if (*cdir != '\0') { 2486 if_ok_return(rv, load_terminfo_from_path(cdir, term)); 2487 } 2488 dir = strtok(NULL, ":"); 2489 } 2490 } 2491 2492 #ifdef TB_TERMINFO_DIR 2493 if_ok_return(rv, load_terminfo_from_path(TB_TERMINFO_DIR, term)); 2494 #endif 2495 if_ok_return(rv, load_terminfo_from_path("/usr/local/etc/terminfo", term)); 2496 if_ok_return(rv, 2497 load_terminfo_from_path("/usr/local/share/terminfo", term)); 2498 if_ok_return(rv, load_terminfo_from_path("/usr/local/lib/terminfo", term)); 2499 if_ok_return(rv, load_terminfo_from_path("/etc/terminfo", term)); 2500 if_ok_return(rv, load_terminfo_from_path("/usr/share/terminfo", term)); 2501 if_ok_return(rv, load_terminfo_from_path("/usr/lib/terminfo", term)); 2502 if_ok_return(rv, load_terminfo_from_path("/usr/share/lib/terminfo", term)); 2503 if_ok_return(rv, load_terminfo_from_path("/lib/terminfo", term)); 2504 2505 return TB_ERR; 2506 } 2507 2508 static int load_terminfo_from_path(const char *path, const char *term) { 2509 int rv; 2510 char tmp[TB_PATH_MAX]; 2511 2512 // Look for term at this terminfo location, e.g., <terminfo>/x/xterm 2513 snprintf_or_return(rv, tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term); 2514 if_ok_return(rv, read_terminfo_path(tmp)); 2515 2516 #ifdef __APPLE__ 2517 // Try the Darwin equivalent path, e.g., <terminfo>/78/xterm 2518 snprintf_or_return(rv, tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term); 2519 return read_terminfo_path(tmp); 2520 #endif 2521 2522 return TB_ERR; 2523 } 2524 2525 static int read_terminfo_path(const char *path) { 2526 FILE *fp = fopen(path, "rb"); 2527 if (!fp) { 2528 return TB_ERR; 2529 } 2530 2531 struct stat st; 2532 if (fstat(fileno(fp), &st) != 0) { 2533 fclose(fp); 2534 return TB_ERR; 2535 } 2536 2537 size_t fsize = st.st_size; 2538 char *data = (char *)tb_malloc(fsize); 2539 if (!data) { 2540 fclose(fp); 2541 return TB_ERR; 2542 } 2543 2544 if (fread(data, 1, fsize, fp) != fsize) { 2545 fclose(fp); 2546 tb_free(data); 2547 return TB_ERR; 2548 } 2549 2550 global.terminfo = data; 2551 global.nterminfo = fsize; 2552 2553 fclose(fp); 2554 return TB_OK; 2555 } 2556 2557 static int parse_terminfo_caps(void) { 2558 // See term(5) "LEGACY STORAGE FORMAT" and "EXTENDED STORAGE FORMAT" for a 2559 // description of this behavior. 2560 2561 // Ensure there's at least a header's worth of data 2562 if (global.nterminfo < 6) { 2563 return TB_ERR; 2564 } 2565 2566 int16_t *header = (int16_t *)global.terminfo; 2567 // header[0] the magic number (octal 0432 or 01036) 2568 // header[1] the size, in bytes, of the names section 2569 // header[2] the number of bytes in the boolean section 2570 // header[3] the number of short integers in the numbers section 2571 // header[4] the number of offsets (short integers) in the strings section 2572 // header[5] the size, in bytes, of the string table 2573 2574 // Legacy ints are 16-bit, extended ints are 32-bit 2575 const int bytes_per_int = header[0] == 01036 ? 4 // 32-bit 2576 : 2; // 16-bit 2577 2578 // > Between the boolean section and the number section, a null byte will be 2579 // > inserted, if necessary, to ensure that the number section begins on an 2580 // > even byte 2581 const int align_offset = (header[1] + header[2]) % 2 != 0 ? 1 : 0; 2582 2583 const int pos_str_offsets = 2584 (6 * sizeof(int16_t)) // header (12 bytes) 2585 + header[1] // length of names section 2586 + header[2] // length of boolean section 2587 + align_offset + 2588 (header[3] * bytes_per_int); // length of numbers section 2589 2590 const int pos_str_table = 2591 pos_str_offsets + 2592 (header[4] * sizeof(int16_t)); // length of string offsets table 2593 2594 // Load caps 2595 int i; 2596 for (i = 0; i < TB_CAP__COUNT; i++) { 2597 const char *cap = get_terminfo_string(pos_str_offsets, header[4], 2598 pos_str_table, header[5], terminfo_cap_indexes[i]); 2599 if (!cap) { 2600 // Something is not right 2601 return TB_ERR; 2602 } 2603 global.caps[i] = cap; 2604 } 2605 2606 return TB_OK; 2607 } 2608 2609 static int load_builtin_caps(void) { 2610 int i, j; 2611 const char *term = getenv("TERM"); 2612 2613 if (!term) { 2614 return TB_ERR_NO_TERM; 2615 } 2616 2617 // Check for exact TERM match 2618 for (i = 0; builtin_terms[i].name != NULL; i++) { 2619 if (strcmp(term, builtin_terms[i].name) == 0) { 2620 for (j = 0; j < TB_CAP__COUNT; j++) { 2621 global.caps[j] = builtin_terms[i].caps[j]; 2622 } 2623 return TB_OK; 2624 } 2625 } 2626 2627 // Check for partial TERM or alias match 2628 for (i = 0; builtin_terms[i].name != NULL; i++) { 2629 if (strstr(term, builtin_terms[i].name) != NULL || 2630 (*(builtin_terms[i].alias) != '\0' && 2631 strstr(term, builtin_terms[i].alias) != NULL)) 2632 { 2633 for (j = 0; j < TB_CAP__COUNT; j++) { 2634 global.caps[j] = builtin_terms[i].caps[j]; 2635 } 2636 return TB_OK; 2637 } 2638 } 2639 2640 return TB_ERR_UNSUPPORTED_TERM; 2641 } 2642 2643 static const char *get_terminfo_string(int16_t str_offsets_pos, 2644 int16_t str_offsets_len, int16_t str_table_pos, int16_t str_table_len, 2645 int16_t str_index) { 2646 const int str_byte_index = (int)str_index * (int)sizeof(int16_t); 2647 if (str_byte_index >= (int)str_offsets_len * (int)sizeof(int16_t)) { 2648 // An offset beyond the table indicates absent 2649 // See `convert_strings` in tinfo `read_entry.c` 2650 return ""; 2651 } 2652 const int16_t *str_offset = 2653 (int16_t *)(global.terminfo + (int)str_offsets_pos + str_byte_index); 2654 if ((char *)str_offset >= global.terminfo + global.nterminfo) { 2655 // str_offset points beyond end of entry 2656 // Truncated/corrupt terminfo entry? 2657 return NULL; 2658 } 2659 if (*str_offset < 0 || *str_offset >= str_table_len) { 2660 // A negative offset indicates absent 2661 // An offset beyond the table indicates absent 2662 // See `convert_strings` in tinfo `read_entry.c` 2663 return ""; 2664 } 2665 if (((size_t)((int)str_table_pos + (int)*str_offset)) >= global.nterminfo) { 2666 // string points beyond end of entry 2667 // Truncated/corrupt terminfo entry? 2668 return NULL; 2669 } 2670 return ( 2671 const char *)(global.terminfo + (int)str_table_pos + (int)*str_offset); 2672 } 2673 2674 static int wait_event(struct tb_event *event, int timeout) { 2675 int rv; 2676 char buf[TB_OPT_READ_BUF]; 2677 2678 memset(event, 0, sizeof(*event)); 2679 if_ok_return(rv, extract_event(event)); 2680 2681 fd_set fds; 2682 struct timeval tv; 2683 tv.tv_sec = timeout / 1000; 2684 tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000; 2685 2686 do { 2687 FD_ZERO(&fds); 2688 FD_SET(global.rfd, &fds); 2689 FD_SET(global.resize_pipefd[0], &fds); 2690 2691 int maxfd = global.resize_pipefd[0] > global.rfd 2692 ? global.resize_pipefd[0] 2693 : global.rfd; 2694 2695 int select_rv = 2696 select(maxfd + 1, &fds, NULL, NULL, (timeout < 0) ? NULL : &tv); 2697 2698 if (select_rv < 0) { 2699 // Let EINTR/EAGAIN bubble up 2700 global.last_errno = errno; 2701 return TB_ERR_POLL; 2702 } else if (select_rv == 0) { 2703 return TB_ERR_NO_EVENT; 2704 } 2705 2706 int tty_has_events = (FD_ISSET(global.rfd, &fds)); 2707 int resize_has_events = (FD_ISSET(global.resize_pipefd[0], &fds)); 2708 2709 if (tty_has_events) { 2710 ssize_t read_rv = read(global.rfd, buf, sizeof(buf)); 2711 if (read_rv < 0) { 2712 global.last_errno = errno; 2713 return TB_ERR_READ; 2714 } else if (read_rv > 0) { 2715 bytebuf_nputs(&global.in, buf, read_rv); 2716 } 2717 } 2718 2719 if (resize_has_events) { 2720 int ignore = 0; 2721 read(global.resize_pipefd[0], &ignore, sizeof(ignore)); 2722 // TODO: Harden against errors encountered mid-resize 2723 if_err_return(rv, update_term_size()); 2724 if_err_return(rv, resize_cellbufs()); 2725 event->type = TB_EVENT_RESIZE; 2726 event->w = global.width; 2727 event->h = global.height; 2728 return TB_OK; 2729 } 2730 2731 memset(event, 0, sizeof(*event)); 2732 if_ok_return(rv, extract_event(event)); 2733 } while (timeout == -1); 2734 2735 return rv; 2736 } 2737 2738 static int extract_event(struct tb_event *event) { 2739 int rv; 2740 struct bytebuf_t *in = &global.in; 2741 2742 if (in->len == 0) { 2743 return TB_ERR; 2744 } 2745 2746 if (in->buf[0] == '\x1b') { 2747 // Escape sequence? 2748 // In TB_INPUT_ESC, skip if the buffer is a single escape char 2749 if (!((global.input_mode & TB_INPUT_ESC) && in->len == 1)) { 2750 if_ok_or_need_more_return(rv, extract_esc(event)); 2751 } 2752 2753 // Escape key? 2754 if (global.input_mode & TB_INPUT_ESC) { 2755 event->type = TB_EVENT_KEY; 2756 event->ch = 0; 2757 event->key = TB_KEY_ESC; 2758 event->mod = 0; 2759 bytebuf_shift(in, 1); 2760 return TB_OK; 2761 } 2762 2763 // Recurse for alt key 2764 event->mod |= TB_MOD_ALT; 2765 bytebuf_shift(in, 1); 2766 return extract_event(event); 2767 } 2768 2769 // ASCII control key? 2770 if ((uint16_t)in->buf[0] < TB_KEY_SPACE || in->buf[0] == TB_KEY_BACKSPACE2) 2771 { 2772 event->type = TB_EVENT_KEY; 2773 event->ch = 0; 2774 event->key = (uint16_t)in->buf[0]; 2775 event->mod |= TB_MOD_CTRL; 2776 bytebuf_shift(in, 1); 2777 return TB_OK; 2778 } 2779 2780 // UTF-8? 2781 if (in->len >= (size_t)tb_utf8_char_length(in->buf[0])) { 2782 event->type = TB_EVENT_KEY; 2783 tb_utf8_char_to_unicode(&event->ch, in->buf); 2784 event->key = 0; 2785 bytebuf_shift(in, tb_utf8_char_length(in->buf[0])); 2786 return TB_OK; 2787 } 2788 2789 // Need more input 2790 return TB_ERR; 2791 } 2792 2793 static int extract_esc(struct tb_event *event) { 2794 int rv; 2795 if_ok_or_need_more_return(rv, extract_esc_user(event, 0)); 2796 if_ok_or_need_more_return(rv, extract_esc_cap(event)); 2797 if_ok_or_need_more_return(rv, extract_esc_mouse(event)); 2798 if_ok_or_need_more_return(rv, extract_esc_user(event, 1)); 2799 return TB_ERR; 2800 } 2801 2802 static int extract_esc_user(struct tb_event *event, int is_post) { 2803 int rv; 2804 size_t consumed = 0; 2805 struct bytebuf_t *in = &global.in; 2806 int (*fn)(struct tb_event *, size_t *); 2807 2808 fn = is_post ? global.fn_extract_esc_post : global.fn_extract_esc_pre; 2809 2810 if (!fn) { 2811 return TB_ERR; 2812 } 2813 2814 rv = fn(event, &consumed); 2815 if (rv == TB_OK) { 2816 bytebuf_shift(in, consumed); 2817 } 2818 2819 if_ok_or_need_more_return(rv, rv); 2820 return TB_ERR; 2821 } 2822 2823 static int extract_esc_cap(struct tb_event *event) { 2824 int rv; 2825 struct bytebuf_t *in = &global.in; 2826 struct cap_trie_t *node; 2827 size_t depth; 2828 2829 if_err_return(rv, cap_trie_find(in->buf, in->len, &node, &depth)); 2830 if (node->is_leaf) { 2831 // Found a leaf node 2832 event->type = TB_EVENT_KEY; 2833 event->ch = 0; 2834 event->key = node->key; 2835 event->mod = node->mod; 2836 bytebuf_shift(in, depth); 2837 return TB_OK; 2838 } else if (node->nchildren > 0 && in->len <= depth) { 2839 // Found a branch node (not enough input) 2840 return TB_ERR_NEED_MORE; 2841 } 2842 2843 return TB_ERR; 2844 } 2845 2846 static int extract_esc_mouse(struct tb_event *event) { 2847 struct bytebuf_t *in = &global.in; 2848 2849 enum { TYPE_VT200 = 0, TYPE_1006, TYPE_1015, TYPE_MAX }; 2850 2851 const char *cmp[TYPE_MAX] = {// 2852 // X10 mouse encoding, the simplest one 2853 // \x1b [ M Cb Cx Cy 2854 [TYPE_VT200] = "\x1b[M", 2855 // xterm 1006 extended mode or urxvt 1015 extended mode 2856 // xterm: \x1b [ < Cb ; Cx ; Cy (M or m) 2857 [TYPE_1006] = "\x1b[<", 2858 // urxvt: \x1b [ Cb ; Cx ; Cy M 2859 [TYPE_1015] = "\x1b["}; 2860 2861 int type = 0; 2862 int ret = TB_ERR; 2863 2864 // Unrolled at compile-time (probably) 2865 for (; type < TYPE_MAX; type++) { 2866 size_t size = strlen(cmp[type]); 2867 2868 if (in->len >= size && (strncmp(cmp[type], in->buf, size)) == 0) { 2869 break; 2870 } 2871 } 2872 2873 if (type == TYPE_MAX) { 2874 ret = TB_ERR; // No match 2875 return ret; 2876 } 2877 2878 size_t buf_shift = 0; 2879 2880 switch (type) { 2881 case TYPE_VT200: 2882 if (in->len >= 6) { 2883 int b = in->buf[3] - 0x20; 2884 int fail = 0; 2885 2886 switch (b & 3) { 2887 case 0: 2888 event->key = ((b & 64) != 0) ? TB_KEY_MOUSE_WHEEL_UP 2889 : TB_KEY_MOUSE_LEFT; 2890 break; 2891 case 1: 2892 event->key = ((b & 64) != 0) ? TB_KEY_MOUSE_WHEEL_DOWN 2893 : TB_KEY_MOUSE_MIDDLE; 2894 break; 2895 case 2: 2896 event->key = TB_KEY_MOUSE_RIGHT; 2897 break; 2898 case 3: 2899 event->key = TB_KEY_MOUSE_RELEASE; 2900 break; 2901 default: 2902 ret = TB_ERR; 2903 fail = 1; 2904 break; 2905 } 2906 2907 if (!fail) { 2908 if ((b & 32) != 0) { 2909 event->mod |= TB_MOD_MOTION; 2910 } 2911 2912 // the coord is 1,1 for upper left 2913 event->x = ((uint8_t)in->buf[4]) - 0x21; 2914 event->y = ((uint8_t)in->buf[5]) - 0x21; 2915 2916 ret = TB_OK; 2917 } 2918 2919 buf_shift = 6; 2920 } 2921 break; 2922 case TYPE_1006: 2923 // fallthrough 2924 case TYPE_1015: { 2925 size_t index_fail = (size_t)-1; 2926 2927 enum { 2928 FIRST_M = 0, 2929 FIRST_SEMICOLON, 2930 LAST_SEMICOLON, 2931 FIRST_LAST_MAX 2932 }; 2933 2934 size_t indices[FIRST_LAST_MAX] = {index_fail, index_fail, 2935 index_fail}; 2936 int m_is_capital = 0; 2937 2938 for (size_t i = 0; i < in->len; i++) { 2939 if (in->buf[i] == ';') { 2940 if (indices[FIRST_SEMICOLON] == index_fail) { 2941 indices[FIRST_SEMICOLON] = i; 2942 } else { 2943 indices[LAST_SEMICOLON] = i; 2944 } 2945 } else if (indices[FIRST_M] == index_fail) { 2946 if (in->buf[i] == 'm' || in->buf[i] == 'M') { 2947 m_is_capital = (in->buf[i] == 'M'); 2948 indices[FIRST_M] = i; 2949 } 2950 } 2951 } 2952 2953 if (indices[FIRST_M] == index_fail || 2954 indices[FIRST_SEMICOLON] == index_fail || 2955 indices[LAST_SEMICOLON] == index_fail) 2956 { 2957 ret = TB_ERR; 2958 } else { 2959 int start = (type == TYPE_1015 ? 2 : 3); 2960 2961 unsigned n1 = strtoul(&in->buf[start], NULL, 10); 2962 unsigned n2 = 2963 strtoul(&in->buf[indices[FIRST_SEMICOLON] + 1], NULL, 10); 2964 unsigned n3 = 2965 strtoul(&in->buf[indices[LAST_SEMICOLON] + 1], NULL, 10); 2966 2967 if (type == TYPE_1015) { 2968 n1 -= 0x20; 2969 } 2970 2971 int fail = 0; 2972 2973 switch (n1 & 3) { 2974 case 0: 2975 event->key = ((n1 & 64) != 0) ? TB_KEY_MOUSE_WHEEL_UP 2976 : TB_KEY_MOUSE_LEFT; 2977 break; 2978 case 1: 2979 event->key = ((n1 & 64) != 0) ? TB_KEY_MOUSE_WHEEL_DOWN 2980 : TB_KEY_MOUSE_MIDDLE; 2981 break; 2982 case 2: 2983 event->key = TB_KEY_MOUSE_RIGHT; 2984 break; 2985 case 3: 2986 event->key = TB_KEY_MOUSE_RELEASE; 2987 break; 2988 default: 2989 ret = TB_ERR; 2990 fail = 1; 2991 break; 2992 } 2993 2994 buf_shift = in->len; 2995 2996 if (!fail) { 2997 if (!m_is_capital) { 2998 // on xterm mouse release is signaled by lowercase m 2999 event->key = TB_KEY_MOUSE_RELEASE; 3000 } 3001 3002 if ((n1 & 32) != 0) { 3003 event->mod |= TB_MOD_MOTION; 3004 } 3005 3006 event->x = ((uint8_t)n2) - 1; 3007 event->y = ((uint8_t)n3) - 1; 3008 3009 ret = TB_OK; 3010 } 3011 } 3012 } break; 3013 case TYPE_MAX: 3014 ret = TB_ERR; 3015 } 3016 3017 if (buf_shift > 0) { 3018 bytebuf_shift(in, buf_shift); 3019 } 3020 3021 if (ret == TB_OK) { 3022 event->type = TB_EVENT_MOUSE; 3023 } 3024 3025 return ret; 3026 } 3027 3028 static int resize_cellbufs(void) { 3029 int rv; 3030 if_err_return(rv, 3031 cellbuf_resize(&global.back, global.width, global.height)); 3032 if_err_return(rv, 3033 cellbuf_resize(&global.front, global.width, global.height)); 3034 if_err_return(rv, cellbuf_clear(&global.front)); 3035 if_err_return(rv, send_clear()); 3036 return TB_OK; 3037 } 3038 3039 static void handle_resize(int sig) { 3040 int errno_copy = errno; 3041 write(global.resize_pipefd[1], &sig, sizeof(sig)); 3042 errno = errno_copy; 3043 } 3044 3045 static int send_attr(uintattr_t fg, uintattr_t bg) { 3046 int rv; 3047 3048 if (fg == global.last_fg && bg == global.last_bg) { 3049 return TB_OK; 3050 } 3051 3052 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0])); 3053 3054 uint32_t cfg, cbg; 3055 switch (global.output_mode) { 3056 default: 3057 case TB_OUTPUT_NORMAL: 3058 // The minus 1 below is because our colors are 1-indexed starting 3059 // from black. Black is represented by a 30, 40, 90, or 100 for fg, 3060 // bg, bright fg, or bright bg respectively. Red is 31, 41, 91, 3061 // 101, etc. 3062 cfg = (fg & TB_BRIGHT ? 90 : 30) + (fg & 0x0f) - 1; 3063 cbg = (bg & TB_BRIGHT ? 100 : 40) + (bg & 0x0f) - 1; 3064 break; 3065 3066 case TB_OUTPUT_256: 3067 cfg = fg & 0xff; 3068 cbg = bg & 0xff; 3069 if (fg & TB_HI_BLACK) cfg = 0; 3070 if (bg & TB_HI_BLACK) cbg = 0; 3071 break; 3072 3073 case TB_OUTPUT_216: 3074 cfg = fg & 0xff; 3075 cbg = bg & 0xff; 3076 if (cfg > 216) cfg = 216; 3077 if (cbg > 216) cbg = 216; 3078 cfg += 0x0f; 3079 cbg += 0x0f; 3080 break; 3081 3082 case TB_OUTPUT_GRAYSCALE: 3083 cfg = fg & 0xff; 3084 cbg = bg & 0xff; 3085 if (cfg > 24) cfg = 24; 3086 if (cbg > 24) cbg = 24; 3087 cfg += 0xe7; 3088 cbg += 0xe7; 3089 break; 3090 3091 #if TB_OPT_ATTR_W >= 32 3092 case TB_OUTPUT_TRUECOLOR: 3093 cfg = fg & 0xffffff; 3094 cbg = bg & 0xffffff; 3095 if (fg & TB_HI_BLACK) cfg = 0; 3096 if (bg & TB_HI_BLACK) cbg = 0; 3097 break; 3098 #endif 3099 } 3100 3101 if (fg & TB_BOLD) 3102 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BOLD])); 3103 3104 if (fg & TB_BLINK) 3105 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BLINK])); 3106 3107 if (fg & TB_UNDERLINE) 3108 if_err_return(rv, 3109 bytebuf_puts(&global.out, global.caps[TB_CAP_UNDERLINE])); 3110 3111 if (fg & TB_ITALIC) 3112 if_err_return(rv, 3113 bytebuf_puts(&global.out, global.caps[TB_CAP_ITALIC])); 3114 3115 if (fg & TB_DIM) 3116 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_DIM])); 3117 3118 #if TB_OPT_ATTR_W == 64 3119 if (fg & TB_STRIKEOUT) 3120 if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_STRIKEOUT)); 3121 3122 if (fg & TB_UNDERLINE_2) 3123 if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_UNDERLINE_2)); 3124 3125 if (fg & TB_OVERLINE) 3126 if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_OVERLINE)); 3127 3128 if (fg & TB_INVISIBLE) 3129 if_err_return(rv, 3130 bytebuf_puts(&global.out, global.caps[TB_CAP_INVISIBLE])); 3131 #endif 3132 3133 if ((fg & TB_REVERSE) || (bg & TB_REVERSE)) 3134 if_err_return(rv, 3135 bytebuf_puts(&global.out, global.caps[TB_CAP_REVERSE])); 3136 3137 int fg_is_default = (fg & 0xff) == 0; 3138 int bg_is_default = (bg & 0xff) == 0; 3139 if (global.output_mode == TB_OUTPUT_256) { 3140 if (fg & TB_HI_BLACK) fg_is_default = 0; 3141 if (bg & TB_HI_BLACK) bg_is_default = 0; 3142 } 3143 #if TB_OPT_ATTR_W >= 32 3144 if (global.output_mode == TB_OUTPUT_TRUECOLOR) { 3145 fg_is_default = ((fg & 0xffffff) == 0) && ((fg & TB_HI_BLACK) == 0); 3146 bg_is_default = ((bg & 0xffffff) == 0) && ((bg & TB_HI_BLACK) == 0); 3147 } 3148 #endif 3149 3150 if_err_return(rv, send_sgr(cfg, cbg, fg_is_default, bg_is_default)); 3151 3152 global.last_fg = fg; 3153 global.last_bg = bg; 3154 3155 return TB_OK; 3156 } 3157 3158 static int send_sgr(uint32_t cfg, uint32_t cbg, int fg_is_default, 3159 int bg_is_default) { 3160 int rv; 3161 char nbuf[32]; 3162 3163 if (fg_is_default && bg_is_default) { 3164 return TB_OK; 3165 } 3166 3167 switch (global.output_mode) { 3168 default: 3169 case TB_OUTPUT_NORMAL: 3170 send_literal(rv, "\x1b["); 3171 if (!fg_is_default) { 3172 send_num(rv, nbuf, cfg); 3173 if (!bg_is_default) { 3174 send_literal(rv, ";"); 3175 } 3176 } 3177 if (!bg_is_default) { 3178 send_num(rv, nbuf, cbg); 3179 } 3180 send_literal(rv, "m"); 3181 break; 3182 3183 case TB_OUTPUT_256: 3184 case TB_OUTPUT_216: 3185 case TB_OUTPUT_GRAYSCALE: 3186 send_literal(rv, "\x1b["); 3187 if (!fg_is_default) { 3188 send_literal(rv, "38;5;"); 3189 send_num(rv, nbuf, cfg); 3190 if (!bg_is_default) { 3191 send_literal(rv, ";"); 3192 } 3193 } 3194 if (!bg_is_default) { 3195 send_literal(rv, "48;5;"); 3196 send_num(rv, nbuf, cbg); 3197 } 3198 send_literal(rv, "m"); 3199 break; 3200 3201 #if TB_OPT_ATTR_W >= 32 3202 case TB_OUTPUT_TRUECOLOR: 3203 send_literal(rv, "\x1b["); 3204 if (!fg_is_default) { 3205 send_literal(rv, "38;2;"); 3206 send_num(rv, nbuf, (cfg >> 16) & 0xff); 3207 send_literal(rv, ";"); 3208 send_num(rv, nbuf, (cfg >> 8) & 0xff); 3209 send_literal(rv, ";"); 3210 send_num(rv, nbuf, cfg & 0xff); 3211 if (!bg_is_default) { 3212 send_literal(rv, ";"); 3213 } 3214 } 3215 if (!bg_is_default) { 3216 send_literal(rv, "48;2;"); 3217 send_num(rv, nbuf, (cbg >> 16) & 0xff); 3218 send_literal(rv, ";"); 3219 send_num(rv, nbuf, (cbg >> 8) & 0xff); 3220 send_literal(rv, ";"); 3221 send_num(rv, nbuf, cbg & 0xff); 3222 } 3223 send_literal(rv, "m"); 3224 break; 3225 #endif 3226 } 3227 return TB_OK; 3228 } 3229 3230 static int send_cursor_if(int x, int y) { 3231 int rv; 3232 char nbuf[32]; 3233 if (x < 0 || y < 0) { 3234 return TB_OK; 3235 } 3236 send_literal(rv, "\x1b["); 3237 send_num(rv, nbuf, y + 1); 3238 send_literal(rv, ";"); 3239 send_num(rv, nbuf, x + 1); 3240 send_literal(rv, "H"); 3241 return TB_OK; 3242 } 3243 3244 static int send_char(int x, int y, uint32_t ch) { 3245 return send_cluster(x, y, &ch, 1); 3246 } 3247 3248 static int send_cluster(int x, int y, uint32_t *ch, size_t nch) { 3249 int rv; 3250 char chu8[8]; 3251 3252 if (global.last_x != x - 1 || global.last_y != y) { 3253 if_err_return(rv, send_cursor_if(x, y)); 3254 } 3255 global.last_x = x; 3256 global.last_y = y; 3257 3258 int i; 3259 for (i = 0; i < (int)nch; i++) { 3260 uint32_t ch32 = *(ch + i); 3261 3262 int chu8_len = tb_utf8_unicode_to_char(chu8, ch32); 3263 if_err_return(rv, bytebuf_nputs(&global.out, chu8, (size_t)chu8_len)); 3264 } 3265 3266 return TB_OK; 3267 } 3268 3269 static int convert_num(uint32_t num, char *buf) { 3270 int i, l = 0; 3271 char ch; 3272 do { 3273 buf[l++] = (char)('0' + (num % 10)); 3274 num /= 10; 3275 } while (num); 3276 for (i = 0; i < l / 2; i++) { 3277 ch = buf[i]; 3278 buf[i] = buf[l - 1 - i]; 3279 buf[l - 1 - i] = ch; 3280 } 3281 return l; 3282 } 3283 3284 static int cell_cmp(struct tb_cell *a, struct tb_cell *b) { 3285 if (a->ch != b->ch || a->fg != b->fg || a->bg != b->bg) { 3286 return 1; 3287 } 3288 #ifdef TB_OPT_EGC 3289 if (a->nech != b->nech) { 3290 return 1; 3291 } else if (a->nech > 0) { // a->nech == b->nech 3292 return memcmp(a->ech, b->ech, a->nech); 3293 } 3294 #endif 3295 return 0; 3296 } 3297 3298 static int cell_copy(struct tb_cell *dst, struct tb_cell *src) { 3299 #ifdef TB_OPT_EGC 3300 if (src->nech > 0) { 3301 return cell_set(dst, src->ech, src->nech, src->fg, src->bg); 3302 } 3303 #endif 3304 return cell_set(dst, &src->ch, 1, src->fg, src->bg); 3305 } 3306 3307 static int cell_set(struct tb_cell *cell, uint32_t *ch, size_t nch, 3308 uintattr_t fg, uintattr_t bg) { 3309 cell->ch = ch ? *ch : 0; 3310 cell->fg = fg; 3311 cell->bg = bg; 3312 #ifdef TB_OPT_EGC 3313 if (nch <= 1) { 3314 cell->nech = 0; 3315 } else { 3316 int rv; 3317 if_err_return(rv, cell_reserve_ech(cell, nch + 1)); 3318 memcpy(cell->ech, ch, sizeof(*ch) * nch); 3319 cell->ech[nch] = '\0'; 3320 cell->nech = nch; 3321 } 3322 #else 3323 (void)nch; 3324 (void)cell_reserve_ech; 3325 #endif 3326 return TB_OK; 3327 } 3328 3329 static int cell_reserve_ech(struct tb_cell *cell, size_t n) { 3330 #ifdef TB_OPT_EGC 3331 if (cell->cech >= n) { 3332 return TB_OK; 3333 } 3334 if (!(cell->ech = (uint32_t*)tb_realloc(cell->ech, n * sizeof(cell->ch)))) { 3335 return TB_ERR_MEM; 3336 } 3337 cell->cech = n; 3338 return TB_OK; 3339 #else 3340 (void)cell; 3341 (void)n; 3342 return TB_ERR; 3343 #endif 3344 } 3345 3346 static int cell_free(struct tb_cell *cell) { 3347 #ifdef TB_OPT_EGC 3348 if (cell->ech) { 3349 tb_free(cell->ech); 3350 } 3351 #endif 3352 memset(cell, 0, sizeof(*cell)); 3353 return TB_OK; 3354 } 3355 3356 static int cellbuf_init(struct cellbuf_t *c, int w, int h) { 3357 c->cells = (struct tb_cell *)tb_malloc(sizeof(struct tb_cell) * w * h); 3358 if (!c->cells) { 3359 return TB_ERR_MEM; 3360 } 3361 memset(c->cells, 0, sizeof(struct tb_cell) * w * h); 3362 c->width = w; 3363 c->height = h; 3364 return TB_OK; 3365 } 3366 3367 static int cellbuf_free(struct cellbuf_t *c) { 3368 if (c->cells) { 3369 int i; 3370 for (i = 0; i < c->width * c->height; i++) { 3371 cell_free(&c->cells[i]); 3372 } 3373 tb_free(c->cells); 3374 } 3375 memset(c, 0, sizeof(*c)); 3376 return TB_OK; 3377 } 3378 3379 static int cellbuf_clear(struct cellbuf_t *c) { 3380 int rv, i; 3381 uint32_t space = (uint32_t)' '; 3382 for (i = 0; i < c->width * c->height; i++) { 3383 if_err_return(rv, 3384 cell_set(&c->cells[i], &space, 1, global.fg, global.bg)); 3385 } 3386 return TB_OK; 3387 } 3388 3389 static int cellbuf_get(struct cellbuf_t *c, int x, int y, 3390 struct tb_cell **out) { 3391 if (!cellbuf_in_bounds(c, x, y)) { 3392 *out = NULL; 3393 return TB_ERR_OUT_OF_BOUNDS; 3394 } 3395 *out = &c->cells[(y * c->width) + x]; 3396 return TB_OK; 3397 } 3398 3399 static int cellbuf_in_bounds(struct cellbuf_t *c, int x, int y) { 3400 if (x < 0 || x >= c->width || y < 0 || y >= c->height) { 3401 return 0; 3402 } 3403 return 1; 3404 } 3405 3406 static int cellbuf_resize(struct cellbuf_t *c, int w, int h) { 3407 int rv; 3408 3409 int ow = c->width; 3410 int oh = c->height; 3411 3412 if (ow == w && oh == h) { 3413 return TB_OK; 3414 } 3415 3416 w = w < 1 ? 1 : w; 3417 h = h < 1 ? 1 : h; 3418 3419 int minw = (w < ow) ? w : ow; 3420 int minh = (h < oh) ? h : oh; 3421 3422 struct tb_cell *prev = c->cells; 3423 3424 if_err_return(rv, cellbuf_init(c, w, h)); 3425 if_err_return(rv, cellbuf_clear(c)); 3426 3427 int x, y; 3428 for (x = 0; x < minw; x++) { 3429 for (y = 0; y < minh; y++) { 3430 struct tb_cell *src, *dst; 3431 src = &prev[(y * ow) + x]; 3432 if_err_return(rv, cellbuf_get(c, x, y, &dst)); 3433 if_err_return(rv, cell_copy(dst, src)); 3434 } 3435 } 3436 3437 tb_free(prev); 3438 3439 return TB_OK; 3440 } 3441 3442 static int bytebuf_puts(struct bytebuf_t *b, const char *str) { 3443 if (!str || strlen(str) <= 0) return TB_OK; // Nothing to do for empty caps 3444 return bytebuf_nputs(b, str, (size_t)strlen(str)); 3445 } 3446 3447 static int bytebuf_nputs(struct bytebuf_t *b, const char *str, size_t nstr) { 3448 int rv; 3449 if_err_return(rv, bytebuf_reserve(b, b->len + nstr + 1)); 3450 memcpy(b->buf + b->len, str, nstr); 3451 b->len += nstr; 3452 b->buf[b->len] = '\0'; 3453 return TB_OK; 3454 } 3455 3456 static int bytebuf_shift(struct bytebuf_t *b, size_t n) { 3457 if (n > b->len) { 3458 n = b->len; 3459 } 3460 size_t nmove = b->len - n; 3461 memmove(b->buf, b->buf + n, nmove); 3462 b->len -= n; 3463 return TB_OK; 3464 } 3465 3466 static int bytebuf_flush(struct bytebuf_t *b, int fd) { 3467 if (b->len <= 0) { 3468 return TB_OK; 3469 } 3470 ssize_t write_rv = write(fd, b->buf, b->len); 3471 if (write_rv < 0 || (size_t)write_rv != b->len) { 3472 // Note, errno will be 0 on partial write 3473 global.last_errno = errno; 3474 return TB_ERR; 3475 } 3476 b->len = 0; 3477 return TB_OK; 3478 } 3479 3480 static int bytebuf_reserve(struct bytebuf_t *b, size_t sz) { 3481 if (b->cap >= sz) { 3482 return TB_OK; 3483 } 3484 size_t newcap = b->cap > 0 ? b->cap : 1; 3485 while (newcap < sz) { 3486 newcap *= 2; 3487 } 3488 char *newbuf; 3489 if (b->buf) { 3490 newbuf = (char *)tb_realloc(b->buf, newcap); 3491 } else { 3492 newbuf = (char *)tb_malloc(newcap); 3493 } 3494 if (!newbuf) { 3495 return TB_ERR_MEM; 3496 } 3497 b->buf = newbuf; 3498 b->cap = newcap; 3499 return TB_OK; 3500 } 3501 3502 static int bytebuf_free(struct bytebuf_t *b) { 3503 if (b->buf) { 3504 tb_free(b->buf); 3505 } 3506 memset(b, 0, sizeof(*b)); 3507 return TB_OK; 3508 } 3509 3510 #endif // TB_IMPL