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