terminal.c (27780B)
1 /* terminal.c 2 * (c) 2002 Mikulas Patocka 3 * This file is a part of the Links program, released under GPL. 4 */ 5 6 #include <limits.h> 7 8 #include "links.h" 9 10 static void in_term(void *); 11 static void check_if_no_terminal(void); 12 13 int 14 hard_write(int fd, const unsigned char *p, int l) 15 { 16 int t = 0; 17 while (l > 0) { 18 int w; 19 EINTRLOOP(w, (int)write(fd, p, l)); 20 if (w < 0) 21 return -1; 22 if (!w) { 23 errno = ENOSPC; 24 break; 25 } 26 t += w; 27 p += w; 28 l -= w; 29 } 30 return t; 31 } 32 33 int 34 hard_read(int fd, unsigned char *p, int l) 35 { 36 int r = 1; 37 int t = 0; 38 while (l > 0 && r) { 39 EINTRLOOP(r, (int)read(fd, p, l)); 40 if (r < 0) 41 return -1; 42 t += r; 43 p += r; 44 l -= r; 45 } 46 return t; 47 } 48 49 unsigned char * 50 get_cwd(void) 51 { 52 int bufsize = 128; 53 unsigned char *buf; 54 unsigned char *gcr; 55 while (1) { 56 buf = xmalloc(bufsize); 57 ENULLLOOP(gcr, cast_uchar getcwd(cast_char buf, bufsize)); 58 if (gcr) 59 return buf; 60 free(buf); 61 if (errno != ERANGE) 62 break; 63 if ((unsigned)bufsize > INT_MAX - 128) 64 overalloc(); 65 bufsize += 128; 66 } 67 return NULL; 68 } 69 70 void 71 set_cwd(unsigned char *path) 72 { 73 int rs; 74 if (path) 75 EINTRLOOP(rs, chdir(cast_const_char path)); 76 } 77 78 unsigned char 79 get_attribute(int fg, int bg) 80 { 81 return ((bg & 7) << 3) | (fg & 7) | ((fg & 8) << 3); 82 } 83 84 struct list_head terminals = { &terminals, &terminals }; 85 86 static void 87 set_margin(struct terminal *term) 88 { 89 term->left_margin = 0; 90 term->x = term->real_x; 91 term->top_margin = 0; 92 term->y = term->real_y; 93 } 94 95 static void 96 alloc_term_screen(struct terminal *term) 97 { 98 chr *s, *t; 99 if (term->x < 0) 100 term->x = 1; 101 if (term->y < 0) 102 term->y = 1; 103 if ((term->x && term->x * term->y / term->x != term->y) 104 || term->x * term->y > INT_MAX / sizeof(*term->screen)) 105 overalloc(); 106 s = xrealloc(term->screen, term->x * term->y * sizeof(*term->screen)); 107 t = xrealloc(term->last_screen, 108 term->x * term->y * sizeof(*term->screen)); 109 memset(t, -1, term->x * term->y * sizeof(*term->screen)); 110 term->last_screen = t; 111 memset(s, 0, term->x * term->y * sizeof(*term->screen)); 112 term->screen = s; 113 term->dirty = 1; 114 term->lcx = -1; 115 term->lcy = -1; 116 } 117 118 static void 119 clear_terminal(struct terminal *term) 120 { 121 fill_area(term, 0, 0, term->x, term->y, ' ', 0); 122 set_cursor(term, 0, 0, 0, 0); 123 } 124 125 void 126 redraw_below_window(struct window *win) 127 { 128 int tr; 129 struct terminal *term = win->term; 130 struct window *w = NULL; 131 struct list_head *lw; 132 struct links_event ev = { EV_REDRAW, 0, 0, 0 }; 133 ev.x = term->x; 134 ev.y = term->y; 135 if (term->redrawing >= 2) 136 return; 137 tr = term->redrawing; 138 win->term->redrawing = 2; 139 foreachback (struct window, w, lw, term->windows) { 140 if (w == win) 141 break; 142 w->handler(w, &ev, 0); 143 } 144 term->redrawing = tr; 145 } 146 147 static void 148 redraw_terminal_ev(struct terminal *term, int e) 149 { 150 struct window *win = NULL; 151 struct list_head *lwin; 152 struct links_event ev = { 0, 0, 0, 0 }; 153 ev.ev = e; 154 ev.x = term->x; 155 ev.y = term->y; 156 clear_terminal(term); 157 term->redrawing = 2; 158 foreachback (struct window, win, lwin, term->windows) 159 win->handler(win, &ev, 0); 160 term->redrawing = 0; 161 } 162 163 static void 164 redraw_terminal(struct terminal *term) 165 { 166 redraw_terminal_ev(term, EV_REDRAW); 167 } 168 169 static void 170 redraw_terminal_all(struct terminal *term) 171 { 172 redraw_terminal_ev(term, EV_RESIZE); 173 } 174 175 static void 176 erase_screen(struct terminal *term) 177 { 178 if (!term->master || !is_blocked()) 179 hard_write(term->fdout, cast_uchar "\033[2J\033[1;1H", 10); 180 } 181 182 static void 183 redraw_terminal_cls(struct terminal *term) 184 { 185 erase_screen(term); 186 set_margin(term); 187 alloc_term_screen(term); 188 redraw_terminal_all(term); 189 } 190 191 void 192 cls_redraw_all_terminals(void) 193 { 194 struct terminal *term = NULL; 195 struct list_head *lterm; 196 foreach (struct terminal, term, lterm, terminals) { 197 redraw_terminal_cls(term); 198 } 199 } 200 201 void 202 draw_to_window(struct window *win, void (*fn)(struct terminal *term, void *), 203 void *data) 204 { 205 struct terminal *term = win->term; 206 struct window *w = NULL; 207 struct list_head *lw; 208 struct links_event ev = { EV_REDRAW, 0, 0, 0 }; 209 210 pr(fn(term, data)){}; 211 term = win->term; 212 if (win->list_entry.prev == &term->windows || term->redrawing) 213 return; 214 term->redrawing = 1; 215 ev.x = term->x; 216 ev.y = term->y; 217 foreachbackfrom (struct window, w, lw, term->windows, 218 win->list_entry.prev) 219 w->handler(w, &ev, 0); 220 term->redrawing = 0; 221 } 222 223 void 224 add_window(struct terminal *term, 225 void (*handler)(struct window *, struct links_event *, int), 226 void *data) 227 { 228 struct links_event ev = { EV_INIT, 0, 0, 0 }; 229 struct window *win; 230 ev.x = term->x; 231 ev.y = term->y; 232 win = mem_calloc(sizeof(struct window)); 233 win->handler = handler; 234 win->data = data; 235 win->term = term; 236 win->xp = win->yp = 0; 237 add_to_list(term->windows, win); 238 win->handler(win, &ev, 0); 239 } 240 241 void 242 delete_window(struct window *win) 243 { 244 struct terminal *term = win->term; 245 struct links_event ev = { EV_ABORT, 0, 0, 0 }; 246 win->handler(win, &ev, 1); 247 del_from_list(win); 248 free(win->data); 249 redraw_terminal(term); 250 free(win); 251 } 252 253 void 254 delete_window_ev(struct window *win, struct links_event *ev) 255 { 256 struct terminal *term = win->term; 257 struct list_head *lw = win->list_entry.next; 258 delete_window(win); 259 if (ev && lw != &term->windows) { 260 struct window *w = list_struct(lw, struct window); 261 w->handler(w, ev, 1); 262 } 263 } 264 265 void 266 set_window_ptr(struct window *win, int x, int y) 267 { 268 if (win->xp == x && win->yp == y) 269 return; 270 win->xp = x; 271 win->yp = y; 272 } 273 274 void 275 get_parent_ptr(struct window *win, int *x, int *y) 276 { 277 if (win->list_entry.next != &win->term->windows) { 278 struct window *next = 279 list_struct(win->list_entry.next, struct window); 280 *x = next->xp; 281 *y = next->yp; 282 } else { 283 *x = *y = 0; 284 } 285 } 286 287 struct ewd { 288 void (*fn)(void *); 289 void *data; 290 int b; 291 }; 292 293 static void 294 empty_window_handler(struct window *win, struct links_event *ev, int fwd) 295 { 296 struct terminal *term = win->term; 297 struct list_head *ln; 298 struct ewd *ewd = win->data; 299 int x, y; 300 void (*fn)(void *) = ewd->fn; 301 void *data = ewd->data; 302 if (ewd->b) 303 return; 304 switch ((int)ev->ev) { 305 case EV_INIT: 306 case EV_RESIZE: 307 case EV_REDRAW: 308 get_parent_ptr(win, &x, &y); 309 set_window_ptr(win, x, y); 310 return; 311 case EV_ABORT: 312 fn(data); 313 return; 314 } 315 ewd->b = 1; 316 ln = win->list_entry.next; 317 delete_window(win); 318 fn(data); 319 if (ln != &term->windows) { 320 struct window *n = list_struct(ln, struct window); 321 n->handler(n, ev, fwd); 322 } 323 } 324 325 void 326 add_empty_window(struct terminal *term, void (*fn)(void *), void *data) 327 { 328 struct ewd *ewd; 329 ewd = xmalloc(sizeof(struct ewd)); 330 ewd->fn = fn; 331 ewd->data = data; 332 ewd->b = 0; 333 add_window(term, empty_window_handler, ewd); 334 } 335 336 struct list_head term_specs = { &term_specs, &term_specs }; 337 338 void 339 free_term_specs(void) 340 { 341 free_list(struct term_spec, term_specs); 342 } 343 344 static struct term_spec dumb_term = { 345 init_list_1st(NULL) "", 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 346 }; 347 348 static struct term_spec * 349 default_term_spec(unsigned char *term) 350 { 351 return &dumb_term; 352 } 353 354 static struct term_spec * 355 get_term_spec(unsigned char *term) 356 { 357 struct term_spec *t = NULL; 358 struct list_head *lt; 359 foreach (struct term_spec, t, lt, term_specs) 360 if (!casestrcmp(t->term, term)) 361 return t; 362 return default_term_spec(term); 363 } 364 365 static void 366 sync_term_specs(void) 367 { 368 struct terminal *term = NULL; 369 struct list_head *lterm; 370 foreach (struct terminal, term, lterm, terminals) 371 term->spec = get_term_spec(term->term); 372 } 373 374 struct term_spec * 375 new_term_spec(unsigned char *term) 376 { 377 struct term_spec *t = NULL; 378 struct list_head *lt; 379 foreach (struct term_spec, t, lt, term_specs) 380 if (!casestrcmp(t->term, term)) 381 return t; 382 t = xmalloc(sizeof(struct term_spec)); 383 memcpy(t, default_term_spec(term), sizeof(struct term_spec)); 384 if (strlen(cast_const_char term) < MAX_TERM_LEN) 385 strcpy(cast_char t->term, cast_const_char term); 386 else { 387 memcpy(t->term, term, MAX_TERM_LEN - 1); 388 t->term[MAX_TERM_LEN - 1] = 0; 389 } 390 add_to_list(term_specs, t); 391 sync_term_specs(); 392 return t; 393 } 394 395 struct terminal * 396 init_term(int fdin, int fdout, 397 void (*root_window)(struct window *, struct links_event *, int)) 398 { 399 static tcount terminal_count = 0; 400 struct terminal *term; 401 struct window *win; 402 term = mem_calloc(sizeof(struct terminal)); 403 term->count = terminal_count++; 404 term->fdin = fdin; 405 term->fdout = fdout; 406 term->master = term->fdout == 1; 407 term->lcx = -1; 408 term->lcy = -1; 409 term->dirty = 1; 410 term->blocked = -1; 411 term->screen = NULL; 412 term->last_screen = NULL; 413 term->spec = default_term_spec(cast_uchar ""); 414 term->input_queue = NULL; 415 init_list(term->windows); 416 term->handle_to_close = -1; 417 win = mem_calloc(sizeof(struct window)); 418 win->handler = root_window; 419 win->term = term; 420 add_to_list(term->windows, win); 421 add_to_list(terminals, term); 422 set_handlers(fdin, in_term, NULL, term); 423 return term; 424 } 425 426 static int 427 process_utf_8(struct terminal *term, struct links_event *ev) 428 { 429 size_t l; 430 unsigned char *p; 431 unsigned c; 432 if (ev->ev == EV_KBD) { 433 if (ev->x <= 0 || ev->x >= 0x100) 434 goto direct; 435 if ((term->utf8_paste_mode ^ ev->y) & KBD_PASTING) { 436 term->utf8_paste_mode = ev->y & KBD_PASTING; 437 term->utf8_buffer[0] = 0; 438 } 439 if ((l = strlen(cast_const_char term->utf8_buffer)) 440 >= sizeof(term->utf8_buffer) - 1 441 || ev->x < 0x80 || ev->x >= 0xc0) { 442 term->utf8_buffer[0] = 0; 443 l = 0; 444 } 445 term->utf8_buffer[l] = (unsigned char)ev->x; 446 term->utf8_buffer[l + 1] = 0; 447 p = term->utf8_buffer; 448 GET_UTF_8(p, c); 449 if (!c) 450 return 0; 451 ev->x = c; 452 direct: 453 term->utf8_buffer[0] = 0; 454 } 455 return 1; 456 } 457 458 static void 459 in_term(void *term_) 460 { 461 struct terminal *term = (struct terminal *)term_; 462 struct links_event *ev; 463 int r; 464 unsigned char *iq; 465 if ((unsigned)term->qlen + ALLOC_GR > INT_MAX) 466 overalloc(); 467 iq = xrealloc(term->input_queue, term->qlen + ALLOC_GR); 468 term->input_queue = iq; 469 EINTRLOOP(r, (int)read(term->fdin, iq + term->qlen, ALLOC_GR)); 470 if (r <= 0) { 471 if (r == -1 && errno != ECONNRESET) 472 error( 473 "ERROR: error %d on terminal: could not read event", 474 errno); 475 destroy_terminal(term); 476 return; 477 } 478 term->qlen += r; 479 test_queue: 480 if ((size_t)term->qlen < sizeof(struct links_event)) 481 return; 482 ev = (struct links_event *)iq; 483 r = sizeof(struct links_event); 484 if (ev->ev != EV_INIT && ev->ev != EV_RESIZE && ev->ev != EV_REDRAW 485 && ev->ev != EV_KBD && ev->ev != EV_MOUSE && ev->ev != EV_ABORT) { 486 error("ERROR: error on terminal: bad event %d", ev->ev); 487 goto mm; 488 } 489 if (ev->ev == EV_INIT) { 490 int init_len; 491 if ((size_t)term->qlen < sizeof(struct links_event) 492 + MAX_TERM_LEN + MAX_CWD_LEN 493 + 3 * sizeof(int)) 494 return; 495 init_len = 496 *(int *)(iq + sizeof(struct links_event) + MAX_TERM_LEN 497 + MAX_CWD_LEN + 2 * sizeof(int)); 498 if ((size_t)term->qlen < sizeof(struct links_event) 499 + MAX_TERM_LEN + MAX_CWD_LEN 500 + 3 * sizeof(int) + init_len) 501 return; 502 memcpy(term->term, iq + sizeof(struct links_event), 503 MAX_TERM_LEN); 504 term->term[MAX_TERM_LEN - 1] = 0; 505 memcpy(term->cwd, 506 iq + sizeof(struct links_event) + MAX_TERM_LEN, 507 MAX_CWD_LEN); 508 term->cwd[MAX_CWD_LEN - 1] = 0; 509 term->environment = *(int *)(iq + sizeof(struct links_event) 510 + MAX_TERM_LEN + MAX_CWD_LEN); 511 ev->b = (long)(iq + sizeof(struct links_event) + MAX_TERM_LEN 512 + MAX_CWD_LEN + 2 * sizeof(int)); 513 r = (int)sizeof(struct links_event) + MAX_TERM_LEN + MAX_CWD_LEN 514 + 3 * (int)sizeof(int) + init_len; 515 sync_term_specs(); 516 } 517 if (ev->ev == EV_REDRAW || ev->ev == EV_RESIZE || ev->ev == EV_INIT) { 518 struct window *win = NULL; 519 struct list_head *lwin; 520 521 term->real_x = ev->x; 522 term->real_y = ev->y; 523 524 set_margin(term); 525 526 send_redraw: 527 if (ev->x < 0 || ev->y < 0) { 528 error("ERROR: bad terminal size: %d, %d", (int)ev->x, 529 (int)ev->y); 530 goto mm; 531 } 532 alloc_term_screen(term); 533 clear_terminal(term); 534 erase_screen(term); 535 term->redrawing = 1; 536 foreachback (struct window, win, lwin, term->windows) 537 win->handler(win, ev, 0); 538 term->redrawing = 0; 539 } 540 if (ev->ev == EV_MOUSE) { 541 ev->x -= term->left_margin; 542 ev->y -= term->top_margin; 543 } 544 if (ev->ev == EV_KBD || ev->ev == EV_MOUSE) { 545 if (ev->ev == EV_KBD && upcase(ev->x) == 'L' 546 && !(ev->y & KBD_PASTING) && ev->y & KBD_CTRL) { 547 ev->ev = EV_REDRAW; 548 ev->x = term->x; 549 ev->y = term->y; 550 goto send_redraw; 551 } 552 if (ev->ev == EV_KBD && ev->x == KBD_STOP) { 553 abort_background_connections(); 554 } 555 if (!list_empty(term->windows)) { 556 if (ev->ev == EV_KBD && ev->x == KBD_CTRL_C 557 && !(ev->y & KBD_PASTING)) { 558 struct window *prev = list_struct( 559 term->windows.prev, struct window); 560 prev->handler(prev, ev, 0); 561 } else { 562 if (process_utf_8(term, ev)) { 563 struct window *next = list_struct( 564 term->windows.next, struct window); 565 next->handler(next, ev, 0); 566 } 567 } 568 } 569 } 570 if (ev->ev == EV_ABORT) { 571 destroy_terminal(term); 572 return; 573 } 574 mm: 575 if (term->qlen == r) 576 term->qlen = 0; 577 else 578 memmove(iq, iq + r, term->qlen -= r); 579 goto test_queue; 580 } 581 582 static inline int 583 getcompcode(int c) 584 { 585 return (c << 1 | (c & 4) >> 2) & 7; 586 } 587 588 unsigned char frame_dumb[49] = 589 " ||||++||++++++--|-+||++--|-+----++++++++ "; 590 static unsigned char frame_vt100[49] = 591 "aaaxuuukkuxkjjjkmvwtqnttmlvwtqnvvwwmmllnnjla "; 592 593 #define SETPOS(x, y) \ 594 { \ 595 l = add_to_str(&a, l, cast_uchar "\033["); \ 596 l = add_num_to_str(&a, l, (y) + 1 + term->top_margin); \ 597 l = add_chr_to_str(&a, l, ';'); \ 598 l = add_num_to_str(&a, l, (x) + 1 + term->left_margin); \ 599 l = add_chr_to_str(&a, l, 'H'); \ 600 } 601 602 #define PRINT_CHAR(p) \ 603 { \ 604 char_t c = term->screen[p].ch; \ 605 unsigned char A = term->screen[p].at & 0x7f; \ 606 unsigned char frm = !!(term->screen[p].at & ATTR_FRAME); \ 607 if (s->mode == TERM_VT100) { \ 608 if (frm != mode) { \ 609 if (!(mode = frm)) \ 610 l = add_to_str(&a, l, \ 611 cast_uchar "\017"); \ 612 else \ 613 l = add_to_str(&a, l, \ 614 cast_uchar "\016"); \ 615 } \ 616 if (frm && c >= 176 && c < 224) \ 617 c = frame_vt100[c - 176]; \ 618 } else if (s->mode == TERM_DUMB && frm && c >= 176 && c < 224) \ 619 c = frame_dumb[c - 176]; \ 620 if (!(A & 0100) && (A >> 3) == (A & 7)) \ 621 A = (A & 070) | 7 * !(A & 020); \ 622 if (A != attrib) { \ 623 attrib = A; \ 624 l = add_to_str(&a, l, cast_uchar "\033[0"); \ 625 if (s->col) { \ 626 unsigned char m[4]; \ 627 m[0] = ';'; \ 628 m[1] = '3'; \ 629 m[3] = 0; \ 630 m[2] = (attrib & 7) + '0'; \ 631 l = add_to_str(&a, l, m); \ 632 m[1] = '4'; \ 633 m[2] = ((attrib >> 3) & 7) + '0'; \ 634 l = add_to_str(&a, l, m); \ 635 } else if (getcompcode(attrib & 7) \ 636 < getcompcode(attrib >> 3 & 7)) \ 637 l = add_to_str(&a, l, cast_uchar ";7"); \ 638 if (attrib & 0100) \ 639 l = add_to_str(&a, l, cast_uchar ";1"); \ 640 l = add_chr_to_str(&a, l, 'm'); \ 641 } \ 642 if (c >= ' ' && c != 127 && c != 155) { \ 643 if (c < 128 || frm) { \ 644 l = add_chr_to_str(&a, l, (unsigned char)c); \ 645 } else { \ 646 /* \ 647 * Linux UTF-8 console is broken and doesn't \ 648 * advance cursor on some characters. So we \ 649 * first print an one-byte replacement, then \ 650 * set the cursor back, then print the UTF-8 \ 651 * character and finally set the cursor again. \ 652 */ \ 653 unsigned char *r; \ 654 r = u2cp(c); \ 655 if (!(r && r[0] >= 32 && r[0] < 127 && !r[1])) \ 656 r = cast_uchar "*"; \ 657 l = add_chr_to_str(&a, l, r[0]); \ 658 if (cx + 1 < term->x) \ 659 l = add_chr_to_str(&a, l, 8); \ 660 else \ 661 SETPOS(cx, y); \ 662 l = add_to_str(&a, l, encode_utf_8(c)); \ 663 SETPOS(cx + 1, y); \ 664 print_next = 1; \ 665 } \ 666 } else if (!c || c == 1) \ 667 l = add_chr_to_str(&a, l, ' '); \ 668 else \ 669 l = add_chr_to_str(&a, l, '.'); \ 670 cx++; \ 671 } 672 673 static void 674 redraw_screen(struct terminal *term) 675 { 676 int x, y, p = 0; 677 int cx = term->lcx, cy = term->lcy; 678 unsigned char *a; 679 int attrib = -1; 680 int mode = -1; 681 int l = 0; 682 int print_next = 0; 683 struct term_spec *s; 684 if (!term->dirty || (term->master && is_blocked())) 685 return; 686 a = NULL; 687 s = term->spec; 688 for (y = 0; y < term->y; y++) { 689 if (!memcmp(&term->screen[p], &term->last_screen[p], 690 sizeof(chr) * term->x)) { 691 p += term->x; 692 continue; 693 } 694 for (x = 0; x < term->x; x++, p++) { 695 int i; 696 if (y == term->y - 1 && x == term->x - 1 697 && term->left_margin + term->x == term->real_x 698 && term->top_margin + term->y == term->real_y) 699 break; 700 if (term->screen[p].ch == term->last_screen[p].ch 701 && term->screen[p].at == term->last_screen[p].at) { 702 /* make sure that padding is identical */ 703 if (chr_has_padding) 704 memcpy(&term->last_screen[p], 705 &term->screen[p], sizeof(chr)); 706 if (print_next) { 707 print_next = 0; 708 goto must_print_next; 709 } 710 continue; 711 } 712 memcpy(&term->last_screen[p], &term->screen[p], 713 sizeof(chr)); 714 must_print_next: 715 if (cx == x && cy == y) 716 goto pc; 717 else if (cy == y && x - cx < 10 && x - cx > 0) { 718 for (i = x - cx; i >= 0; i--) { 719 ppc: 720 PRINT_CHAR(p - i); 721 } 722 } else { 723 SETPOS(x, y); 724 cx = x; 725 cy = y; 726 pc: 727 i = 0; 728 goto ppc; 729 } 730 } 731 if (print_next && term->left_margin + term->x < term->real_x) { 732 l = add_to_str(&a, l, cast_uchar "\033[0m "); 733 attrib = -1; 734 print_next = 0; 735 } 736 } 737 if (l) { 738 if (s->col) 739 l = add_to_str(&a, l, cast_uchar "\033[37;40m"); 740 l = add_to_str(&a, l, cast_uchar "\033[0m"); 741 if (s->mode == TERM_VT100) 742 l = add_to_str(&a, l, cast_uchar "\017"); 743 } 744 term->lcx = cx; 745 term->lcy = cy; 746 if (term->cx != term->lcx || term->cy != term->lcy) { 747 term->lcx = term->cx; 748 term->lcy = term->cy; 749 l = add_to_str(&a, l, cast_uchar "\033["); 750 l = add_num_to_str(&a, l, term->cy + 1 + term->top_margin); 751 l = add_chr_to_str(&a, l, ';'); 752 l = add_num_to_str(&a, l, term->cx + 1 + term->left_margin); 753 l = add_chr_to_str(&a, l, 'H'); 754 } 755 hard_write(term->fdout, a, l); 756 free(a); 757 term->dirty = 0; 758 } 759 760 void 761 redraw_all_terminals(void) 762 { 763 struct terminal *term = NULL; 764 struct list_head *lterm; 765 foreach (struct terminal, term, lterm, terminals) 766 redraw_screen(term); 767 } 768 769 void 770 flush_terminal(struct terminal *term) 771 { 772 redraw_screen(term); 773 } 774 775 void 776 destroy_terminal(void *term_) 777 { 778 struct terminal *term = (struct terminal *)term_; 779 int rs; 780 unregister_bottom_half(destroy_terminal, term); 781 while (!list_empty(term->windows)) { 782 delete_window(list_struct(term->windows.next, struct window)); 783 } 784 del_from_list(term); 785 close_socket(&term->blocked); 786 free(term->title); 787 free(term->screen); 788 free(term->last_screen); 789 free(term->input_queue); 790 set_handlers(term->fdin, NULL, NULL, NULL); 791 EINTRLOOP(rs, close(term->fdin)); 792 if (!term->master) { 793 if (term->fdout != term->fdin) 794 EINTRLOOP(rs, close(term->fdout)); 795 } else { 796 unhandle_terminal_signals(term); 797 free_all_itrms(); 798 if (!list_empty(terminals)) { 799 os_detach_console(); 800 } 801 } 802 if (term->handle_to_close != -1) { 803 hard_write(term->handle_to_close, cast_uchar "x", 1); 804 close_socket(&term->handle_to_close); 805 } 806 free(term); 807 check_if_no_terminal(); 808 } 809 810 void 811 destroy_all_terminals(void) 812 { 813 while (!list_empty(terminals)) { 814 destroy_terminal(list_struct(terminals.next, struct terminal)); 815 } 816 } 817 818 static void 819 check_if_no_terminal(void) 820 { 821 if (!list_empty(terminals)) 822 return; 823 terminate_loop = 1; 824 } 825 826 void 827 set_char(struct terminal *t, int x, int y, unsigned ch, unsigned char at) 828 { 829 t->dirty = 1; 830 if (x >= 0 && x < t->x && y >= 0 && y < t->y) { 831 chr *cc = &t->screen[x + t->x * y]; 832 cc->ch = ch; 833 cc->at = at; 834 } 835 } 836 837 const chr * 838 get_char(struct terminal *t, int x, int y) 839 { 840 int lx, ly; 841 lx = t->x - 1; 842 ly = t->y - 1; 843 if ((lx | ly) < 0) { 844 static const chr empty = { ' ', 070 }; 845 return ∅ 846 } 847 if (x > lx) 848 x = lx; 849 else if (x < 0) 850 x = 0; 851 if (y > ly) 852 y = ly; 853 else if (y < 0) 854 y = 0; 855 return &t->screen[x + t->x * y]; 856 } 857 858 void 859 set_color(struct terminal *t, int x, int y, unsigned char c) 860 { 861 t->dirty = 1; 862 if (x >= 0 && x < t->x && y >= 0 && y < t->y) 863 t->screen[x + t->x * y].at = 864 (t->screen[x + t->x * y].at & ATTR_FRAME) 865 | (c & ~ATTR_FRAME); 866 } 867 868 void 869 set_only_char(struct terminal *t, int x, int y, unsigned ch, unsigned char at) 870 { 871 const chr *cc; 872 t->dirty = 1; 873 cc = get_char(t, x, y); 874 at = (at & ATTR_FRAME) | (cc->at & ~ATTR_FRAME); 875 set_char(t, x, y, ch, at); 876 } 877 878 void 879 set_line(struct terminal *t, int x, int y, int l, chr *line) 880 { 881 int i; 882 chr *cc; 883 t->dirty = 1; 884 if (y < 0 || y >= t->y) 885 return; 886 i = x >= 0 ? 0 : -x; 887 cc = &t->screen[x + i + t->x * y]; 888 line = &line[i]; 889 i = (x + l <= t->x ? l : t->x - x) - i; 890 if (i <= 0) 891 return; 892 memcpy(cc, line, i * sizeof(chr)); 893 } 894 895 void 896 set_line_color(struct terminal *t, int x, int y, int l, unsigned char c) 897 { 898 int i; 899 t->dirty = 1; 900 if (y < 0 || y >= t->y) 901 return; 902 for (i = x >= 0 ? 0 : -x; i < (x + l <= t->x ? l : t->x - x); i++) 903 t->screen[x + i + t->x * y].at = 904 (t->screen[x + i + t->x * y].at & ATTR_FRAME) 905 | (c & ~ATTR_FRAME); 906 } 907 908 void 909 fill_area(struct terminal *t, int x, int y, int xw, int yw, unsigned ch, 910 unsigned char at) 911 { 912 int i; 913 chr *p, *ps; 914 if (x < 0) { 915 xw += x; 916 x = 0; 917 } 918 if (x + xw > t->x) 919 xw = t->x - x; 920 if (xw <= 0) 921 return; 922 if (y < 0) { 923 yw += y; 924 y = 0; 925 } 926 if (y + yw > t->y) 927 yw = t->y - y; 928 if (yw <= 0) 929 return; 930 t->dirty = 1; 931 p = ps = &t->screen[x + t->x * y]; 932 for (i = 0; i < xw; i++) { 933 p->ch = ch; 934 p->at = at; 935 p++; 936 } 937 p = ps; 938 for (i = 1; i < yw; i++) { 939 p += t->x; 940 memcpy(p, ps, xw * sizeof(chr)); 941 } 942 } 943 944 static int p1[] = { 218, 191, 192, 217, 179, 196 }; 945 static int p2[] = { 201, 187, 200, 188, 186, 205 }; 946 947 void 948 draw_frame(struct terminal *t, int x, int y, int xw, int yw, unsigned char c, 949 int w) 950 { 951 int *p = w > 1 ? p2 : p1; 952 c |= ATTR_FRAME; 953 set_char(t, x, y, p[0], c); 954 set_char(t, x + xw - 1, y, p[1], c); 955 set_char(t, x, y + yw - 1, p[2], c); 956 set_char(t, x + xw - 1, y + yw - 1, p[3], c); 957 fill_area(t, x, y + 1, 1, yw - 2, p[4], c); 958 fill_area(t, x + xw - 1, y + 1, 1, yw - 2, p[4], c); 959 fill_area(t, x + 1, y, xw - 2, 1, p[5], c); 960 fill_area(t, x + 1, y + yw - 1, xw - 2, 1, p[5], c); 961 } 962 963 void 964 print_text(struct terminal *t, int x, int y, int l, unsigned char *text, 965 unsigned char c) 966 { 967 for (; l--; x++) { 968 unsigned u = GET_TERM_CHAR(t, &text); 969 if (!u) 970 break; 971 set_char(t, x, y, u, c); 972 } 973 } 974 975 void 976 set_cursor(struct terminal *term, int x, int y, int altx, int alty) 977 { 978 term->dirty = 1; 979 if (term->spec->block_cursor) { 980 x = altx; 981 y = alty; 982 } 983 if (x >= term->x) 984 x = term->x - 1; 985 if (y >= term->y) 986 y = term->y - 1; 987 if (x < 0) 988 x = 0; 989 if (y < 0) 990 y = 0; 991 term->cx = x; 992 term->cy = y; 993 } 994 995 static void 996 exec_thread(void *path_, int p) 997 { 998 char *path = path_; 999 int rs; 1000 if (path[0] == 2) 1001 EINTRLOOP(rs, setpgid(0, 0)); 1002 exe(path + 1, path[0]); 1003 if (path[1 + strlen(path + 1) + 1]) 1004 EINTRLOOP(rs, unlink(path + 1 + strlen(path + 1) + 1)); 1005 } 1006 1007 static void 1008 close_handle(void *p) 1009 { 1010 int *h = p; 1011 close_socket(h); 1012 } 1013 1014 static void 1015 unblock_terminal(void *term_) 1016 { 1017 struct terminal *term = (struct terminal *)term_; 1018 close_handle(&term->blocked); 1019 term->blocked = -1; 1020 set_handlers(term->fdin, in_term, NULL, term); 1021 unblock_itrm(term->fdin); 1022 /* clear the dirty flag because unblock_itrm queued a resize 1023 event - so avoid double redraw */ 1024 term->dirty = 0; 1025 } 1026 1027 void 1028 exec_on_terminal(struct terminal *term, unsigned char *path, 1029 unsigned char *delet, unsigned char fg) 1030 { 1031 int rs; 1032 if (path && !*path) 1033 return; 1034 if (!path) 1035 path = cast_uchar ""; 1036 if (term->master) { 1037 if (!*path) { 1038 dispatch_special(delet); 1039 } else { 1040 int blockh; 1041 unsigned char *param; 1042 size_t paraml; 1043 if (is_blocked() && fg) { 1044 if (*delet) 1045 EINTRLOOP( 1046 rs, unlink(cast_const_char delet)); 1047 return; 1048 } 1049 param = NULL; 1050 paraml = add_chr_to_str(¶m, 0, fg); 1051 paraml = add_to_str(¶m, paraml, path); 1052 paraml = add_chr_to_str(¶m, paraml, 0); 1053 paraml = add_to_str(¶m, paraml, delet); 1054 if (fg == 1) 1055 block_itrm(term->fdin); 1056 if ((blockh = start_thread(exec_thread, param, 1057 paraml + 1, *delet != 0)) 1058 == -1) { 1059 if (fg == 1) 1060 unblock_itrm(term->fdin); 1061 free(param); 1062 return; 1063 } 1064 free(param); 1065 if (fg == 1) { 1066 term->blocked = blockh; 1067 set_handlers(blockh, unblock_terminal, NULL, 1068 term); 1069 set_handlers(term->fdin, NULL, NULL, term); 1070 } else { 1071 set_handlers(blockh, close_handle, NULL, 1072 &blockh); 1073 } 1074 } 1075 } else { 1076 unsigned char *data; 1077 size_t datal; 1078 data = NULL; 1079 datal = add_chr_to_str(&data, 0, 0); 1080 datal = add_chr_to_str(&data, datal, fg); 1081 datal = add_to_str(&data, datal, path); 1082 datal = add_chr_to_str(&data, datal, 0); 1083 datal = add_to_str(&data, datal, delet); 1084 hard_write(term->fdout, data, datal + 1); 1085 free(data); 1086 } 1087 } 1088 1089 void 1090 do_terminal_function(struct terminal *term, unsigned char code, 1091 unsigned char *data) 1092 { 1093 unsigned char *x_data; 1094 size_t x_datal; 1095 x_data = NULL; 1096 x_datal = add_chr_to_str(&x_data, 0, code); 1097 x_datal = add_to_str(&x_data, x_datal, data); 1098 exec_on_terminal(term, NULL, x_data, 0); 1099 free(x_data); 1100 } 1101 1102 void 1103 set_terminal_title(struct terminal *term, unsigned char *title) 1104 { 1105 if (strlen(cast_const_char title) > 10000) 1106 title[10000] = 0; 1107 if (strchr(cast_const_char title, 1)) { 1108 unsigned char *a, *b; 1109 for (a = title, b = title; *a; a++) 1110 if (*a != 1) 1111 *b++ = *a; 1112 *b = 0; 1113 } 1114 if (term->title 1115 && !strcmp(cast_const_char title, cast_const_char term->title)) 1116 goto ret; 1117 free(term->title); 1118 term->title = stracpy(title); 1119 do_terminal_function(term, TERM_FN_TITLE, title); 1120 ret: 1121 free(title); 1122 } 1123 1124 struct terminal * 1125 find_terminal(tcount count) 1126 { 1127 struct terminal *term = NULL; 1128 struct list_head *lterm; 1129 foreach (struct terminal, term, lterm, terminals) 1130 if (term->count == count) 1131 return term; 1132 return NULL; 1133 }