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