termbox2.h (106208B)
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, uintattr_t bg); 505 int tb_extend_cell(int x, int y, uint32_t ch); 506 507 /* Set the input mode. Termbox has two input modes: 508 * 509 * 1. `TB_INPUT_ESC` 510 * When escape (`\x1b`) is in the buffer and there's no match for an escape 511 * sequence, a key event for `TB_KEY_ESC` is returned. 512 * 513 * 2. `TB_INPUT_ALT` 514 * When escape (`\x1b`) is in the buffer and there's no match for an escape 515 * sequence, the next keyboard event is returned with a `TB_MOD_ALT` 516 * modifier. 517 * 518 * You can also apply `TB_INPUT_MOUSE` via bitwise OR operation to either of the 519 * modes (e.g., `TB_INPUT_ESC | TB_INPUT_MOUSE`) to receive `TB_EVENT_MOUSE` 520 * events. If none of the main two modes were set, but the mouse mode was, 521 * `TB_INPUT_ESC` is used. If for some reason you've decided to use 522 * `TB_INPUT_ESC | TB_INPUT_ALT`, it will behave as if only `TB_INPUT_ESC` was 523 * selected. 524 * 525 * If mode is `TB_INPUT_CURRENT`, return the current input mode. 526 * 527 * The default input mode is `TB_INPUT_ESC`. 528 */ 529 int tb_set_input_mode(int mode); 530 531 /* Set the output mode. Termbox has multiple output modes: 532 * 533 * 1. `TB_OUTPUT_NORMAL` => [0..8] 534 * 535 * This mode provides 8 different colors: 536 * `TB_BLACK`, `TB_RED`, `TB_GREEN`, `TB_YELLOW`, 537 * `TB_BLUE`, `TB_MAGENTA`, `TB_CYAN`, `TB_WHITE` 538 * 539 * Plus `TB_DEFAULT` which skips sending a color code (i.e., uses the 540 * terminal's default color). 541 * 542 * Colors (including `TB_DEFAULT`) may be bitwise OR'd with attributes: 543 * `TB_BOLD`, `TB_UNDERLINE`, `TB_REVERSE`, `TB_ITALIC`, `TB_BLINK`, 544 * `TB_BRIGHT`, `TB_DIM` 545 * 546 * The following style attributes are also available if compiled with 547 * `TB_OPT_ATTR_W` set to 64: 548 * `TB_STRIKEOUT`, `TB_UNDERLINE_2`, `TB_OVERLINE`, `TB_INVISIBLE` 549 * 550 * As in all modes, the value 0 is interpreted as `TB_DEFAULT` for 551 * convenience. 552 * 553 * Some notes: `TB_REVERSE` and `TB_BRIGHT` can be applied as either `fg` or 554 * `bg` attributes for the same effect. The rest of the attributes apply to 555 * `fg` only and are ignored as `bg` attributes. 556 * 557 * Example usage: `tb_set_cell(x, y, '@', TB_BLACK | TB_BOLD, TB_RED)` 558 * 559 * 2. `TB_OUTPUT_256` => [0..255] + `TB_HI_BLACK` 560 * 561 * In this mode you get 256 distinct colors (plus default): 562 * 0x00 (1): `TB_DEFAULT` 563 * `TB_HI_BLACK` (1): `TB_BLACK` in `TB_OUTPUT_NORMAL` 564 * 0x01..0x07 (7): the next 7 colors as in `TB_OUTPUT_NORMAL` 565 * 0x08..0x0f (8): bright versions of the above 566 * 0x10..0xe7 (216): 216 different colors 567 * 0xe8..0xff (24): 24 different shades of gray 568 * 569 * All `TB_*` style attributes except `TB_BRIGHT` may be bitwise OR'd as in 570 * `TB_OUTPUT_NORMAL`. 571 * 572 * Note `TB_HI_BLACK` must be used for black, as 0x00 represents default. 573 * 574 * 3. `TB_OUTPUT_216` => [0..216] 575 * 576 * This mode supports the 216-color range of `TB_OUTPUT_256` only, but you 577 * don't need to provide an offset: 578 * 0x00 (1): `TB_DEFAULT` 579 * 0x01..0xd8 (216): 216 different colors 580 * 581 * 4. `TB_OUTPUT_GRAYSCALE` => [0..24] 582 * 583 * This mode supports the 24-color range of `TB_OUTPUT_256` only, but you 584 * don't need to provide an offset: 585 * 0x00 (1): `TB_DEFAULT` 586 * 0x01..0x18 (24): 24 different shades of gray 587 * 588 * 5. `TB_OUTPUT_TRUECOLOR` => [0x000000..0xffffff] + `TB_HI_BLACK` 589 * 590 * This mode provides 24-bit color on supported terminals. The format is 591 * 0xRRGGBB. 592 * 593 * All `TB_*` style attributes except `TB_BRIGHT` may be bitwise OR'd as in 594 * `TB_OUTPUT_NORMAL`. 595 * 596 * Note `TB_HI_BLACK` must be used for black, as 0x000000 represents default. 597 * 598 * To use the terminal default color (i.e., to not send an escape code), pass 599 * `TB_DEFAULT`. For convenience, the value 0 is interpreted as `TB_DEFAULT` in 600 * all modes. 601 * 602 * Note, cell attributes persist after switching output modes. Any translation 603 * between, for example, `TB_OUTPUT_NORMAL`'s `TB_RED` and 604 * `TB_OUTPUT_TRUECOLOR`'s 0xff0000 must be performed by the caller. Also note 605 * that cells previously rendered in one mode may persist unchanged until the 606 * front buffer is cleared (such as after a resize event) at which point it will 607 * be re-interpreted and flushed according to the current mode. Callers may 608 * invoke `tb_invalidate` if it is desirable to immediately re-interpret and 609 * flush the entire screen according to the current mode. 610 * 611 * Note, not all terminals support all output modes, especially beyond 612 * `TB_OUTPUT_NORMAL`. There is also no very reliable way to determine color 613 * support dynamically. If portability is desired, callers are recommended to 614 * use `TB_OUTPUT_NORMAL` or make output mode end-user configurable. The same 615 * advice applies to style attributes. 616 * 617 * If mode is `TB_OUTPUT_CURRENT`, return the current output mode. 618 * 619 * The default output mode is `TB_OUTPUT_NORMAL`. 620 */ 621 int tb_set_output_mode(int mode); 622 623 /* Wait for an event up to `timeout_ms` milliseconds and populate `event` with 624 * it. If no event is available within the timeout period, `TB_ERR_NO_EVENT` 625 * is returned. On a resize event, the underlying `select(2)` call may be 626 * interrupted, yielding a return code of `TB_ERR_POLL`. In this case, you may 627 * check `errno` via `tb_last_errno`. If it's `EINTR`, you may elect to ignore 628 * that and call `tb_peek_event` again. 629 */ 630 int tb_peek_event(struct tb_event* event, int timeout_ms); 631 632 /* Same as `tb_peek_event` except no timeout. */ 633 int tb_poll_event(struct tb_event* event); 634 635 /* Internal termbox fds that can be used with `poll(2)`, `select(2)`, etc. 636 * externally. Callers must invoke `tb_poll_event` or `tb_peek_event` if 637 * fds become readable. */ 638 int tb_get_fds(int* ttyfd, int* resizefd); 639 640 /* Print and printf functions. Specify param `out_w` to determine width of 641 * printed string. Strings are interpreted as UTF-8. 642 * 643 * Non-printable characters (`iswprint(3)`) and truncated UTF-8 byte sequences 644 * are replaced with U+FFFD. 645 * 646 * Newlines (`\n`) are supported with the caveat that `out_w` will return the 647 * width of the string as if it were on a single line. 648 * 649 * If the starting coordinate is out of bounds, `TB_ERR_OUT_OF_BOUNDS` is 650 * returned. If the starting coordinate is in bounds, but goes out of bounds, 651 * then the out-of-bounds portions of the string are ignored. 652 * 653 * For finer control, use `tb_set_cell`. 654 */ 655 int tb_print(int x, int y, uintattr_t fg, uintattr_t bg, const char* str); 656 int tb_printf(int x, int y, uintattr_t fg, uintattr_t bg, const char* fmt, ...); 657 int tb_print_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t* out_w, const char* str); 658 int tb_printf_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t* out_w, const char* fmt, ...); 659 660 /* Send raw bytes to terminal. */ 661 int tb_send(const char* buf, size_t nbuf); 662 int tb_sendf(const char* fmt, ...); 663 664 /* Deprecated. Set custom callbacks. `fn_type` is one of `TB_FUNC_*` constants, 665 * `fn` is a compatible function pointer, or NULL to clear. 666 * 667 * `TB_FUNC_EXTRACT_PRE`: 668 * If specified, invoke this function BEFORE termbox tries to extract any 669 * escape sequences from the input buffer. 670 * 671 * `TB_FUNC_EXTRACT_POST`: 672 * If specified, invoke this function AFTER termbox tries (and fails) to 673 * extract any escape sequences from the input buffer. 674 */ 675 int tb_set_func(int fn_type, int (*fn)(struct tb_event*, size_t*)); 676 677 /* Return byte length of codepoint given first byte of UTF-8 sequence (1-6). */ 678 int tb_utf8_char_length(char c); 679 680 /* Convert UTF-8 null-terminated byte sequence to UTF-32 codepoint. 681 * 682 * If `c` is an empty C string, return 0. `out` is left unchanged. 683 * 684 * If a null byte is encountered in the middle of the codepoint, return a 685 * negative number indicating how many bytes were processed. `out` is left 686 * unchanged. 687 * 688 * Otherwise, return byte length of codepoint (1-6). 689 */ 690 int tb_utf8_char_to_unicode(uint32_t* out, const char* c); 691 692 /* Convert UTF-32 codepoint to UTF-8 null-terminated byte sequence. 693 * 694 * `out` must be char[7] or greater. Return byte length of codepoint (1-6). 695 */ 696 int tb_utf8_unicode_to_char(char* out, uint32_t c); 697 698 /* Library utility functions */ 699 int tb_last_errno(void); 700 const char* tb_strerror(int err); 701 struct tb_cell* tb_cell_buffer(void); // Deprecated 702 int tb_has_truecolor(void); 703 int tb_has_egc(void); 704 int tb_attr_width(void); 705 const char* tb_version(void); 706 707 /* Deprecation notice! 708 * 709 * The following will be removed in version 3.x (ABI version 3): 710 * 711 * TB_256_BLACK (use TB_HI_BLACK) 712 * TB_OPT_TRUECOLOR (use TB_OPT_ATTR_W) 713 * TB_TRUECOLOR_BOLD (use TB_BOLD) 714 * TB_TRUECOLOR_UNDERLINE (use TB_UNDERLINE) 715 * TB_TRUECOLOR_REVERSE (use TB_REVERSE) 716 * TB_TRUECOLOR_ITALIC (use TB_ITALICe) 717 * TB_TRUECOLOR_BLINK (use TB_BLINK) 718 * TB_TRUECOLOR_BLACK (use TB_HI_BLACK) 719 * tb_cell_buffer 720 * tb_set_func 721 * TB_FUNC_EXTRACT_PRE 722 * TB_FUNC_EXTRACT_POST 723 */ 724 725 #ifdef __cplusplus 726 } 727 #endif 728 729 #endif // TERMBOX_H_INCL 730 731 #ifdef TB_IMPL 732 733 #define if_err_return(rv, expr) \ 734 if (((rv) = (expr)) != TB_OK) return (rv) 735 #define if_err_break(rv, expr) \ 736 if (((rv) = (expr)) != TB_OK) break 737 #define if_ok_return(rv, expr) \ 738 if (((rv) = (expr)) == TB_OK) return (rv) 739 #define if_ok_or_need_more_return(rv, expr) \ 740 if (((rv) = (expr)) == TB_OK || (rv) == TB_ERR_NEED_MORE) return (rv) 741 742 #define send_literal(rv, a) if_err_return((rv), bytebuf_nputs(&global.out, (a), sizeof(a) - 1)) 743 744 #define send_num(rv, nbuf, n) \ 745 if_err_return((rv), bytebuf_nputs(&global.out, (nbuf), convert_num((n), (nbuf)))) 746 747 #define snprintf_or_return(rv, str, sz, fmt, ...) \ 748 do { \ 749 (rv) = snprintf((str), (sz), (fmt), __VA_ARGS__); \ 750 if ((rv) < 0 || (rv) >= (int)(sz)) return TB_ERR; \ 751 } while (0) 752 753 #define if_not_init_return() \ 754 if (!global.initialized) return TB_ERR_NOT_INIT 755 756 struct bytebuf_t { 757 char* buf; 758 size_t len; 759 size_t cap; 760 }; 761 762 struct cellbuf_t { 763 int width; 764 int height; 765 struct tb_cell* cells; 766 }; 767 768 struct cap_trie_t { 769 char c; 770 struct cap_trie_t* children; 771 size_t nchildren; 772 int is_leaf; 773 uint16_t key; 774 uint8_t mod; 775 }; 776 777 struct tb_global_t { 778 int ttyfd; 779 int rfd; 780 int wfd; 781 int ttyfd_open; 782 int resize_pipefd[2]; 783 int width; 784 int height; 785 int cursor_x; 786 int cursor_y; 787 int last_x; 788 int last_y; 789 uintattr_t fg; 790 uintattr_t bg; 791 uintattr_t last_fg; 792 uintattr_t last_bg; 793 int input_mode; 794 int output_mode; 795 char* terminfo; 796 size_t nterminfo; 797 const char* caps[TB_CAP__COUNT]; 798 struct cap_trie_t cap_trie; 799 struct bytebuf_t in; 800 struct bytebuf_t out; 801 struct cellbuf_t back; 802 struct cellbuf_t front; 803 struct termios orig_tios; 804 int has_orig_tios; 805 int last_errno; 806 int initialized; 807 int (*fn_extract_esc_pre)(struct tb_event*, size_t*); 808 int (*fn_extract_esc_post)(struct tb_event*, size_t*); 809 char errbuf[1024]; 810 }; 811 812 static struct tb_global_t global = { 0 }; 813 814 /* BEGIN codegen c */ 815 /* Produced by ./codegen.sh on Tue, 03 Sep 2024 04:17:48 +0000 */ 816 817 static const int16_t terminfo_cap_indexes[] = { 818 66, // kf1 (TB_CAP_F1) 819 68, // kf2 (TB_CAP_F2) 820 69, // kf3 (TB_CAP_F3) 821 70, // kf4 (TB_CAP_F4) 822 71, // kf5 (TB_CAP_F5) 823 72, // kf6 (TB_CAP_F6) 824 73, // kf7 (TB_CAP_F7) 825 74, // kf8 (TB_CAP_F8) 826 75, // kf9 (TB_CAP_F9) 827 67, // kf10 (TB_CAP_F10) 828 216, // kf11 (TB_CAP_F11) 829 217, // kf12 (TB_CAP_F12) 830 77, // kich1 (TB_CAP_INSERT) 831 59, // kdch1 (TB_CAP_DELETE) 832 76, // khome (TB_CAP_HOME) 833 164, // kend (TB_CAP_END) 834 82, // kpp (TB_CAP_PGUP) 835 81, // knp (TB_CAP_PGDN) 836 87, // kcuu1 (TB_CAP_ARROW_UP) 837 61, // kcud1 (TB_CAP_ARROW_DOWN) 838 79, // kcub1 (TB_CAP_ARROW_LEFT) 839 83, // kcuf1 (TB_CAP_ARROW_RIGHT) 840 148, // kcbt (TB_CAP_BACK_TAB) 841 28, // smcup (TB_CAP_ENTER_CA) 842 40, // rmcup (TB_CAP_EXIT_CA) 843 16, // cnorm (TB_CAP_SHOW_CURSOR) 844 13, // civis (TB_CAP_HIDE_CURSOR) 845 5, // clear (TB_CAP_CLEAR_SCREEN) 846 39, // sgr0 (TB_CAP_SGR0) 847 36, // smul (TB_CAP_UNDERLINE) 848 27, // bold (TB_CAP_BOLD) 849 26, // blink (TB_CAP_BLINK) 850 311, // sitm (TB_CAP_ITALIC) 851 34, // rev (TB_CAP_REVERSE) 852 89, // smkx (TB_CAP_ENTER_KEYPAD) 853 88, // rmkx (TB_CAP_EXIT_KEYPAD) 854 30, // dim (TB_CAP_DIM) 855 32, // invis (TB_CAP_INVISIBLE) 856 }; 857 858 // xterm 859 static const char* xterm_caps[] = { 860 "\033OP", // kf1 (TB_CAP_F1) 861 "\033OQ", // kf2 (TB_CAP_F2) 862 "\033OR", // kf3 (TB_CAP_F3) 863 "\033OS", // kf4 (TB_CAP_F4) 864 "\033[15~", // kf5 (TB_CAP_F5) 865 "\033[17~", // kf6 (TB_CAP_F6) 866 "\033[18~", // kf7 (TB_CAP_F7) 867 "\033[19~", // kf8 (TB_CAP_F8) 868 "\033[20~", // kf9 (TB_CAP_F9) 869 "\033[21~", // kf10 (TB_CAP_F10) 870 "\033[23~", // kf11 (TB_CAP_F11) 871 "\033[24~", // kf12 (TB_CAP_F12) 872 "\033[2~", // kich1 (TB_CAP_INSERT) 873 "\033[3~", // kdch1 (TB_CAP_DELETE) 874 "\033OH", // khome (TB_CAP_HOME) 875 "\033OF", // kend (TB_CAP_END) 876 "\033[5~", // kpp (TB_CAP_PGUP) 877 "\033[6~", // knp (TB_CAP_PGDN) 878 "\033OA", // kcuu1 (TB_CAP_ARROW_UP) 879 "\033OB", // kcud1 (TB_CAP_ARROW_DOWN) 880 "\033OD", // kcub1 (TB_CAP_ARROW_LEFT) 881 "\033OC", // kcuf1 (TB_CAP_ARROW_RIGHT) 882 "\033[Z", // kcbt (TB_CAP_BACK_TAB) 883 "\033[?1049h\033[22;0;0t", // smcup (TB_CAP_ENTER_CA) 884 "\033[?1049l\033[23;0;0t", // rmcup (TB_CAP_EXIT_CA) 885 "\033[?12l\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR) 886 "\033[?25l", // civis (TB_CAP_HIDE_CURSOR) 887 "\033[H\033[2J", // clear (TB_CAP_CLEAR_SCREEN) 888 "\033(B\033[m", // sgr0 (TB_CAP_SGR0) 889 "\033[4m", // smul (TB_CAP_UNDERLINE) 890 "\033[1m", // bold (TB_CAP_BOLD) 891 "\033[5m", // blink (TB_CAP_BLINK) 892 "\033[3m", // sitm (TB_CAP_ITALIC) 893 "\033[7m", // rev (TB_CAP_REVERSE) 894 "\033[?1h\033=", // smkx (TB_CAP_ENTER_KEYPAD) 895 "\033[?1l\033>", // rmkx (TB_CAP_EXIT_KEYPAD) 896 "\033[2m", // dim (TB_CAP_DIM) 897 "\033[8m", // invis (TB_CAP_INVISIBLE) 898 }; 899 900 // linux 901 static const char* linux_caps[] = { 902 "\033[[A", // kf1 (TB_CAP_F1) 903 "\033[[B", // kf2 (TB_CAP_F2) 904 "\033[[C", // kf3 (TB_CAP_F3) 905 "\033[[D", // kf4 (TB_CAP_F4) 906 "\033[[E", // kf5 (TB_CAP_F5) 907 "\033[17~", // kf6 (TB_CAP_F6) 908 "\033[18~", // kf7 (TB_CAP_F7) 909 "\033[19~", // kf8 (TB_CAP_F8) 910 "\033[20~", // kf9 (TB_CAP_F9) 911 "\033[21~", // kf10 (TB_CAP_F10) 912 "\033[23~", // kf11 (TB_CAP_F11) 913 "\033[24~", // kf12 (TB_CAP_F12) 914 "\033[2~", // kich1 (TB_CAP_INSERT) 915 "\033[3~", // kdch1 (TB_CAP_DELETE) 916 "\033[1~", // khome (TB_CAP_HOME) 917 "\033[4~", // kend (TB_CAP_END) 918 "\033[5~", // kpp (TB_CAP_PGUP) 919 "\033[6~", // knp (TB_CAP_PGDN) 920 "\033[A", // kcuu1 (TB_CAP_ARROW_UP) 921 "\033[B", // kcud1 (TB_CAP_ARROW_DOWN) 922 "\033[D", // kcub1 (TB_CAP_ARROW_LEFT) 923 "\033[C", // kcuf1 (TB_CAP_ARROW_RIGHT) 924 "\033\011", // kcbt (TB_CAP_BACK_TAB) 925 "", // smcup (TB_CAP_ENTER_CA) 926 "", // rmcup (TB_CAP_EXIT_CA) 927 "\033[?25h\033[?0c", // cnorm (TB_CAP_SHOW_CURSOR) 928 "\033[?25l\033[?1c", // civis (TB_CAP_HIDE_CURSOR) 929 "\033[H\033[J", // clear (TB_CAP_CLEAR_SCREEN) 930 "\033[m\017", // sgr0 (TB_CAP_SGR0) 931 "\033[4m", // smul (TB_CAP_UNDERLINE) 932 "\033[1m", // bold (TB_CAP_BOLD) 933 "\033[5m", // blink (TB_CAP_BLINK) 934 "", // sitm (TB_CAP_ITALIC) 935 "\033[7m", // rev (TB_CAP_REVERSE) 936 "", // smkx (TB_CAP_ENTER_KEYPAD) 937 "", // rmkx (TB_CAP_EXIT_KEYPAD) 938 "\033[2m", // dim (TB_CAP_DIM) 939 "", // invis (TB_CAP_INVISIBLE) 940 }; 941 942 // screen 943 static const char* screen_caps[] = { 944 "\033OP", // kf1 (TB_CAP_F1) 945 "\033OQ", // kf2 (TB_CAP_F2) 946 "\033OR", // kf3 (TB_CAP_F3) 947 "\033OS", // kf4 (TB_CAP_F4) 948 "\033[15~", // kf5 (TB_CAP_F5) 949 "\033[17~", // kf6 (TB_CAP_F6) 950 "\033[18~", // kf7 (TB_CAP_F7) 951 "\033[19~", // kf8 (TB_CAP_F8) 952 "\033[20~", // kf9 (TB_CAP_F9) 953 "\033[21~", // kf10 (TB_CAP_F10) 954 "\033[23~", // kf11 (TB_CAP_F11) 955 "\033[24~", // kf12 (TB_CAP_F12) 956 "\033[2~", // kich1 (TB_CAP_INSERT) 957 "\033[3~", // kdch1 (TB_CAP_DELETE) 958 "\033[1~", // khome (TB_CAP_HOME) 959 "\033[4~", // kend (TB_CAP_END) 960 "\033[5~", // kpp (TB_CAP_PGUP) 961 "\033[6~", // knp (TB_CAP_PGDN) 962 "\033OA", // kcuu1 (TB_CAP_ARROW_UP) 963 "\033OB", // kcud1 (TB_CAP_ARROW_DOWN) 964 "\033OD", // kcub1 (TB_CAP_ARROW_LEFT) 965 "\033OC", // kcuf1 (TB_CAP_ARROW_RIGHT) 966 "\033[Z", // kcbt (TB_CAP_BACK_TAB) 967 "\033[?1049h", // smcup (TB_CAP_ENTER_CA) 968 "\033[?1049l", // rmcup (TB_CAP_EXIT_CA) 969 "\033[34h\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR) 970 "\033[?25l", // civis (TB_CAP_HIDE_CURSOR) 971 "\033[H\033[J", // clear (TB_CAP_CLEAR_SCREEN) 972 "\033[m\017", // sgr0 (TB_CAP_SGR0) 973 "\033[4m", // smul (TB_CAP_UNDERLINE) 974 "\033[1m", // bold (TB_CAP_BOLD) 975 "\033[5m", // blink (TB_CAP_BLINK) 976 "", // sitm (TB_CAP_ITALIC) 977 "\033[7m", // rev (TB_CAP_REVERSE) 978 "\033[?1h\033=", // smkx (TB_CAP_ENTER_KEYPAD) 979 "\033[?1l\033>", // rmkx (TB_CAP_EXIT_KEYPAD) 980 "\033[2m", // dim (TB_CAP_DIM) 981 "", // invis (TB_CAP_INVISIBLE) 982 }; 983 984 // rxvt-256color 985 static const char* rxvt_256color_caps[] = { 986 "\033[11~", // kf1 (TB_CAP_F1) 987 "\033[12~", // kf2 (TB_CAP_F2) 988 "\033[13~", // kf3 (TB_CAP_F3) 989 "\033[14~", // kf4 (TB_CAP_F4) 990 "\033[15~", // kf5 (TB_CAP_F5) 991 "\033[17~", // kf6 (TB_CAP_F6) 992 "\033[18~", // kf7 (TB_CAP_F7) 993 "\033[19~", // kf8 (TB_CAP_F8) 994 "\033[20~", // kf9 (TB_CAP_F9) 995 "\033[21~", // kf10 (TB_CAP_F10) 996 "\033[23~", // kf11 (TB_CAP_F11) 997 "\033[24~", // kf12 (TB_CAP_F12) 998 "\033[2~", // kich1 (TB_CAP_INSERT) 999 "\033[3~", // kdch1 (TB_CAP_DELETE) 1000 "\033[7~", // khome (TB_CAP_HOME) 1001 "\033[8~", // kend (TB_CAP_END) 1002 "\033[5~", // kpp (TB_CAP_PGUP) 1003 "\033[6~", // knp (TB_CAP_PGDN) 1004 "\033[A", // kcuu1 (TB_CAP_ARROW_UP) 1005 "\033[B", // kcud1 (TB_CAP_ARROW_DOWN) 1006 "\033[D", // kcub1 (TB_CAP_ARROW_LEFT) 1007 "\033[C", // kcuf1 (TB_CAP_ARROW_RIGHT) 1008 "\033[Z", // kcbt (TB_CAP_BACK_TAB) 1009 "\0337\033[?47h", // smcup (TB_CAP_ENTER_CA) 1010 "\033[2J\033[?47l\0338", // rmcup (TB_CAP_EXIT_CA) 1011 "\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR) 1012 "\033[?25l", // civis (TB_CAP_HIDE_CURSOR) 1013 "\033[H\033[2J", // clear (TB_CAP_CLEAR_SCREEN) 1014 "\033[m\017", // sgr0 (TB_CAP_SGR0) 1015 "\033[4m", // smul (TB_CAP_UNDERLINE) 1016 "\033[1m", // bold (TB_CAP_BOLD) 1017 "\033[5m", // blink (TB_CAP_BLINK) 1018 "", // sitm (TB_CAP_ITALIC) 1019 "\033[7m", // rev (TB_CAP_REVERSE) 1020 "\033=", // smkx (TB_CAP_ENTER_KEYPAD) 1021 "\033>", // rmkx (TB_CAP_EXIT_KEYPAD) 1022 "", // dim (TB_CAP_DIM) 1023 "", // invis (TB_CAP_INVISIBLE) 1024 }; 1025 1026 // rxvt-unicode 1027 static const char* rxvt_unicode_caps[] = { 1028 "\033[11~", // kf1 (TB_CAP_F1) 1029 "\033[12~", // kf2 (TB_CAP_F2) 1030 "\033[13~", // kf3 (TB_CAP_F3) 1031 "\033[14~", // kf4 (TB_CAP_F4) 1032 "\033[15~", // kf5 (TB_CAP_F5) 1033 "\033[17~", // kf6 (TB_CAP_F6) 1034 "\033[18~", // kf7 (TB_CAP_F7) 1035 "\033[19~", // kf8 (TB_CAP_F8) 1036 "\033[20~", // kf9 (TB_CAP_F9) 1037 "\033[21~", // kf10 (TB_CAP_F10) 1038 "\033[23~", // kf11 (TB_CAP_F11) 1039 "\033[24~", // kf12 (TB_CAP_F12) 1040 "\033[2~", // kich1 (TB_CAP_INSERT) 1041 "\033[3~", // kdch1 (TB_CAP_DELETE) 1042 "\033[7~", // khome (TB_CAP_HOME) 1043 "\033[8~", // kend (TB_CAP_END) 1044 "\033[5~", // kpp (TB_CAP_PGUP) 1045 "\033[6~", // knp (TB_CAP_PGDN) 1046 "\033[A", // kcuu1 (TB_CAP_ARROW_UP) 1047 "\033[B", // kcud1 (TB_CAP_ARROW_DOWN) 1048 "\033[D", // kcub1 (TB_CAP_ARROW_LEFT) 1049 "\033[C", // kcuf1 (TB_CAP_ARROW_RIGHT) 1050 "\033[Z", // kcbt (TB_CAP_BACK_TAB) 1051 "\033[?1049h", // smcup (TB_CAP_ENTER_CA) 1052 "\033[r\033[?1049l", // rmcup (TB_CAP_EXIT_CA) 1053 "\033[?12l\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR) 1054 "\033[?25l", // civis (TB_CAP_HIDE_CURSOR) 1055 "\033[H\033[2J", // clear (TB_CAP_CLEAR_SCREEN) 1056 "\033[m\033(B", // sgr0 (TB_CAP_SGR0) 1057 "\033[4m", // smul (TB_CAP_UNDERLINE) 1058 "\033[1m", // bold (TB_CAP_BOLD) 1059 "\033[5m", // blink (TB_CAP_BLINK) 1060 "\033[3m", // sitm (TB_CAP_ITALIC) 1061 "\033[7m", // rev (TB_CAP_REVERSE) 1062 "\033=", // smkx (TB_CAP_ENTER_KEYPAD) 1063 "\033>", // rmkx (TB_CAP_EXIT_KEYPAD) 1064 "", // dim (TB_CAP_DIM) 1065 "", // invis (TB_CAP_INVISIBLE) 1066 }; 1067 1068 // Eterm 1069 static const char* eterm_caps[] = { 1070 "\033[11~", // kf1 (TB_CAP_F1) 1071 "\033[12~", // kf2 (TB_CAP_F2) 1072 "\033[13~", // kf3 (TB_CAP_F3) 1073 "\033[14~", // kf4 (TB_CAP_F4) 1074 "\033[15~", // kf5 (TB_CAP_F5) 1075 "\033[17~", // kf6 (TB_CAP_F6) 1076 "\033[18~", // kf7 (TB_CAP_F7) 1077 "\033[19~", // kf8 (TB_CAP_F8) 1078 "\033[20~", // kf9 (TB_CAP_F9) 1079 "\033[21~", // kf10 (TB_CAP_F10) 1080 "\033[23~", // kf11 (TB_CAP_F11) 1081 "\033[24~", // kf12 (TB_CAP_F12) 1082 "\033[2~", // kich1 (TB_CAP_INSERT) 1083 "\033[3~", // kdch1 (TB_CAP_DELETE) 1084 "\033[7~", // khome (TB_CAP_HOME) 1085 "\033[8~", // kend (TB_CAP_END) 1086 "\033[5~", // kpp (TB_CAP_PGUP) 1087 "\033[6~", // knp (TB_CAP_PGDN) 1088 "\033[A", // kcuu1 (TB_CAP_ARROW_UP) 1089 "\033[B", // kcud1 (TB_CAP_ARROW_DOWN) 1090 "\033[D", // kcub1 (TB_CAP_ARROW_LEFT) 1091 "\033[C", // kcuf1 (TB_CAP_ARROW_RIGHT) 1092 "", // kcbt (TB_CAP_BACK_TAB) 1093 "\0337\033[?47h", // smcup (TB_CAP_ENTER_CA) 1094 "\033[2J\033[?47l\0338", // rmcup (TB_CAP_EXIT_CA) 1095 "\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR) 1096 "\033[?25l", // civis (TB_CAP_HIDE_CURSOR) 1097 "\033[H\033[2J", // clear (TB_CAP_CLEAR_SCREEN) 1098 "\033[m\017", // sgr0 (TB_CAP_SGR0) 1099 "\033[4m", // smul (TB_CAP_UNDERLINE) 1100 "\033[1m", // bold (TB_CAP_BOLD) 1101 "\033[5m", // blink (TB_CAP_BLINK) 1102 "", // sitm (TB_CAP_ITALIC) 1103 "\033[7m", // rev (TB_CAP_REVERSE) 1104 "", // smkx (TB_CAP_ENTER_KEYPAD) 1105 "", // rmkx (TB_CAP_EXIT_KEYPAD) 1106 "", // dim (TB_CAP_DIM) 1107 "", // invis (TB_CAP_INVISIBLE) 1108 }; 1109 1110 static struct { 1111 const char* name; 1112 const char** caps; 1113 const char* alias; 1114 } builtin_terms[] = { 1115 { "xterm", xterm_caps, "" }, 1116 { "linux", linux_caps, "" }, 1117 { "screen", screen_caps, "tmux" }, 1118 { "rxvt-256color", rxvt_256color_caps, "" }, 1119 { "rxvt-unicode", rxvt_unicode_caps, "rxvt" }, 1120 { "Eterm", eterm_caps, "" }, 1121 { NULL, NULL, NULL }, 1122 }; 1123 1124 /* END codegen c */ 1125 1126 static struct { 1127 const char* cap; 1128 const uint16_t key; 1129 const uint8_t mod; 1130 } builtin_mod_caps[] = { 1131 // xterm arrows 1132 { "\x1b[1;2A", TB_KEY_ARROW_UP, TB_MOD_SHIFT }, 1133 { "\x1b[1;3A", TB_KEY_ARROW_UP, TB_MOD_ALT }, 1134 { "\x1b[1;4A", TB_KEY_ARROW_UP, TB_MOD_ALT | TB_MOD_SHIFT }, 1135 { "\x1b[1;5A", TB_KEY_ARROW_UP, TB_MOD_CTRL }, 1136 { "\x1b[1;6A", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_SHIFT }, 1137 { "\x1b[1;7A", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT }, 1138 { "\x1b[1;8A", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1139 1140 { "\x1b[1;2B", TB_KEY_ARROW_DOWN, TB_MOD_SHIFT }, 1141 { "\x1b[1;3B", TB_KEY_ARROW_DOWN, TB_MOD_ALT }, 1142 { "\x1b[1;4B", TB_KEY_ARROW_DOWN, TB_MOD_ALT | TB_MOD_SHIFT }, 1143 { "\x1b[1;5B", TB_KEY_ARROW_DOWN, TB_MOD_CTRL }, 1144 { "\x1b[1;6B", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_SHIFT }, 1145 { "\x1b[1;7B", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_ALT }, 1146 { "\x1b[1;8B", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1147 1148 { "\x1b[1;2C", TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT }, 1149 { "\x1b[1;3C", TB_KEY_ARROW_RIGHT, TB_MOD_ALT }, 1150 { "\x1b[1;4C", TB_KEY_ARROW_RIGHT, TB_MOD_ALT | TB_MOD_SHIFT }, 1151 { "\x1b[1;5C", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL }, 1152 { "\x1b[1;6C", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_SHIFT }, 1153 { "\x1b[1;7C", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT }, 1154 { "\x1b[1;8C", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1155 1156 { "\x1b[1;2D", TB_KEY_ARROW_LEFT, TB_MOD_SHIFT }, 1157 { "\x1b[1;3D", TB_KEY_ARROW_LEFT, TB_MOD_ALT }, 1158 { "\x1b[1;4D", TB_KEY_ARROW_LEFT, TB_MOD_ALT | TB_MOD_SHIFT }, 1159 { "\x1b[1;5D", TB_KEY_ARROW_LEFT, TB_MOD_CTRL }, 1160 { "\x1b[1;6D", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_SHIFT }, 1161 { "\x1b[1;7D", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_ALT }, 1162 { "\x1b[1;8D", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1163 1164 // xterm keys 1165 { "\x1b[1;2H", TB_KEY_HOME, TB_MOD_SHIFT }, 1166 { "\x1b[1;3H", TB_KEY_HOME, TB_MOD_ALT }, 1167 { "\x1b[1;4H", TB_KEY_HOME, TB_MOD_ALT | TB_MOD_SHIFT }, 1168 { "\x1b[1;5H", TB_KEY_HOME, TB_MOD_CTRL }, 1169 { "\x1b[1;6H", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_SHIFT }, 1170 { "\x1b[1;7H", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_ALT }, 1171 { "\x1b[1;8H", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1172 1173 { "\x1b[1;2F", TB_KEY_END, TB_MOD_SHIFT }, 1174 { "\x1b[1;3F", TB_KEY_END, TB_MOD_ALT }, 1175 { "\x1b[1;4F", TB_KEY_END, TB_MOD_ALT | TB_MOD_SHIFT }, 1176 { "\x1b[1;5F", TB_KEY_END, TB_MOD_CTRL }, 1177 { "\x1b[1;6F", TB_KEY_END, TB_MOD_CTRL | TB_MOD_SHIFT }, 1178 { "\x1b[1;7F", TB_KEY_END, TB_MOD_CTRL | TB_MOD_ALT }, 1179 { "\x1b[1;8F", TB_KEY_END, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1180 1181 { "\x1b[2;2~", TB_KEY_INSERT, TB_MOD_SHIFT }, 1182 { "\x1b[2;3~", TB_KEY_INSERT, TB_MOD_ALT }, 1183 { "\x1b[2;4~", TB_KEY_INSERT, TB_MOD_ALT | TB_MOD_SHIFT }, 1184 { "\x1b[2;5~", TB_KEY_INSERT, TB_MOD_CTRL }, 1185 { "\x1b[2;6~", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_SHIFT }, 1186 { "\x1b[2;7~", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_ALT }, 1187 { "\x1b[2;8~", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1188 1189 { "\x1b[3;2~", TB_KEY_DELETE, TB_MOD_SHIFT }, 1190 { "\x1b[3;3~", TB_KEY_DELETE, TB_MOD_ALT }, 1191 { "\x1b[3;4~", TB_KEY_DELETE, TB_MOD_ALT | TB_MOD_SHIFT }, 1192 { "\x1b[3;5~", TB_KEY_DELETE, TB_MOD_CTRL }, 1193 { "\x1b[3;6~", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_SHIFT }, 1194 { "\x1b[3;7~", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_ALT }, 1195 { "\x1b[3;8~", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1196 1197 { "\x1b[5;2~", TB_KEY_PGUP, TB_MOD_SHIFT }, 1198 { "\x1b[5;3~", TB_KEY_PGUP, TB_MOD_ALT }, 1199 { "\x1b[5;4~", TB_KEY_PGUP, TB_MOD_ALT | TB_MOD_SHIFT }, 1200 { "\x1b[5;5~", TB_KEY_PGUP, TB_MOD_CTRL }, 1201 { "\x1b[5;6~", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_SHIFT }, 1202 { "\x1b[5;7~", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_ALT }, 1203 { "\x1b[5;8~", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1204 1205 { "\x1b[6;2~", TB_KEY_PGDN, TB_MOD_SHIFT }, 1206 { "\x1b[6;3~", TB_KEY_PGDN, TB_MOD_ALT }, 1207 { "\x1b[6;4~", TB_KEY_PGDN, TB_MOD_ALT | TB_MOD_SHIFT }, 1208 { "\x1b[6;5~", TB_KEY_PGDN, TB_MOD_CTRL }, 1209 { "\x1b[6;6~", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_SHIFT }, 1210 { "\x1b[6;7~", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_ALT }, 1211 { "\x1b[6;8~", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1212 1213 { "\x1b[1;2P", TB_KEY_F1, TB_MOD_SHIFT }, 1214 { "\x1b[1;3P", TB_KEY_F1, TB_MOD_ALT }, 1215 { "\x1b[1;4P", TB_KEY_F1, TB_MOD_ALT | TB_MOD_SHIFT }, 1216 { "\x1b[1;5P", TB_KEY_F1, TB_MOD_CTRL }, 1217 { "\x1b[1;6P", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_SHIFT }, 1218 { "\x1b[1;7P", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_ALT }, 1219 { "\x1b[1;8P", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1220 1221 { "\x1b[1;2Q", TB_KEY_F2, TB_MOD_SHIFT }, 1222 { "\x1b[1;3Q", TB_KEY_F2, TB_MOD_ALT }, 1223 { "\x1b[1;4Q", TB_KEY_F2, TB_MOD_ALT | TB_MOD_SHIFT }, 1224 { "\x1b[1;5Q", TB_KEY_F2, TB_MOD_CTRL }, 1225 { "\x1b[1;6Q", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_SHIFT }, 1226 { "\x1b[1;7Q", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_ALT }, 1227 { "\x1b[1;8Q", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1228 1229 { "\x1b[1;2R", TB_KEY_F3, TB_MOD_SHIFT }, 1230 { "\x1b[1;3R", TB_KEY_F3, TB_MOD_ALT }, 1231 { "\x1b[1;4R", TB_KEY_F3, TB_MOD_ALT | TB_MOD_SHIFT }, 1232 { "\x1b[1;5R", TB_KEY_F3, TB_MOD_CTRL }, 1233 { "\x1b[1;6R", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_SHIFT }, 1234 { "\x1b[1;7R", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_ALT }, 1235 { "\x1b[1;8R", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1236 1237 { "\x1b[1;2S", TB_KEY_F4, TB_MOD_SHIFT }, 1238 { "\x1b[1;3S", TB_KEY_F4, TB_MOD_ALT }, 1239 { "\x1b[1;4S", TB_KEY_F4, TB_MOD_ALT | TB_MOD_SHIFT }, 1240 { "\x1b[1;5S", TB_KEY_F4, TB_MOD_CTRL }, 1241 { "\x1b[1;6S", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_SHIFT }, 1242 { "\x1b[1;7S", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_ALT }, 1243 { "\x1b[1;8S", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1244 1245 { "\x1b[15;2~", TB_KEY_F5, TB_MOD_SHIFT }, 1246 { "\x1b[15;3~", TB_KEY_F5, TB_MOD_ALT }, 1247 { "\x1b[15;4~", TB_KEY_F5, TB_MOD_ALT | TB_MOD_SHIFT }, 1248 { "\x1b[15;5~", TB_KEY_F5, TB_MOD_CTRL }, 1249 { "\x1b[15;6~", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_SHIFT }, 1250 { "\x1b[15;7~", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_ALT }, 1251 { "\x1b[15;8~", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1252 1253 { "\x1b[17;2~", TB_KEY_F6, TB_MOD_SHIFT }, 1254 { "\x1b[17;3~", TB_KEY_F6, TB_MOD_ALT }, 1255 { "\x1b[17;4~", TB_KEY_F6, TB_MOD_ALT | TB_MOD_SHIFT }, 1256 { "\x1b[17;5~", TB_KEY_F6, TB_MOD_CTRL }, 1257 { "\x1b[17;6~", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_SHIFT }, 1258 { "\x1b[17;7~", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_ALT }, 1259 { "\x1b[17;8~", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1260 1261 { "\x1b[18;2~", TB_KEY_F7, TB_MOD_SHIFT }, 1262 { "\x1b[18;3~", TB_KEY_F7, TB_MOD_ALT }, 1263 { "\x1b[18;4~", TB_KEY_F7, TB_MOD_ALT | TB_MOD_SHIFT }, 1264 { "\x1b[18;5~", TB_KEY_F7, TB_MOD_CTRL }, 1265 { "\x1b[18;6~", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_SHIFT }, 1266 { "\x1b[18;7~", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_ALT }, 1267 { "\x1b[18;8~", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1268 1269 { "\x1b[19;2~", TB_KEY_F8, TB_MOD_SHIFT }, 1270 { "\x1b[19;3~", TB_KEY_F8, TB_MOD_ALT }, 1271 { "\x1b[19;4~", TB_KEY_F8, TB_MOD_ALT | TB_MOD_SHIFT }, 1272 { "\x1b[19;5~", TB_KEY_F8, TB_MOD_CTRL }, 1273 { "\x1b[19;6~", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_SHIFT }, 1274 { "\x1b[19;7~", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_ALT }, 1275 { "\x1b[19;8~", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1276 1277 { "\x1b[20;2~", TB_KEY_F9, TB_MOD_SHIFT }, 1278 { "\x1b[20;3~", TB_KEY_F9, TB_MOD_ALT }, 1279 { "\x1b[20;4~", TB_KEY_F9, TB_MOD_ALT | TB_MOD_SHIFT }, 1280 { "\x1b[20;5~", TB_KEY_F9, TB_MOD_CTRL }, 1281 { "\x1b[20;6~", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_SHIFT }, 1282 { "\x1b[20;7~", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_ALT }, 1283 { "\x1b[20;8~", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1284 1285 { "\x1b[21;2~", TB_KEY_F10, TB_MOD_SHIFT }, 1286 { "\x1b[21;3~", TB_KEY_F10, TB_MOD_ALT }, 1287 { "\x1b[21;4~", TB_KEY_F10, TB_MOD_ALT | TB_MOD_SHIFT }, 1288 { "\x1b[21;5~", TB_KEY_F10, TB_MOD_CTRL }, 1289 { "\x1b[21;6~", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_SHIFT }, 1290 { "\x1b[21;7~", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_ALT }, 1291 { "\x1b[21;8~", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1292 1293 { "\x1b[23;2~", TB_KEY_F11, TB_MOD_SHIFT }, 1294 { "\x1b[23;3~", TB_KEY_F11, TB_MOD_ALT }, 1295 { "\x1b[23;4~", TB_KEY_F11, TB_MOD_ALT | TB_MOD_SHIFT }, 1296 { "\x1b[23;5~", TB_KEY_F11, TB_MOD_CTRL }, 1297 { "\x1b[23;6~", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_SHIFT }, 1298 { "\x1b[23;7~", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_ALT }, 1299 { "\x1b[23;8~", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1300 1301 { "\x1b[24;2~", TB_KEY_F12, TB_MOD_SHIFT }, 1302 { "\x1b[24;3~", TB_KEY_F12, TB_MOD_ALT }, 1303 { "\x1b[24;4~", TB_KEY_F12, TB_MOD_ALT | TB_MOD_SHIFT }, 1304 { "\x1b[24;5~", TB_KEY_F12, TB_MOD_CTRL }, 1305 { "\x1b[24;6~", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_SHIFT }, 1306 { "\x1b[24;7~", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_ALT }, 1307 { "\x1b[24;8~", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1308 1309 // rxvt arrows 1310 { "\x1b[a", TB_KEY_ARROW_UP, TB_MOD_SHIFT }, 1311 { "\x1b\x1b[A", TB_KEY_ARROW_UP, TB_MOD_ALT }, 1312 { "\x1b\x1b[a", TB_KEY_ARROW_UP, TB_MOD_ALT | TB_MOD_SHIFT }, 1313 { "\x1bOa", TB_KEY_ARROW_UP, TB_MOD_CTRL }, 1314 { "\x1b\x1bOa", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT }, 1315 1316 { "\x1b[b", TB_KEY_ARROW_DOWN, TB_MOD_SHIFT }, 1317 { "\x1b\x1b[B", TB_KEY_ARROW_DOWN, TB_MOD_ALT }, 1318 { "\x1b\x1b[b", TB_KEY_ARROW_DOWN, TB_MOD_ALT | TB_MOD_SHIFT }, 1319 { "\x1bOb", TB_KEY_ARROW_DOWN, TB_MOD_CTRL }, 1320 { "\x1b\x1bOb", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_ALT }, 1321 1322 { "\x1b[c", TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT }, 1323 { "\x1b\x1b[C", TB_KEY_ARROW_RIGHT, TB_MOD_ALT }, 1324 { "\x1b\x1b[c", TB_KEY_ARROW_RIGHT, TB_MOD_ALT | TB_MOD_SHIFT }, 1325 { "\x1bOc", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL }, 1326 { "\x1b\x1bOc", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT }, 1327 1328 { "\x1b[d", TB_KEY_ARROW_LEFT, TB_MOD_SHIFT }, 1329 { "\x1b\x1b[D", TB_KEY_ARROW_LEFT, TB_MOD_ALT }, 1330 { "\x1b\x1b[d", TB_KEY_ARROW_LEFT, TB_MOD_ALT | TB_MOD_SHIFT }, 1331 { "\x1bOd", TB_KEY_ARROW_LEFT, TB_MOD_CTRL }, 1332 { "\x1b\x1bOd", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_ALT }, 1333 1334 // rxvt keys 1335 { "\x1b[7$", TB_KEY_HOME, TB_MOD_SHIFT }, 1336 { "\x1b\x1b[7~", TB_KEY_HOME, TB_MOD_ALT }, 1337 { "\x1b\x1b[7$", TB_KEY_HOME, TB_MOD_ALT | TB_MOD_SHIFT }, 1338 { "\x1b[7^", TB_KEY_HOME, TB_MOD_CTRL }, 1339 { "\x1b[7@", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_SHIFT }, 1340 { "\x1b\x1b[7^", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_ALT }, 1341 { "\x1b\x1b[7@", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1342 1343 { "\x1b\x1b[8~", TB_KEY_END, TB_MOD_ALT }, 1344 { "\x1b\x1b[8$", TB_KEY_END, TB_MOD_ALT | TB_MOD_SHIFT }, 1345 { "\x1b[8^", TB_KEY_END, TB_MOD_CTRL }, 1346 { "\x1b\x1b[8^", TB_KEY_END, TB_MOD_CTRL | TB_MOD_ALT }, 1347 { "\x1b\x1b[8@", TB_KEY_END, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1348 { "\x1b[8@", TB_KEY_END, TB_MOD_CTRL | TB_MOD_SHIFT }, 1349 { "\x1b[8$", TB_KEY_END, TB_MOD_SHIFT }, 1350 1351 { "\x1b\x1b[2~", TB_KEY_INSERT, TB_MOD_ALT }, 1352 { "\x1b\x1b[2$", TB_KEY_INSERT, TB_MOD_ALT | TB_MOD_SHIFT }, 1353 { "\x1b[2^", TB_KEY_INSERT, TB_MOD_CTRL }, 1354 { "\x1b\x1b[2^", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_ALT }, 1355 { "\x1b\x1b[2@", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1356 { "\x1b[2@", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_SHIFT }, 1357 { "\x1b[2$", TB_KEY_INSERT, TB_MOD_SHIFT }, 1358 1359 { "\x1b\x1b[3~", TB_KEY_DELETE, TB_MOD_ALT }, 1360 { "\x1b\x1b[3$", TB_KEY_DELETE, TB_MOD_ALT | TB_MOD_SHIFT }, 1361 { "\x1b[3^", TB_KEY_DELETE, TB_MOD_CTRL }, 1362 { "\x1b\x1b[3^", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_ALT }, 1363 { "\x1b\x1b[3@", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1364 { "\x1b[3@", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_SHIFT }, 1365 { "\x1b[3$", TB_KEY_DELETE, TB_MOD_SHIFT }, 1366 1367 { "\x1b\x1b[5~", TB_KEY_PGUP, TB_MOD_ALT }, 1368 { "\x1b\x1b[5$", TB_KEY_PGUP, TB_MOD_ALT | TB_MOD_SHIFT }, 1369 { "\x1b[5^", TB_KEY_PGUP, TB_MOD_CTRL }, 1370 { "\x1b\x1b[5^", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_ALT }, 1371 { "\x1b\x1b[5@", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1372 { "\x1b[5@", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_SHIFT }, 1373 { "\x1b[5$", TB_KEY_PGUP, TB_MOD_SHIFT }, 1374 1375 { "\x1b\x1b[6~", TB_KEY_PGDN, TB_MOD_ALT }, 1376 { "\x1b\x1b[6$", TB_KEY_PGDN, TB_MOD_ALT | TB_MOD_SHIFT }, 1377 { "\x1b[6^", TB_KEY_PGDN, TB_MOD_CTRL }, 1378 { "\x1b\x1b[6^", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_ALT }, 1379 { "\x1b\x1b[6@", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1380 { "\x1b[6@", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_SHIFT }, 1381 { "\x1b[6$", TB_KEY_PGDN, TB_MOD_SHIFT }, 1382 1383 { "\x1b\x1b[11~", TB_KEY_F1, TB_MOD_ALT }, 1384 { "\x1b\x1b[23~", TB_KEY_F1, TB_MOD_ALT | TB_MOD_SHIFT }, 1385 { "\x1b[11^", TB_KEY_F1, TB_MOD_CTRL }, 1386 { "\x1b\x1b[11^", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_ALT }, 1387 { "\x1b\x1b[23^", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1388 { "\x1b[23^", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_SHIFT }, 1389 { "\x1b[23~", TB_KEY_F1, TB_MOD_SHIFT }, 1390 1391 { "\x1b\x1b[12~", TB_KEY_F2, TB_MOD_ALT }, 1392 { "\x1b\x1b[24~", TB_KEY_F2, TB_MOD_ALT | TB_MOD_SHIFT }, 1393 { "\x1b[12^", TB_KEY_F2, TB_MOD_CTRL }, 1394 { "\x1b\x1b[12^", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_ALT }, 1395 { "\x1b\x1b[24^", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1396 { "\x1b[24^", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_SHIFT }, 1397 { "\x1b[24~", TB_KEY_F2, TB_MOD_SHIFT }, 1398 1399 { "\x1b\x1b[13~", TB_KEY_F3, TB_MOD_ALT }, 1400 { "\x1b\x1b[25~", TB_KEY_F3, TB_MOD_ALT | TB_MOD_SHIFT }, 1401 { "\x1b[13^", TB_KEY_F3, TB_MOD_CTRL }, 1402 { "\x1b\x1b[13^", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_ALT }, 1403 { "\x1b\x1b[25^", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1404 { "\x1b[25^", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_SHIFT }, 1405 { "\x1b[25~", TB_KEY_F3, TB_MOD_SHIFT }, 1406 1407 { "\x1b\x1b[14~", TB_KEY_F4, TB_MOD_ALT }, 1408 { "\x1b\x1b[26~", TB_KEY_F4, TB_MOD_ALT | TB_MOD_SHIFT }, 1409 { "\x1b[14^", TB_KEY_F4, TB_MOD_CTRL }, 1410 { "\x1b\x1b[14^", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_ALT }, 1411 { "\x1b\x1b[26^", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1412 { "\x1b[26^", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_SHIFT }, 1413 { "\x1b[26~", TB_KEY_F4, TB_MOD_SHIFT }, 1414 1415 { "\x1b\x1b[15~", TB_KEY_F5, TB_MOD_ALT }, 1416 { "\x1b\x1b[28~", TB_KEY_F5, TB_MOD_ALT | TB_MOD_SHIFT }, 1417 { "\x1b[15^", TB_KEY_F5, TB_MOD_CTRL }, 1418 { "\x1b\x1b[15^", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_ALT }, 1419 { "\x1b\x1b[28^", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1420 { "\x1b[28^", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_SHIFT }, 1421 { "\x1b[28~", TB_KEY_F5, TB_MOD_SHIFT }, 1422 1423 { "\x1b\x1b[17~", TB_KEY_F6, TB_MOD_ALT }, 1424 { "\x1b\x1b[29~", TB_KEY_F6, TB_MOD_ALT | TB_MOD_SHIFT }, 1425 { "\x1b[17^", TB_KEY_F6, TB_MOD_CTRL }, 1426 { "\x1b\x1b[17^", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_ALT }, 1427 { "\x1b\x1b[29^", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1428 { "\x1b[29^", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_SHIFT }, 1429 { "\x1b[29~", TB_KEY_F6, TB_MOD_SHIFT }, 1430 1431 { "\x1b\x1b[18~", TB_KEY_F7, TB_MOD_ALT }, 1432 { "\x1b\x1b[31~", TB_KEY_F7, TB_MOD_ALT | TB_MOD_SHIFT }, 1433 { "\x1b[18^", TB_KEY_F7, TB_MOD_CTRL }, 1434 { "\x1b\x1b[18^", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_ALT }, 1435 { "\x1b\x1b[31^", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1436 { "\x1b[31^", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_SHIFT }, 1437 { "\x1b[31~", TB_KEY_F7, TB_MOD_SHIFT }, 1438 1439 { "\x1b\x1b[19~", TB_KEY_F8, TB_MOD_ALT }, 1440 { "\x1b\x1b[32~", TB_KEY_F8, TB_MOD_ALT | TB_MOD_SHIFT }, 1441 { "\x1b[19^", TB_KEY_F8, TB_MOD_CTRL }, 1442 { "\x1b\x1b[19^", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_ALT }, 1443 { "\x1b\x1b[32^", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1444 { "\x1b[32^", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_SHIFT }, 1445 { "\x1b[32~", TB_KEY_F8, TB_MOD_SHIFT }, 1446 1447 { "\x1b\x1b[20~", TB_KEY_F9, TB_MOD_ALT }, 1448 { "\x1b\x1b[33~", TB_KEY_F9, TB_MOD_ALT | TB_MOD_SHIFT }, 1449 { "\x1b[20^", TB_KEY_F9, TB_MOD_CTRL }, 1450 { "\x1b\x1b[20^", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_ALT }, 1451 { "\x1b\x1b[33^", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1452 { "\x1b[33^", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_SHIFT }, 1453 { "\x1b[33~", TB_KEY_F9, TB_MOD_SHIFT }, 1454 1455 { "\x1b\x1b[21~", TB_KEY_F10, TB_MOD_ALT }, 1456 { "\x1b\x1b[34~", TB_KEY_F10, TB_MOD_ALT | TB_MOD_SHIFT }, 1457 { "\x1b[21^", TB_KEY_F10, TB_MOD_CTRL }, 1458 { "\x1b\x1b[21^", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_ALT }, 1459 { "\x1b\x1b[34^", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1460 { "\x1b[34^", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_SHIFT }, 1461 { "\x1b[34~", TB_KEY_F10, TB_MOD_SHIFT }, 1462 1463 { "\x1b\x1b[23~", TB_KEY_F11, TB_MOD_ALT }, 1464 { "\x1b\x1b[23$", TB_KEY_F11, TB_MOD_ALT | TB_MOD_SHIFT }, 1465 { "\x1b[23^", TB_KEY_F11, TB_MOD_CTRL }, 1466 { "\x1b\x1b[23^", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_ALT }, 1467 { "\x1b\x1b[23@", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1468 { "\x1b[23@", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_SHIFT }, 1469 { "\x1b[23$", TB_KEY_F11, TB_MOD_SHIFT }, 1470 1471 { "\x1b\x1b[24~", TB_KEY_F12, TB_MOD_ALT }, 1472 { "\x1b\x1b[24$", TB_KEY_F12, TB_MOD_ALT | TB_MOD_SHIFT }, 1473 { "\x1b[24^", TB_KEY_F12, TB_MOD_CTRL }, 1474 { "\x1b\x1b[24^", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_ALT }, 1475 { "\x1b\x1b[24@", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT }, 1476 { "\x1b[24@", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_SHIFT }, 1477 { "\x1b[24$", TB_KEY_F12, TB_MOD_SHIFT }, 1478 1479 // linux console/putty arrows 1480 { "\x1b[A", TB_KEY_ARROW_UP, TB_MOD_SHIFT }, 1481 { "\x1b[B", TB_KEY_ARROW_DOWN, TB_MOD_SHIFT }, 1482 { "\x1b[C", TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT }, 1483 { "\x1b[D", TB_KEY_ARROW_LEFT, TB_MOD_SHIFT }, 1484 1485 // more putty arrows 1486 { "\x1bOA", TB_KEY_ARROW_UP, TB_MOD_CTRL }, 1487 { "\x1b\x1bOA", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT }, 1488 { "\x1bOB", TB_KEY_ARROW_DOWN, TB_MOD_CTRL }, 1489 { "\x1b\x1bOB", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_ALT }, 1490 { "\x1bOC", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL }, 1491 { "\x1b\x1bOC", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT }, 1492 { "\x1bOD", TB_KEY_ARROW_LEFT, TB_MOD_CTRL }, 1493 { "\x1b\x1bOD", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_ALT }, 1494 1495 { NULL, 0, 0 }, 1496 }; 1497 1498 static const unsigned char utf8_length[256] = { 1, 1499 1, 1500 1, 1501 1, 1502 1, 1503 1, 1504 1, 1505 1, 1506 1, 1507 1, 1508 1, 1509 1, 1510 1, 1511 1, 1512 1, 1513 1, 1514 1, 1515 1, 1516 1, 1517 1, 1518 1, 1519 1, 1520 1, 1521 1, 1522 1, 1523 1, 1524 1, 1525 1, 1526 1, 1527 1, 1528 1, 1529 1, 1530 1, 1531 1, 1532 1, 1533 1, 1534 1, 1535 1, 1536 1, 1537 1, 1538 1, 1539 1, 1540 1, 1541 1, 1542 1, 1543 1, 1544 1, 1545 1, 1546 1, 1547 1, 1548 1, 1549 1, 1550 1, 1551 1, 1552 1, 1553 1, 1554 1, 1555 1, 1556 1, 1557 1, 1558 1, 1559 1, 1560 1, 1561 1, 1562 1, 1563 1, 1564 1, 1565 1, 1566 1, 1567 1, 1568 1, 1569 1, 1570 1, 1571 1, 1572 1, 1573 1, 1574 1, 1575 1, 1576 1, 1577 1, 1578 1, 1579 1, 1580 1, 1581 1, 1582 1, 1583 1, 1584 1, 1585 1, 1586 1, 1587 1, 1588 1, 1589 1, 1590 1, 1591 1, 1592 1, 1593 1, 1594 1, 1595 1, 1596 1, 1597 1, 1598 1, 1599 1, 1600 1, 1601 1, 1602 1, 1603 1, 1604 1, 1605 1, 1606 1, 1607 1, 1608 1, 1609 1, 1610 1, 1611 1, 1612 1, 1613 1, 1614 1, 1615 1, 1616 1, 1617 1, 1618 1, 1619 1, 1620 1, 1621 1, 1622 1, 1623 1, 1624 1, 1625 1, 1626 1, 1627 1, 1628 1, 1629 1, 1630 1, 1631 1, 1632 1, 1633 1, 1634 1, 1635 1, 1636 1, 1637 1, 1638 1, 1639 1, 1640 1, 1641 1, 1642 1, 1643 1, 1644 1, 1645 1, 1646 1, 1647 1, 1648 1, 1649 1, 1650 1, 1651 1, 1652 1, 1653 1, 1654 1, 1655 1, 1656 1, 1657 1, 1658 1, 1659 1, 1660 1, 1661 1, 1662 1, 1663 1, 1664 1, 1665 1, 1666 1, 1667 1, 1668 1, 1669 1, 1670 1, 1671 1, 1672 1, 1673 1, 1674 1, 1675 1, 1676 1, 1677 1, 1678 1, 1679 1, 1680 1, 1681 1, 1682 1, 1683 1, 1684 1, 1685 1, 1686 1, 1687 1, 1688 1, 1689 1, 1690 2, 1691 2, 1692 2, 1693 2, 1694 2, 1695 2, 1696 2, 1697 2, 1698 2, 1699 2, 1700 2, 1701 2, 1702 2, 1703 2, 1704 2, 1705 2, 1706 2, 1707 2, 1708 2, 1709 2, 1710 2, 1711 2, 1712 2, 1713 2, 1714 2, 1715 2, 1716 2, 1717 2, 1718 2, 1719 2, 1720 2, 1721 2, 1722 3, 1723 3, 1724 3, 1725 3, 1726 3, 1727 3, 1728 3, 1729 3, 1730 3, 1731 3, 1732 3, 1733 3, 1734 3, 1735 3, 1736 3, 1737 3, 1738 4, 1739 4, 1740 4, 1741 4, 1742 4, 1743 4, 1744 4, 1745 4, 1746 5, 1747 5, 1748 5, 1749 5, 1750 6, 1751 6, 1752 1, 1753 1 }; 1754 1755 static const unsigned char utf8_mask[6] = { 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; 1756 1757 static int tb_reset(void); 1758 static int tb_printf_inner( 1759 int x, int y, uintattr_t fg, uintattr_t bg, size_t* out_w, const char* fmt, va_list vl); 1760 static int init_term_attrs(void); 1761 static int init_term_caps(void); 1762 static int init_cap_trie(void); 1763 static int cap_trie_add(const char* cap, uint16_t key, uint8_t mod); 1764 static int cap_trie_find(const char* buf, size_t nbuf, struct cap_trie_t** last, size_t* depth); 1765 static int cap_trie_deinit(struct cap_trie_t* node); 1766 static int init_resize_handler(void); 1767 static int send_init_escape_codes(void); 1768 static int send_clear(void); 1769 static int update_term_size(void); 1770 static int update_term_size_via_esc(void); 1771 static int init_cellbuf(void); 1772 static int tb_deinit(void); 1773 static int load_terminfo(void); 1774 static int load_terminfo_from_path(const char* path, const char* term); 1775 static int read_terminfo_path(const char* path); 1776 static int parse_terminfo_caps(void); 1777 static int load_builtin_caps(void); 1778 static const char* get_terminfo_string(int16_t str_offsets_pos, 1779 int16_t str_offsets_len, 1780 int16_t str_table_pos, 1781 int16_t str_table_len, 1782 int16_t str_index); 1783 static int wait_event(struct tb_event* event, int timeout); 1784 static int extract_event(struct tb_event* event); 1785 static int extract_esc(struct tb_event* event); 1786 static int extract_esc_user(struct tb_event* event, int is_post); 1787 static int extract_esc_cap(struct tb_event* event); 1788 static int extract_esc_mouse(struct tb_event* event); 1789 static int resize_cellbufs(void); 1790 static void handle_resize(int sig); 1791 static int send_attr(uintattr_t fg, uintattr_t bg); 1792 static int send_sgr(uint32_t fg, uint32_t bg, int fg_is_default, int bg_is_default); 1793 static int send_cursor_if(int x, int y); 1794 static int send_char(int x, int y, uint32_t ch); 1795 static int send_cluster(int x, int y, uint32_t* ch, size_t nch); 1796 static int convert_num(uint32_t num, char* buf); 1797 static int cell_cmp(struct tb_cell* a, struct tb_cell* b); 1798 static int cell_copy(struct tb_cell* dst, struct tb_cell* src); 1799 static int cell_set(struct tb_cell* cell, uint32_t* ch, size_t nch, uintattr_t fg, uintattr_t bg); 1800 static int cell_reserve_ech(struct tb_cell* cell, size_t n); 1801 static int cell_free(struct tb_cell* cell); 1802 static int cellbuf_init(struct cellbuf_t* c, int w, int h); 1803 static int cellbuf_free(struct cellbuf_t* c); 1804 static int cellbuf_clear(struct cellbuf_t* c); 1805 static int cellbuf_get(struct cellbuf_t* c, int x, int y, struct tb_cell** out); 1806 static int cellbuf_in_bounds(struct cellbuf_t* c, int x, int y); 1807 static int cellbuf_resize(struct cellbuf_t* c, int w, int h); 1808 static int bytebuf_puts(struct bytebuf_t* b, const char* str); 1809 static int bytebuf_nputs(struct bytebuf_t* b, const char* str, size_t nstr); 1810 static int bytebuf_shift(struct bytebuf_t* b, size_t n); 1811 static int bytebuf_flush(struct bytebuf_t* b, int fd); 1812 static int bytebuf_reserve(struct bytebuf_t* b, size_t sz); 1813 static int bytebuf_free(struct bytebuf_t* b); 1814 1815 int 1816 tb_init(void) 1817 { 1818 return tb_init_file("/dev/tty"); 1819 } 1820 1821 int 1822 tb_init_file(const char* path) 1823 { 1824 if (global.initialized) return TB_ERR_INIT_ALREADY; 1825 int ttyfd = open(path, O_RDWR); 1826 if (ttyfd < 0) { 1827 global.last_errno = errno; 1828 return TB_ERR_INIT_OPEN; 1829 } 1830 global.ttyfd_open = 1; 1831 return tb_init_fd(ttyfd); 1832 } 1833 1834 int 1835 tb_init_fd(int ttyfd) 1836 { 1837 return tb_init_rwfd(ttyfd, ttyfd); 1838 } 1839 1840 int 1841 tb_init_rwfd(int rfd, int wfd) 1842 { 1843 int rv; 1844 1845 tb_reset(); 1846 global.ttyfd = rfd == wfd && isatty(rfd) ? rfd : -1; 1847 global.rfd = rfd; 1848 global.wfd = wfd; 1849 1850 do { 1851 if_err_break(rv, init_term_attrs()); 1852 if_err_break(rv, init_term_caps()); 1853 if_err_break(rv, init_cap_trie()); 1854 if_err_break(rv, init_resize_handler()); 1855 if_err_break(rv, send_init_escape_codes()); 1856 if_err_break(rv, send_clear()); 1857 if_err_break(rv, update_term_size()); 1858 if_err_break(rv, init_cellbuf()); 1859 global.initialized = 1; 1860 } while (0); 1861 1862 if (rv != TB_OK) { tb_deinit(); } 1863 1864 return rv; 1865 } 1866 1867 int 1868 tb_shutdown(void) 1869 { 1870 if_not_init_return(); 1871 tb_deinit(); 1872 return TB_OK; 1873 } 1874 1875 int 1876 tb_width(void) 1877 { 1878 if_not_init_return(); 1879 return global.width; 1880 } 1881 1882 int 1883 tb_height(void) 1884 { 1885 if_not_init_return(); 1886 return global.height; 1887 } 1888 1889 int 1890 tb_clear(void) 1891 { 1892 if_not_init_return(); 1893 return cellbuf_clear(&global.back); 1894 } 1895 1896 int 1897 tb_set_clear_attrs(uintattr_t fg, uintattr_t bg) 1898 { 1899 if_not_init_return(); 1900 global.fg = fg; 1901 global.bg = bg; 1902 return TB_OK; 1903 } 1904 1905 int 1906 tb_present(void) 1907 { 1908 if_not_init_return(); 1909 1910 int rv; 1911 1912 // TODO: Assert global.back.(width,height) == global.front.(width,height) 1913 1914 global.last_x = -1; 1915 global.last_y = -1; 1916 1917 int x, y, i; 1918 for (y = 0; y < global.front.height; y++) { 1919 for (x = 0; x < global.front.width;) { 1920 struct tb_cell *back, *front; 1921 if_err_return(rv, cellbuf_get(&global.back, x, y, &back)); 1922 if_err_return(rv, cellbuf_get(&global.front, x, y, &front)); 1923 1924 int w; 1925 { 1926 #ifdef TB_OPT_EGC 1927 if (back->nech > 0) 1928 w = wcswidth((wchar_t*)back->ech, back->nech); 1929 else 1930 #endif 1931 // wcwidth simply returns -1 on overflow of wchar_t 1932 w = wcwidth((wchar_t)back->ch); 1933 } 1934 if (w < 1) w = 1; 1935 1936 if (cell_cmp(back, front) != 0) { 1937 cell_copy(front, back); 1938 1939 send_attr(back->fg, back->bg); 1940 if (w > 1 && x >= global.front.width - (w - 1)) { 1941 // Not enough room for wide char, send spaces 1942 for (i = x; i < global.front.width; i++) { 1943 send_char(i, y, ' '); 1944 } 1945 } else { 1946 { 1947 #ifdef TB_OPT_EGC 1948 if (back->nech > 0) 1949 send_cluster(x, y, back->ech, back->nech); 1950 else 1951 #endif 1952 send_char(x, y, back->ch); 1953 } 1954 1955 // When wcwidth>1, we need to advance the cursor by more 1956 // than 1, thereby skipping some cells. Set these skipped 1957 // cells to an invalid codepoint in the front buffer, so 1958 // that if this cell is later replaced by a wcwidth==1 char, 1959 // we'll get a cell_cmp diff for the skipped cells and 1960 // properly re-render. 1961 for (i = 1; i < w; i++) { 1962 struct tb_cell* front_wide; 1963 uint32_t invalid = -1; 1964 if_err_return(rv, 1965 cellbuf_get(&global.front, x + i, y, &front_wide)); 1966 if_err_return(rv, cell_set(front_wide, &invalid, 1, -1, -1)); 1967 } 1968 } 1969 } 1970 x += w; 1971 } 1972 } 1973 1974 if_err_return(rv, send_cursor_if(global.cursor_x, global.cursor_y)); 1975 if_err_return(rv, bytebuf_flush(&global.out, global.wfd)); 1976 1977 return TB_OK; 1978 } 1979 1980 int 1981 tb_invalidate(void) 1982 { 1983 int rv; 1984 if_not_init_return(); 1985 if_err_return(rv, resize_cellbufs()); 1986 return TB_OK; 1987 } 1988 1989 int 1990 tb_set_cursor(int cx, int cy) 1991 { 1992 if_not_init_return(); 1993 int rv; 1994 if (cx < 0) cx = 0; 1995 if (cy < 0) cy = 0; 1996 if (global.cursor_x == -1) { 1997 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR])); 1998 } 1999 if_err_return(rv, send_cursor_if(cx, cy)); 2000 global.cursor_x = cx; 2001 global.cursor_y = cy; 2002 return TB_OK; 2003 } 2004 2005 int 2006 tb_hide_cursor(void) 2007 { 2008 if_not_init_return(); 2009 int rv; 2010 if (global.cursor_x >= 0) { 2011 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_HIDE_CURSOR])); 2012 } 2013 global.cursor_x = -1; 2014 global.cursor_y = -1; 2015 return TB_OK; 2016 } 2017 2018 int 2019 tb_set_cell(int x, int y, uint32_t ch, uintattr_t fg, uintattr_t bg) 2020 { 2021 return tb_set_cell_ex(x, y, &ch, 1, fg, bg); 2022 } 2023 2024 int 2025 tb_set_cell_ex(int x, int y, uint32_t* ch, size_t nch, uintattr_t fg, uintattr_t bg) 2026 { 2027 if_not_init_return(); 2028 int rv; 2029 struct tb_cell* cell; 2030 if_err_return(rv, cellbuf_get(&global.back, x, y, &cell)); 2031 if_err_return(rv, cell_set(cell, ch, nch, fg, bg)); 2032 return TB_OK; 2033 } 2034 2035 int 2036 tb_extend_cell(int x, int y, uint32_t ch) 2037 { 2038 if_not_init_return(); 2039 #ifdef TB_OPT_EGC 2040 int rv; 2041 struct tb_cell* cell; 2042 size_t nech; 2043 if_err_return(rv, cellbuf_get(&global.back, x, y, &cell)); 2044 if (cell->nech > 0) { // append to ech 2045 nech = cell->nech + 1; 2046 if_err_return(rv, cell_reserve_ech(cell, nech + 1)); 2047 cell->ech[nech - 1] = ch; 2048 } else { // make new ech 2049 nech = 2; 2050 if_err_return(rv, cell_reserve_ech(cell, nech + 1)); 2051 cell->ech[0] = cell->ch; 2052 cell->ech[1] = ch; 2053 } 2054 cell->ech[nech] = '\0'; 2055 cell->nech = nech; 2056 return TB_OK; 2057 #else 2058 (void)x; 2059 (void)y; 2060 (void)ch; 2061 return TB_ERR; 2062 #endif 2063 } 2064 2065 int 2066 tb_set_input_mode(int mode) 2067 { 2068 if_not_init_return(); 2069 if (mode == TB_INPUT_CURRENT) { return global.input_mode; } 2070 2071 if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == 0) { mode |= TB_INPUT_ESC; } 2072 2073 if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == (TB_INPUT_ESC | TB_INPUT_ALT)) { 2074 mode &= ~TB_INPUT_ALT; 2075 } 2076 2077 if (mode & TB_INPUT_MOUSE) { 2078 bytebuf_puts(&global.out, TB_HARDCAP_ENTER_MOUSE); 2079 bytebuf_flush(&global.out, global.wfd); 2080 } else { 2081 bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE); 2082 bytebuf_flush(&global.out, global.wfd); 2083 } 2084 2085 global.input_mode = mode; 2086 return TB_OK; 2087 } 2088 2089 int 2090 tb_set_output_mode(int mode) 2091 { 2092 if_not_init_return(); 2093 switch (mode) { 2094 case TB_OUTPUT_CURRENT: 2095 return global.output_mode; 2096 case TB_OUTPUT_NORMAL: 2097 case TB_OUTPUT_256: 2098 case TB_OUTPUT_216: 2099 case TB_OUTPUT_GRAYSCALE: 2100 #if TB_OPT_ATTR_W >= 32 2101 case TB_OUTPUT_TRUECOLOR: 2102 #endif 2103 global.last_fg = ~global.fg; 2104 global.last_bg = ~global.bg; 2105 global.output_mode = mode; 2106 return TB_OK; 2107 } 2108 return TB_ERR; 2109 } 2110 2111 int 2112 tb_peek_event(struct tb_event* event, int timeout_ms) 2113 { 2114 if_not_init_return(); 2115 return wait_event(event, timeout_ms); 2116 } 2117 2118 int 2119 tb_poll_event(struct tb_event* event) 2120 { 2121 if_not_init_return(); 2122 return wait_event(event, -1); 2123 } 2124 2125 int 2126 tb_get_fds(int* ttyfd, int* resizefd) 2127 { 2128 if_not_init_return(); 2129 2130 *ttyfd = global.rfd; 2131 *resizefd = global.resize_pipefd[0]; 2132 2133 return TB_OK; 2134 } 2135 2136 int 2137 tb_print(int x, int y, uintattr_t fg, uintattr_t bg, const char* str) 2138 { 2139 return tb_print_ex(x, y, fg, bg, NULL, str); 2140 } 2141 2142 int 2143 tb_print_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t* out_w, const char* str) 2144 { 2145 int rv, w, ix, x_prev; 2146 uint32_t uni; 2147 2148 if_not_init_return(); 2149 2150 if (!cellbuf_in_bounds(&global.back, x, y)) { return TB_ERR_OUT_OF_BOUNDS; } 2151 2152 ix = x; 2153 x_prev = x; 2154 if (out_w) *out_w = 0; 2155 2156 while (*str) { 2157 rv = tb_utf8_char_to_unicode(&uni, str); 2158 2159 if (rv < 0) { 2160 uni = 0xfffd; // replace invalid UTF-8 char with U+FFFD 2161 str += rv * -1; 2162 } else if (rv > 0) { 2163 str += rv; 2164 } else { 2165 break; // shouldn't get here 2166 } 2167 2168 if (uni == '\n') { // TODO: \r, \t, \v, \f, etc? 2169 x = ix; 2170 x_prev = x; 2171 y += 1; 2172 continue; 2173 } else if (!iswprint((wint_t)uni)) { 2174 uni = 0xfffd; // replace non-printable with U+FFFD 2175 } 2176 2177 w = wcwidth((wchar_t)uni); 2178 if (w < 0) { 2179 return TB_ERR; // shouldn't happen if iswprint 2180 } else if (w == 0) { // combining character 2181 if (cellbuf_in_bounds(&global.back, x_prev, y)) { 2182 if_err_return(rv, tb_extend_cell(x_prev, y, uni)); 2183 } 2184 } else { 2185 if (cellbuf_in_bounds(&global.back, x, y)) { 2186 if_err_return(rv, tb_set_cell(x, y, uni, fg, bg)); 2187 } 2188 x_prev = x; 2189 x += w; 2190 if (out_w) *out_w += w; 2191 } 2192 } 2193 2194 return TB_OK; 2195 } 2196 2197 int 2198 tb_printf(int x, int y, uintattr_t fg, uintattr_t bg, const char* fmt, ...) 2199 { 2200 int rv; 2201 va_list vl; 2202 va_start(vl, fmt); 2203 rv = tb_printf_inner(x, y, fg, bg, NULL, fmt, vl); 2204 va_end(vl); 2205 return rv; 2206 } 2207 2208 int 2209 tb_printf_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t* out_w, const char* fmt, ...) 2210 { 2211 int rv; 2212 va_list vl; 2213 va_start(vl, fmt); 2214 rv = tb_printf_inner(x, y, fg, bg, out_w, fmt, vl); 2215 va_end(vl); 2216 return rv; 2217 } 2218 2219 int 2220 tb_send(const char* buf, size_t nbuf) 2221 { 2222 return bytebuf_nputs(&global.out, buf, nbuf); 2223 } 2224 2225 int 2226 tb_sendf(const char* fmt, ...) 2227 { 2228 int rv; 2229 char buf[TB_OPT_PRINTF_BUF]; 2230 va_list vl; 2231 va_start(vl, fmt); 2232 rv = vsnprintf(buf, sizeof(buf), fmt, vl); 2233 va_end(vl); 2234 if (rv < 0 || rv >= (int)sizeof(buf)) { return TB_ERR; } 2235 return tb_send(buf, (size_t)rv); 2236 } 2237 2238 int 2239 tb_set_func(int fn_type, int (*fn)(struct tb_event*, size_t*)) 2240 { 2241 switch (fn_type) { 2242 case TB_FUNC_EXTRACT_PRE: 2243 global.fn_extract_esc_pre = fn; 2244 return TB_OK; 2245 case TB_FUNC_EXTRACT_POST: 2246 global.fn_extract_esc_post = fn; 2247 return TB_OK; 2248 } 2249 return TB_ERR; 2250 } 2251 2252 struct tb_cell* 2253 tb_cell_buffer(void) 2254 { 2255 if (!global.initialized) return NULL; 2256 return global.back.cells; 2257 } 2258 2259 int 2260 tb_utf8_char_length(char c) 2261 { 2262 return utf8_length[(unsigned char)c]; 2263 } 2264 2265 int 2266 tb_utf8_char_to_unicode(uint32_t* out, const char* c) 2267 { 2268 if (*c == '\0') return 0; 2269 2270 int i; 2271 unsigned char len = tb_utf8_char_length(*c); 2272 unsigned char mask = utf8_mask[len - 1]; 2273 uint32_t result = c[0] & mask; 2274 for (i = 1; i < len && c[i] != '\0'; ++i) { 2275 result <<= 6; 2276 result |= c[i] & 0x3f; 2277 } 2278 2279 if (i != len) return i * -1; 2280 2281 *out = result; 2282 return (int)len; 2283 } 2284 2285 int 2286 tb_utf8_unicode_to_char(char* out, uint32_t c) 2287 { 2288 int len = 0; 2289 int first; 2290 int i; 2291 2292 if (c < 0x80) { 2293 first = 0; 2294 len = 1; 2295 } else if (c < 0x800) { 2296 first = 0xc0; 2297 len = 2; 2298 } else if (c < 0x10000) { 2299 first = 0xe0; 2300 len = 3; 2301 } else if (c < 0x200000) { 2302 first = 0xf0; 2303 len = 4; 2304 } else if (c < 0x4000000) { 2305 first = 0xf8; 2306 len = 5; 2307 } else { 2308 first = 0xfc; 2309 len = 6; 2310 } 2311 2312 for (i = len - 1; i > 0; --i) { 2313 out[i] = (c & 0x3f) | 0x80; 2314 c >>= 6; 2315 } 2316 out[0] = c | first; 2317 out[len] = '\0'; 2318 2319 return len; 2320 } 2321 2322 int 2323 tb_last_errno(void) 2324 { 2325 return global.last_errno; 2326 } 2327 2328 const char* 2329 tb_strerror(int err) 2330 { 2331 switch (err) { 2332 case TB_OK: 2333 return "Success"; 2334 case TB_ERR_NEED_MORE: 2335 return "Not enough input"; 2336 case TB_ERR_INIT_ALREADY: 2337 return "Termbox initialized already"; 2338 case TB_ERR_MEM: 2339 return "Out of memory"; 2340 case TB_ERR_NO_EVENT: 2341 return "No event"; 2342 case TB_ERR_NO_TERM: 2343 return "No TERM in environment"; 2344 case TB_ERR_NOT_INIT: 2345 return "Termbox not initialized"; 2346 case TB_ERR_OUT_OF_BOUNDS: 2347 return "Out of bounds"; 2348 case TB_ERR_UNSUPPORTED_TERM: 2349 return "Unsupported terminal"; 2350 case TB_ERR_CAP_COLLISION: 2351 return "Termcaps collision"; 2352 case TB_ERR_RESIZE_SSCANF: 2353 return "Terminal width/height not received by sscanf() after " 2354 "resize"; 2355 case TB_ERR: 2356 case TB_ERR_INIT_OPEN: 2357 case TB_ERR_READ: 2358 case TB_ERR_RESIZE_IOCTL: 2359 case TB_ERR_RESIZE_PIPE: 2360 case TB_ERR_RESIZE_SIGACTION: 2361 case TB_ERR_POLL: 2362 case TB_ERR_TCGETATTR: 2363 case TB_ERR_TCSETATTR: 2364 case TB_ERR_RESIZE_WRITE: 2365 case TB_ERR_RESIZE_POLL: 2366 case TB_ERR_RESIZE_READ: 2367 default: 2368 strerror_r(global.last_errno, global.errbuf, sizeof(global.errbuf)); 2369 return (const char*)global.errbuf; 2370 } 2371 } 2372 2373 int 2374 tb_has_truecolor(void) 2375 { 2376 #if TB_OPT_ATTR_W >= 32 2377 return 1; 2378 #else 2379 return 0; 2380 #endif 2381 } 2382 2383 int 2384 tb_has_egc(void) 2385 { 2386 #ifdef TB_OPT_EGC 2387 return 1; 2388 #else 2389 return 0; 2390 #endif 2391 } 2392 2393 int 2394 tb_attr_width(void) 2395 { 2396 return TB_OPT_ATTR_W; 2397 } 2398 2399 const char* 2400 tb_version(void) 2401 { 2402 return TB_VERSION_STR; 2403 } 2404 2405 static int 2406 tb_reset(void) 2407 { 2408 int ttyfd_open = global.ttyfd_open; 2409 memset(&global, 0, sizeof(global)); 2410 global.ttyfd = -1; 2411 global.rfd = -1; 2412 global.wfd = -1; 2413 global.ttyfd_open = ttyfd_open; 2414 global.resize_pipefd[0] = -1; 2415 global.resize_pipefd[1] = -1; 2416 global.width = -1; 2417 global.height = -1; 2418 global.cursor_x = -1; 2419 global.cursor_y = -1; 2420 global.last_x = -1; 2421 global.last_y = -1; 2422 global.fg = TB_DEFAULT; 2423 global.bg = TB_DEFAULT; 2424 global.last_fg = ~global.fg; 2425 global.last_bg = ~global.bg; 2426 global.input_mode = TB_INPUT_ESC; 2427 global.output_mode = TB_OUTPUT_NORMAL; 2428 return TB_OK; 2429 } 2430 2431 static int 2432 init_term_attrs(void) 2433 { 2434 if (global.ttyfd < 0) { return TB_OK; } 2435 2436 if (tcgetattr(global.ttyfd, &global.orig_tios) != 0) { 2437 global.last_errno = errno; 2438 return TB_ERR_TCGETATTR; 2439 } 2440 2441 struct termios tios; 2442 memcpy(&tios, &global.orig_tios, sizeof(tios)); 2443 global.has_orig_tios = 1; 2444 2445 cfmakeraw(&tios); 2446 tios.c_cc[VMIN] = 1; 2447 tios.c_cc[VTIME] = 0; 2448 2449 if (tcsetattr(global.ttyfd, TCSAFLUSH, &tios) != 0) { 2450 global.last_errno = errno; 2451 return TB_ERR_TCSETATTR; 2452 } 2453 2454 return TB_OK; 2455 } 2456 2457 int 2458 tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg, size_t* out_w, const char* fmt, va_list vl) 2459 { 2460 int rv; 2461 char buf[TB_OPT_PRINTF_BUF]; 2462 rv = vsnprintf(buf, sizeof(buf), fmt, vl); 2463 if (rv < 0 || rv >= (int)sizeof(buf)) { return TB_ERR; } 2464 return tb_print_ex(x, y, fg, bg, out_w, buf); 2465 } 2466 2467 static int 2468 init_term_caps(void) 2469 { 2470 if (load_terminfo() == TB_OK) { return parse_terminfo_caps(); } 2471 return load_builtin_caps(); 2472 } 2473 2474 static int 2475 init_cap_trie(void) 2476 { 2477 int rv, i; 2478 2479 // Add caps from terminfo or built-in 2480 // 2481 // Collisions are expected as some terminfo entries have dupes. (For 2482 // example, att605-pc collides on TB_CAP_F4 and TB_CAP_DELETE.) First cap 2483 // in TB_CAP_* index order will win. 2484 // 2485 // TODO: Reorder TB_CAP_* so more critical caps come first. 2486 for (i = 0; i < TB_CAP__COUNT_KEYS; i++) { 2487 rv = cap_trie_add(global.caps[i], tb_key_i(i), 0); 2488 if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv; 2489 } 2490 2491 // Add built-in mod caps 2492 // 2493 // Collisions are OK here as well. This can happen if global.caps collides 2494 // with builtin_mod_caps. It is desirable to give precedence to global.caps 2495 // here. 2496 for (i = 0; builtin_mod_caps[i].cap != NULL; i++) { 2497 rv = cap_trie_add(builtin_mod_caps[i].cap, builtin_mod_caps[i].key, builtin_mod_caps[i].mod); 2498 if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv; 2499 } 2500 2501 return TB_OK; 2502 } 2503 2504 static int 2505 cap_trie_add(const char* cap, uint16_t key, uint8_t mod) 2506 { 2507 struct cap_trie_t *next, *node = &global.cap_trie; 2508 size_t i, j; 2509 2510 if (!cap || strlen(cap) <= 0) return TB_OK; // Nothing to do for empty caps 2511 2512 for (i = 0; cap[i] != '\0'; i++) { 2513 char c = cap[i]; 2514 next = NULL; 2515 2516 // Check if c is already a child of node 2517 for (j = 0; j < node->nchildren; j++) { 2518 if (node->children[j].c == c) { 2519 next = &node->children[j]; 2520 break; 2521 } 2522 } 2523 if (!next) { 2524 // We need to add a new child to node 2525 node->nchildren += 1; 2526 node->children = (struct cap_trie_t*)tb_realloc( 2527 node->children, sizeof(*node) * node->nchildren); 2528 if (!node->children) { return TB_ERR_MEM; } 2529 next = &node->children[node->nchildren - 1]; 2530 memset(next, 0, sizeof(*next)); 2531 next->c = c; 2532 } 2533 2534 // Continue 2535 node = next; 2536 } 2537 2538 if (node->is_leaf) { 2539 // Already a leaf here 2540 return TB_ERR_CAP_COLLISION; 2541 } 2542 2543 node->is_leaf = 1; 2544 node->key = key; 2545 node->mod = mod; 2546 return TB_OK; 2547 } 2548 2549 static int 2550 cap_trie_find(const char* buf, size_t nbuf, struct cap_trie_t** last, size_t* depth) 2551 { 2552 struct cap_trie_t *next, *node = &global.cap_trie; 2553 size_t i, j; 2554 *last = node; 2555 *depth = 0; 2556 for (i = 0; i < nbuf; i++) { 2557 char c = buf[i]; 2558 next = NULL; 2559 2560 // Find c in node.children 2561 for (j = 0; j < node->nchildren; j++) { 2562 if (node->children[j].c == c) { 2563 next = &node->children[j]; 2564 break; 2565 } 2566 } 2567 if (!next) { 2568 // Not found 2569 return TB_OK; 2570 } 2571 node = next; 2572 *last = node; 2573 *depth += 1; 2574 if (node->is_leaf && node->nchildren < 1) { break; } 2575 } 2576 return TB_OK; 2577 } 2578 2579 static int 2580 cap_trie_deinit(struct cap_trie_t* node) 2581 { 2582 size_t j; 2583 for (j = 0; j < node->nchildren; j++) { 2584 cap_trie_deinit(&node->children[j]); 2585 } 2586 if (node->children) { tb_free(node->children); } 2587 memset(node, 0, sizeof(*node)); 2588 return TB_OK; 2589 } 2590 2591 static int 2592 init_resize_handler(void) 2593 { 2594 if (pipe(global.resize_pipefd) != 0) { 2595 global.last_errno = errno; 2596 return TB_ERR_RESIZE_PIPE; 2597 } 2598 2599 struct sigaction sa; 2600 memset(&sa, 0, sizeof(sa)); 2601 sa.sa_handler = handle_resize; 2602 if (sigaction(SIGWINCH, &sa, NULL) != 0) { 2603 global.last_errno = errno; 2604 return TB_ERR_RESIZE_SIGACTION; 2605 } 2606 2607 return TB_OK; 2608 } 2609 2610 static int 2611 send_init_escape_codes(void) 2612 { 2613 int rv; 2614 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_CA])); 2615 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_KEYPAD])); 2616 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_HIDE_CURSOR])); 2617 return TB_OK; 2618 } 2619 2620 static int 2621 send_clear(void) 2622 { 2623 int rv; 2624 2625 if_err_return(rv, send_attr(global.fg, global.bg)); 2626 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_CLEAR_SCREEN])); 2627 2628 if_err_return(rv, send_cursor_if(global.cursor_x, global.cursor_y)); 2629 if_err_return(rv, bytebuf_flush(&global.out, global.wfd)); 2630 2631 global.last_x = -1; 2632 global.last_y = -1; 2633 2634 return TB_OK; 2635 } 2636 2637 static int 2638 update_term_size(void) 2639 { 2640 int rv, ioctl_errno; 2641 2642 if (global.ttyfd < 0) { return TB_OK; } 2643 2644 struct winsize sz; 2645 memset(&sz, 0, sizeof(sz)); 2646 2647 // Try ioctl TIOCGWINSZ 2648 if (ioctl(global.ttyfd, TIOCGWINSZ, &sz) == 0) { 2649 global.width = sz.ws_col; 2650 global.height = sz.ws_row; 2651 return TB_OK; 2652 } 2653 ioctl_errno = errno; 2654 2655 // Try >cursor(9999,9999), >u7, <u6 2656 if_ok_return(rv, update_term_size_via_esc()); 2657 2658 global.last_errno = ioctl_errno; 2659 return TB_ERR_RESIZE_IOCTL; 2660 } 2661 2662 static int 2663 update_term_size_via_esc(void) 2664 { 2665 #ifndef TB_RESIZE_FALLBACK_MS 2666 #define TB_RESIZE_FALLBACK_MS 1000 2667 #endif 2668 2669 char move_and_report[] = "\x1b[9999;9999H\x1b[6n"; 2670 ssize_t write_rv = write(global.wfd, move_and_report, strlen(move_and_report)); 2671 if (write_rv != (ssize_t)strlen(move_and_report)) { return TB_ERR_RESIZE_WRITE; } 2672 2673 fd_set fds; 2674 FD_ZERO(&fds); 2675 FD_SET(global.rfd, &fds); 2676 2677 struct timeval timeout; 2678 timeout.tv_sec = 0; 2679 timeout.tv_usec = TB_RESIZE_FALLBACK_MS * 1000; 2680 2681 int select_rv = select(global.rfd + 1, &fds, NULL, NULL, &timeout); 2682 2683 if (select_rv != 1) { 2684 global.last_errno = errno; 2685 return TB_ERR_RESIZE_POLL; 2686 } 2687 2688 char buf[TB_OPT_READ_BUF]; 2689 ssize_t read_rv = read(global.rfd, buf, sizeof(buf) - 1); 2690 if (read_rv < 1) { 2691 global.last_errno = errno; 2692 return TB_ERR_RESIZE_READ; 2693 } 2694 buf[read_rv] = '\0'; 2695 2696 int rw, rh; 2697 if (sscanf(buf, "\x1b[%d;%dR", &rh, &rw) != 2) { return TB_ERR_RESIZE_SSCANF; } 2698 2699 global.width = rw; 2700 global.height = rh; 2701 return TB_OK; 2702 } 2703 2704 static int 2705 init_cellbuf(void) 2706 { 2707 int rv; 2708 if_err_return(rv, cellbuf_init(&global.back, global.width, global.height)); 2709 if_err_return(rv, cellbuf_init(&global.front, global.width, global.height)); 2710 if_err_return(rv, cellbuf_clear(&global.back)); 2711 if_err_return(rv, cellbuf_clear(&global.front)); 2712 return TB_OK; 2713 } 2714 2715 static int 2716 tb_deinit(void) 2717 { 2718 if (global.caps[0] != NULL && global.wfd >= 0) { 2719 bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]); 2720 bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]); 2721 bytebuf_puts(&global.out, global.caps[TB_CAP_CLEAR_SCREEN]); 2722 bytebuf_puts(&global.out, global.caps[TB_CAP_EXIT_CA]); 2723 bytebuf_puts(&global.out, global.caps[TB_CAP_EXIT_KEYPAD]); 2724 bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE); 2725 bytebuf_flush(&global.out, global.wfd); 2726 } 2727 if (global.ttyfd >= 0) { 2728 if (global.has_orig_tios) { tcsetattr(global.ttyfd, TCSAFLUSH, &global.orig_tios); } 2729 if (global.ttyfd_open) { 2730 close(global.ttyfd); 2731 global.ttyfd_open = 0; 2732 } 2733 } 2734 2735 struct sigaction sa; 2736 memset(&sa, 0, sizeof(sa)); 2737 sa.sa_handler = SIG_DFL; 2738 sigaction(SIGWINCH, &sa, NULL); 2739 if (global.resize_pipefd[0] >= 0) close(global.resize_pipefd[0]); 2740 if (global.resize_pipefd[1] >= 0) close(global.resize_pipefd[1]); 2741 2742 cellbuf_free(&global.back); 2743 cellbuf_free(&global.front); 2744 bytebuf_free(&global.in); 2745 bytebuf_free(&global.out); 2746 2747 if (global.terminfo) tb_free(global.terminfo); 2748 2749 cap_trie_deinit(&global.cap_trie); 2750 2751 tb_reset(); 2752 return TB_OK; 2753 } 2754 2755 static int 2756 load_terminfo(void) 2757 { 2758 int rv; 2759 char tmp[TB_PATH_MAX]; 2760 2761 // See terminfo(5) "Fetching Compiled Descriptions" for a description of 2762 // this behavior. Some of these paths are compile-time ncurses options, so 2763 // best guesses are used here. 2764 const char* term = getenv("TERM"); 2765 if (!term) { return TB_ERR; } 2766 2767 // If TERMINFO is set, try that directory and stop 2768 const char* terminfo = getenv("TERMINFO"); 2769 if (terminfo) { return load_terminfo_from_path(terminfo, term); } 2770 2771 // Next try ~/.terminfo 2772 const char* home = getenv("HOME"); 2773 if (home) { 2774 snprintf_or_return(rv, tmp, sizeof(tmp), "%s/.terminfo", home); 2775 if_ok_return(rv, load_terminfo_from_path(tmp, term)); 2776 } 2777 2778 // Next try TERMINFO_DIRS 2779 // 2780 // Note, empty entries are supposed to be interpretted as the "compiled-in 2781 // default", which is of course system-dependent. Previously /etc/terminfo 2782 // was used here. Let's skip empty entries altogether rather than give 2783 // precedence to a guess, and check common paths after this loop. 2784 const char* dirs = getenv("TERMINFO_DIRS"); 2785 if (dirs) { 2786 snprintf_or_return(rv, tmp, sizeof(tmp), "%s", dirs); 2787 char* dir = strtok(tmp, ":"); 2788 while (dir) { 2789 const char* cdir = dir; 2790 if (*cdir != '\0') { if_ok_return(rv, load_terminfo_from_path(cdir, term)); } 2791 dir = strtok(NULL, ":"); 2792 } 2793 } 2794 2795 #ifdef TB_TERMINFO_DIR 2796 if_ok_return(rv, load_terminfo_from_path(TB_TERMINFO_DIR, term)); 2797 #endif 2798 if_ok_return(rv, load_terminfo_from_path("/usr/local/etc/terminfo", term)); 2799 if_ok_return(rv, load_terminfo_from_path("/usr/local/share/terminfo", term)); 2800 if_ok_return(rv, load_terminfo_from_path("/usr/local/lib/terminfo", term)); 2801 if_ok_return(rv, load_terminfo_from_path("/etc/terminfo", term)); 2802 if_ok_return(rv, load_terminfo_from_path("/usr/share/terminfo", term)); 2803 if_ok_return(rv, load_terminfo_from_path("/usr/lib/terminfo", term)); 2804 if_ok_return(rv, load_terminfo_from_path("/usr/share/lib/terminfo", term)); 2805 if_ok_return(rv, load_terminfo_from_path("/lib/terminfo", term)); 2806 2807 return TB_ERR; 2808 } 2809 2810 static int 2811 load_terminfo_from_path(const char* path, const char* term) 2812 { 2813 int rv; 2814 char tmp[TB_PATH_MAX]; 2815 2816 // Look for term at this terminfo location, e.g., <terminfo>/x/xterm 2817 snprintf_or_return(rv, tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term); 2818 if_ok_return(rv, read_terminfo_path(tmp)); 2819 2820 #ifdef __APPLE__ 2821 // Try the Darwin equivalent path, e.g., <terminfo>/78/xterm 2822 snprintf_or_return(rv, tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term); 2823 return read_terminfo_path(tmp); 2824 #endif 2825 2826 return TB_ERR; 2827 } 2828 2829 static int 2830 read_terminfo_path(const char* path) 2831 { 2832 FILE* fp = fopen(path, "rb"); 2833 if (!fp) { return TB_ERR; } 2834 2835 struct stat st; 2836 if (fstat(fileno(fp), &st) != 0) { 2837 fclose(fp); 2838 return TB_ERR; 2839 } 2840 2841 size_t fsize = st.st_size; 2842 char* data = (char*)tb_malloc(fsize); 2843 if (!data) { 2844 fclose(fp); 2845 return TB_ERR; 2846 } 2847 2848 if (fread(data, 1, fsize, fp) != fsize) { 2849 fclose(fp); 2850 tb_free(data); 2851 return TB_ERR; 2852 } 2853 2854 global.terminfo = data; 2855 global.nterminfo = fsize; 2856 2857 fclose(fp); 2858 return TB_OK; 2859 } 2860 2861 static int 2862 parse_terminfo_caps(void) 2863 { 2864 // See term(5) "LEGACY STORAGE FORMAT" and "EXTENDED STORAGE FORMAT" for a 2865 // description of this behavior. 2866 2867 // Ensure there's at least a header's worth of data 2868 if (global.nterminfo < 6) { return TB_ERR; } 2869 2870 int16_t* header = (int16_t*)global.terminfo; 2871 // header[0] the magic number (octal 0432 or 01036) 2872 // header[1] the size, in bytes, of the names section 2873 // header[2] the number of bytes in the boolean section 2874 // header[3] the number of short integers in the numbers section 2875 // header[4] the number of offsets (short integers) in the strings section 2876 // header[5] the size, in bytes, of the string table 2877 2878 // Legacy ints are 16-bit, extended ints are 32-bit 2879 const int bytes_per_int = header[0] == 01036 ? 4 // 32-bit 2880 : 2; // 16-bit 2881 2882 // > Between the boolean section and the number section, a null byte will be 2883 // > inserted, if necessary, to ensure that the number section begins on an 2884 // > even byte 2885 const int align_offset = (header[1] + header[2]) % 2 != 0 ? 1 : 0; 2886 2887 const int pos_str_offsets = (6 * sizeof(int16_t)) // header (12 bytes) 2888 + header[1] // length of names section 2889 + header[2] // length of boolean section 2890 + align_offset + (header[3] * bytes_per_int); // length of numbers section 2891 2892 const int pos_str_table 2893 = pos_str_offsets + (header[4] * sizeof(int16_t)); // length of string offsets table 2894 2895 // Load caps 2896 int i; 2897 for (i = 0; i < TB_CAP__COUNT; i++) { 2898 const char* cap = get_terminfo_string( 2899 pos_str_offsets, header[4], pos_str_table, header[5], terminfo_cap_indexes[i]); 2900 if (!cap) { 2901 // Something is not right 2902 return TB_ERR; 2903 } 2904 global.caps[i] = cap; 2905 } 2906 2907 return TB_OK; 2908 } 2909 2910 static int 2911 load_builtin_caps(void) 2912 { 2913 int i, j; 2914 const char* term = getenv("TERM"); 2915 2916 if (!term) { return TB_ERR_NO_TERM; } 2917 2918 // Check for exact TERM match 2919 for (i = 0; builtin_terms[i].name != NULL; i++) { 2920 if (strcmp(term, builtin_terms[i].name) == 0) { 2921 for (j = 0; j < TB_CAP__COUNT; j++) { 2922 global.caps[j] = builtin_terms[i].caps[j]; 2923 } 2924 return TB_OK; 2925 } 2926 } 2927 2928 // Check for partial TERM or alias match 2929 for (i = 0; builtin_terms[i].name != NULL; i++) { 2930 if (strstr(term, builtin_terms[i].name) != NULL 2931 || (*(builtin_terms[i].alias) != '\0' 2932 && strstr(term, builtin_terms[i].alias) != NULL)) { 2933 for (j = 0; j < TB_CAP__COUNT; j++) { 2934 global.caps[j] = builtin_terms[i].caps[j]; 2935 } 2936 return TB_OK; 2937 } 2938 } 2939 2940 return TB_ERR_UNSUPPORTED_TERM; 2941 } 2942 2943 static const char* 2944 get_terminfo_string(int16_t str_offsets_pos, 2945 int16_t str_offsets_len, 2946 int16_t str_table_pos, 2947 int16_t str_table_len, 2948 int16_t str_index) 2949 { 2950 const int str_byte_index = (int)str_index * (int)sizeof(int16_t); 2951 if (str_byte_index >= (int)str_offsets_len * (int)sizeof(int16_t)) { 2952 // An offset beyond the table indicates absent 2953 // See `convert_strings` in tinfo `read_entry.c` 2954 return ""; 2955 } 2956 const int16_t* str_offset = (int16_t*)(global.terminfo + (int)str_offsets_pos + str_byte_index); 2957 if ((char*)str_offset >= global.terminfo + global.nterminfo) { 2958 // str_offset points beyond end of entry 2959 // Truncated/corrupt terminfo entry? 2960 return NULL; 2961 } 2962 if (*str_offset < 0 || *str_offset >= str_table_len) { 2963 // A negative offset indicates absent 2964 // An offset beyond the table indicates absent 2965 // See `convert_strings` in tinfo `read_entry.c` 2966 return ""; 2967 } 2968 if (((size_t)((int)str_table_pos + (int)*str_offset)) >= global.nterminfo) { 2969 // string points beyond end of entry 2970 // Truncated/corrupt terminfo entry? 2971 return NULL; 2972 } 2973 return (const char*)(global.terminfo + (int)str_table_pos + (int)*str_offset); 2974 } 2975 2976 static int 2977 wait_event(struct tb_event* event, int timeout) 2978 { 2979 int rv; 2980 char buf[TB_OPT_READ_BUF]; 2981 2982 memset(event, 0, sizeof(*event)); 2983 if_ok_return(rv, extract_event(event)); 2984 2985 fd_set fds; 2986 struct timeval tv; 2987 tv.tv_sec = timeout / 1000; 2988 tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000; 2989 2990 do { 2991 FD_ZERO(&fds); 2992 FD_SET(global.rfd, &fds); 2993 FD_SET(global.resize_pipefd[0], &fds); 2994 2995 int maxfd = global.resize_pipefd[0] > global.rfd ? global.resize_pipefd[0] : global.rfd; 2996 2997 int select_rv = select(maxfd + 1, &fds, NULL, NULL, (timeout < 0) ? NULL : &tv); 2998 2999 if (select_rv < 0) { 3000 // Let EINTR/EAGAIN bubble up 3001 global.last_errno = errno; 3002 return TB_ERR_POLL; 3003 } else if (select_rv == 0) { 3004 return TB_ERR_NO_EVENT; 3005 } 3006 3007 int tty_has_events = (FD_ISSET(global.rfd, &fds)); 3008 int resize_has_events = (FD_ISSET(global.resize_pipefd[0], &fds)); 3009 3010 if (tty_has_events) { 3011 ssize_t read_rv = read(global.rfd, buf, sizeof(buf)); 3012 if (read_rv < 0) { 3013 global.last_errno = errno; 3014 return TB_ERR_READ; 3015 } else if (read_rv > 0) { 3016 bytebuf_nputs(&global.in, buf, read_rv); 3017 } 3018 } 3019 3020 if (resize_has_events) { 3021 int ignore = 0; 3022 read(global.resize_pipefd[0], &ignore, sizeof(ignore)); 3023 // TODO: Harden against errors encountered mid-resize 3024 if_err_return(rv, update_term_size()); 3025 if_err_return(rv, resize_cellbufs()); 3026 event->type = TB_EVENT_RESIZE; 3027 event->w = global.width; 3028 event->h = global.height; 3029 return TB_OK; 3030 } 3031 3032 memset(event, 0, sizeof(*event)); 3033 if_ok_return(rv, extract_event(event)); 3034 } while (timeout == -1); 3035 3036 return rv; 3037 } 3038 3039 static int 3040 extract_event(struct tb_event* event) 3041 { 3042 int rv; 3043 struct bytebuf_t* in = &global.in; 3044 3045 if (in->len == 0) { return TB_ERR; } 3046 3047 if (in->buf[0] == '\x1b') { 3048 // Escape sequence? 3049 // In TB_INPUT_ESC, skip if the buffer is a single escape char 3050 if (!((global.input_mode & TB_INPUT_ESC) && in->len == 1)) { 3051 if_ok_or_need_more_return(rv, extract_esc(event)); 3052 } 3053 3054 // Escape key? 3055 if (global.input_mode & TB_INPUT_ESC) { 3056 event->type = TB_EVENT_KEY; 3057 event->ch = 0; 3058 event->key = TB_KEY_ESC; 3059 event->mod = 0; 3060 bytebuf_shift(in, 1); 3061 return TB_OK; 3062 } 3063 3064 // Recurse for alt key 3065 event->mod |= TB_MOD_ALT; 3066 bytebuf_shift(in, 1); 3067 return extract_event(event); 3068 } 3069 3070 // ASCII control key? 3071 if ((uint16_t)in->buf[0] < TB_KEY_SPACE || in->buf[0] == TB_KEY_BACKSPACE2) { 3072 event->type = TB_EVENT_KEY; 3073 event->ch = 0; 3074 event->key = (uint16_t)in->buf[0]; 3075 event->mod |= TB_MOD_CTRL; 3076 bytebuf_shift(in, 1); 3077 return TB_OK; 3078 } 3079 3080 // UTF-8? 3081 if (in->len >= (size_t)tb_utf8_char_length(in->buf[0])) { 3082 event->type = TB_EVENT_KEY; 3083 tb_utf8_char_to_unicode(&event->ch, in->buf); 3084 event->key = 0; 3085 bytebuf_shift(in, tb_utf8_char_length(in->buf[0])); 3086 return TB_OK; 3087 } 3088 3089 // Need more input 3090 return TB_ERR; 3091 } 3092 3093 static int 3094 extract_esc(struct tb_event* event) 3095 { 3096 int rv; 3097 if_ok_or_need_more_return(rv, extract_esc_user(event, 0)); 3098 if_ok_or_need_more_return(rv, extract_esc_cap(event)); 3099 if_ok_or_need_more_return(rv, extract_esc_mouse(event)); 3100 if_ok_or_need_more_return(rv, extract_esc_user(event, 1)); 3101 return TB_ERR; 3102 } 3103 3104 static int 3105 extract_esc_user(struct tb_event* event, int is_post) 3106 { 3107 int rv; 3108 size_t consumed = 0; 3109 struct bytebuf_t* in = &global.in; 3110 int (*fn)(struct tb_event*, size_t*); 3111 3112 fn = is_post ? global.fn_extract_esc_post : global.fn_extract_esc_pre; 3113 3114 if (!fn) { return TB_ERR; } 3115 3116 rv = fn(event, &consumed); 3117 if (rv == TB_OK) { bytebuf_shift(in, consumed); } 3118 3119 if_ok_or_need_more_return(rv, rv); 3120 return TB_ERR; 3121 } 3122 3123 static int 3124 extract_esc_cap(struct tb_event* event) 3125 { 3126 int rv; 3127 struct bytebuf_t* in = &global.in; 3128 struct cap_trie_t* node; 3129 size_t depth; 3130 3131 if_err_return(rv, cap_trie_find(in->buf, in->len, &node, &depth)); 3132 if (node->is_leaf) { 3133 // Found a leaf node 3134 event->type = TB_EVENT_KEY; 3135 event->ch = 0; 3136 event->key = node->key; 3137 event->mod = node->mod; 3138 bytebuf_shift(in, depth); 3139 return TB_OK; 3140 } else if (node->nchildren > 0 && in->len <= depth) { 3141 // Found a branch node (not enough input) 3142 return TB_ERR_NEED_MORE; 3143 } 3144 3145 return TB_ERR; 3146 } 3147 3148 static int 3149 extract_esc_mouse(struct tb_event* event) 3150 { 3151 struct bytebuf_t* in = &global.in; 3152 3153 enum { TYPE_VT200 = 0, TYPE_1006, TYPE_1015, TYPE_MAX }; 3154 3155 const char* cmp[TYPE_MAX] = { // 3156 // X10 mouse encoding, the simplest one 3157 // \x1b [ M Cb Cx Cy 3158 [TYPE_VT200] = "\x1b[M", 3159 // xterm 1006 extended mode or urxvt 1015 extended mode 3160 // xterm: \x1b [ < Cb ; Cx ; Cy (M or m) 3161 [TYPE_1006] = "\x1b[<", 3162 // urxvt: \x1b [ Cb ; Cx ; Cy M 3163 [TYPE_1015] = "\x1b[" 3164 }; 3165 3166 int type = 0; 3167 int ret = TB_ERR; 3168 3169 // Unrolled at compile-time (probably) 3170 for (; type < TYPE_MAX; type++) { 3171 size_t size = strlen(cmp[type]); 3172 3173 if (in->len >= size && (strncmp(cmp[type], in->buf, size)) == 0) { break; } 3174 } 3175 3176 if (type == TYPE_MAX) { 3177 ret = TB_ERR; // No match 3178 return ret; 3179 } 3180 3181 size_t buf_shift = 0; 3182 3183 switch (type) { 3184 case TYPE_VT200: 3185 if (in->len >= 6) { 3186 int b = in->buf[3] - 0x20; 3187 int fail = 0; 3188 3189 switch (b & 3) { 3190 case 0: 3191 event->key = ((b & 64) != 0) ? TB_KEY_MOUSE_WHEEL_UP : TB_KEY_MOUSE_LEFT; 3192 break; 3193 case 1: 3194 event->key = ((b & 64) != 0) ? TB_KEY_MOUSE_WHEEL_DOWN : TB_KEY_MOUSE_MIDDLE; 3195 break; 3196 case 2: 3197 event->key = TB_KEY_MOUSE_RIGHT; 3198 break; 3199 case 3: 3200 event->key = TB_KEY_MOUSE_RELEASE; 3201 break; 3202 default: 3203 ret = TB_ERR; 3204 fail = 1; 3205 break; 3206 } 3207 3208 if (!fail) { 3209 if ((b & 32) != 0) { event->mod |= TB_MOD_MOTION; } 3210 3211 // the coord is 1,1 for upper left 3212 event->x = ((uint8_t)in->buf[4]) - 0x21; 3213 event->y = ((uint8_t)in->buf[5]) - 0x21; 3214 3215 ret = TB_OK; 3216 } 3217 3218 buf_shift = 6; 3219 } 3220 break; 3221 case TYPE_1006: 3222 // fallthrough 3223 case TYPE_1015: { 3224 size_t index_fail = (size_t)-1; 3225 3226 enum { FIRST_M = 0, FIRST_SEMICOLON, LAST_SEMICOLON, FIRST_LAST_MAX }; 3227 3228 size_t indices[FIRST_LAST_MAX] = { index_fail, index_fail, index_fail }; 3229 int m_is_capital = 0; 3230 3231 for (size_t i = 0; i < in->len; i++) { 3232 if (in->buf[i] == ';') { 3233 if (indices[FIRST_SEMICOLON] == index_fail) { 3234 indices[FIRST_SEMICOLON] = i; 3235 } else { 3236 indices[LAST_SEMICOLON] = i; 3237 } 3238 } else if (indices[FIRST_M] == index_fail) { 3239 if (in->buf[i] == 'm' || in->buf[i] == 'M') { 3240 m_is_capital = (in->buf[i] == 'M'); 3241 indices[FIRST_M] = i; 3242 } 3243 } 3244 } 3245 3246 if (indices[FIRST_M] == index_fail || indices[FIRST_SEMICOLON] == index_fail 3247 || indices[LAST_SEMICOLON] == index_fail) { 3248 ret = TB_ERR; 3249 } else { 3250 int start = (type == TYPE_1015 ? 2 : 3); 3251 3252 unsigned n1 = strtoul(&in->buf[start], NULL, 10); 3253 unsigned n2 = strtoul(&in->buf[indices[FIRST_SEMICOLON] + 1], NULL, 10); 3254 unsigned n3 = strtoul(&in->buf[indices[LAST_SEMICOLON] + 1], NULL, 10); 3255 3256 if (type == TYPE_1015) { n1 -= 0x20; } 3257 3258 int fail = 0; 3259 3260 switch (n1 & 3) { 3261 case 0: 3262 event->key = ((n1 & 64) != 0) ? TB_KEY_MOUSE_WHEEL_UP : TB_KEY_MOUSE_LEFT; 3263 break; 3264 case 1: 3265 event->key = ((n1 & 64) != 0) ? TB_KEY_MOUSE_WHEEL_DOWN : TB_KEY_MOUSE_MIDDLE; 3266 break; 3267 case 2: 3268 event->key = TB_KEY_MOUSE_RIGHT; 3269 break; 3270 case 3: 3271 event->key = TB_KEY_MOUSE_RELEASE; 3272 break; 3273 default: 3274 ret = TB_ERR; 3275 fail = 1; 3276 break; 3277 } 3278 3279 buf_shift = in->len; 3280 3281 if (!fail) { 3282 if (!m_is_capital) { 3283 // on xterm mouse release is signaled by lowercase m 3284 event->key = TB_KEY_MOUSE_RELEASE; 3285 } 3286 3287 if ((n1 & 32) != 0) { event->mod |= TB_MOD_MOTION; } 3288 3289 event->x = ((uint8_t)n2) - 1; 3290 event->y = ((uint8_t)n3) - 1; 3291 3292 ret = TB_OK; 3293 } 3294 } 3295 } break; 3296 case TYPE_MAX: 3297 ret = TB_ERR; 3298 } 3299 3300 if (buf_shift > 0) { bytebuf_shift(in, buf_shift); } 3301 3302 if (ret == TB_OK) { event->type = TB_EVENT_MOUSE; } 3303 3304 return ret; 3305 } 3306 3307 static int 3308 resize_cellbufs(void) 3309 { 3310 int rv; 3311 if_err_return(rv, cellbuf_resize(&global.back, global.width, global.height)); 3312 if_err_return(rv, cellbuf_resize(&global.front, global.width, global.height)); 3313 if_err_return(rv, cellbuf_clear(&global.front)); 3314 if_err_return(rv, send_clear()); 3315 return TB_OK; 3316 } 3317 3318 static void 3319 handle_resize(int sig) 3320 { 3321 int errno_copy = errno; 3322 write(global.resize_pipefd[1], &sig, sizeof(sig)); 3323 errno = errno_copy; 3324 } 3325 3326 static int 3327 send_attr(uintattr_t fg, uintattr_t bg) 3328 { 3329 int rv; 3330 3331 if (fg == global.last_fg && bg == global.last_bg) { return TB_OK; } 3332 3333 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0])); 3334 3335 uint32_t cfg, cbg; 3336 switch (global.output_mode) { 3337 default: 3338 case TB_OUTPUT_NORMAL: 3339 // The minus 1 below is because our colors are 1-indexed starting 3340 // from black. Black is represented by a 30, 40, 90, or 100 for fg, 3341 // bg, bright fg, or bright bg respectively. Red is 31, 41, 91, 3342 // 101, etc. 3343 cfg = (fg & TB_BRIGHT ? 90 : 30) + (fg & 0x0f) - 1; 3344 cbg = (bg & TB_BRIGHT ? 100 : 40) + (bg & 0x0f) - 1; 3345 break; 3346 3347 case TB_OUTPUT_256: 3348 cfg = fg & 0xff; 3349 cbg = bg & 0xff; 3350 if (fg & TB_HI_BLACK) cfg = 0; 3351 if (bg & TB_HI_BLACK) cbg = 0; 3352 break; 3353 3354 case TB_OUTPUT_216: 3355 cfg = fg & 0xff; 3356 cbg = bg & 0xff; 3357 if (cfg > 216) cfg = 216; 3358 if (cbg > 216) cbg = 216; 3359 cfg += 0x0f; 3360 cbg += 0x0f; 3361 break; 3362 3363 case TB_OUTPUT_GRAYSCALE: 3364 cfg = fg & 0xff; 3365 cbg = bg & 0xff; 3366 if (cfg > 24) cfg = 24; 3367 if (cbg > 24) cbg = 24; 3368 cfg += 0xe7; 3369 cbg += 0xe7; 3370 break; 3371 3372 #if TB_OPT_ATTR_W >= 32 3373 case TB_OUTPUT_TRUECOLOR: 3374 cfg = fg & 0xffffff; 3375 cbg = bg & 0xffffff; 3376 if (fg & TB_HI_BLACK) cfg = 0; 3377 if (bg & TB_HI_BLACK) cbg = 0; 3378 break; 3379 #endif 3380 } 3381 3382 if (fg & TB_BOLD) if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BOLD])); 3383 3384 if (fg & TB_BLINK) if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BLINK])); 3385 3386 if (fg & TB_UNDERLINE) if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_UNDERLINE])); 3387 3388 if (fg & TB_ITALIC) if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_ITALIC])); 3389 3390 if (fg & TB_DIM) if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_DIM])); 3391 3392 #if TB_OPT_ATTR_W == 64 3393 if (fg & TB_STRIKEOUT) if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_STRIKEOUT)); 3394 3395 if (fg & TB_UNDERLINE_2) if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_UNDERLINE_2)); 3396 3397 if (fg & TB_OVERLINE) if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_OVERLINE)); 3398 3399 if (fg & TB_INVISIBLE) if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_INVISIBLE])); 3400 #endif 3401 3402 if ((fg & TB_REVERSE) || (bg & TB_REVERSE)) 3403 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_REVERSE])); 3404 3405 int fg_is_default = (fg & 0xff) == 0; 3406 int bg_is_default = (bg & 0xff) == 0; 3407 if (global.output_mode == TB_OUTPUT_256) { 3408 if (fg & TB_HI_BLACK) fg_is_default = 0; 3409 if (bg & TB_HI_BLACK) bg_is_default = 0; 3410 } 3411 #if TB_OPT_ATTR_W >= 32 3412 if (global.output_mode == TB_OUTPUT_TRUECOLOR) { 3413 fg_is_default = ((fg & 0xffffff) == 0) && ((fg & TB_HI_BLACK) == 0); 3414 bg_is_default = ((bg & 0xffffff) == 0) && ((bg & TB_HI_BLACK) == 0); 3415 } 3416 #endif 3417 3418 if_err_return(rv, send_sgr(cfg, cbg, fg_is_default, bg_is_default)); 3419 3420 global.last_fg = fg; 3421 global.last_bg = bg; 3422 3423 return TB_OK; 3424 } 3425 3426 static int 3427 send_sgr(uint32_t cfg, uint32_t cbg, int fg_is_default, int bg_is_default) 3428 { 3429 int rv; 3430 char nbuf[32]; 3431 3432 if (fg_is_default && bg_is_default) { return TB_OK; } 3433 3434 switch (global.output_mode) { 3435 default: 3436 case TB_OUTPUT_NORMAL: 3437 send_literal(rv, "\x1b["); 3438 if (!fg_is_default) { 3439 send_num(rv, nbuf, cfg); 3440 if (!bg_is_default) { send_literal(rv, ";"); } 3441 } 3442 if (!bg_is_default) { send_num(rv, nbuf, cbg); } 3443 send_literal(rv, "m"); 3444 break; 3445 3446 case TB_OUTPUT_256: 3447 case TB_OUTPUT_216: 3448 case TB_OUTPUT_GRAYSCALE: 3449 send_literal(rv, "\x1b["); 3450 if (!fg_is_default) { 3451 send_literal(rv, "38;5;"); 3452 send_num(rv, nbuf, cfg); 3453 if (!bg_is_default) { send_literal(rv, ";"); } 3454 } 3455 if (!bg_is_default) { 3456 send_literal(rv, "48;5;"); 3457 send_num(rv, nbuf, cbg); 3458 } 3459 send_literal(rv, "m"); 3460 break; 3461 3462 #if TB_OPT_ATTR_W >= 32 3463 case TB_OUTPUT_TRUECOLOR: 3464 send_literal(rv, "\x1b["); 3465 if (!fg_is_default) { 3466 send_literal(rv, "38;2;"); 3467 send_num(rv, nbuf, (cfg >> 16) & 0xff); 3468 send_literal(rv, ";"); 3469 send_num(rv, nbuf, (cfg >> 8) & 0xff); 3470 send_literal(rv, ";"); 3471 send_num(rv, nbuf, cfg & 0xff); 3472 if (!bg_is_default) { send_literal(rv, ";"); } 3473 } 3474 if (!bg_is_default) { 3475 send_literal(rv, "48;2;"); 3476 send_num(rv, nbuf, (cbg >> 16) & 0xff); 3477 send_literal(rv, ";"); 3478 send_num(rv, nbuf, (cbg >> 8) & 0xff); 3479 send_literal(rv, ";"); 3480 send_num(rv, nbuf, cbg & 0xff); 3481 } 3482 send_literal(rv, "m"); 3483 break; 3484 #endif 3485 } 3486 return TB_OK; 3487 } 3488 3489 static int 3490 send_cursor_if(int x, int y) 3491 { 3492 int rv; 3493 char nbuf[32]; 3494 if (x < 0 || y < 0) { return TB_OK; } 3495 send_literal(rv, "\x1b["); 3496 send_num(rv, nbuf, y + 1); 3497 send_literal(rv, ";"); 3498 send_num(rv, nbuf, x + 1); 3499 send_literal(rv, "H"); 3500 return TB_OK; 3501 } 3502 3503 static int 3504 send_char(int x, int y, uint32_t ch) 3505 { 3506 return send_cluster(x, y, &ch, 1); 3507 } 3508 3509 static int 3510 send_cluster(int x, int y, uint32_t* ch, size_t nch) 3511 { 3512 int rv; 3513 char chu8[8]; 3514 3515 if (global.last_x != x - 1 || global.last_y != y) { if_err_return(rv, send_cursor_if(x, y)); } 3516 global.last_x = x; 3517 global.last_y = y; 3518 3519 int i; 3520 for (i = 0; i < (int)nch; i++) { 3521 uint32_t ch32 = *(ch + i); 3522 if (!iswprint((wint_t)ch32)) { 3523 ch32 = 0xfffd; // replace non-printable codepoints with U+FFFD 3524 } 3525 int chu8_len = tb_utf8_unicode_to_char(chu8, ch32); 3526 if_err_return(rv, bytebuf_nputs(&global.out, chu8, (size_t)chu8_len)); 3527 } 3528 3529 return TB_OK; 3530 } 3531 3532 static int 3533 convert_num(uint32_t num, char* buf) 3534 { 3535 int i, l = 0; 3536 char ch; 3537 do { 3538 buf[l++] = (char)('0' + (num % 10)); 3539 num /= 10; 3540 } while (num); 3541 for (i = 0; i < l / 2; i++) { 3542 ch = buf[i]; 3543 buf[i] = buf[l - 1 - i]; 3544 buf[l - 1 - i] = ch; 3545 } 3546 return l; 3547 } 3548 3549 static int 3550 cell_cmp(struct tb_cell* a, struct tb_cell* b) 3551 { 3552 if (a->ch != b->ch || a->fg != b->fg || a->bg != b->bg) { return 1; } 3553 #ifdef TB_OPT_EGC 3554 if (a->nech != b->nech) { 3555 return 1; 3556 } else if (a->nech > 0) { // a->nech == b->nech 3557 return memcmp(a->ech, b->ech, a->nech); 3558 } 3559 #endif 3560 return 0; 3561 } 3562 3563 static int 3564 cell_copy(struct tb_cell* dst, struct tb_cell* src) 3565 { 3566 #ifdef TB_OPT_EGC 3567 if (src->nech > 0) { return cell_set(dst, src->ech, src->nech, src->fg, src->bg); } 3568 #endif 3569 return cell_set(dst, &src->ch, 1, src->fg, src->bg); 3570 } 3571 3572 static int 3573 cell_set(struct tb_cell* cell, uint32_t* ch, size_t nch, uintattr_t fg, uintattr_t bg) 3574 { 3575 cell->ch = ch ? *ch : 0; 3576 cell->fg = fg; 3577 cell->bg = bg; 3578 #ifdef TB_OPT_EGC 3579 if (nch <= 1) { 3580 cell->nech = 0; 3581 } else { 3582 int rv; 3583 if_err_return(rv, cell_reserve_ech(cell, nch + 1)); 3584 memcpy(cell->ech, ch, sizeof(*ch) * nch); 3585 cell->ech[nch] = '\0'; 3586 cell->nech = nch; 3587 } 3588 #else 3589 (void)nch; 3590 (void)cell_reserve_ech; 3591 #endif 3592 return TB_OK; 3593 } 3594 3595 static int 3596 cell_reserve_ech(struct tb_cell* cell, size_t n) 3597 { 3598 #ifdef TB_OPT_EGC 3599 if (cell->cech >= n) { return TB_OK; } 3600 if (!(cell->ech = tb_realloc(cell->ech, n * sizeof(cell->ch)))) { return TB_ERR_MEM; } 3601 cell->cech = n; 3602 return TB_OK; 3603 #else 3604 (void)cell; 3605 (void)n; 3606 return TB_ERR; 3607 #endif 3608 } 3609 3610 static int 3611 cell_free(struct tb_cell* cell) 3612 { 3613 #ifdef TB_OPT_EGC 3614 if (cell->ech) { tb_free(cell->ech); } 3615 #endif 3616 memset(cell, 0, sizeof(*cell)); 3617 return TB_OK; 3618 } 3619 3620 static int 3621 cellbuf_init(struct cellbuf_t* c, int w, int h) 3622 { 3623 c->cells = (struct tb_cell*)tb_malloc(sizeof(struct tb_cell) * w * h); 3624 if (!c->cells) { return TB_ERR_MEM; } 3625 memset(c->cells, 0, sizeof(struct tb_cell) * w * h); 3626 c->width = w; 3627 c->height = h; 3628 return TB_OK; 3629 } 3630 3631 static int 3632 cellbuf_free(struct cellbuf_t* c) 3633 { 3634 if (c->cells) { 3635 int i; 3636 for (i = 0; i < c->width * c->height; i++) { 3637 cell_free(&c->cells[i]); 3638 } 3639 tb_free(c->cells); 3640 } 3641 memset(c, 0, sizeof(*c)); 3642 return TB_OK; 3643 } 3644 3645 static int 3646 cellbuf_clear(struct cellbuf_t* c) 3647 { 3648 int rv, i; 3649 uint32_t space = (uint32_t)' '; 3650 for (i = 0; i < c->width * c->height; i++) { 3651 if_err_return(rv, cell_set(&c->cells[i], &space, 1, global.fg, global.bg)); 3652 } 3653 return TB_OK; 3654 } 3655 3656 static int 3657 cellbuf_get(struct cellbuf_t* c, int x, int y, struct tb_cell** out) 3658 { 3659 if (!cellbuf_in_bounds(c, x, y)) { 3660 *out = NULL; 3661 return TB_ERR_OUT_OF_BOUNDS; 3662 } 3663 *out = &c->cells[(y * c->width) + x]; 3664 return TB_OK; 3665 } 3666 3667 static int 3668 cellbuf_in_bounds(struct cellbuf_t* c, int x, int y) 3669 { 3670 if (x < 0 || x >= c->width || y < 0 || y >= c->height) { return 0; } 3671 return 1; 3672 } 3673 3674 static int 3675 cellbuf_resize(struct cellbuf_t* c, int w, int h) 3676 { 3677 int rv; 3678 3679 int ow = c->width; 3680 int oh = c->height; 3681 3682 if (ow == w && oh == h) { return TB_OK; } 3683 3684 w = w < 1 ? 1 : w; 3685 h = h < 1 ? 1 : h; 3686 3687 int minw = (w < ow) ? w : ow; 3688 int minh = (h < oh) ? h : oh; 3689 3690 struct tb_cell* prev = c->cells; 3691 3692 if_err_return(rv, cellbuf_init(c, w, h)); 3693 if_err_return(rv, cellbuf_clear(c)); 3694 3695 int x, y; 3696 for (x = 0; x < minw; x++) { 3697 for (y = 0; y < minh; y++) { 3698 struct tb_cell *src, *dst; 3699 src = &prev[(y * ow) + x]; 3700 if_err_return(rv, cellbuf_get(c, x, y, &dst)); 3701 if_err_return(rv, cell_copy(dst, src)); 3702 } 3703 } 3704 3705 tb_free(prev); 3706 3707 return TB_OK; 3708 } 3709 3710 static int 3711 bytebuf_puts(struct bytebuf_t* b, const char* str) 3712 { 3713 if (!str || strlen(str) <= 0) return TB_OK; // Nothing to do for empty caps 3714 return bytebuf_nputs(b, str, (size_t)strlen(str)); 3715 } 3716 3717 static int 3718 bytebuf_nputs(struct bytebuf_t* b, const char* str, size_t nstr) 3719 { 3720 int rv; 3721 if_err_return(rv, bytebuf_reserve(b, b->len + nstr + 1)); 3722 memcpy(b->buf + b->len, str, nstr); 3723 b->len += nstr; 3724 b->buf[b->len] = '\0'; 3725 return TB_OK; 3726 } 3727 3728 static int 3729 bytebuf_shift(struct bytebuf_t* b, size_t n) 3730 { 3731 if (n > b->len) { n = b->len; } 3732 size_t nmove = b->len - n; 3733 memmove(b->buf, b->buf + n, nmove); 3734 b->len -= n; 3735 return TB_OK; 3736 } 3737 3738 static int 3739 bytebuf_flush(struct bytebuf_t* b, int fd) 3740 { 3741 if (b->len <= 0) { return TB_OK; } 3742 ssize_t write_rv = write(fd, b->buf, b->len); 3743 if (write_rv < 0 || (size_t)write_rv != b->len) { 3744 // Note, errno will be 0 on partial write 3745 global.last_errno = errno; 3746 return TB_ERR; 3747 } 3748 b->len = 0; 3749 return TB_OK; 3750 } 3751 3752 static int 3753 bytebuf_reserve(struct bytebuf_t* b, size_t sz) 3754 { 3755 if (b->cap >= sz) { return TB_OK; } 3756 size_t newcap = b->cap > 0 ? b->cap : 1; 3757 while (newcap < sz) { 3758 newcap *= 2; 3759 } 3760 char* newbuf; 3761 if (b->buf) { 3762 newbuf = (char*)tb_realloc(b->buf, newcap); 3763 } else { 3764 newbuf = (char*)tb_malloc(newcap); 3765 } 3766 if (!newbuf) { return TB_ERR_MEM; } 3767 b->buf = newbuf; 3768 b->cap = newcap; 3769 return TB_OK; 3770 } 3771 3772 static int 3773 bytebuf_free(struct bytebuf_t* b) 3774 { 3775 if (b->buf) { tb_free(b->buf); } 3776 memset(b, 0, sizeof(*b)); 3777 return TB_OK; 3778 } 3779 3780 #endif // TB_IMPL