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