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