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