1 /* $OpenBSD$ */ 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 <ctype.h> 23 #include <errno.h> 24 #include <fcntl.h> 25 #include <fnmatch.h> 26 #include <regex.h> 27 #include <signal.h> 28 #include <stdint.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <time.h> 32 #include <unistd.h> 33 34 #include "tmux.h" 35 36 /* 37 * Each window is attached to a number of panes, each of which is a pty. This 38 * file contains code to handle them. 39 * 40 * A pane has two buffers attached, these are filled and emptied by the main 41 * server poll loop. Output data is received from pty's in screen format, 42 * translated and returned as a series of escape sequences and strings via 43 * input_parse (in input.c). Input data is received as key codes and written 44 * directly via input_key. 45 * 46 * Each pane also has a "virtual" screen (screen.c) which contains the current 47 * state and is redisplayed when the window is reattached to a client. 48 * 49 * Windows are stored directly on a global array and wrapped in any number of 50 * winlink structs to be linked onto local session RB trees. A reference count 51 * is maintained and a window removed from the global list and destroyed when 52 * it reaches zero. 53 */ 54 55 /* Global window list. */ 56 struct windows windows; 57 58 /* Global panes tree. */ 59 struct window_pane_tree all_window_panes; 60 static u_int next_window_pane_id; 61 static u_int next_window_id; 62 static u_int next_active_point; 63 64 /* List of window modes. */ 65 const struct window_mode *all_window_modes[] = { 66 &window_buffer_mode, 67 &window_client_mode, 68 &window_clock_mode, 69 &window_copy_mode, 70 &window_tree_mode, 71 &window_view_mode, 72 NULL 73 }; 74 75 struct window_pane_input_data { 76 struct cmdq_item *item; 77 u_int wp; 78 }; 79 80 static struct window_pane *window_pane_create(struct window *, u_int, u_int, 81 u_int); 82 static void window_pane_destroy(struct window_pane *); 83 84 RB_GENERATE(windows, window, entry, window_cmp); 85 RB_GENERATE(winlinks, winlink, entry, winlink_cmp); 86 RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp); 87 88 int 89 window_cmp(struct window *w1, struct window *w2) 90 { 91 return (w1->id - w2->id); 92 } 93 94 int 95 winlink_cmp(struct winlink *wl1, struct winlink *wl2) 96 { 97 return (wl1->idx - wl2->idx); 98 } 99 100 int 101 window_pane_cmp(struct window_pane *wp1, struct window_pane *wp2) 102 { 103 return (wp1->id - wp2->id); 104 } 105 106 struct winlink * 107 winlink_find_by_window(struct winlinks *wwl, struct window *w) 108 { 109 struct winlink *wl; 110 111 RB_FOREACH(wl, winlinks, wwl) { 112 if (wl->window == w) 113 return (wl); 114 } 115 116 return (NULL); 117 } 118 119 struct winlink * 120 winlink_find_by_index(struct winlinks *wwl, int idx) 121 { 122 struct winlink wl; 123 124 if (idx < 0) 125 fatalx("bad index"); 126 127 wl.idx = idx; 128 return (RB_FIND(winlinks, wwl, &wl)); 129 } 130 131 struct winlink * 132 winlink_find_by_window_id(struct winlinks *wwl, u_int id) 133 { 134 struct winlink *wl; 135 136 RB_FOREACH(wl, winlinks, wwl) { 137 if (wl->window->id == id) 138 return (wl); 139 } 140 return (NULL); 141 } 142 143 static int 144 winlink_next_index(struct winlinks *wwl, int idx) 145 { 146 int i; 147 148 i = idx; 149 do { 150 if (winlink_find_by_index(wwl, i) == NULL) 151 return (i); 152 if (i == INT_MAX) 153 i = 0; 154 else 155 i++; 156 } while (i != idx); 157 return (-1); 158 } 159 160 u_int 161 winlink_count(struct winlinks *wwl) 162 { 163 struct winlink *wl; 164 u_int n; 165 166 n = 0; 167 RB_FOREACH(wl, winlinks, wwl) 168 n++; 169 170 return (n); 171 } 172 173 struct winlink * 174 winlink_add(struct winlinks *wwl, int idx) 175 { 176 struct winlink *wl; 177 178 if (idx < 0) { 179 if ((idx = winlink_next_index(wwl, -idx - 1)) == -1) 180 return (NULL); 181 } else if (winlink_find_by_index(wwl, idx) != NULL) 182 return (NULL); 183 184 wl = xcalloc(1, sizeof *wl); 185 wl->idx = idx; 186 RB_INSERT(winlinks, wwl, wl); 187 188 return (wl); 189 } 190 191 void 192 winlink_set_window(struct winlink *wl, struct window *w) 193 { 194 if (wl->window != NULL) { 195 TAILQ_REMOVE(&wl->window->winlinks, wl, wentry); 196 window_remove_ref(wl->window, __func__); 197 } 198 TAILQ_INSERT_TAIL(&w->winlinks, wl, wentry); 199 wl->window = w; 200 window_add_ref(w, __func__); 201 } 202 203 void 204 winlink_remove(struct winlinks *wwl, struct winlink *wl) 205 { 206 struct window *w = wl->window; 207 208 if (w != NULL) { 209 TAILQ_REMOVE(&w->winlinks, wl, wentry); 210 window_remove_ref(w, __func__); 211 } 212 213 RB_REMOVE(winlinks, wwl, wl); 214 free(wl); 215 } 216 217 struct winlink * 218 winlink_next(struct winlink *wl) 219 { 220 return (RB_NEXT(winlinks, wwl, wl)); 221 } 222 223 struct winlink * 224 winlink_previous(struct winlink *wl) 225 { 226 return (RB_PREV(winlinks, wwl, wl)); 227 } 228 229 struct winlink * 230 winlink_next_by_number(struct winlink *wl, struct session *s, int n) 231 { 232 for (; n > 0; n--) { 233 if ((wl = RB_NEXT(winlinks, wwl, wl)) == NULL) 234 wl = RB_MIN(winlinks, &s->windows); 235 } 236 237 return (wl); 238 } 239 240 struct winlink * 241 winlink_previous_by_number(struct winlink *wl, struct session *s, int n) 242 { 243 for (; n > 0; n--) { 244 if ((wl = RB_PREV(winlinks, wwl, wl)) == NULL) 245 wl = RB_MAX(winlinks, &s->windows); 246 } 247 248 return (wl); 249 } 250 251 void 252 winlink_stack_push(struct winlink_stack *stack, struct winlink *wl) 253 { 254 if (wl == NULL) 255 return; 256 257 winlink_stack_remove(stack, wl); 258 TAILQ_INSERT_HEAD(stack, wl, sentry); 259 } 260 261 void 262 winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl) 263 { 264 struct winlink *wl2; 265 266 if (wl == NULL) 267 return; 268 269 TAILQ_FOREACH(wl2, stack, sentry) { 270 if (wl2 == wl) { 271 TAILQ_REMOVE(stack, wl, sentry); 272 return; 273 } 274 } 275 } 276 277 struct window * 278 window_find_by_id_str(const char *s) 279 { 280 const char *errstr; 281 u_int id; 282 283 if (*s != '@') 284 return (NULL); 285 286 id = strtonum(s + 1, 0, UINT_MAX, &errstr); 287 if (errstr != NULL) 288 return (NULL); 289 return (window_find_by_id(id)); 290 } 291 292 struct window * 293 window_find_by_id(u_int id) 294 { 295 struct window w; 296 297 w.id = id; 298 return (RB_FIND(windows, &windows, &w)); 299 } 300 301 void 302 window_update_activity(struct window *w) 303 { 304 gettimeofday(&w->activity_time, NULL); 305 alerts_queue(w, WINDOW_ACTIVITY); 306 } 307 308 struct window * 309 window_create(u_int sx, u_int sy) 310 { 311 struct window *w; 312 313 w = xcalloc(1, sizeof *w); 314 w->name = NULL; 315 w->flags = 0; 316 317 TAILQ_INIT(&w->panes); 318 w->active = NULL; 319 320 w->lastlayout = -1; 321 w->layout_root = NULL; 322 323 w->sx = sx; 324 w->sy = sy; 325 326 w->options = options_create(global_w_options); 327 328 w->references = 0; 329 TAILQ_INIT(&w->winlinks); 330 331 w->id = next_window_id++; 332 RB_INSERT(windows, &windows, w); 333 334 window_update_activity(w); 335 336 return (w); 337 } 338 339 static void 340 window_destroy(struct window *w) 341 { 342 log_debug("window @%u destroyed (%d references)", w->id, w->references); 343 344 RB_REMOVE(windows, &windows, w); 345 346 if (w->layout_root != NULL) 347 layout_free_cell(w->layout_root); 348 if (w->saved_layout_root != NULL) 349 layout_free_cell(w->saved_layout_root); 350 free(w->old_layout); 351 352 window_destroy_panes(w); 353 354 if (event_initialized(&w->name_event)) 355 evtimer_del(&w->name_event); 356 357 if (event_initialized(&w->alerts_timer)) 358 evtimer_del(&w->alerts_timer); 359 if (event_initialized(&w->offset_timer)) 360 event_del(&w->offset_timer); 361 362 options_free(w->options); 363 364 free(w->name); 365 free(w); 366 } 367 368 int 369 window_pane_destroy_ready(struct window_pane *wp) 370 { 371 int n; 372 373 if (wp->pipe_fd != -1) { 374 if (EVBUFFER_LENGTH(wp->pipe_event->output) != 0) 375 return (0); 376 if (ioctl(wp->fd, FIONREAD, &n) != -1 && n > 0) 377 return (0); 378 } 379 380 if (~wp->flags & PANE_EXITED) 381 return (0); 382 return (1); 383 } 384 385 void 386 window_add_ref(struct window *w, const char *from) 387 { 388 w->references++; 389 log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references); 390 } 391 392 void 393 window_remove_ref(struct window *w, const char *from) 394 { 395 w->references--; 396 log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references); 397 398 if (w->references == 0) 399 window_destroy(w); 400 } 401 402 void 403 window_set_name(struct window *w, const char *new_name) 404 { 405 free(w->name); 406 utf8_stravis(&w->name, new_name, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); 407 notify_window("window-renamed", w); 408 } 409 410 void 411 window_resize(struct window *w, u_int sx, u_int sy) 412 { 413 log_debug("%s: @%u resize %ux%u", __func__, w->id, sx, sy); 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 #ifdef HAVE_UTEMPTER 840 utempter_remove_record(wp->fd); 841 #endif 842 bufferevent_free(wp->event); 843 close(wp->fd); 844 } 845 846 input_free(wp); 847 848 screen_free(&wp->status_screen); 849 850 screen_free(&wp->base); 851 if (wp->saved_grid != NULL) 852 grid_destroy(wp->saved_grid); 853 854 if (wp->pipe_fd != -1) { 855 bufferevent_free(wp->pipe_event); 856 close(wp->pipe_fd); 857 } 858 859 if (event_initialized(&wp->resize_timer)) 860 event_del(&wp->resize_timer); 861 862 RB_REMOVE(window_pane_tree, &all_window_panes, wp); 863 864 options_free(wp->options); 865 free(__UNCONST(wp->cwd)); 866 free(wp->shell); 867 cmd_free_argv(wp->argc, wp->argv); 868 free(wp->palette); 869 free(wp); 870 } 871 872 static void 873 window_pane_read_callback(__unused struct bufferevent *bufev, void *data) 874 { 875 struct window_pane *wp = data; 876 struct evbuffer *evb = wp->event->input; 877 size_t size = EVBUFFER_LENGTH(evb); 878 char *new_data; 879 size_t new_size; 880 881 882 new_size = size - wp->pipe_off; 883 if (wp->pipe_fd != -1 && new_size > 0) { 884 new_data = (char *)EVBUFFER_DATA(evb) + wp->pipe_off; 885 bufferevent_write(wp->pipe_event, new_data, new_size); 886 } 887 888 log_debug("%%%u has %zu bytes", wp->id, size); 889 input_parse(wp); 890 891 wp->pipe_off = EVBUFFER_LENGTH(evb); 892 } 893 894 static void 895 window_pane_error_callback(__unused struct bufferevent *bufev, 896 __unused short what, void *data) 897 { 898 struct window_pane *wp = data; 899 900 log_debug("%%%u error", wp->id); 901 wp->flags |= PANE_EXITED; 902 903 if (window_pane_destroy_ready(wp)) 904 server_destroy_pane(wp, 1); 905 } 906 907 void 908 window_pane_set_event(struct window_pane *wp) 909 { 910 setblocking(wp->fd, 0); 911 912 wp->event = bufferevent_new(wp->fd, window_pane_read_callback, 913 NULL, window_pane_error_callback, wp); 914 915 bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); 916 bufferevent_enable(wp->event, EV_READ|EV_WRITE); 917 } 918 919 void 920 window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) 921 { 922 struct window_mode_entry *wme; 923 924 if (sx == wp->sx && sy == wp->sy) 925 return; 926 wp->sx = sx; 927 wp->sy = sy; 928 929 log_debug("%s: %%%u resize %ux%u", __func__, wp->id, sx, sy); 930 screen_resize(&wp->base, sx, sy, wp->saved_grid == NULL); 931 932 wme = TAILQ_FIRST(&wp->modes); 933 if (wme != NULL && wme->mode->resize != NULL) 934 wme->mode->resize(wme, sx, sy); 935 936 wp->flags |= PANE_RESIZE; 937 } 938 939 /* 940 * Enter alternative screen mode. A copy of the visible screen is saved and the 941 * history is not updated 942 */ 943 void 944 window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc, 945 int cursor) 946 { 947 struct screen *s = &wp->base; 948 u_int sx, sy; 949 950 if (wp->saved_grid != NULL) 951 return; 952 if (!options_get_number(wp->options, "alternate-screen")) 953 return; 954 sx = screen_size_x(s); 955 sy = screen_size_y(s); 956 957 wp->saved_grid = grid_create(sx, sy, 0); 958 grid_duplicate_lines(wp->saved_grid, 0, s->grid, screen_hsize(s), sy); 959 if (cursor) { 960 wp->saved_cx = s->cx; 961 wp->saved_cy = s->cy; 962 } 963 memcpy(&wp->saved_cell, gc, sizeof wp->saved_cell); 964 965 grid_view_clear(s->grid, 0, 0, sx, sy, 8); 966 967 wp->base.grid->flags &= ~GRID_HISTORY; 968 969 wp->flags |= PANE_REDRAW; 970 } 971 972 /* Exit alternate screen mode and restore the copied grid. */ 973 void 974 window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc, 975 int cursor) 976 { 977 struct screen *s = &wp->base; 978 u_int sx, sy; 979 980 if (!options_get_number(wp->options, "alternate-screen")) 981 return; 982 983 /* 984 * Restore the cursor position and cell. This happens even if not 985 * currently in the alternate screen. 986 */ 987 if (cursor && wp->saved_cx != UINT_MAX && wp->saved_cy != UINT_MAX) { 988 s->cx = wp->saved_cx; 989 if (s->cx > screen_size_x(s) - 1) 990 s->cx = screen_size_x(s) - 1; 991 s->cy = wp->saved_cy; 992 if (s->cy > screen_size_y(s) - 1) 993 s->cy = screen_size_y(s) - 1; 994 memcpy(gc, &wp->saved_cell, sizeof *gc); 995 } 996 997 if (wp->saved_grid == NULL) 998 return; 999 sx = screen_size_x(s); 1000 sy = screen_size_y(s); 1001 1002 /* 1003 * If the current size is bigger, temporarily resize to the old size 1004 * before copying back. 1005 */ 1006 if (sy > wp->saved_grid->sy) 1007 screen_resize(s, sx, wp->saved_grid->sy, 1); 1008 1009 /* Restore the saved grid. */ 1010 grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy); 1011 1012 /* 1013 * Turn history back on (so resize can use it) and then resize back to 1014 * the current size. 1015 */ 1016 wp->base.grid->flags |= GRID_HISTORY; 1017 if (sy > wp->saved_grid->sy || sx != wp->saved_grid->sx) 1018 screen_resize(s, sx, sy, 1); 1019 1020 grid_destroy(wp->saved_grid); 1021 wp->saved_grid = NULL; 1022 1023 wp->flags |= PANE_REDRAW; 1024 } 1025 1026 void 1027 window_pane_set_palette(struct window_pane *wp, u_int n, int colour) 1028 { 1029 if (n > 0xff) 1030 return; 1031 1032 if (wp->palette == NULL) 1033 wp->palette = xcalloc(0x100, sizeof *wp->palette); 1034 1035 wp->palette[n] = colour; 1036 wp->flags |= PANE_REDRAW; 1037 } 1038 1039 void 1040 window_pane_unset_palette(struct window_pane *wp, u_int n) 1041 { 1042 if (n > 0xff || wp->palette == NULL) 1043 return; 1044 1045 wp->palette[n] = 0; 1046 wp->flags |= PANE_REDRAW; 1047 } 1048 1049 void 1050 window_pane_reset_palette(struct window_pane *wp) 1051 { 1052 if (wp->palette == NULL) 1053 return; 1054 1055 free(wp->palette); 1056 wp->palette = NULL; 1057 wp->flags |= PANE_REDRAW; 1058 } 1059 1060 int 1061 window_pane_get_palette(struct window_pane *wp, int c) 1062 { 1063 int new; 1064 1065 if (wp == NULL || wp->palette == NULL) 1066 return (-1); 1067 1068 new = -1; 1069 if (c < 8) 1070 new = wp->palette[c]; 1071 else if (c >= 90 && c <= 97) 1072 new = wp->palette[8 + c - 90]; 1073 else if (c & COLOUR_FLAG_256) 1074 new = wp->palette[c & ~COLOUR_FLAG_256]; 1075 if (new == 0) 1076 return (-1); 1077 return (new); 1078 } 1079 1080 static void 1081 window_pane_mode_timer(__unused int fd, __unused short events, void *arg) 1082 { 1083 struct window_pane *wp = arg; 1084 struct timeval tv = { .tv_sec = 10 }; 1085 int n = 0; 1086 1087 evtimer_del(&wp->modetimer); 1088 evtimer_add(&wp->modetimer, &tv); 1089 1090 log_debug("%%%u in mode: last=%ld", wp->id, (long)wp->modelast); 1091 1092 if (wp->modelast < time(NULL) - WINDOW_MODE_TIMEOUT) { 1093 if (ioctl(wp->fd, FIONREAD, &n) == -1 || n > 0) 1094 window_pane_reset_mode_all(wp); 1095 } 1096 } 1097 1098 int 1099 window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode, 1100 struct cmd_find_state *fs, struct args *args) 1101 { 1102 struct timeval tv = { .tv_sec = 10 }; 1103 struct window_mode_entry *wme; 1104 1105 if (!TAILQ_EMPTY(&wp->modes) && TAILQ_FIRST(&wp->modes)->mode == mode) 1106 return (1); 1107 1108 wp->modelast = time(NULL); 1109 if (TAILQ_EMPTY(&wp->modes)) { 1110 evtimer_set(&wp->modetimer, window_pane_mode_timer, wp); 1111 evtimer_add(&wp->modetimer, &tv); 1112 } 1113 1114 TAILQ_FOREACH(wme, &wp->modes, entry) { 1115 if (wme->mode == mode) 1116 break; 1117 } 1118 if (wme != NULL) { 1119 TAILQ_REMOVE(&wp->modes, wme, entry); 1120 TAILQ_INSERT_HEAD(&wp->modes, wme, entry); 1121 } else { 1122 wme = xcalloc(1, sizeof *wme); 1123 wme->wp = wp; 1124 wme->mode = mode; 1125 wme->prefix = 1; 1126 TAILQ_INSERT_HEAD(&wp->modes, wme, entry); 1127 wme->screen = wme->mode->init(wme, fs, args); 1128 } 1129 1130 wp->screen = wme->screen; 1131 wp->flags |= (PANE_REDRAW|PANE_CHANGED); 1132 1133 server_status_window(wp->window); 1134 notify_pane("pane-mode-changed", wp); 1135 1136 return (0); 1137 } 1138 1139 void 1140 window_pane_reset_mode(struct window_pane *wp) 1141 { 1142 struct window_mode_entry *wme, *next; 1143 1144 if (TAILQ_EMPTY(&wp->modes)) 1145 return; 1146 1147 wme = TAILQ_FIRST(&wp->modes); 1148 TAILQ_REMOVE(&wp->modes, wme, entry); 1149 wme->mode->free(wme); 1150 free(wme); 1151 1152 next = TAILQ_FIRST(&wp->modes); 1153 if (next == NULL) { 1154 log_debug("%s: no next mode", __func__); 1155 evtimer_del(&wp->modetimer); 1156 wp->screen = &wp->base; 1157 } else { 1158 log_debug("%s: next mode is %s", __func__, next->mode->name); 1159 wp->screen = next->screen; 1160 if (next->mode->resize != NULL) 1161 next->mode->resize(next, wp->sx, wp->sy); 1162 } 1163 wp->flags |= (PANE_REDRAW|PANE_CHANGED); 1164 1165 server_status_window(wp->window); 1166 notify_pane("pane-mode-changed", wp); 1167 } 1168 1169 void 1170 window_pane_reset_mode_all(struct window_pane *wp) 1171 { 1172 while (!TAILQ_EMPTY(&wp->modes)) 1173 window_pane_reset_mode(wp); 1174 } 1175 1176 void 1177 window_pane_key(struct window_pane *wp, struct client *c, struct session *s, 1178 struct winlink *wl, key_code key, struct mouse_event *m) 1179 { 1180 struct window_mode_entry *wme; 1181 struct window_pane *wp2; 1182 1183 if (KEYC_IS_MOUSE(key) && m == NULL) 1184 return; 1185 1186 wme = TAILQ_FIRST(&wp->modes); 1187 if (wme != NULL) { 1188 wp->modelast = time(NULL); 1189 if (wme->mode->key != NULL) 1190 wme->mode->key(wme, c, s, wl, (key & ~KEYC_XTERM), m); 1191 return; 1192 } 1193 1194 if (wp->fd == -1 || wp->flags & PANE_INPUTOFF) 1195 return; 1196 1197 input_key(wp, key, m); 1198 1199 if (KEYC_IS_MOUSE(key)) 1200 return; 1201 if (options_get_number(wp->window->options, "synchronize-panes")) { 1202 TAILQ_FOREACH(wp2, &wp->window->panes, entry) { 1203 if (wp2 != wp && 1204 TAILQ_EMPTY(&wp2->modes) && 1205 wp2->fd != -1 && 1206 (~wp2->flags & PANE_INPUTOFF) && 1207 window_pane_visible(wp2)) 1208 input_key(wp2, key, NULL); 1209 } 1210 } 1211 } 1212 1213 int 1214 window_pane_visible(struct window_pane *wp) 1215 { 1216 if (~wp->window->flags & WINDOW_ZOOMED) 1217 return (1); 1218 return (wp == wp->window->active); 1219 } 1220 1221 u_int 1222 window_pane_search(struct window_pane *wp, const char *term, int regex, 1223 int ignore) 1224 { 1225 struct screen *s = &wp->base; 1226 regex_t r; 1227 char *new = NULL, *line; 1228 u_int i; 1229 int flags = 0, found; 1230 size_t n; 1231 1232 if (!regex) { 1233 if (ignore) 1234 flags |= FNM_CASEFOLD; 1235 xasprintf(&new, "*%s*", term); 1236 } else { 1237 if (ignore) 1238 flags |= REG_ICASE; 1239 if (regcomp(&r, term, flags|REG_EXTENDED) != 0) 1240 return (0); 1241 } 1242 1243 for (i = 0; i < screen_size_y(s); i++) { 1244 line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s)); 1245 for (n = strlen(line); n > 0; n--) { 1246 if (!isspace((u_char)line[n - 1])) 1247 break; 1248 line[n - 1] = '\0'; 1249 } 1250 log_debug("%s: %s", __func__, line); 1251 if (!regex) 1252 found = (fnmatch(new, line, 0) == 0); 1253 else 1254 found = (regexec(&r, line, 0, NULL, 0) == 0); 1255 free(line); 1256 if (found) 1257 break; 1258 } 1259 if (!regex) 1260 free(new); 1261 else 1262 regfree(&r); 1263 1264 if (i == screen_size_y(s)) 1265 return (0); 1266 return (i + 1); 1267 } 1268 1269 /* Get MRU pane from a list. */ 1270 static struct window_pane * 1271 window_pane_choose_best(struct window_pane **list, u_int size) 1272 { 1273 struct window_pane *next, *best; 1274 u_int i; 1275 1276 if (size == 0) 1277 return (NULL); 1278 1279 best = list[0]; 1280 for (i = 1; i < size; i++) { 1281 next = list[i]; 1282 if (next->active_point > best->active_point) 1283 best = next; 1284 } 1285 return (best); 1286 } 1287 1288 /* 1289 * Find the pane directly above another. We build a list of those adjacent to 1290 * top edge and then choose the best. 1291 */ 1292 struct window_pane * 1293 window_pane_find_up(struct window_pane *wp) 1294 { 1295 struct window *w; 1296 struct window_pane *next, *best, **list; 1297 u_int edge, left, right, end, size; 1298 int status, found; 1299 1300 if (wp == NULL) 1301 return (NULL); 1302 w = wp->window; 1303 status = options_get_number(w->options, "pane-border-status"); 1304 1305 list = NULL; 1306 size = 0; 1307 1308 edge = wp->yoff; 1309 if (status == PANE_STATUS_TOP) { 1310 if (edge == 1) 1311 edge = w->sy + 1; 1312 } else if (status == PANE_STATUS_BOTTOM) { 1313 if (edge == 0) 1314 edge = w->sy; 1315 } else { 1316 if (edge == 0) 1317 edge = w->sy + 1; 1318 } 1319 1320 left = wp->xoff; 1321 right = wp->xoff + wp->sx; 1322 1323 TAILQ_FOREACH(next, &w->panes, entry) { 1324 if (next == wp) 1325 continue; 1326 if (next->yoff + next->sy + 1 != edge) 1327 continue; 1328 end = next->xoff + next->sx - 1; 1329 1330 found = 0; 1331 if (next->xoff < left && end > right) 1332 found = 1; 1333 else if (next->xoff >= left && next->xoff <= right) 1334 found = 1; 1335 else if (end >= left && end <= right) 1336 found = 1; 1337 if (!found) 1338 continue; 1339 list = xreallocarray(list, size + 1, sizeof *list); 1340 list[size++] = next; 1341 } 1342 1343 best = window_pane_choose_best(list, size); 1344 free(list); 1345 return (best); 1346 } 1347 1348 /* Find the pane directly below another. */ 1349 struct window_pane * 1350 window_pane_find_down(struct window_pane *wp) 1351 { 1352 struct window *w; 1353 struct window_pane *next, *best, **list; 1354 u_int edge, left, right, end, size; 1355 int status, found; 1356 1357 if (wp == NULL) 1358 return (NULL); 1359 w = wp->window; 1360 status = options_get_number(w->options, "pane-border-status"); 1361 1362 list = NULL; 1363 size = 0; 1364 1365 edge = wp->yoff + wp->sy + 1; 1366 if (status == PANE_STATUS_TOP) { 1367 if (edge >= w->sy) 1368 edge = 1; 1369 } else if (status == PANE_STATUS_BOTTOM) { 1370 if (edge >= w->sy - 1) 1371 edge = 0; 1372 } else { 1373 if (edge >= w->sy) 1374 edge = 0; 1375 } 1376 1377 left = wp->xoff; 1378 right = wp->xoff + wp->sx; 1379 1380 TAILQ_FOREACH(next, &w->panes, entry) { 1381 if (next == wp) 1382 continue; 1383 if (next->yoff != edge) 1384 continue; 1385 end = next->xoff + next->sx - 1; 1386 1387 found = 0; 1388 if (next->xoff < left && end > right) 1389 found = 1; 1390 else if (next->xoff >= left && next->xoff <= right) 1391 found = 1; 1392 else if (end >= left && end <= right) 1393 found = 1; 1394 if (!found) 1395 continue; 1396 list = xreallocarray(list, size + 1, sizeof *list); 1397 list[size++] = next; 1398 } 1399 1400 best = window_pane_choose_best(list, size); 1401 free(list); 1402 return (best); 1403 } 1404 1405 /* Find the pane directly to the left of another. */ 1406 struct window_pane * 1407 window_pane_find_left(struct window_pane *wp) 1408 { 1409 struct window *w; 1410 struct window_pane *next, *best, **list; 1411 u_int edge, top, bottom, end, size; 1412 int found; 1413 1414 if (wp == NULL) 1415 return (NULL); 1416 w = wp->window; 1417 1418 list = NULL; 1419 size = 0; 1420 1421 edge = wp->xoff; 1422 if (edge == 0) 1423 edge = w->sx + 1; 1424 1425 top = wp->yoff; 1426 bottom = wp->yoff + wp->sy; 1427 1428 TAILQ_FOREACH(next, &w->panes, entry) { 1429 if (next == wp) 1430 continue; 1431 if (next->xoff + next->sx + 1 != edge) 1432 continue; 1433 end = next->yoff + next->sy - 1; 1434 1435 found = 0; 1436 if (next->yoff < top && end > bottom) 1437 found = 1; 1438 else if (next->yoff >= top && next->yoff <= bottom) 1439 found = 1; 1440 else if (end >= top && end <= bottom) 1441 found = 1; 1442 if (!found) 1443 continue; 1444 list = xreallocarray(list, size + 1, sizeof *list); 1445 list[size++] = next; 1446 } 1447 1448 best = window_pane_choose_best(list, size); 1449 free(list); 1450 return (best); 1451 } 1452 1453 /* Find the pane directly to the right of another. */ 1454 struct window_pane * 1455 window_pane_find_right(struct window_pane *wp) 1456 { 1457 struct window *w; 1458 struct window_pane *next, *best, **list; 1459 u_int edge, top, bottom, end, size; 1460 int found; 1461 1462 if (wp == NULL) 1463 return (NULL); 1464 w = wp->window; 1465 1466 list = NULL; 1467 size = 0; 1468 1469 edge = wp->xoff + wp->sx + 1; 1470 if (edge >= w->sx) 1471 edge = 0; 1472 1473 top = wp->yoff; 1474 bottom = wp->yoff + wp->sy; 1475 1476 TAILQ_FOREACH(next, &w->panes, entry) { 1477 if (next == wp) 1478 continue; 1479 if (next->xoff != edge) 1480 continue; 1481 end = next->yoff + next->sy - 1; 1482 1483 found = 0; 1484 if (next->yoff < top && end > bottom) 1485 found = 1; 1486 else if (next->yoff >= top && next->yoff <= bottom) 1487 found = 1; 1488 else if (end >= top && end <= bottom) 1489 found = 1; 1490 if (!found) 1491 continue; 1492 list = xreallocarray(list, size + 1, sizeof *list); 1493 list[size++] = next; 1494 } 1495 1496 best = window_pane_choose_best(list, size); 1497 free(list); 1498 return (best); 1499 } 1500 1501 /* Clear alert flags for a winlink */ 1502 void 1503 winlink_clear_flags(struct winlink *wl) 1504 { 1505 struct winlink *loop; 1506 1507 wl->window->flags &= ~WINDOW_ALERTFLAGS; 1508 TAILQ_FOREACH(loop, &wl->window->winlinks, wentry) { 1509 if ((loop->flags & WINLINK_ALERTFLAGS) != 0) { 1510 loop->flags &= ~WINLINK_ALERTFLAGS; 1511 server_status_session(loop->session); 1512 } 1513 } 1514 } 1515 1516 /* Shuffle window indexes up. */ 1517 int 1518 winlink_shuffle_up(struct session *s, struct winlink *wl) 1519 { 1520 int idx, last; 1521 1522 if (wl == NULL) 1523 return (-1); 1524 idx = wl->idx + 1; 1525 1526 /* Find the next free index. */ 1527 for (last = idx; last < INT_MAX; last++) { 1528 if (winlink_find_by_index(&s->windows, last) == NULL) 1529 break; 1530 } 1531 if (last == INT_MAX) 1532 return (-1); 1533 1534 /* Move everything from last - 1 to idx up a bit. */ 1535 for (; last > idx; last--) { 1536 wl = winlink_find_by_index(&s->windows, last - 1); 1537 server_link_window(s, wl, s, last, 0, 0, NULL); 1538 server_unlink_window(s, wl); 1539 } 1540 1541 return (idx); 1542 } 1543 1544 static void 1545 window_pane_input_callback(struct client *c, int closed, void *data) 1546 { 1547 struct window_pane_input_data *cdata = data; 1548 struct window_pane *wp; 1549 struct evbuffer *evb = c->stdin_data; 1550 u_char *buf = EVBUFFER_DATA(evb); 1551 size_t len = EVBUFFER_LENGTH(evb); 1552 1553 wp = window_pane_find_by_id(cdata->wp); 1554 if (wp == NULL || closed || c->flags & CLIENT_DEAD) { 1555 if (wp == NULL) 1556 c->flags |= CLIENT_EXIT; 1557 evbuffer_drain(evb, len); 1558 1559 c->stdin_callback = NULL; 1560 server_client_unref(c); 1561 1562 cmdq_continue(cdata->item); 1563 free(cdata); 1564 1565 return; 1566 } 1567 1568 input_parse_buffer(wp, buf, len); 1569 evbuffer_drain(evb, len); 1570 } 1571 1572 int 1573 window_pane_start_input(struct window_pane *wp, struct cmdq_item *item, 1574 char **cause) 1575 { 1576 struct client *c = item->client; 1577 struct window_pane_input_data *cdata; 1578 1579 if (~wp->flags & PANE_EMPTY) { 1580 *cause = xstrdup("pane is not empty"); 1581 return (-1); 1582 } 1583 1584 cdata = xmalloc(sizeof *cdata); 1585 cdata->item = item; 1586 cdata->wp = wp->id; 1587 1588 return (server_set_stdin_callback(c, window_pane_input_callback, cdata, 1589 cause)); 1590 } 1591