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