1 /* $OpenBSD: window.c,v 1.235 2019/06/20 13:40:22 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/ioctl.h> 21 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <fnmatch.h> 25 #include <regex.h> 26 #include <signal.h> 27 #include <stdint.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <time.h> 31 #include <unistd.h> 32 #include <util.h> 33 #include <vis.h> 34 35 #include "tmux.h" 36 37 /* 38 * Each window is attached to a number of panes, each of which is a pty. This 39 * file contains code to handle them. 40 * 41 * A pane has two buffers attached, these are filled and emptied by the main 42 * server poll loop. Output data is received from pty's in screen format, 43 * translated and returned as a series of escape sequences and strings via 44 * input_parse (in input.c). Input data is received as key codes and written 45 * directly via input_key. 46 * 47 * Each pane also has a "virtual" screen (screen.c) which contains the current 48 * state and is redisplayed when the window is reattached to a client. 49 * 50 * Windows are stored directly on a global array and wrapped in any number of 51 * winlink structs to be linked onto local session RB trees. A reference count 52 * is maintained and a window removed from the global list and destroyed when 53 * it reaches zero. 54 */ 55 56 /* Global window list. */ 57 struct windows windows; 58 59 /* Global panes tree. */ 60 struct window_pane_tree all_window_panes; 61 static u_int next_window_pane_id; 62 static u_int next_window_id; 63 static u_int next_active_point; 64 65 /* List of window modes. */ 66 const struct window_mode *all_window_modes[] = { 67 &window_buffer_mode, 68 &window_client_mode, 69 &window_clock_mode, 70 &window_copy_mode, 71 &window_tree_mode, 72 &window_view_mode, 73 NULL 74 }; 75 76 struct window_pane_input_data { 77 struct cmdq_item *item; 78 u_int wp; 79 }; 80 81 static struct window_pane *window_pane_create(struct window *, u_int, u_int, 82 u_int); 83 static void window_pane_destroy(struct window_pane *); 84 85 RB_GENERATE(windows, window, entry, window_cmp); 86 RB_GENERATE(winlinks, winlink, entry, winlink_cmp); 87 RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp); 88 89 int 90 window_cmp(struct window *w1, struct window *w2) 91 { 92 return (w1->id - w2->id); 93 } 94 95 int 96 winlink_cmp(struct winlink *wl1, struct winlink *wl2) 97 { 98 return (wl1->idx - wl2->idx); 99 } 100 101 int 102 window_pane_cmp(struct window_pane *wp1, struct window_pane *wp2) 103 { 104 return (wp1->id - wp2->id); 105 } 106 107 struct winlink * 108 winlink_find_by_window(struct winlinks *wwl, struct window *w) 109 { 110 struct winlink *wl; 111 112 RB_FOREACH(wl, winlinks, wwl) { 113 if (wl->window == w) 114 return (wl); 115 } 116 117 return (NULL); 118 } 119 120 struct winlink * 121 winlink_find_by_index(struct winlinks *wwl, int idx) 122 { 123 struct winlink wl; 124 125 if (idx < 0) 126 fatalx("bad index"); 127 128 wl.idx = idx; 129 return (RB_FIND(winlinks, wwl, &wl)); 130 } 131 132 struct winlink * 133 winlink_find_by_window_id(struct winlinks *wwl, u_int id) 134 { 135 struct winlink *wl; 136 137 RB_FOREACH(wl, winlinks, wwl) { 138 if (wl->window->id == id) 139 return (wl); 140 } 141 return (NULL); 142 } 143 144 static int 145 winlink_next_index(struct winlinks *wwl, int idx) 146 { 147 int i; 148 149 i = idx; 150 do { 151 if (winlink_find_by_index(wwl, i) == NULL) 152 return (i); 153 if (i == INT_MAX) 154 i = 0; 155 else 156 i++; 157 } while (i != idx); 158 return (-1); 159 } 160 161 u_int 162 winlink_count(struct winlinks *wwl) 163 { 164 struct winlink *wl; 165 u_int n; 166 167 n = 0; 168 RB_FOREACH(wl, winlinks, wwl) 169 n++; 170 171 return (n); 172 } 173 174 struct winlink * 175 winlink_add(struct winlinks *wwl, int idx) 176 { 177 struct winlink *wl; 178 179 if (idx < 0) { 180 if ((idx = winlink_next_index(wwl, -idx - 1)) == -1) 181 return (NULL); 182 } else if (winlink_find_by_index(wwl, idx) != NULL) 183 return (NULL); 184 185 wl = xcalloc(1, sizeof *wl); 186 wl->idx = idx; 187 RB_INSERT(winlinks, wwl, wl); 188 189 return (wl); 190 } 191 192 void 193 winlink_set_window(struct winlink *wl, struct window *w) 194 { 195 if (wl->window != NULL) { 196 TAILQ_REMOVE(&wl->window->winlinks, wl, wentry); 197 window_remove_ref(wl->window, __func__); 198 } 199 TAILQ_INSERT_TAIL(&w->winlinks, wl, wentry); 200 wl->window = w; 201 window_add_ref(w, __func__); 202 } 203 204 void 205 winlink_remove(struct winlinks *wwl, struct winlink *wl) 206 { 207 struct window *w = wl->window; 208 209 if (w != NULL) { 210 TAILQ_REMOVE(&w->winlinks, wl, wentry); 211 window_remove_ref(w, __func__); 212 } 213 214 RB_REMOVE(winlinks, wwl, wl); 215 free(wl); 216 } 217 218 struct winlink * 219 winlink_next(struct winlink *wl) 220 { 221 return (RB_NEXT(winlinks, wwl, wl)); 222 } 223 224 struct winlink * 225 winlink_previous(struct winlink *wl) 226 { 227 return (RB_PREV(winlinks, wwl, wl)); 228 } 229 230 struct winlink * 231 winlink_next_by_number(struct winlink *wl, struct session *s, int n) 232 { 233 for (; n > 0; n--) { 234 if ((wl = RB_NEXT(winlinks, wwl, wl)) == NULL) 235 wl = RB_MIN(winlinks, &s->windows); 236 } 237 238 return (wl); 239 } 240 241 struct winlink * 242 winlink_previous_by_number(struct winlink *wl, struct session *s, int n) 243 { 244 for (; n > 0; n--) { 245 if ((wl = RB_PREV(winlinks, wwl, wl)) == NULL) 246 wl = RB_MAX(winlinks, &s->windows); 247 } 248 249 return (wl); 250 } 251 252 void 253 winlink_stack_push(struct winlink_stack *stack, struct winlink *wl) 254 { 255 if (wl == NULL) 256 return; 257 258 winlink_stack_remove(stack, wl); 259 TAILQ_INSERT_HEAD(stack, wl, sentry); 260 } 261 262 void 263 winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl) 264 { 265 struct winlink *wl2; 266 267 if (wl == NULL) 268 return; 269 270 TAILQ_FOREACH(wl2, stack, sentry) { 271 if (wl2 == wl) { 272 TAILQ_REMOVE(stack, wl, sentry); 273 return; 274 } 275 } 276 } 277 278 struct window * 279 window_find_by_id_str(const char *s) 280 { 281 const char *errstr; 282 u_int id; 283 284 if (*s != '@') 285 return (NULL); 286 287 id = strtonum(s + 1, 0, UINT_MAX, &errstr); 288 if (errstr != NULL) 289 return (NULL); 290 return (window_find_by_id(id)); 291 } 292 293 struct window * 294 window_find_by_id(u_int id) 295 { 296 struct window w; 297 298 w.id = id; 299 return (RB_FIND(windows, &windows, &w)); 300 } 301 302 void 303 window_update_activity(struct window *w) 304 { 305 gettimeofday(&w->activity_time, NULL); 306 alerts_queue(w, WINDOW_ACTIVITY); 307 } 308 309 struct window * 310 window_create(u_int sx, u_int sy) 311 { 312 struct window *w; 313 314 w = xcalloc(1, sizeof *w); 315 w->name = NULL; 316 w->flags = 0; 317 318 TAILQ_INIT(&w->panes); 319 w->active = NULL; 320 321 w->lastlayout = -1; 322 w->layout_root = NULL; 323 324 w->sx = sx; 325 w->sy = sy; 326 327 w->options = options_create(global_w_options); 328 329 w->references = 0; 330 TAILQ_INIT(&w->winlinks); 331 332 w->id = next_window_id++; 333 RB_INSERT(windows, &windows, w); 334 335 window_update_activity(w); 336 337 return (w); 338 } 339 340 void 341 window_destroy(struct window *w) 342 { 343 log_debug("window @%u destroyed (%d references)", w->id, w->references); 344 345 RB_REMOVE(windows, &windows, w); 346 347 if (w->layout_root != NULL) 348 layout_free_cell(w->layout_root); 349 if (w->saved_layout_root != NULL) 350 layout_free_cell(w->saved_layout_root); 351 free(w->old_layout); 352 353 window_destroy_panes(w); 354 355 if (event_initialized(&w->name_event)) 356 evtimer_del(&w->name_event); 357 358 if (event_initialized(&w->alerts_timer)) 359 evtimer_del(&w->alerts_timer); 360 if (event_initialized(&w->offset_timer)) 361 event_del(&w->offset_timer); 362 363 options_free(w->options); 364 365 free(w->name); 366 free(w); 367 } 368 369 int 370 window_pane_destroy_ready(struct window_pane *wp) 371 { 372 int n; 373 374 if (wp->pipe_fd != -1) { 375 if (EVBUFFER_LENGTH(wp->pipe_event->output) != 0) 376 return (0); 377 if (ioctl(wp->fd, FIONREAD, &n) != -1 && n > 0) 378 return (0); 379 } 380 381 if (~wp->flags & PANE_EXITED) 382 return (0); 383 return (1); 384 } 385 386 void 387 window_add_ref(struct window *w, const char *from) 388 { 389 w->references++; 390 log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references); 391 } 392 393 void 394 window_remove_ref(struct window *w, const char *from) 395 { 396 w->references--; 397 log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references); 398 399 if (w->references == 0) 400 window_destroy(w); 401 } 402 403 void 404 window_set_name(struct window *w, const char *new_name) 405 { 406 free(w->name); 407 utf8_stravis(&w->name, new_name, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); 408 notify_window("window-renamed", w); 409 } 410 411 void 412 window_resize(struct window *w, u_int sx, u_int sy) 413 { 414 w->sx = sx; 415 w->sy = sy; 416 } 417 418 int 419 window_has_pane(struct window *w, struct window_pane *wp) 420 { 421 struct window_pane *wp1; 422 423 TAILQ_FOREACH(wp1, &w->panes, entry) { 424 if (wp1 == wp) 425 return (1); 426 } 427 return (0); 428 } 429 430 int 431 window_set_active_pane(struct window *w, struct window_pane *wp, int notify) 432 { 433 log_debug("%s: pane %%%u", __func__, wp->id); 434 435 if (wp == w->active) 436 return (0); 437 w->last = w->active; 438 439 w->active = wp; 440 w->active->active_point = next_active_point++; 441 w->active->flags |= PANE_CHANGED; 442 443 tty_update_window_offset(w); 444 445 if (notify) 446 notify_window("window-pane-changed", w); 447 return (1); 448 } 449 450 void 451 window_redraw_active_switch(struct window *w, struct window_pane *wp) 452 { 453 struct style *sy1, *sy2; 454 int c1, c2; 455 456 if (wp == w->active) 457 return; 458 459 for (;;) { 460 /* 461 * If the active and inactive styles or palettes are different, 462 * need to redraw the panes. 463 */ 464 sy1 = &wp->cached_style; 465 sy2 = &wp->cached_active_style; 466 if (!style_equal(sy1, sy2)) 467 wp->flags |= PANE_REDRAW; 468 else { 469 c1 = window_pane_get_palette(wp, sy1->gc.fg); 470 c2 = window_pane_get_palette(wp, sy2->gc.fg); 471 if (c1 != c2) 472 wp->flags |= PANE_REDRAW; 473 else { 474 c1 = window_pane_get_palette(wp, sy1->gc.bg); 475 c2 = window_pane_get_palette(wp, sy2->gc.bg); 476 if (c1 != c2) 477 wp->flags |= PANE_REDRAW; 478 } 479 } 480 if (wp == w->active) 481 break; 482 wp = w->active; 483 } 484 } 485 486 struct window_pane * 487 window_get_active_at(struct window *w, u_int x, u_int y) 488 { 489 struct window_pane *wp; 490 491 TAILQ_FOREACH(wp, &w->panes, entry) { 492 if (!window_pane_visible(wp)) 493 continue; 494 if (x < wp->xoff || x > wp->xoff + wp->sx) 495 continue; 496 if (y < wp->yoff || y > wp->yoff + wp->sy) 497 continue; 498 return (wp); 499 } 500 return (NULL); 501 } 502 503 struct window_pane * 504 window_find_string(struct window *w, const char *s) 505 { 506 u_int x, y; 507 508 x = w->sx / 2; 509 y = w->sy / 2; 510 511 if (strcasecmp(s, "top") == 0) 512 y = 0; 513 else if (strcasecmp(s, "bottom") == 0) 514 y = w->sy - 1; 515 else if (strcasecmp(s, "left") == 0) 516 x = 0; 517 else if (strcasecmp(s, "right") == 0) 518 x = w->sx - 1; 519 else if (strcasecmp(s, "top-left") == 0) { 520 x = 0; 521 y = 0; 522 } else if (strcasecmp(s, "top-right") == 0) { 523 x = w->sx - 1; 524 y = 0; 525 } else if (strcasecmp(s, "bottom-left") == 0) { 526 x = 0; 527 y = w->sy - 1; 528 } else if (strcasecmp(s, "bottom-right") == 0) { 529 x = w->sx - 1; 530 y = w->sy - 1; 531 } else 532 return (NULL); 533 534 return (window_get_active_at(w, x, y)); 535 } 536 537 int 538 window_zoom(struct window_pane *wp) 539 { 540 struct window *w = wp->window; 541 struct window_pane *wp1; 542 543 if (w->flags & WINDOW_ZOOMED) 544 return (-1); 545 546 if (window_count_panes(w) == 1) 547 return (-1); 548 549 if (w->active != wp) 550 window_set_active_pane(w, wp, 1); 551 552 TAILQ_FOREACH(wp1, &w->panes, entry) { 553 wp1->saved_layout_cell = wp1->layout_cell; 554 wp1->layout_cell = NULL; 555 } 556 557 w->saved_layout_root = w->layout_root; 558 layout_init(w, wp); 559 w->flags |= WINDOW_ZOOMED; 560 notify_window("window-layout-changed", w); 561 562 return (0); 563 } 564 565 int 566 window_unzoom(struct window *w) 567 { 568 struct window_pane *wp; 569 570 if (!(w->flags & WINDOW_ZOOMED)) 571 return (-1); 572 573 w->flags &= ~WINDOW_ZOOMED; 574 layout_free(w); 575 w->layout_root = w->saved_layout_root; 576 w->saved_layout_root = NULL; 577 578 TAILQ_FOREACH(wp, &w->panes, entry) { 579 wp->layout_cell = wp->saved_layout_cell; 580 wp->saved_layout_cell = NULL; 581 } 582 layout_fix_panes(w); 583 notify_window("window-layout-changed", w); 584 585 return (0); 586 } 587 588 struct window_pane * 589 window_add_pane(struct window *w, struct window_pane *other, u_int hlimit, 590 int flags) 591 { 592 struct window_pane *wp; 593 594 if (other == NULL) 595 other = w->active; 596 597 wp = window_pane_create(w, w->sx, w->sy, hlimit); 598 if (TAILQ_EMPTY(&w->panes)) { 599 log_debug("%s: @%u at start", __func__, w->id); 600 TAILQ_INSERT_HEAD(&w->panes, wp, entry); 601 } else if (flags & SPAWN_BEFORE) { 602 log_debug("%s: @%u before %%%u", __func__, w->id, wp->id); 603 if (flags & SPAWN_FULLSIZE) 604 TAILQ_INSERT_HEAD(&w->panes, wp, entry); 605 else 606 TAILQ_INSERT_BEFORE(other, wp, entry); 607 } else { 608 log_debug("%s: @%u after %%%u", __func__, w->id, wp->id); 609 if (flags & SPAWN_FULLSIZE) 610 TAILQ_INSERT_TAIL(&w->panes, wp, entry); 611 else 612 TAILQ_INSERT_AFTER(&w->panes, other, wp, entry); 613 } 614 return (wp); 615 } 616 617 void 618 window_lost_pane(struct window *w, struct window_pane *wp) 619 { 620 log_debug("%s: @%u pane %%%u", __func__, w->id, wp->id); 621 622 if (wp == marked_pane.wp) 623 server_clear_marked(); 624 625 if (wp == w->active) { 626 w->active = w->last; 627 w->last = NULL; 628 if (w->active == NULL) { 629 w->active = TAILQ_PREV(wp, window_panes, entry); 630 if (w->active == NULL) 631 w->active = TAILQ_NEXT(wp, entry); 632 } 633 if (w->active != NULL) { 634 w->active->flags |= PANE_CHANGED; 635 notify_window("window-pane-changed", w); 636 } 637 } else if (wp == w->last) 638 w->last = NULL; 639 } 640 641 void 642 window_remove_pane(struct window *w, struct window_pane *wp) 643 { 644 window_lost_pane(w, wp); 645 646 TAILQ_REMOVE(&w->panes, wp, entry); 647 window_pane_destroy(wp); 648 } 649 650 struct window_pane * 651 window_pane_at_index(struct window *w, u_int idx) 652 { 653 struct window_pane *wp; 654 u_int n; 655 656 n = options_get_number(w->options, "pane-base-index"); 657 TAILQ_FOREACH(wp, &w->panes, entry) { 658 if (n == idx) 659 return (wp); 660 n++; 661 } 662 return (NULL); 663 } 664 665 struct window_pane * 666 window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n) 667 { 668 for (; n > 0; n--) { 669 if ((wp = TAILQ_NEXT(wp, entry)) == NULL) 670 wp = TAILQ_FIRST(&w->panes); 671 } 672 673 return (wp); 674 } 675 676 struct window_pane * 677 window_pane_previous_by_number(struct window *w, struct window_pane *wp, 678 u_int n) 679 { 680 for (; n > 0; n--) { 681 if ((wp = TAILQ_PREV(wp, window_panes, entry)) == NULL) 682 wp = TAILQ_LAST(&w->panes, window_panes); 683 } 684 685 return (wp); 686 } 687 688 int 689 window_pane_index(struct window_pane *wp, u_int *i) 690 { 691 struct window_pane *wq; 692 struct window *w = wp->window; 693 694 *i = options_get_number(w->options, "pane-base-index"); 695 TAILQ_FOREACH(wq, &w->panes, entry) { 696 if (wp == wq) { 697 return (0); 698 } 699 (*i)++; 700 } 701 702 return (-1); 703 } 704 705 u_int 706 window_count_panes(struct window *w) 707 { 708 struct window_pane *wp; 709 u_int n; 710 711 n = 0; 712 TAILQ_FOREACH(wp, &w->panes, entry) 713 n++; 714 return (n); 715 } 716 717 void 718 window_destroy_panes(struct window *w) 719 { 720 struct window_pane *wp; 721 722 while (!TAILQ_EMPTY(&w->panes)) { 723 wp = TAILQ_FIRST(&w->panes); 724 TAILQ_REMOVE(&w->panes, wp, entry); 725 window_pane_destroy(wp); 726 } 727 } 728 729 const char * 730 window_printable_flags(struct winlink *wl) 731 { 732 struct session *s = wl->session; 733 static char flags[32]; 734 int pos; 735 736 pos = 0; 737 if (wl->flags & WINLINK_ACTIVITY) 738 flags[pos++] = '#'; 739 if (wl->flags & WINLINK_BELL) 740 flags[pos++] = '!'; 741 if (wl->flags & WINLINK_SILENCE) 742 flags[pos++] = '~'; 743 if (wl == s->curw) 744 flags[pos++] = '*'; 745 if (wl == TAILQ_FIRST(&s->lastw)) 746 flags[pos++] = '-'; 747 if (server_check_marked() && wl == marked_pane.wl) 748 flags[pos++] = 'M'; 749 if (wl->window->flags & WINDOW_ZOOMED) 750 flags[pos++] = 'Z'; 751 flags[pos] = '\0'; 752 return (flags); 753 } 754 755 struct window_pane * 756 window_pane_find_by_id_str(const char *s) 757 { 758 const char *errstr; 759 u_int id; 760 761 if (*s != '%') 762 return (NULL); 763 764 id = strtonum(s + 1, 0, UINT_MAX, &errstr); 765 if (errstr != NULL) 766 return (NULL); 767 return (window_pane_find_by_id(id)); 768 } 769 770 struct window_pane * 771 window_pane_find_by_id(u_int id) 772 { 773 struct window_pane wp; 774 775 wp.id = id; 776 return (RB_FIND(window_pane_tree, &all_window_panes, &wp)); 777 } 778 779 static struct window_pane * 780 window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) 781 { 782 struct window_pane *wp; 783 char host[HOST_NAME_MAX + 1]; 784 785 wp = xcalloc(1, sizeof *wp); 786 wp->window = w; 787 wp->options = options_create(w->options); 788 wp->flags = PANE_STYLECHANGED; 789 790 wp->id = next_window_pane_id++; 791 RB_INSERT(window_pane_tree, &all_window_panes, wp); 792 793 wp->argc = 0; 794 wp->argv = NULL; 795 wp->shell = NULL; 796 wp->cwd = NULL; 797 798 wp->fd = -1; 799 wp->event = NULL; 800 801 TAILQ_INIT(&wp->modes); 802 803 wp->layout_cell = NULL; 804 805 wp->xoff = 0; 806 wp->yoff = 0; 807 808 wp->sx = wp->osx = sx; 809 wp->sy = wp->osx = sy; 810 811 wp->pipe_fd = -1; 812 wp->pipe_off = 0; 813 wp->pipe_event = NULL; 814 815 wp->saved_grid = NULL; 816 wp->saved_cx = UINT_MAX; 817 wp->saved_cy = UINT_MAX; 818 819 screen_init(&wp->base, sx, sy, hlimit); 820 wp->screen = &wp->base; 821 822 screen_init(&wp->status_screen, 1, 1, 0); 823 824 if (gethostname(host, sizeof host) == 0) 825 screen_set_title(&wp->base, host); 826 827 input_init(wp); 828 829 return (wp); 830 } 831 832 static void 833 window_pane_destroy(struct window_pane *wp) 834 { 835 window_pane_reset_mode_all(wp); 836 free(wp->searchstr); 837 838 if (wp->fd != -1) { 839 bufferevent_free(wp->event); 840 close(wp->fd); 841 } 842 843 input_free(wp); 844 845 screen_free(&wp->status_screen); 846 847 screen_free(&wp->base); 848 if (wp->saved_grid != NULL) 849 grid_destroy(wp->saved_grid); 850 851 if (wp->pipe_fd != -1) { 852 bufferevent_free(wp->pipe_event); 853 close(wp->pipe_fd); 854 } 855 856 if (event_initialized(&wp->resize_timer)) 857 event_del(&wp->resize_timer); 858 859 RB_REMOVE(window_pane_tree, &all_window_panes, wp); 860 861 options_free(wp->options); 862 free((void *)wp->cwd); 863 free(wp->shell); 864 cmd_free_argv(wp->argc, wp->argv); 865 free(wp->palette); 866 free(wp); 867 } 868 869 static void 870 window_pane_read_callback(__unused struct bufferevent *bufev, void *data) 871 { 872 struct window_pane *wp = data; 873 struct evbuffer *evb = wp->event->input; 874 size_t size = EVBUFFER_LENGTH(evb); 875 char *new_data; 876 size_t new_size; 877 878 new_size = size - wp->pipe_off; 879 if (wp->pipe_fd != -1 && new_size > 0) { 880 new_data = EVBUFFER_DATA(evb) + wp->pipe_off; 881 bufferevent_write(wp->pipe_event, new_data, new_size); 882 } 883 884 log_debug("%%%u has %zu bytes", wp->id, size); 885 input_parse(wp); 886 887 wp->pipe_off = EVBUFFER_LENGTH(evb); 888 } 889 890 static void 891 window_pane_error_callback(__unused struct bufferevent *bufev, 892 __unused short what, void *data) 893 { 894 struct window_pane *wp = data; 895 896 log_debug("%%%u error", wp->id); 897 wp->flags |= PANE_EXITED; 898 899 if (window_pane_destroy_ready(wp)) 900 server_destroy_pane(wp, 1); 901 } 902 903 void 904 window_pane_set_event(struct window_pane *wp) 905 { 906 setblocking(wp->fd, 0); 907 908 wp->event = bufferevent_new(wp->fd, window_pane_read_callback, 909 NULL, window_pane_error_callback, wp); 910 911 bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); 912 bufferevent_enable(wp->event, EV_READ|EV_WRITE); 913 } 914 915 void 916 window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) 917 { 918 struct window_mode_entry *wme; 919 920 if (sx == wp->sx && sy == wp->sy) 921 return; 922 wp->sx = sx; 923 wp->sy = sy; 924 925 screen_resize(&wp->base, sx, sy, wp->saved_grid == NULL); 926 927 wme = TAILQ_FIRST(&wp->modes); 928 if (wme != NULL && wme->mode->resize != NULL) 929 wme->mode->resize(wme, sx, sy); 930 931 wp->flags |= PANE_RESIZE; 932 } 933 934 /* 935 * Enter alternative screen mode. A copy of the visible screen is saved and the 936 * history is not updated 937 */ 938 void 939 window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc, 940 int cursor) 941 { 942 struct screen *s = &wp->base; 943 u_int sx, sy; 944 945 if (wp->saved_grid != NULL) 946 return; 947 if (!options_get_number(wp->options, "alternate-screen")) 948 return; 949 sx = screen_size_x(s); 950 sy = screen_size_y(s); 951 952 wp->saved_grid = grid_create(sx, sy, 0); 953 grid_duplicate_lines(wp->saved_grid, 0, s->grid, screen_hsize(s), sy); 954 if (cursor) { 955 wp->saved_cx = s->cx; 956 wp->saved_cy = s->cy; 957 } 958 memcpy(&wp->saved_cell, gc, sizeof wp->saved_cell); 959 960 grid_view_clear(s->grid, 0, 0, sx, sy, 8); 961 962 wp->base.grid->flags &= ~GRID_HISTORY; 963 964 wp->flags |= PANE_REDRAW; 965 } 966 967 /* Exit alternate screen mode and restore the copied grid. */ 968 void 969 window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc, 970 int cursor) 971 { 972 struct screen *s = &wp->base; 973 u_int sx, sy; 974 975 if (!options_get_number(wp->options, "alternate-screen")) 976 return; 977 978 /* 979 * Restore the cursor position and cell. This happens even if not 980 * currently in the alternate screen. 981 */ 982 if (cursor && wp->saved_cx != UINT_MAX && wp->saved_cy != UINT_MAX) { 983 s->cx = wp->saved_cx; 984 if (s->cx > screen_size_x(s) - 1) 985 s->cx = screen_size_x(s) - 1; 986 s->cy = wp->saved_cy; 987 if (s->cy > screen_size_y(s) - 1) 988 s->cy = screen_size_y(s) - 1; 989 memcpy(gc, &wp->saved_cell, sizeof *gc); 990 } 991 992 if (wp->saved_grid == NULL) 993 return; 994 sx = screen_size_x(s); 995 sy = screen_size_y(s); 996 997 /* 998 * If the current size is bigger, temporarily resize to the old size 999 * before copying back. 1000 */ 1001 if (sy > wp->saved_grid->sy) 1002 screen_resize(s, sx, wp->saved_grid->sy, 1); 1003 1004 /* Restore the saved grid. */ 1005 grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy); 1006 1007 /* 1008 * Turn history back on (so resize can use it) and then resize back to 1009 * the current size. 1010 */ 1011 wp->base.grid->flags |= GRID_HISTORY; 1012 if (sy > wp->saved_grid->sy || sx != wp->saved_grid->sx) 1013 screen_resize(s, sx, sy, 1); 1014 1015 grid_destroy(wp->saved_grid); 1016 wp->saved_grid = NULL; 1017 1018 wp->flags |= PANE_REDRAW; 1019 } 1020 1021 void 1022 window_pane_set_palette(struct window_pane *wp, u_int n, int colour) 1023 { 1024 if (n > 0xff) 1025 return; 1026 1027 if (wp->palette == NULL) 1028 wp->palette = xcalloc(0x100, sizeof *wp->palette); 1029 1030 wp->palette[n] = colour; 1031 wp->flags |= PANE_REDRAW; 1032 } 1033 1034 void 1035 window_pane_unset_palette(struct window_pane *wp, u_int n) 1036 { 1037 if (n > 0xff || wp->palette == NULL) 1038 return; 1039 1040 wp->palette[n] = 0; 1041 wp->flags |= PANE_REDRAW; 1042 } 1043 1044 void 1045 window_pane_reset_palette(struct window_pane *wp) 1046 { 1047 if (wp->palette == NULL) 1048 return; 1049 1050 free(wp->palette); 1051 wp->palette = NULL; 1052 wp->flags |= PANE_REDRAW; 1053 } 1054 1055 int 1056 window_pane_get_palette(struct window_pane *wp, int c) 1057 { 1058 int new; 1059 1060 if (wp == NULL || wp->palette == NULL) 1061 return (-1); 1062 1063 new = -1; 1064 if (c < 8) 1065 new = wp->palette[c]; 1066 else if (c >= 90 && c <= 97) 1067 new = wp->palette[8 + c - 90]; 1068 else if (c & COLOUR_FLAG_256) 1069 new = wp->palette[c & ~COLOUR_FLAG_256]; 1070 if (new == 0) 1071 return (-1); 1072 return (new); 1073 } 1074 1075 static void 1076 window_pane_mode_timer(__unused int fd, __unused short events, void *arg) 1077 { 1078 struct window_pane *wp = arg; 1079 struct timeval tv = { .tv_sec = 10 }; 1080 int n = 0; 1081 1082 evtimer_del(&wp->modetimer); 1083 evtimer_add(&wp->modetimer, &tv); 1084 1085 log_debug("%%%u in mode: last=%ld", wp->id, (long)wp->modelast); 1086 1087 if (wp->modelast < time(NULL) - WINDOW_MODE_TIMEOUT) { 1088 if (ioctl(wp->fd, FIONREAD, &n) == -1 || n > 0) 1089 window_pane_reset_mode_all(wp); 1090 } 1091 } 1092 1093 int 1094 window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode, 1095 struct cmd_find_state *fs, struct args *args) 1096 { 1097 struct timeval tv = { .tv_sec = 10 }; 1098 struct window_mode_entry *wme; 1099 1100 if (!TAILQ_EMPTY(&wp->modes) && TAILQ_FIRST(&wp->modes)->mode == mode) 1101 return (1); 1102 1103 wp->modelast = time(NULL); 1104 if (TAILQ_EMPTY(&wp->modes)) { 1105 evtimer_set(&wp->modetimer, window_pane_mode_timer, wp); 1106 evtimer_add(&wp->modetimer, &tv); 1107 } 1108 1109 TAILQ_FOREACH(wme, &wp->modes, entry) { 1110 if (wme->mode == mode) 1111 break; 1112 } 1113 if (wme != NULL) { 1114 TAILQ_REMOVE(&wp->modes, wme, entry); 1115 TAILQ_INSERT_HEAD(&wp->modes, wme, entry); 1116 } else { 1117 wme = xcalloc(1, sizeof *wme); 1118 wme->wp = wp; 1119 wme->mode = mode; 1120 wme->prefix = 1; 1121 TAILQ_INSERT_HEAD(&wp->modes, wme, entry); 1122 wme->screen = wme->mode->init(wme, fs, args); 1123 } 1124 1125 wp->screen = wme->screen; 1126 wp->flags |= (PANE_REDRAW|PANE_CHANGED); 1127 1128 server_status_window(wp->window); 1129 notify_pane("pane-mode-changed", wp); 1130 1131 return (0); 1132 } 1133 1134 void 1135 window_pane_reset_mode(struct window_pane *wp) 1136 { 1137 struct window_mode_entry *wme, *next; 1138 1139 if (TAILQ_EMPTY(&wp->modes)) 1140 return; 1141 1142 wme = TAILQ_FIRST(&wp->modes); 1143 TAILQ_REMOVE(&wp->modes, wme, entry); 1144 wme->mode->free(wme); 1145 free(wme); 1146 1147 next = TAILQ_FIRST(&wp->modes); 1148 if (next == NULL) { 1149 log_debug("%s: no next mode", __func__); 1150 evtimer_del(&wp->modetimer); 1151 wp->screen = &wp->base; 1152 } else { 1153 log_debug("%s: next mode is %s", __func__, next->mode->name); 1154 wp->screen = next->screen; 1155 if (next->mode->resize != NULL) 1156 next->mode->resize(next, wp->sx, wp->sy); 1157 } 1158 wp->flags |= (PANE_REDRAW|PANE_CHANGED); 1159 1160 server_status_window(wp->window); 1161 notify_pane("pane-mode-changed", wp); 1162 } 1163 1164 void 1165 window_pane_reset_mode_all(struct window_pane *wp) 1166 { 1167 while (!TAILQ_EMPTY(&wp->modes)) 1168 window_pane_reset_mode(wp); 1169 } 1170 1171 void 1172 window_pane_key(struct window_pane *wp, struct client *c, struct session *s, 1173 struct winlink *wl, key_code key, struct mouse_event *m) 1174 { 1175 struct window_mode_entry *wme; 1176 struct window_pane *wp2; 1177 1178 if (KEYC_IS_MOUSE(key) && m == NULL) 1179 return; 1180 1181 wme = TAILQ_FIRST(&wp->modes); 1182 if (wme != NULL) { 1183 wp->modelast = time(NULL); 1184 if (wme->mode->key != NULL) 1185 wme->mode->key(wme, c, s, wl, (key & ~KEYC_XTERM), m); 1186 return; 1187 } 1188 1189 if (wp->fd == -1 || wp->flags & PANE_INPUTOFF) 1190 return; 1191 1192 input_key(wp, key, m); 1193 1194 if (KEYC_IS_MOUSE(key)) 1195 return; 1196 if (options_get_number(wp->window->options, "synchronize-panes")) { 1197 TAILQ_FOREACH(wp2, &wp->window->panes, entry) { 1198 if (wp2 != wp && 1199 TAILQ_EMPTY(&wp2->modes) && 1200 wp2->fd != -1 && 1201 (~wp2->flags & PANE_INPUTOFF) && 1202 window_pane_visible(wp2)) 1203 input_key(wp2, key, NULL); 1204 } 1205 } 1206 } 1207 1208 int 1209 window_pane_visible(struct window_pane *wp) 1210 { 1211 if (~wp->window->flags & WINDOW_ZOOMED) 1212 return (1); 1213 return (wp == wp->window->active); 1214 } 1215 1216 u_int 1217 window_pane_search(struct window_pane *wp, const char *term, int regex, 1218 int ignore) 1219 { 1220 struct screen *s = &wp->base; 1221 regex_t r; 1222 char *new = NULL, *line; 1223 u_int i; 1224 int flags = 0, found; 1225 1226 if (!regex) { 1227 if (ignore) 1228 flags |= FNM_CASEFOLD; 1229 xasprintf(&new, "*%s*", term); 1230 } else { 1231 if (ignore) 1232 flags |= REG_ICASE; 1233 if (regcomp(&r, term, flags|REG_EXTENDED) != 0) 1234 return (0); 1235 } 1236 1237 for (i = 0; i < screen_size_y(s); i++) { 1238 line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s)); 1239 if (!regex) 1240 found = (fnmatch(new, line, 0) == 0); 1241 else 1242 found = (regexec(&r, line, 0, NULL, 0) == 0); 1243 free(line); 1244 if (found) 1245 break; 1246 } 1247 if (!regex) 1248 free(new); 1249 else 1250 regfree(&r); 1251 1252 if (i == screen_size_y(s)) 1253 return (0); 1254 return (i + 1); 1255 } 1256 1257 /* Get MRU pane from a list. */ 1258 static struct window_pane * 1259 window_pane_choose_best(struct window_pane **list, u_int size) 1260 { 1261 struct window_pane *next, *best; 1262 u_int i; 1263 1264 if (size == 0) 1265 return (NULL); 1266 1267 best = list[0]; 1268 for (i = 1; i < size; i++) { 1269 next = list[i]; 1270 if (next->active_point > best->active_point) 1271 best = next; 1272 } 1273 return (best); 1274 } 1275 1276 /* 1277 * Find the pane directly above another. We build a list of those adjacent to 1278 * top edge and then choose the best. 1279 */ 1280 struct window_pane * 1281 window_pane_find_up(struct window_pane *wp) 1282 { 1283 struct window_pane *next, *best, **list; 1284 u_int edge, left, right, end, size; 1285 int status, found; 1286 1287 if (wp == NULL) 1288 return (NULL); 1289 status = options_get_number(wp->window->options, "pane-border-status"); 1290 1291 list = NULL; 1292 size = 0; 1293 1294 edge = wp->yoff; 1295 if (edge == (status == 1 ? 1 : 0)) 1296 edge = wp->window->sy + 1 - (status == 2 ? 1 : 0); 1297 1298 left = wp->xoff; 1299 right = wp->xoff + wp->sx; 1300 1301 TAILQ_FOREACH(next, &wp->window->panes, entry) { 1302 if (next == wp) 1303 continue; 1304 if (next->yoff + next->sy + 1 != edge) 1305 continue; 1306 end = next->xoff + next->sx - 1; 1307 1308 found = 0; 1309 if (next->xoff < left && end > right) 1310 found = 1; 1311 else if (next->xoff >= left && next->xoff <= right) 1312 found = 1; 1313 else if (end >= left && end <= right) 1314 found = 1; 1315 if (!found) 1316 continue; 1317 list = xreallocarray(list, size + 1, sizeof *list); 1318 list[size++] = next; 1319 } 1320 1321 best = window_pane_choose_best(list, size); 1322 free(list); 1323 return (best); 1324 } 1325 1326 /* Find the pane directly below another. */ 1327 struct window_pane * 1328 window_pane_find_down(struct window_pane *wp) 1329 { 1330 struct window_pane *next, *best, **list; 1331 u_int edge, left, right, end, size; 1332 int status, found; 1333 1334 if (wp == NULL) 1335 return (NULL); 1336 status = options_get_number(wp->window->options, "pane-border-status"); 1337 1338 list = NULL; 1339 size = 0; 1340 1341 edge = wp->yoff + wp->sy + 1; 1342 if (edge >= wp->window->sy - (status == 2 ? 1 : 0)) 1343 edge = (status == 1 ? 1 : 0); 1344 1345 left = wp->xoff; 1346 right = wp->xoff + wp->sx; 1347 1348 TAILQ_FOREACH(next, &wp->window->panes, entry) { 1349 if (next == wp) 1350 continue; 1351 if (next->yoff != edge) 1352 continue; 1353 end = next->xoff + next->sx - 1; 1354 1355 found = 0; 1356 if (next->xoff < left && end > right) 1357 found = 1; 1358 else if (next->xoff >= left && next->xoff <= right) 1359 found = 1; 1360 else if (end >= left && end <= right) 1361 found = 1; 1362 if (!found) 1363 continue; 1364 list = xreallocarray(list, size + 1, sizeof *list); 1365 list[size++] = next; 1366 } 1367 1368 best = window_pane_choose_best(list, size); 1369 free(list); 1370 return (best); 1371 } 1372 1373 /* Find the pane directly to the left of another. */ 1374 struct window_pane * 1375 window_pane_find_left(struct window_pane *wp) 1376 { 1377 struct window_pane *next, *best, **list; 1378 u_int edge, top, bottom, end, size; 1379 int found; 1380 1381 if (wp == NULL) 1382 return (NULL); 1383 1384 list = NULL; 1385 size = 0; 1386 1387 edge = wp->xoff; 1388 if (edge == 0) 1389 edge = wp->window->sx + 1; 1390 1391 top = wp->yoff; 1392 bottom = wp->yoff + wp->sy; 1393 1394 TAILQ_FOREACH(next, &wp->window->panes, entry) { 1395 if (next == wp) 1396 continue; 1397 if (next->xoff + next->sx + 1 != edge) 1398 continue; 1399 end = next->yoff + next->sy - 1; 1400 1401 found = 0; 1402 if (next->yoff < top && end > bottom) 1403 found = 1; 1404 else if (next->yoff >= top && next->yoff <= bottom) 1405 found = 1; 1406 else if (end >= top && end <= bottom) 1407 found = 1; 1408 if (!found) 1409 continue; 1410 list = xreallocarray(list, size + 1, sizeof *list); 1411 list[size++] = next; 1412 } 1413 1414 best = window_pane_choose_best(list, size); 1415 free(list); 1416 return (best); 1417 } 1418 1419 /* Find the pane directly to the right of another. */ 1420 struct window_pane * 1421 window_pane_find_right(struct window_pane *wp) 1422 { 1423 struct window_pane *next, *best, **list; 1424 u_int edge, top, bottom, end, size; 1425 int found; 1426 1427 if (wp == NULL) 1428 return (NULL); 1429 1430 list = NULL; 1431 size = 0; 1432 1433 edge = wp->xoff + wp->sx + 1; 1434 if (edge >= wp->window->sx) 1435 edge = 0; 1436 1437 top = wp->yoff; 1438 bottom = wp->yoff + wp->sy; 1439 1440 TAILQ_FOREACH(next, &wp->window->panes, entry) { 1441 if (next == wp) 1442 continue; 1443 if (next->xoff != edge) 1444 continue; 1445 end = next->yoff + next->sy - 1; 1446 1447 found = 0; 1448 if (next->yoff < top && end > bottom) 1449 found = 1; 1450 else if (next->yoff >= top && next->yoff <= bottom) 1451 found = 1; 1452 else if (end >= top && end <= bottom) 1453 found = 1; 1454 if (!found) 1455 continue; 1456 list = xreallocarray(list, size + 1, sizeof *list); 1457 list[size++] = next; 1458 } 1459 1460 best = window_pane_choose_best(list, size); 1461 free(list); 1462 return (best); 1463 } 1464 1465 /* Clear alert flags for a winlink */ 1466 void 1467 winlink_clear_flags(struct winlink *wl) 1468 { 1469 struct winlink *loop; 1470 1471 wl->window->flags &= ~WINDOW_ALERTFLAGS; 1472 TAILQ_FOREACH(loop, &wl->window->winlinks, wentry) { 1473 if ((loop->flags & WINLINK_ALERTFLAGS) != 0) { 1474 loop->flags &= ~WINLINK_ALERTFLAGS; 1475 server_status_session(loop->session); 1476 } 1477 } 1478 } 1479 1480 /* Shuffle window indexes up. */ 1481 int 1482 winlink_shuffle_up(struct session *s, struct winlink *wl) 1483 { 1484 int idx, last; 1485 1486 if (wl == NULL) 1487 return (-1); 1488 idx = wl->idx + 1; 1489 1490 /* Find the next free index. */ 1491 for (last = idx; last < INT_MAX; last++) { 1492 if (winlink_find_by_index(&s->windows, last) == NULL) 1493 break; 1494 } 1495 if (last == INT_MAX) 1496 return (-1); 1497 1498 /* Move everything from last - 1 to idx up a bit. */ 1499 for (; last > idx; last--) { 1500 wl = winlink_find_by_index(&s->windows, last - 1); 1501 server_link_window(s, wl, s, last, 0, 0, NULL); 1502 server_unlink_window(s, wl); 1503 } 1504 1505 return (idx); 1506 } 1507 1508 static void 1509 window_pane_input_callback(struct client *c, int closed, void *data) 1510 { 1511 struct window_pane_input_data *cdata = data; 1512 struct window_pane *wp; 1513 struct evbuffer *evb = c->stdin_data; 1514 u_char *buf = EVBUFFER_DATA(evb); 1515 size_t len = EVBUFFER_LENGTH(evb); 1516 1517 wp = window_pane_find_by_id(cdata->wp); 1518 if (wp == NULL || closed || c->flags & CLIENT_DEAD) { 1519 c->stdin_callback = NULL; 1520 server_client_unref(c); 1521 1522 cmdq_continue(cdata->item); 1523 free(cdata); 1524 1525 return; 1526 } 1527 1528 input_parse_buffer(wp, buf, len); 1529 evbuffer_drain(evb, len); 1530 } 1531 1532 int 1533 window_pane_start_input(struct window_pane *wp, struct cmdq_item *item, 1534 char **cause) 1535 { 1536 struct client *c = item->client; 1537 struct window_pane_input_data *cdata; 1538 1539 if (~wp->flags & PANE_EMPTY) { 1540 *cause = xstrdup("pane is not empty"); 1541 return (-1); 1542 } 1543 1544 cdata = xmalloc(sizeof *cdata); 1545 cdata->item = item; 1546 cdata->wp = wp->id; 1547 1548 return (server_set_stdin_callback(c, window_pane_input_callback, cdata, 1549 cause)); 1550 } 1551