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