1 /* $OpenBSD: window.c,v 1.144 2015/08/29 23:55:55 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> 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 <stdint.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <termios.h> 28 #include <unistd.h> 29 #include <util.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 options_init(&w->options, &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, int cwd, struct environ *env, struct termios *tio, 320 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 struct window_pane * 427 window_get_active_at(struct window *w, u_int x, u_int y) 428 { 429 struct window_pane *wp; 430 431 TAILQ_FOREACH(wp, &w->panes, entry) { 432 if (!window_pane_visible(wp)) 433 continue; 434 if (x < wp->xoff || x > wp->xoff + wp->sx) 435 continue; 436 if (y < wp->yoff || y > wp->yoff + wp->sy) 437 continue; 438 return (wp); 439 } 440 return (NULL); 441 } 442 443 struct window_pane * 444 window_find_string(struct window *w, const char *s) 445 { 446 u_int x, y; 447 448 x = w->sx / 2; 449 y = w->sy / 2; 450 451 if (strcasecmp(s, "top") == 0) 452 y = 0; 453 else if (strcasecmp(s, "bottom") == 0) 454 y = w->sy - 1; 455 else if (strcasecmp(s, "left") == 0) 456 x = 0; 457 else if (strcasecmp(s, "right") == 0) 458 x = w->sx - 1; 459 else if (strcasecmp(s, "top-left") == 0) { 460 x = 0; 461 y = 0; 462 } else if (strcasecmp(s, "top-right") == 0) { 463 x = w->sx - 1; 464 y = 0; 465 } else if (strcasecmp(s, "bottom-left") == 0) { 466 x = 0; 467 y = w->sy - 1; 468 } else if (strcasecmp(s, "bottom-right") == 0) { 469 x = w->sx - 1; 470 y = w->sy - 1; 471 } else 472 return (NULL); 473 474 return (window_get_active_at(w, x, y)); 475 } 476 477 int 478 window_zoom(struct window_pane *wp) 479 { 480 struct window *w = wp->window; 481 struct window_pane *wp1; 482 483 if (w->flags & WINDOW_ZOOMED) 484 return (-1); 485 486 if (!window_pane_visible(wp)) 487 return (-1); 488 489 if (window_count_panes(w) == 1) 490 return (-1); 491 492 if (w->active != wp) 493 window_set_active_pane(w, wp); 494 495 TAILQ_FOREACH(wp1, &w->panes, entry) { 496 wp1->saved_layout_cell = wp1->layout_cell; 497 wp1->layout_cell = NULL; 498 } 499 500 w->saved_layout_root = w->layout_root; 501 layout_init(w, wp); 502 w->flags |= WINDOW_ZOOMED; 503 notify_window_layout_changed(w); 504 505 return (0); 506 } 507 508 int 509 window_unzoom(struct window *w) 510 { 511 struct window_pane *wp; 512 513 if (!(w->flags & WINDOW_ZOOMED)) 514 return (-1); 515 516 w->flags &= ~WINDOW_ZOOMED; 517 layout_free(w); 518 w->layout_root = w->saved_layout_root; 519 w->saved_layout_root = NULL; 520 521 TAILQ_FOREACH(wp, &w->panes, entry) { 522 wp->layout_cell = wp->saved_layout_cell; 523 wp->saved_layout_cell = NULL; 524 } 525 layout_fix_panes(w, w->sx, w->sy); 526 notify_window_layout_changed(w); 527 528 return (0); 529 } 530 531 struct window_pane * 532 window_add_pane(struct window *w, u_int hlimit) 533 { 534 struct window_pane *wp; 535 536 wp = window_pane_create(w, w->sx, w->sy, hlimit); 537 if (TAILQ_EMPTY(&w->panes)) 538 TAILQ_INSERT_HEAD(&w->panes, wp, entry); 539 else 540 TAILQ_INSERT_AFTER(&w->panes, w->active, wp, entry); 541 return (wp); 542 } 543 544 void 545 window_lost_pane(struct window *w, struct window_pane *wp) 546 { 547 if (wp == marked_window_pane) 548 server_clear_marked(); 549 550 if (wp == w->active) { 551 w->active = w->last; 552 w->last = NULL; 553 if (w->active == NULL) { 554 w->active = TAILQ_PREV(wp, window_panes, entry); 555 if (w->active == NULL) 556 w->active = TAILQ_NEXT(wp, entry); 557 } 558 } else if (wp == w->last) 559 w->last = NULL; 560 } 561 562 void 563 window_remove_pane(struct window *w, struct window_pane *wp) 564 { 565 window_lost_pane(w, wp); 566 567 TAILQ_REMOVE(&w->panes, wp, entry); 568 window_pane_destroy(wp); 569 } 570 571 struct window_pane * 572 window_pane_at_index(struct window *w, u_int idx) 573 { 574 struct window_pane *wp; 575 u_int n; 576 577 n = options_get_number(&w->options, "pane-base-index"); 578 TAILQ_FOREACH(wp, &w->panes, entry) { 579 if (n == idx) 580 return (wp); 581 n++; 582 } 583 return (NULL); 584 } 585 586 struct window_pane * 587 window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n) 588 { 589 for (; n > 0; n--) { 590 if ((wp = TAILQ_NEXT(wp, entry)) == NULL) 591 wp = TAILQ_FIRST(&w->panes); 592 } 593 594 return (wp); 595 } 596 597 struct window_pane * 598 window_pane_previous_by_number(struct window *w, struct window_pane *wp, 599 u_int n) 600 { 601 for (; n > 0; n--) { 602 if ((wp = TAILQ_PREV(wp, window_panes, entry)) == NULL) 603 wp = TAILQ_LAST(&w->panes, window_panes); 604 } 605 606 return (wp); 607 } 608 609 int 610 window_pane_index(struct window_pane *wp, u_int *i) 611 { 612 struct window_pane *wq; 613 struct window *w = wp->window; 614 615 *i = options_get_number(&w->options, "pane-base-index"); 616 TAILQ_FOREACH(wq, &w->panes, entry) { 617 if (wp == wq) { 618 return (0); 619 } 620 (*i)++; 621 } 622 623 return (-1); 624 } 625 626 u_int 627 window_count_panes(struct window *w) 628 { 629 struct window_pane *wp; 630 u_int n; 631 632 n = 0; 633 TAILQ_FOREACH(wp, &w->panes, entry) 634 n++; 635 return (n); 636 } 637 638 void 639 window_destroy_panes(struct window *w) 640 { 641 struct window_pane *wp; 642 643 while (!TAILQ_EMPTY(&w->panes)) { 644 wp = TAILQ_FIRST(&w->panes); 645 TAILQ_REMOVE(&w->panes, wp, entry); 646 window_pane_destroy(wp); 647 } 648 } 649 650 /* Retuns the printable flags on a window, empty string if no flags set. */ 651 char * 652 window_printable_flags(struct session *s, struct winlink *wl) 653 { 654 char flags[32]; 655 int pos; 656 657 pos = 0; 658 if (wl->flags & WINLINK_ACTIVITY) 659 flags[pos++] = '#'; 660 if (wl->flags & WINLINK_BELL) 661 flags[pos++] = '!'; 662 if (wl->flags & WINLINK_SILENCE) 663 flags[pos++] = '~'; 664 if (wl == s->curw) 665 flags[pos++] = '*'; 666 if (wl == TAILQ_FIRST(&s->lastw)) 667 flags[pos++] = '-'; 668 if (server_check_marked() && wl == marked_winlink) 669 flags[pos++] = 'M'; 670 if (wl->window->flags & WINDOW_ZOOMED) 671 flags[pos++] = 'Z'; 672 flags[pos] = '\0'; 673 return (xstrdup(flags)); 674 } 675 676 struct window_pane * 677 window_pane_find_by_id_str(const char *s) 678 { 679 const char *errstr; 680 u_int id; 681 682 if (*s != '%') 683 return (NULL); 684 685 id = strtonum(s + 1, 0, UINT_MAX, &errstr); 686 if (errstr != NULL) 687 return (NULL); 688 return (window_pane_find_by_id(id)); 689 } 690 691 struct window_pane * 692 window_pane_find_by_id(u_int id) 693 { 694 struct window_pane wp; 695 696 wp.id = id; 697 return (RB_FIND(window_pane_tree, &all_window_panes, &wp)); 698 } 699 700 struct window_pane * 701 window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) 702 { 703 struct window_pane *wp; 704 char host[HOST_NAME_MAX + 1]; 705 706 wp = xcalloc(1, sizeof *wp); 707 wp->window = w; 708 709 wp->id = next_window_pane_id++; 710 RB_INSERT(window_pane_tree, &all_window_panes, wp); 711 712 wp->argc = 0; 713 wp->argv = NULL; 714 wp->shell = NULL; 715 wp->cwd = -1; 716 717 wp->fd = -1; 718 wp->event = NULL; 719 720 wp->mode = NULL; 721 722 wp->layout_cell = NULL; 723 724 wp->xoff = 0; 725 wp->yoff = 0; 726 727 wp->sx = sx; 728 wp->sy = sy; 729 730 wp->pipe_fd = -1; 731 wp->pipe_off = 0; 732 wp->pipe_event = NULL; 733 734 wp->saved_grid = NULL; 735 736 memcpy(&wp->colgc, &grid_default_cell, sizeof wp->colgc); 737 738 screen_init(&wp->base, sx, sy, hlimit); 739 wp->screen = &wp->base; 740 741 if (gethostname(host, sizeof host) == 0) 742 screen_set_title(&wp->base, host); 743 744 input_init(wp); 745 746 return (wp); 747 } 748 749 void 750 window_pane_destroy(struct window_pane *wp) 751 { 752 window_pane_reset_mode(wp); 753 754 if (event_initialized(&wp->timer)) 755 evtimer_del(&wp->timer); 756 757 if (wp->fd != -1) { 758 bufferevent_free(wp->event); 759 close(wp->fd); 760 } 761 762 input_free(wp); 763 764 screen_free(&wp->base); 765 if (wp->saved_grid != NULL) 766 grid_destroy(wp->saved_grid); 767 768 if (wp->pipe_fd != -1) { 769 bufferevent_free(wp->pipe_event); 770 close(wp->pipe_fd); 771 } 772 773 RB_REMOVE(window_pane_tree, &all_window_panes, wp); 774 775 close(wp->cwd); 776 free(wp->shell); 777 cmd_free_argv(wp->argc, wp->argv); 778 free(wp); 779 } 780 781 int 782 window_pane_spawn(struct window_pane *wp, int argc, char **argv, 783 const char *path, const char *shell, int cwd, struct environ *env, 784 struct termios *tio, char **cause) 785 { 786 struct winsize ws; 787 char *argv0, *cmd, **argvp, paneid[16]; 788 const char *ptr, *first; 789 struct termios tio2; 790 int i; 791 792 if (wp->fd != -1) { 793 bufferevent_free(wp->event); 794 close(wp->fd); 795 } 796 if (argc > 0) { 797 cmd_free_argv(wp->argc, wp->argv); 798 wp->argc = argc; 799 wp->argv = cmd_copy_argv(argc, argv); 800 } 801 if (shell != NULL) { 802 free(wp->shell); 803 wp->shell = xstrdup(shell); 804 } 805 if (cwd != -1) { 806 close(wp->cwd); 807 wp->cwd = dup(cwd); 808 } 809 810 cmd = cmd_stringify_argv(wp->argc, wp->argv); 811 log_debug("spawn: %s -- %s", wp->shell, cmd); 812 for (i = 0; i < wp->argc; i++) 813 log_debug("spawn: argv[%d] = %s", i, wp->argv[i]); 814 815 memset(&ws, 0, sizeof ws); 816 ws.ws_col = screen_size_x(&wp->base); 817 ws.ws_row = screen_size_y(&wp->base); 818 819 switch (wp->pid = forkpty(&wp->fd, wp->tty, NULL, &ws)) { 820 case -1: 821 wp->fd = -1; 822 xasprintf(cause, "%s: %s", cmd, strerror(errno)); 823 free(cmd); 824 return (-1); 825 case 0: 826 if (fchdir(wp->cwd) != 0) 827 chdir("/"); 828 829 if (tcgetattr(STDIN_FILENO, &tio2) != 0) 830 fatal("tcgetattr failed"); 831 if (tio != NULL) 832 memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc); 833 tio2.c_cc[VERASE] = '\177'; 834 if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0) 835 fatal("tcgetattr failed"); 836 837 closefrom(STDERR_FILENO + 1); 838 839 if (path != NULL) 840 environ_set(env, "PATH", path); 841 xsnprintf(paneid, sizeof paneid, "%%%u", wp->id); 842 environ_set(env, "TMUX_PANE", paneid); 843 environ_push(env); 844 845 clear_signals(1); 846 log_close(); 847 848 setenv("SHELL", wp->shell, 1); 849 ptr = strrchr(wp->shell, '/'); 850 851 /* 852 * If given one argument, assume it should be passed to sh -c; 853 * with more than one argument, use execvp(). If there is no 854 * arguments, create a login shell. 855 */ 856 if (wp->argc > 0) { 857 if (wp->argc != 1) { 858 /* Copy to ensure argv ends in NULL. */ 859 argvp = cmd_copy_argv(wp->argc, wp->argv); 860 execvp(argvp[0], argvp); 861 fatal("execvp failed"); 862 } 863 first = wp->argv[0]; 864 865 if (ptr != NULL && *(ptr + 1) != '\0') 866 xasprintf(&argv0, "%s", ptr + 1); 867 else 868 xasprintf(&argv0, "%s", wp->shell); 869 execl(wp->shell, argv0, "-c", first, (char *)NULL); 870 fatal("execl failed"); 871 } 872 if (ptr != NULL && *(ptr + 1) != '\0') 873 xasprintf(&argv0, "-%s", ptr + 1); 874 else 875 xasprintf(&argv0, "-%s", wp->shell); 876 execl(wp->shell, argv0, (char *)NULL); 877 fatal("execl failed"); 878 } 879 880 setblocking(wp->fd, 0); 881 882 wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, 883 window_pane_error_callback, wp); 884 885 bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); 886 bufferevent_enable(wp->event, EV_READ|EV_WRITE); 887 888 free(cmd); 889 return (0); 890 } 891 892 void 893 window_pane_timer_callback(unused int fd, unused short events, void *data) 894 { 895 window_pane_read_callback(NULL, data); 896 } 897 898 void 899 window_pane_read_callback(unused struct bufferevent *bufev, void *data) 900 { 901 struct window_pane *wp = data; 902 struct evbuffer *evb = wp->event->input; 903 char *new_data; 904 size_t new_size, available; 905 struct client *c; 906 struct timeval tv; 907 908 if (event_initialized(&wp->timer)) 909 evtimer_del(&wp->timer); 910 911 log_debug("%%%u has %zu bytes", wp->id, EVBUFFER_LENGTH(evb)); 912 913 TAILQ_FOREACH(c, &clients, entry) { 914 if (!tty_client_ready(c, wp)) 915 continue; 916 917 available = EVBUFFER_LENGTH(c->tty.event->output); 918 if (available > READ_BACKOFF) 919 goto start_timer; 920 } 921 922 new_size = EVBUFFER_LENGTH(evb) - wp->pipe_off; 923 if (wp->pipe_fd != -1 && new_size > 0) { 924 new_data = EVBUFFER_DATA(evb); 925 bufferevent_write(wp->pipe_event, new_data, new_size); 926 } 927 928 input_parse(wp); 929 930 wp->pipe_off = EVBUFFER_LENGTH(evb); 931 return; 932 933 start_timer: 934 log_debug("%%%u backing off (%s %zu > %d)", wp->id, c->ttyname, 935 available, READ_BACKOFF); 936 937 tv.tv_sec = 0; 938 tv.tv_usec = READ_TIME; 939 940 evtimer_set(&wp->timer, window_pane_timer_callback, wp); 941 evtimer_add(&wp->timer, &tv); 942 } 943 944 void 945 window_pane_error_callback(unused struct bufferevent *bufev, unused short what, 946 void *data) 947 { 948 struct window_pane *wp = data; 949 950 server_destroy_pane(wp); 951 } 952 953 void 954 window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) 955 { 956 if (sx == wp->sx && sy == wp->sy) 957 return; 958 wp->sx = sx; 959 wp->sy = sy; 960 961 screen_resize(&wp->base, sx, sy, wp->saved_grid == NULL); 962 if (wp->mode != NULL) 963 wp->mode->resize(wp, sx, sy); 964 965 wp->flags |= PANE_RESIZE; 966 } 967 968 /* 969 * Enter alternative screen mode. A copy of the visible screen is saved and the 970 * history is not updated 971 */ 972 void 973 window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc, 974 int cursor) 975 { 976 struct screen *s = &wp->base; 977 u_int sx, sy; 978 979 if (wp->saved_grid != NULL) 980 return; 981 if (!options_get_number(&wp->window->options, "alternate-screen")) 982 return; 983 sx = screen_size_x(s); 984 sy = screen_size_y(s); 985 986 wp->saved_grid = grid_create(sx, sy, 0); 987 grid_duplicate_lines(wp->saved_grid, 0, s->grid, screen_hsize(s), sy); 988 if (cursor) { 989 wp->saved_cx = s->cx; 990 wp->saved_cy = s->cy; 991 } 992 memcpy(&wp->saved_cell, gc, sizeof wp->saved_cell); 993 994 grid_view_clear(s->grid, 0, 0, sx, sy); 995 996 wp->base.grid->flags &= ~GRID_HISTORY; 997 998 wp->flags |= PANE_REDRAW; 999 } 1000 1001 /* Exit alternate screen mode and restore the copied grid. */ 1002 void 1003 window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc, 1004 int cursor) 1005 { 1006 struct screen *s = &wp->base; 1007 u_int sx, sy; 1008 1009 if (wp->saved_grid == NULL) 1010 return; 1011 if (!options_get_number(&wp->window->options, "alternate-screen")) 1012 return; 1013 sx = screen_size_x(s); 1014 sy = screen_size_y(s); 1015 1016 /* 1017 * If the current size is bigger, temporarily resize to the old size 1018 * before copying back. 1019 */ 1020 if (sy > wp->saved_grid->sy) 1021 screen_resize(s, sx, wp->saved_grid->sy, 1); 1022 1023 /* Restore the grid, cursor position and cell. */ 1024 grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy); 1025 if (cursor) 1026 s->cx = wp->saved_cx; 1027 if (s->cx > screen_size_x(s) - 1) 1028 s->cx = screen_size_x(s) - 1; 1029 if (cursor) 1030 s->cy = wp->saved_cy; 1031 if (s->cy > screen_size_y(s) - 1) 1032 s->cy = screen_size_y(s) - 1; 1033 memcpy(gc, &wp->saved_cell, sizeof *gc); 1034 1035 /* 1036 * Turn history back on (so resize can use it) and then resize back to 1037 * the current size. 1038 */ 1039 wp->base.grid->flags |= GRID_HISTORY; 1040 if (sy > wp->saved_grid->sy || sx != wp->saved_grid->sx) 1041 screen_resize(s, sx, sy, 1); 1042 1043 grid_destroy(wp->saved_grid); 1044 wp->saved_grid = NULL; 1045 1046 wp->flags |= PANE_REDRAW; 1047 } 1048 1049 int 1050 window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode) 1051 { 1052 struct screen *s; 1053 1054 if (wp->mode != NULL) 1055 return (1); 1056 wp->mode = mode; 1057 1058 if ((s = wp->mode->init(wp)) != NULL) 1059 wp->screen = s; 1060 wp->flags |= (PANE_REDRAW|PANE_CHANGED); 1061 return (0); 1062 } 1063 1064 void 1065 window_pane_reset_mode(struct window_pane *wp) 1066 { 1067 if (wp->mode == NULL) 1068 return; 1069 1070 wp->mode->free(wp); 1071 wp->mode = NULL; 1072 1073 wp->screen = &wp->base; 1074 wp->flags |= (PANE_REDRAW|PANE_CHANGED); 1075 } 1076 1077 void 1078 window_pane_key(struct window_pane *wp, struct client *c, struct session *s, 1079 int key, struct mouse_event *m) 1080 { 1081 struct window_pane *wp2; 1082 1083 if (KEYC_IS_MOUSE(key) && m == NULL) 1084 return; 1085 1086 if (wp->mode != NULL) { 1087 if (wp->mode->key != NULL) 1088 wp->mode->key(wp, c, s, key, m); 1089 return; 1090 } 1091 1092 if (wp->fd == -1 || wp->flags & PANE_INPUTOFF) 1093 return; 1094 1095 input_key(wp, key, m); 1096 1097 if (KEYC_IS_MOUSE(key)) 1098 return; 1099 if (options_get_number(&wp->window->options, "synchronize-panes")) { 1100 TAILQ_FOREACH(wp2, &wp->window->panes, entry) { 1101 if (wp2 == wp || wp2->mode != NULL) 1102 continue; 1103 if (wp2->fd != -1 && window_pane_visible(wp2)) 1104 input_key(wp2, key, NULL); 1105 } 1106 } 1107 } 1108 1109 int 1110 window_pane_visible(struct window_pane *wp) 1111 { 1112 struct window *w = wp->window; 1113 1114 if (wp->layout_cell == NULL) 1115 return (0); 1116 if (wp->xoff >= w->sx || wp->yoff >= w->sy) 1117 return (0); 1118 if (wp->xoff + wp->sx > w->sx || wp->yoff + wp->sy > w->sy) 1119 return (0); 1120 return (1); 1121 } 1122 1123 char * 1124 window_pane_search(struct window_pane *wp, const char *searchstr, 1125 u_int *lineno) 1126 { 1127 struct screen *s = &wp->base; 1128 char *newsearchstr, *line, *msg; 1129 u_int i; 1130 1131 msg = NULL; 1132 xasprintf(&newsearchstr, "*%s*", searchstr); 1133 1134 for (i = 0; i < screen_size_y(s); i++) { 1135 line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s)); 1136 if (fnmatch(newsearchstr, line, 0) == 0) { 1137 msg = line; 1138 if (lineno != NULL) 1139 *lineno = i; 1140 break; 1141 } 1142 free(line); 1143 } 1144 1145 free(newsearchstr); 1146 return (msg); 1147 } 1148 1149 /* Get MRU pane from a list. */ 1150 struct window_pane * 1151 window_pane_choose_best(struct window_pane **list, u_int size) 1152 { 1153 struct window_pane *next, *best; 1154 u_int i; 1155 1156 if (size == 0) 1157 return (NULL); 1158 1159 best = list[0]; 1160 for (i = 1; i < size; i++) { 1161 next = list[i]; 1162 if (next->active_point > best->active_point) 1163 best = next; 1164 } 1165 return (best); 1166 } 1167 1168 /* 1169 * Find the pane directly above another. We build a list of those adjacent to 1170 * top edge and then choose the best. 1171 */ 1172 struct window_pane * 1173 window_pane_find_up(struct window_pane *wp) 1174 { 1175 struct window_pane *next, *best, **list; 1176 u_int edge, left, right, end, size; 1177 int found; 1178 1179 if (wp == NULL || !window_pane_visible(wp)) 1180 return (NULL); 1181 1182 list = NULL; 1183 size = 0; 1184 1185 edge = wp->yoff; 1186 if (edge == 0) 1187 edge = wp->window->sy + 1; 1188 1189 left = wp->xoff; 1190 right = wp->xoff + wp->sx; 1191 1192 TAILQ_FOREACH(next, &wp->window->panes, entry) { 1193 if (next == wp || !window_pane_visible(next)) 1194 continue; 1195 if (next->yoff + next->sy + 1 != edge) 1196 continue; 1197 end = next->xoff + next->sx - 1; 1198 1199 found = 0; 1200 if (next->xoff < left && end > right) 1201 found = 1; 1202 else if (next->xoff >= left && next->xoff <= right) 1203 found = 1; 1204 else if (end >= left && end <= right) 1205 found = 1; 1206 if (!found) 1207 continue; 1208 list = xreallocarray(list, size + 1, sizeof *list); 1209 list[size++] = next; 1210 } 1211 1212 best = window_pane_choose_best(list, size); 1213 free(list); 1214 return (best); 1215 } 1216 1217 /* Find the pane directly below another. */ 1218 struct window_pane * 1219 window_pane_find_down(struct window_pane *wp) 1220 { 1221 struct window_pane *next, *best, **list; 1222 u_int edge, left, right, end, size; 1223 int found; 1224 1225 if (wp == NULL || !window_pane_visible(wp)) 1226 return (NULL); 1227 1228 list = NULL; 1229 size = 0; 1230 1231 edge = wp->yoff + wp->sy + 1; 1232 if (edge >= wp->window->sy) 1233 edge = 0; 1234 1235 left = wp->xoff; 1236 right = wp->xoff + wp->sx; 1237 1238 TAILQ_FOREACH(next, &wp->window->panes, entry) { 1239 if (next == wp || !window_pane_visible(next)) 1240 continue; 1241 if (next->yoff != edge) 1242 continue; 1243 end = next->xoff + next->sx - 1; 1244 1245 found = 0; 1246 if (next->xoff < left && end > right) 1247 found = 1; 1248 else if (next->xoff >= left && next->xoff <= right) 1249 found = 1; 1250 else if (end >= left && end <= right) 1251 found = 1; 1252 if (!found) 1253 continue; 1254 list = xreallocarray(list, size + 1, sizeof *list); 1255 list[size++] = next; 1256 } 1257 1258 best = window_pane_choose_best(list, size); 1259 free(list); 1260 return (best); 1261 } 1262 1263 /* Find the pane directly to the left of another. */ 1264 struct window_pane * 1265 window_pane_find_left(struct window_pane *wp) 1266 { 1267 struct window_pane *next, *best, **list; 1268 u_int edge, top, bottom, end, size; 1269 int found; 1270 1271 if (wp == NULL || !window_pane_visible(wp)) 1272 return (NULL); 1273 1274 list = NULL; 1275 size = 0; 1276 1277 edge = wp->xoff; 1278 if (edge == 0) 1279 edge = wp->window->sx + 1; 1280 1281 top = wp->yoff; 1282 bottom = wp->yoff + wp->sy; 1283 1284 TAILQ_FOREACH(next, &wp->window->panes, entry) { 1285 if (next == wp || !window_pane_visible(next)) 1286 continue; 1287 if (next->xoff + next->sx + 1 != edge) 1288 continue; 1289 end = next->yoff + next->sy - 1; 1290 1291 found = 0; 1292 if (next->yoff < top && end > bottom) 1293 found = 1; 1294 else if (next->yoff >= top && next->yoff <= bottom) 1295 found = 1; 1296 else if (end >= top && end <= bottom) 1297 found = 1; 1298 if (!found) 1299 continue; 1300 list = xreallocarray(list, size + 1, sizeof *list); 1301 list[size++] = next; 1302 } 1303 1304 best = window_pane_choose_best(list, size); 1305 free(list); 1306 return (best); 1307 } 1308 1309 /* Find the pane directly to the right of another. */ 1310 struct window_pane * 1311 window_pane_find_right(struct window_pane *wp) 1312 { 1313 struct window_pane *next, *best, **list; 1314 u_int edge, top, bottom, end, size; 1315 int found; 1316 1317 if (wp == NULL || !window_pane_visible(wp)) 1318 return (NULL); 1319 1320 list = NULL; 1321 size = 0; 1322 1323 edge = wp->xoff + wp->sx + 1; 1324 if (edge >= wp->window->sx) 1325 edge = 0; 1326 1327 top = wp->yoff; 1328 bottom = wp->yoff + wp->sy; 1329 1330 TAILQ_FOREACH(next, &wp->window->panes, entry) { 1331 if (next == wp || !window_pane_visible(next)) 1332 continue; 1333 if (next->xoff != edge) 1334 continue; 1335 end = next->yoff + next->sy - 1; 1336 1337 found = 0; 1338 if (next->yoff < top && end > bottom) 1339 found = 1; 1340 else if (next->yoff >= top && next->yoff <= bottom) 1341 found = 1; 1342 else if (end >= top && end <= bottom) 1343 found = 1; 1344 if (!found) 1345 continue; 1346 list = xreallocarray(list, size + 1, sizeof *list); 1347 list[size++] = next; 1348 } 1349 1350 best = window_pane_choose_best(list, size); 1351 free(list); 1352 return (best); 1353 } 1354 1355 /* Clear alert flags for a winlink */ 1356 void 1357 winlink_clear_flags(struct winlink *wl) 1358 { 1359 struct session *s; 1360 struct winlink *wl_loop; 1361 1362 RB_FOREACH(s, sessions, &sessions) { 1363 RB_FOREACH(wl_loop, winlinks, &s->windows) { 1364 if (wl_loop->window != wl->window) 1365 continue; 1366 if ((wl_loop->flags & WINLINK_ALERTFLAGS) == 0) 1367 continue; 1368 1369 wl_loop->flags &= ~WINLINK_ALERTFLAGS; 1370 wl_loop->window->flags &= ~WINDOW_ALERTFLAGS; 1371 server_status_session(s); 1372 } 1373 } 1374 } 1375 1376 int 1377 winlink_shuffle_up(struct session *s, struct winlink *wl) 1378 { 1379 int idx, last; 1380 1381 idx = wl->idx + 1; 1382 1383 /* Find the next free index. */ 1384 for (last = idx; last < INT_MAX; last++) { 1385 if (winlink_find_by_index(&s->windows, last) == NULL) 1386 break; 1387 } 1388 if (last == INT_MAX) 1389 return (-1); 1390 1391 /* Move everything from last - 1 to idx up a bit. */ 1392 for (; last > idx; last--) { 1393 wl = winlink_find_by_index(&s->windows, last - 1); 1394 server_link_window(s, wl, s, last, 0, 0, NULL); 1395 server_unlink_window(s, wl); 1396 } 1397 1398 return (idx); 1399 } 1400