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