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