💾 Archived View for gmi.noulin.net › gitRepositories › heartbeat › file › shpPackages › termbox › te… captured on 2024-06-20 at 11:55:53. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
termbox.c (18963B)
1 #include <assert.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <errno.h> 5 #include <fcntl.h> 6 #include <signal.h> 7 #include <stdio.h> 8 #include <stdarg.h> 9 #include <stdbool.h> 10 #include <sys/select.h> 11 #include <sys/ioctl.h> 12 #include <sys/time.h> 13 #include <sys/stat.h> 14 #include <termios.h> 15 #include <unistd.h> 16 #include <wchar.h> 17 18 #include "termbox.h" 19 #include "utf8.h" 20 21 #include "bytebuffer.h" 22 #include "term.h" 23 #include "input.h" 24 25 uint32_t tb_palette[16] = { 26 0x000000, // BLACK 27 0xAA2222, // RED 28 0x22AA22, // GREEN 29 0xAA5522, // YELLOW 30 0x3333AA, // BLUE 31 0xAA22AA, // MAGENTA 32 0x22AAAA, // CYAN 33 0xDDDDDD, // WHITE 34 0x999999, // LIGHT BLACK 35 0xFF5555, // LIGHT RED 36 0x55FF55, // LIGHT GREEN 37 0xFFFF55, // LIGHT YELLOW 38 0x5555FF, // LIGHT BLUE 39 0xFF55FF, // LIGHT MAGENTA 40 0x55FFFF, // LIGHT CYAN 41 0xFFFFFF, // LIGHT WHITE 42 }; 43 44 struct cellbuf { 45 int width; 46 int height; 47 struct tb_cell *cells; 48 }; 49 50 #define CELL(buf, x, y) (buf)->cells[(y) * (buf)->width + (x)] 51 #define IS_CURSOR_HIDDEN(cx, cy) (cx == -1 || cy == -1) 52 #define LAST_COORD_INIT -1 53 54 static struct termios orig_tios; 55 56 static struct cellbuf back_buffer; 57 static struct cellbuf front_buffer; 58 static struct bytebuffer output_buffer; 59 static struct bytebuffer input_buffer; 60 61 static char print_buf[8192]; 62 63 static int termw = -1; 64 static int termh = -1; 65 66 static int inputmode = TB_INPUT_ESC; 67 static int outputmode = TB_OUTPUT_NORMAL; 68 69 static int inout; 70 static int winch_fds[2]; 71 72 static int lastx = LAST_COORD_INIT; 73 static int lasty = LAST_COORD_INIT; 74 static int cursor_x = -1; 75 static int cursor_y = -1; 76 77 static uint32_t background = TB_DEFAULT; 78 static uint32_t foreground = TB_DEFAULT; 79 80 static void write_cursor(int x, int y); 81 static void write_sgr(uint32_t fg, uint32_t bg); 82 83 static void cellbuf_init(struct cellbuf *buf, int width, int height); 84 static void cellbuf_resize(struct cellbuf *buf, int width, int height); 85 static void cellbuf_clear(struct cellbuf *buf); 86 static void cellbuf_free(struct cellbuf *buf); 87 88 static void update_size(void); 89 static void update_term_size(void); 90 static void send_attr(uint32_t fg, uint32_t bg); 91 static void send_char(int x, int y, uint32_t c); 92 static void send_clear(void); 93 static void sigwinch_handler(int xxx); 94 static int wait_fill_event(struct tb_event *event, struct timeval *timeout, int sock); 95 96 /* may happen in a different thread */ 97 static volatile int buffer_size_change_request; 98 99 /* -------------------------------------------------------- */ 100 101 int tb_init_fd(int inout_) 102 { 103 inout = inout_; 104 if (inout == -1) { 105 return TB_EFAILED_TO_OPEN_TTY; 106 } 107 108 if (init_term() < 0) { 109 close(inout); 110 return TB_EUNSUPPORTED_TERMINAL; 111 } 112 113 if (pipe(winch_fds) < 0) { 114 close(inout); 115 return TB_EPIPE_TRAP_ERROR; 116 } 117 118 struct sigaction sa = {0}; 119 sa.sa_handler = sigwinch_handler; 120 sa.sa_flags = 0; 121 sigaction(SIGWINCH, &sa, 0); 122 123 tcgetattr(inout, &orig_tios); 124 125 struct termios tios; 126 memcpy(&tios, &orig_tios, sizeof(tios)); 127 128 tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP 129 | INLCR | IGNCR | ICRNL | IXON); 130 tios.c_oflag &= ~OPOST; 131 tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); 132 tios.c_cflag &= ~(CSIZE | PARENB); 133 tios.c_cflag |= CS8; 134 tios.c_cc[VMIN] = 0; 135 tios.c_cc[VTIME] = 0; 136 tcsetattr(inout, TCSAFLUSH, &tios); 137 138 bytebuffer_init(&input_buffer, 128); 139 bytebuffer_init(&output_buffer, 32 * 1024); 140 141 bytebuffer_puts(&output_buffer, funcs[T_ENTER_CA]); 142 bytebuffer_puts(&output_buffer, funcs[T_ENTER_KEYPAD]); 143 bytebuffer_puts(&output_buffer, funcs[T_HIDE_CURSOR]); 144 send_clear(); 145 146 update_term_size(); 147 cellbuf_init(&back_buffer, termw, termh); 148 cellbuf_init(&front_buffer, termw, termh); 149 cellbuf_clear(&back_buffer); 150 cellbuf_clear(&front_buffer); 151 152 return 0; 153 } 154 155 int tb_init_file(const char* name){ 156 return tb_init_fd(open(name, O_RDWR)); 157 } 158 159 int tb_init(void) 160 { 161 return tb_init_file("/dev/tty"); 162 } 163 164 void tb_shutdown(void) 165 { 166 if (termw == -1) { 167 fputs("tb_shutdown() should not be called twice.", stderr); 168 abort(); 169 } 170 171 bytebuffer_puts(&output_buffer, funcs[T_SHOW_CURSOR]); 172 bytebuffer_puts(&output_buffer, funcs[T_SGR0]); 173 bytebuffer_puts(&output_buffer, funcs[T_CLEAR_SCREEN]); 174 bytebuffer_puts(&output_buffer, funcs[T_EXIT_CA]); 175 bytebuffer_puts(&output_buffer, funcs[T_EXIT_KEYPAD]); 176 bytebuffer_puts(&output_buffer, funcs[T_EXIT_MOUSE]); 177 bytebuffer_flush(&output_buffer, inout); 178 tcsetattr(inout, TCSAFLUSH, &orig_tios); 179 180 shutdown_term(); 181 close(inout); 182 close(winch_fds[0]); 183 close(winch_fds[1]); 184 185 cellbuf_free(&back_buffer); 186 cellbuf_free(&front_buffer); 187 bytebuffer_free(&output_buffer); 188 bytebuffer_free(&input_buffer); 189 termw = termh = -1; 190 } 191 192 void tb_present(void) 193 { 194 int x,y,w,i; 195 struct tb_cell *back, *front; 196 197 /* invalidate cursor position */ 198 lastx = LAST_COORD_INIT; 199 lasty = LAST_COORD_INIT; 200 201 if (buffer_size_change_request) { 202 update_size(); 203 buffer_size_change_request = 0; 204 } 205 206 for (y = 0; y < front_buffer.height; ++y) { 207 for (x = 0; x < front_buffer.width; ) { 208 back = &CELL(&back_buffer, x, y); 209 front = &CELL(&front_buffer, x, y); 210 w = wcwidth(back->ch); 211 if (w < 1) w = 1; 212 if (memcmp(back, front, sizeof(struct tb_cell)) == 0) { 213 x += w; 214 continue; 215 } 216 memcpy(front, back, sizeof(struct tb_cell)); 217 send_attr(back->fg, back->bg); 218 if (w > 1 && x >= front_buffer.width - (w - 1)) { 219 // Not enough room for wide ch, so send spaces 220 for (i = x; i < front_buffer.width; ++i) { 221 send_char(i, y, ' '); 222 } 223 } else { 224 send_char(x, y, back->ch); 225 for (i = 1; i < w; ++i) { 226 front = &CELL(&front_buffer, x + i, y); 227 front->ch = 0; 228 front->fg = back->fg; 229 front->bg = back->bg; 230 } 231 } 232 x += w; 233 } 234 } 235 if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y)) 236 write_cursor(cursor_x, cursor_y); 237 bytebuffer_flush(&output_buffer, inout); 238 } 239 240 void tb_set_cursor(int cx, int cy) 241 { 242 if (IS_CURSOR_HIDDEN(cursor_x, cursor_y) && !IS_CURSOR_HIDDEN(cx, cy)) 243 bytebuffer_puts(&output_buffer, funcs[T_SHOW_CURSOR]); 244 245 if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y) && IS_CURSOR_HIDDEN(cx, cy)) 246 bytebuffer_puts(&output_buffer, funcs[T_HIDE_CURSOR]); 247 248 cursor_x = cx; 249 cursor_y = cy; 250 if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y)) 251 write_cursor(cursor_x, cursor_y); 252 } 253 254 void tb_put_cell(int x, int y, const struct tb_cell *cell) 255 { 256 if ((unsigned)x >= (unsigned)back_buffer.width) 257 return; 258 if ((unsigned)y >= (unsigned)back_buffer.height) 259 return; 260 CELL(&back_buffer, x, y) = *cell; 261 } 262 263 void tb_change_cell(int x, int y, uint32_t ch, uint32_t fg, uint32_t bg) 264 { 265 struct tb_cell c = {ch, fg, bg}; 266 tb_put_cell(x, y, &c); 267 } 268 269 int tb_string_with_limit(int x, int y, uint32_t fg, uint32_t bg, int limit, const char *str) { 270 uint32_t uni; 271 int l = 0; 272 int userX = x; 273 274 while (*str && l < limit) { 275 if (*str == '\n') { 276 // new line 277 l = 0; 278 x = userX; 279 y++; 280 str++; 281 } 282 else { 283 str += tb_utf8_char_to_unicode(&uni, str); 284 tb_change_cell(x, y, uni, fg, bg); 285 x++; 286 l++; 287 } 288 } 289 290 return l; 291 } 292 293 int tb_string(int x, int y, uint32_t fg, uint32_t bg, const char *str) { 294 return tb_string_with_limit(x, y, fg, bg, sizeof(print_buf), str); 295 } 296 297 int tb_stringf(int x, int y, uint32_t fg, uint32_t bg, const char *fmt, ...) { 298 va_list vl; 299 va_start(vl, fmt); 300 vsnprintf(print_buf, sizeof(print_buf), fmt, vl); 301 va_end(vl); 302 return tb_string_with_limit(x, y, fg, bg, sizeof(print_buf), print_buf); 303 } 304 305 int tb_stringf_with_limit(int x, int y, uint32_t fg, uint32_t bg, int limit, const char *fmt, ...) { 306 va_list vl; 307 va_start(vl, fmt); 308 // the string is utf8 encoded so character count is unknown and different from byte count 309 vsnprintf(print_buf, sizeof(print_buf), fmt, vl); 310 va_end(vl); 311 return tb_string_with_limit(x, y, fg, bg, limit, print_buf); 312 } 313 314 void tb_blit(int x, int y, int w, int h, const struct tb_cell *cells) 315 { 316 if (x + w < 0 || x >= back_buffer.width) 317 return; 318 if (y + h < 0 || y >= back_buffer.height) 319 return; 320 int xo = 0, yo = 0, ww = w, hh = h; 321 if (x < 0) { 322 xo = -x; 323 ww -= xo; 324 x = 0; 325 } 326 if (y < 0) { 327 yo = -y; 328 hh -= yo; 329 y = 0; 330 } 331 if (ww > back_buffer.width - x) 332 ww = back_buffer.width - x; 333 if (hh > back_buffer.height - y) 334 hh = back_buffer.height - y; 335 336 int sy; 337 struct tb_cell *dst = &CELL(&back_buffer, x, y); 338 const struct tb_cell *src = cells + yo * w + xo; 339 size_t size = sizeof(struct tb_cell) * ww; 340 341 for (sy = 0; sy < hh; ++sy) { 342 memcpy(dst, src, size); 343 dst += back_buffer.width; 344 src += w; 345 } 346 } 347 348 struct tb_cell *tb_cell_buffer(void) 349 { 350 return back_buffer.cells; 351 } 352 353 int tb_poll_event(struct tb_event *event, int sock) 354 { 355 return wait_fill_event(event, NULL, sock); 356 } 357 358 int tb_peek_event(struct tb_event *event, int timeout) 359 { 360 struct timeval tv; 361 tv.tv_sec = timeout / 1000; 362 tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000; 363 return wait_fill_event(event, &tv, 0/*sock*/); 364 } 365 366 int tb_width(void) 367 { 368 return termw; 369 } 370 371 int tb_height(void) 372 { 373 return termh; 374 } 375 376 void tb_clear(void) 377 { 378 if (buffer_size_change_request) { 379 update_size(); 380 buffer_size_change_request = 0; 381 } 382 cellbuf_clear(&back_buffer); 383 } 384 385 int tb_select_input_mode(int mode) 386 { 387 if (mode) { 388 if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == 0) 389 mode |= TB_INPUT_ESC; 390 391 /* technically termbox can handle that, but let's be nice and show here 392 what mode is actually used */ 393 if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == (TB_INPUT_ESC | TB_INPUT_ALT)) 394 mode &= ~TB_INPUT_ALT; 395 396 inputmode = mode; 397 if (mode&TB_INPUT_MOUSE) { 398 bytebuffer_puts(&output_buffer, funcs[T_ENTER_MOUSE]); 399 bytebuffer_flush(&output_buffer, inout); 400 } else { 401 bytebuffer_puts(&output_buffer, funcs[T_EXIT_MOUSE]); 402 bytebuffer_flush(&output_buffer, inout); 403 } 404 } 405 return inputmode; 406 } 407 408 int tb_select_output_mode(int mode) 409 { 410 if (mode) 411 outputmode = mode; 412 return outputmode; 413 } 414 415 void tb_set_clear_attributes(uint32_t fg, uint32_t bg) 416 { 417 foreground = fg; 418 background = bg; 419 } 420 421 /* -------------------------------------------------------- */ 422 423 static int convertnum(uint32_t num, char* buf) { 424 int i, l = 0; 425 int ch; 426 do { 427 buf[l++] = '0' + (num % 10); 428 num /= 10; 429 } while (num); 430 for(i = 0; i < l / 2; i++) { 431 ch = buf[i]; 432 buf[i] = buf[l - 1 - i]; 433 buf[l - 1 - i] = ch; 434 } 435 return l; 436 } 437 438 #define WRITE_LITERAL(X) bytebuffer_append(&output_buffer, (X), sizeof(X)-1) 439 #define WRITE_INT(X) bytebuffer_append(&output_buffer, buf, convertnum((X), buf)) 440 441 static void write_cursor(int x, int y) { 442 char buf[32]; 443 WRITE_LITERAL("\033["); 444 WRITE_INT(y+1); 445 WRITE_LITERAL(";"); 446 WRITE_INT(x+1); 447 WRITE_LITERAL("H"); 448 } 449 450 static void write_sgr(uint32_t fg, uint32_t bg) { 451 char buf[32]; 452 453 if (fg & TB_DEFAULT && bg & TB_DEFAULT) 454 return; 455 456 switch (outputmode) { 457 case TB_OUTPUT_256: 458 case TB_OUTPUT_216: 459 case TB_OUTPUT_GRAYSCALE: 460 WRITE_LITERAL("\033["); 461 if (!(fg & TB_DEFAULT)) { 462 WRITE_LITERAL("38;5;"); 463 WRITE_INT(fg); 464 if (!(bg & TB_DEFAULT)) { 465 WRITE_LITERAL(";"); 466 } 467 } 468 if (!(bg & TB_DEFAULT)) { 469 WRITE_LITERAL("48;5;"); 470 WRITE_INT(bg); 471 } 472 WRITE_LITERAL("m"); 473 break; 474 case TB_OUTPUT_TRUECOLOR: 475 WRITE_LITERAL("\033["); 476 if (!(fg & TB_DEFAULT)) { 477 if (fg & TB_PALETTE) { 478 fg = tb_palette[fg & 0xF]; 479 } 480 WRITE_LITERAL("38;2;"); 481 WRITE_INT(fg >> 16 & 0xFF); // fg R 482 WRITE_LITERAL(";"); 483 WRITE_INT(fg >> 8 & 0xFF); // fg G 484 WRITE_LITERAL(";"); 485 WRITE_INT(fg & 0xFF); // fg B 486 if (!(bg & TB_DEFAULT)) { 487 WRITE_LITERAL(";"); 488 } 489 } 490 if (!(bg & TB_DEFAULT)) { 491 if (bg & TB_PALETTE) { 492 bg = tb_palette[bg & 0xF]; 493 } 494 WRITE_LITERAL("48;2;"); 495 WRITE_INT(bg >> 16 & 0xFF); // bg R 496 WRITE_LITERAL(";"); 497 WRITE_INT(bg >> 8 & 0xFF); // bg G 498 WRITE_LITERAL(";"); 499 WRITE_INT(bg & 0xFF); // bg B 500 } 501 WRITE_LITERAL("m"); 502 break; 503 case TB_OUTPUT_NORMAL: 504 default: 505 // 16 color ISO 506 // num fg bg 507 // 0-7 3(N)m 4(N)m 508 // 8-15 1;3(N-8)m 1;4(N-8)m 509 510 // in bold 511 // 0-7 9(N)m 10(N)m 512 // 8-15 1;9(N-8)m 1;9(N-8)m 513 WRITE_LITERAL("\033["); 514 if (!(fg & TB_DEFAULT)) { 515 if (fg > 7) { // 8 light colors 516 WRITE_LITERAL("1;3"); 517 WRITE_INT(fg - 8); 518 } 519 else { 520 WRITE_LITERAL("3"); 521 WRITE_INT(fg); 522 } 523 if (!(bg & TB_DEFAULT)) { 524 WRITE_LITERAL(";"); 525 } 526 } 527 if (!(bg & TB_DEFAULT)) { 528 if (bg > 7 ) { // 8 light colors 529 WRITE_LITERAL("10"); 530 WRITE_INT(bg - 8); 531 } 532 else { 533 WRITE_LITERAL("4"); 534 WRITE_INT(bg); 535 } 536 } 537 WRITE_LITERAL("m"); 538 break; 539 } 540 } 541 542 static void cellbuf_init(struct cellbuf *buf, int width, int height) 543 { 544 buf->cells = (struct tb_cell*)malloc(sizeof(struct tb_cell) * width * height); 545 assert(buf->cells); 546 buf->width = width; 547 buf->height = height; 548 } 549 550 static void cellbuf_resize(struct cellbuf *buf, int width, int height) 551 { 552 if (buf->width == width && buf->height == height) 553 return; 554 555 int oldw = buf->width; 556 int oldh = buf->height; 557 struct tb_cell *oldcells = buf->cells; 558 559 cellbuf_init(buf, width, height); 560 cellbuf_clear(buf); 561 562 int minw = (width < oldw) ? width : oldw; 563 int minh = (height < oldh) ? height : oldh; 564 int i; 565 566 for (i = 0; i < minh; ++i) { 567 struct tb_cell *csrc = oldcells + (i * oldw); 568 struct tb_cell *cdst = buf->cells + (i * width); 569 memcpy(cdst, csrc, sizeof(struct tb_cell) * minw); 570 } 571 572 free(oldcells); 573 } 574 575 static void cellbuf_clear(struct cellbuf *buf) 576 { 577 int i; 578 int ncells = buf->width * buf->height; 579 580 for (i = 0; i < ncells; ++i) { 581 buf->cells[i].ch = ' '; 582 buf->cells[i].fg = foreground; 583 buf->cells[i].bg = background; 584 } 585 } 586 587 static void cellbuf_free(struct cellbuf *buf) 588 { 589 free(buf->cells); 590 } 591 592 static void get_term_size(int *w, int *h) 593 { 594 struct winsize sz = {0}; 595 596 ioctl(inout, TIOCGWINSZ, &sz); 597 598 if (w) *w = sz.ws_col; 599 if (h) *h = sz.ws_row; 600 } 601 602 static void update_term_size(void) 603 { 604 struct winsize sz = {0}; 605 606 ioctl(inout, TIOCGWINSZ, &sz); 607 608 termw = sz.ws_col; 609 termh = sz.ws_row; 610 } 611 612 static void send_attr(uint32_t fg, uint32_t bg) 613 { 614 #define LAST_ATTR_INIT 0xFFFFFFFF 615 static uint32_t lastfg = LAST_ATTR_INIT, lastbg = LAST_ATTR_INIT; 616 if (fg != lastfg || bg != lastbg) { 617 bytebuffer_puts(&output_buffer, funcs[T_SGR0]); 618 619 uint32_t fgcol; 620 uint32_t bgcol; 621 622 switch (outputmode) { 623 case TB_OUTPUT_256: 624 fgcol = fg & (0xFF | TB_DEFAULT); 625 bgcol = bg & (0xFF | TB_DEFAULT); 626 break; 627 628 case TB_OUTPUT_216: 629 fgcol = fg & 0xFF; if (fgcol > 215) fgcol = 7; 630 bgcol = bg & 0xFF; if (bgcol > 215) bgcol = 0; 631 fgcol += 0x10 | (fg & TB_DEFAULT); 632 bgcol += 0x10 | (bg & TB_DEFAULT); 633 break; 634 635 case TB_OUTPUT_GRAYSCALE: 636 fgcol = fg & 0xFF; if (fgcol > 23) fgcol = 23; 637 bgcol = bg & 0xFF; if (bgcol > 23) bgcol = 0; 638 fgcol += 0xe8 | (fg & TB_DEFAULT); 639 bgcol += 0xe8 | (bg & TB_DEFAULT); 640 break; 641 642 case TB_OUTPUT_TRUECOLOR: 643 fgcol = fg; 644 bgcol = bg; 645 break; 646 case TB_OUTPUT_NORMAL: 647 default: 648 fgcol = fg & (0x0F | TB_DEFAULT); 649 bgcol = bg & (0x0F | TB_DEFAULT); 650 } 651 652 if (fg & TB_BOLD) 653 bytebuffer_puts(&output_buffer, funcs[T_BOLD]); 654 if (bg & TB_BLINK) 655 bytebuffer_puts(&output_buffer, funcs[T_BLINK]); 656 if (fg & TB_UNDERLINE) 657 bytebuffer_puts(&output_buffer, funcs[T_UNDERLINE]); 658 if (fg & TB_REVERSE) 659 bytebuffer_puts(&output_buffer, funcs[T_REVERSE]); 660 if (fg & TB_FAINT) 661 bytebuffer_puts(&output_buffer, "\x1B[2m"); 662 if (fg & TB_ITALIC) 663 bytebuffer_puts(&output_buffer, "\x1B[3m"); 664 if (bg & TB_HIDDEN) 665 bytebuffer_puts(&output_buffer, "\x1B[8m"); 666 if (bg & TB_CROSSED) 667 bytebuffer_puts(&output_buffer, "\x1B[9m"); 668 669 write_sgr(fgcol, bgcol); 670 671 lastfg = fg; 672 lastbg = bg; 673 } 674 } 675 676 static void send_char(int x, int y, uint32_t c) 677 { 678 char buf[7]; 679 int bw = tb_utf8_unicode_to_char(buf, c); 680 if (x-1 != lastx || y != lasty) 681 write_cursor(x, y); 682 lastx = x; lasty = y; 683 if(!c) buf[0] = ' '; // replace 0 with whitespace 684 bytebuffer_append(&output_buffer, buf, bw); 685 } 686 687 static void send_clear(void) 688 { 689 send_attr(foreground, background); 690 bytebuffer_puts(&output_buffer, funcs[T_CLEAR_SCREEN]); 691 if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y)) 692 write_cursor(cursor_x, cursor_y); 693 bytebuffer_flush(&output_buffer, inout); 694 695 /* we need to invalidate cursor position too and these two vars are 696 * used only for simple cursor positioning optimization, cursor 697 * actually may be in the correct place, but we simply discard 698 * optimization once and it gives us simple solution for the case when 699 * cursor moved */ 700 lastx = LAST_COORD_INIT; 701 lasty = LAST_COORD_INIT; 702 } 703 704 static void sigwinch_handler(int xxx) 705 { 706 (void) xxx; 707 const int zzz = 1; 708 write(winch_fds[1], &zzz, sizeof(int)); 709 } 710 711 static void update_size(void) 712 { 713 update_term_size(); 714 cellbuf_resize(&back_buffer, termw, termh); 715 cellbuf_resize(&front_buffer, termw, termh); 716 cellbuf_clear(&front_buffer); 717 send_clear(); 718 } 719 720 static int read_up_to(int n) { 721 assert(n > 0); 722 const int prevlen = input_buffer.len; 723 bytebuffer_resize(&input_buffer, prevlen + n); 724 725 int read_n = 0; 726 while (read_n <= n) { 727 ssize_t r = 0; 728 if (read_n < n) { 729 r = read(inout, input_buffer.buf + prevlen + read_n, n - read_n); 730 } 731 #ifdef __CYGWIN__ 732 // While linux man for tty says when VMIN == 0 && VTIME == 0, read 733 // should return 0 when there is nothing to read, cygwin's read returns 734 // -1. Not sure why and if it's correct to ignore it, but let's pretend 735 // it's zero. 736 if (r < 0) r = 0; 737 #endif 738 if (r < 0) { 739 // EAGAIN / EWOULDBLOCK shouldn't occur here 740 assert(errno != EAGAIN && errno != EWOULDBLOCK); 741 return -1; 742 } else if (r > 0) { 743 read_n += r; 744 } else { 745 bytebuffer_resize(&input_buffer, prevlen + read_n); 746 return read_n; 747 } 748 } 749 assert(!"unreachable"); 750 return 0; 751 } 752 753 static int wait_fill_event(struct tb_event *event, struct timeval *timeout, int sock) 754 { 755 // ;-) 756 #define ENOUGH_DATA_FOR_PARSING 64 757 fd_set events = {0}; 758 759 // try to extract event from input buffer, return on success 760 event->type = TB_EVENT_KEY; 761 if (extract_event(event, &input_buffer, inputmode)) 762 return event->type; 763 764 // it looks like input buffer is incomplete, let's try the short path, 765 // but first make sure there is enough space 766 int n = read_up_to(ENOUGH_DATA_FOR_PARSING); 767 if (n < 0) 768 return -1; 769 if (n > 0 && extract_event(event, &input_buffer, inputmode)) 770 return event->type; 771 772 // n == 0, or not enough data, let's go to select 773 while (1) { 774 FD_ZERO(&events); 775 FD_SET(inout, &events); 776 FD_SET(winch_fds[0], &events); 777 if (sock) FD_SET(sock, &events); 778 int maxfd = (winch_fds[0] > inout) ? winch_fds[0] : inout; 779 maxfd = maxfd > sock ? maxfd : sock; 780 int result = select(maxfd+1, &events, NULL, NULL, timeout); 781 if (!result) 782 return 0; 783 784 if (FD_ISSET(inout, &events)) { 785 event->type = TB_EVENT_KEY; 786 n = read_up_to(ENOUGH_DATA_FOR_PARSING); 787 if (n < 0) 788 return -1; 789 790 if (n == 0) 791 continue; 792 793 if (extract_event(event, &input_buffer, inputmode)) 794 return event->type; 795 } 796 if (FD_ISSET(winch_fds[0], &events)) { 797 event->type = TB_EVENT_RESIZE; 798 int zzz = 0; 799 read(winch_fds[0], &zzz, sizeof(int)); 800 buffer_size_change_request = 1; 801 get_term_size(&event->w, &event->h); 802 return TB_EVENT_RESIZE; 803 } 804 if (sock && FD_ISSET(sock, &events)) { 805 event->type = TB_EVENT_SOCKET; 806 return TB_EVENT_SOCKET; 807 } 808 } 809 }