1 /* $OpenBSD: window.c,v 1.110 2014/05/13 08:08:32 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 void 390 window_set_active_pane(struct window *w, struct window_pane *wp) 391 { 392 if (wp == w->active) 393 return; 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; 402 } 403 w->active->active_point = next_active_point++; 404 } 405 406 struct window_pane * 407 window_get_active_at(struct window *w, u_int x, u_int y) 408 { 409 struct window_pane *wp; 410 411 TAILQ_FOREACH(wp, &w->panes, entry) { 412 if (!window_pane_visible(wp)) 413 continue; 414 if (x < wp->xoff || x > wp->xoff + wp->sx) 415 continue; 416 if (y < wp->yoff || y > wp->yoff + wp->sy) 417 continue; 418 return (wp); 419 } 420 return (NULL); 421 } 422 423 void 424 window_set_active_at(struct window *w, u_int x, u_int y) 425 { 426 struct window_pane *wp; 427 428 wp = window_get_active_at(w, x, y); 429 if (wp != NULL && wp != w->active) 430 window_set_active_pane(w, wp); 431 } 432 433 struct window_pane * 434 window_find_string(struct window *w, const char *s) 435 { 436 u_int x, y; 437 438 x = w->sx / 2; 439 y = w->sy / 2; 440 441 if (strcasecmp(s, "top") == 0) 442 y = 0; 443 else if (strcasecmp(s, "bottom") == 0) 444 y = w->sy - 1; 445 else if (strcasecmp(s, "left") == 0) 446 x = 0; 447 else if (strcasecmp(s, "right") == 0) 448 x = w->sx - 1; 449 else if (strcasecmp(s, "top-left") == 0) { 450 x = 0; 451 y = 0; 452 } else if (strcasecmp(s, "top-right") == 0) { 453 x = w->sx - 1; 454 y = 0; 455 } else if (strcasecmp(s, "bottom-left") == 0) { 456 x = 0; 457 y = w->sy - 1; 458 } else if (strcasecmp(s, "bottom-right") == 0) { 459 x = w->sx - 1; 460 y = w->sy - 1; 461 } else 462 return (NULL); 463 464 return (window_get_active_at(w, x, y)); 465 } 466 467 int 468 window_zoom(struct window_pane *wp) 469 { 470 struct window *w = wp->window; 471 struct window_pane *wp1; 472 473 if (w->flags & WINDOW_ZOOMED) 474 return (-1); 475 476 if (!window_pane_visible(wp)) 477 return (-1); 478 479 if (window_count_panes(w) == 1) 480 return (-1); 481 482 if (w->active != wp) 483 window_set_active_pane(w, wp); 484 485 TAILQ_FOREACH(wp1, &w->panes, entry) { 486 wp1->saved_layout_cell = wp1->layout_cell; 487 wp1->layout_cell = NULL; 488 } 489 490 w->saved_layout_root = w->layout_root; 491 layout_init(w, wp); 492 w->flags |= WINDOW_ZOOMED; 493 494 return (0); 495 } 496 497 int 498 window_unzoom(struct window *w) 499 { 500 struct window_pane *wp; 501 502 if (!(w->flags & WINDOW_ZOOMED)) 503 return (-1); 504 505 w->flags &= ~WINDOW_ZOOMED; 506 layout_free(w); 507 w->layout_root = w->saved_layout_root; 508 509 TAILQ_FOREACH(wp, &w->panes, entry) { 510 wp->layout_cell = wp->saved_layout_cell; 511 wp->saved_layout_cell = NULL; 512 } 513 layout_fix_panes(w, w->sx, w->sy); 514 515 return (0); 516 } 517 518 struct window_pane * 519 window_add_pane(struct window *w, u_int hlimit) 520 { 521 struct window_pane *wp; 522 523 wp = window_pane_create(w, w->sx, w->sy, hlimit); 524 if (TAILQ_EMPTY(&w->panes)) 525 TAILQ_INSERT_HEAD(&w->panes, wp, entry); 526 else 527 TAILQ_INSERT_AFTER(&w->panes, w->active, wp, entry); 528 return (wp); 529 } 530 531 void 532 window_lost_pane(struct window *w, struct window_pane *wp) 533 { 534 if (wp == w->active) { 535 w->active = w->last; 536 w->last = NULL; 537 if (w->active == NULL) { 538 w->active = TAILQ_PREV(wp, window_panes, entry); 539 if (w->active == NULL) 540 w->active = TAILQ_NEXT(wp, entry); 541 } 542 } else if (wp == w->last) 543 w->last = NULL; 544 } 545 546 void 547 window_remove_pane(struct window *w, struct window_pane *wp) 548 { 549 window_lost_pane(w, wp); 550 551 TAILQ_REMOVE(&w->panes, wp, entry); 552 window_pane_destroy(wp); 553 } 554 555 struct window_pane * 556 window_pane_at_index(struct window *w, u_int idx) 557 { 558 struct window_pane *wp; 559 u_int n; 560 561 n = options_get_number(&w->options, "pane-base-index"); 562 TAILQ_FOREACH(wp, &w->panes, entry) { 563 if (n == idx) 564 return (wp); 565 n++; 566 } 567 return (NULL); 568 } 569 570 struct window_pane * 571 window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n) 572 { 573 for (; n > 0; n--) { 574 if ((wp = TAILQ_NEXT(wp, entry)) == NULL) 575 wp = TAILQ_FIRST(&w->panes); 576 } 577 578 return (wp); 579 } 580 581 struct window_pane * 582 window_pane_previous_by_number(struct window *w, struct window_pane *wp, 583 u_int n) 584 { 585 for (; n > 0; n--) { 586 if ((wp = TAILQ_PREV(wp, window_panes, entry)) == NULL) 587 wp = TAILQ_LAST(&w->panes, window_panes); 588 } 589 590 return (wp); 591 } 592 593 int 594 window_pane_index(struct window_pane *wp, u_int *i) 595 { 596 struct window_pane *wq; 597 struct window *w = wp->window; 598 599 *i = options_get_number(&w->options, "pane-base-index"); 600 TAILQ_FOREACH(wq, &w->panes, entry) { 601 if (wp == wq) { 602 return (0); 603 } 604 (*i)++; 605 } 606 607 return (-1); 608 } 609 610 u_int 611 window_count_panes(struct window *w) 612 { 613 struct window_pane *wp; 614 u_int n; 615 616 n = 0; 617 TAILQ_FOREACH(wp, &w->panes, entry) 618 n++; 619 return (n); 620 } 621 622 void 623 window_destroy_panes(struct window *w) 624 { 625 struct window_pane *wp; 626 627 while (!TAILQ_EMPTY(&w->panes)) { 628 wp = TAILQ_FIRST(&w->panes); 629 TAILQ_REMOVE(&w->panes, wp, entry); 630 window_pane_destroy(wp); 631 } 632 } 633 634 /* Return list of printable window flag symbols. No flags is just a space. */ 635 char * 636 window_printable_flags(struct session *s, struct winlink *wl) 637 { 638 char flags[BUFSIZ]; 639 int pos; 640 641 pos = 0; 642 if (wl->flags & WINLINK_ACTIVITY) 643 flags[pos++] = '#'; 644 if (wl->flags & WINLINK_BELL) 645 flags[pos++] = '!'; 646 if (wl->flags & WINLINK_SILENCE) 647 flags[pos++] = '~'; 648 if (wl == s->curw) 649 flags[pos++] = '*'; 650 if (wl == TAILQ_FIRST(&s->lastw)) 651 flags[pos++] = '-'; 652 if (wl->window->flags & WINDOW_ZOOMED) 653 flags[pos++] = 'Z'; 654 if (pos == 0) 655 flags[pos++] = ' '; 656 flags[pos] = '\0'; 657 return (xstrdup(flags)); 658 } 659 660 /* Find pane in global tree by id. */ 661 struct window_pane * 662 window_pane_find_by_id(u_int id) 663 { 664 struct window_pane wp; 665 666 wp.id = id; 667 return (RB_FIND(window_pane_tree, &all_window_panes, &wp)); 668 } 669 670 struct window_pane * 671 window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) 672 { 673 struct window_pane *wp; 674 675 wp = xcalloc(1, sizeof *wp); 676 wp->window = w; 677 678 wp->id = next_window_pane_id++; 679 RB_INSERT(window_pane_tree, &all_window_panes, wp); 680 681 wp->argc = 0; 682 wp->argv = NULL; 683 wp->shell = NULL; 684 wp->cwd = -1; 685 686 wp->fd = -1; 687 wp->event = NULL; 688 689 wp->mode = NULL; 690 691 wp->layout_cell = NULL; 692 693 wp->xoff = 0; 694 wp->yoff = 0; 695 696 wp->sx = sx; 697 wp->sy = sy; 698 699 wp->pipe_fd = -1; 700 wp->pipe_off = 0; 701 wp->pipe_event = NULL; 702 703 wp->saved_grid = NULL; 704 705 screen_init(&wp->base, sx, sy, hlimit); 706 wp->screen = &wp->base; 707 708 input_init(wp); 709 710 return (wp); 711 } 712 713 void 714 window_pane_destroy(struct window_pane *wp) 715 { 716 window_pane_reset_mode(wp); 717 718 if (event_initialized(&wp->changes_timer)) 719 evtimer_del(&wp->changes_timer); 720 721 if (wp->fd != -1) { 722 bufferevent_free(wp->event); 723 close(wp->fd); 724 } 725 726 input_free(wp); 727 728 screen_free(&wp->base); 729 if (wp->saved_grid != NULL) 730 grid_destroy(wp->saved_grid); 731 732 if (wp->pipe_fd != -1) { 733 bufferevent_free(wp->pipe_event); 734 close(wp->pipe_fd); 735 } 736 737 RB_REMOVE(window_pane_tree, &all_window_panes, wp); 738 739 close(wp->cwd); 740 free(wp->shell); 741 cmd_free_argv(wp->argc, wp->argv); 742 free(wp); 743 } 744 745 int 746 window_pane_spawn(struct window_pane *wp, int argc, char **argv, 747 const char *path, const char *shell, int cwd, struct environ *env, 748 struct termios *tio, char **cause) 749 { 750 struct winsize ws; 751 char *argv0, *cmd, **argvp, paneid[16]; 752 const char *ptr, *first; 753 struct termios tio2; 754 int i; 755 756 if (wp->fd != -1) { 757 bufferevent_free(wp->event); 758 close(wp->fd); 759 } 760 if (argc > 0) { 761 cmd_free_argv(wp->argc, wp->argv); 762 wp->argc = argc; 763 wp->argv = cmd_copy_argv(argc, argv); 764 } 765 if (shell != NULL) { 766 free(wp->shell); 767 wp->shell = xstrdup(shell); 768 } 769 if (cwd != -1) { 770 close(wp->cwd); 771 wp->cwd = dup(cwd); 772 } 773 774 cmd = cmd_stringify_argv(wp->argc, wp->argv); 775 log_debug("spawn: %s -- %s", wp->shell, cmd); 776 for (i = 0; i < wp->argc; i++) 777 log_debug("spawn: argv[%d] = %s", i, wp->argv[i]); 778 779 memset(&ws, 0, sizeof ws); 780 ws.ws_col = screen_size_x(&wp->base); 781 ws.ws_row = screen_size_y(&wp->base); 782 783 switch (wp->pid = forkpty(&wp->fd, wp->tty, NULL, &ws)) { 784 case -1: 785 wp->fd = -1; 786 xasprintf(cause, "%s: %s", cmd, strerror(errno)); 787 free(cmd); 788 return (-1); 789 case 0: 790 if (fchdir(wp->cwd) != 0) 791 chdir("/"); 792 793 if (tcgetattr(STDIN_FILENO, &tio2) != 0) 794 fatal("tcgetattr failed"); 795 if (tio != NULL) 796 memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc); 797 tio2.c_cc[VERASE] = '\177'; 798 if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0) 799 fatal("tcgetattr failed"); 800 801 closefrom(STDERR_FILENO + 1); 802 803 if (path != NULL) 804 environ_set(env, "PATH", path); 805 xsnprintf(paneid, sizeof paneid, "%%%u", wp->id); 806 environ_set(env, "TMUX_PANE", paneid); 807 environ_push(env); 808 809 clear_signals(1); 810 log_close(); 811 812 setenv("SHELL", wp->shell, 1); 813 ptr = strrchr(wp->shell, '/'); 814 815 /* 816 * If given one argument, assume it should be passed to sh -c; 817 * with more than one argument, use execvp(). If there is no 818 * arguments, create a login shell. 819 */ 820 if (wp->argc > 0) { 821 if (wp->argc != 1) { 822 /* Copy to ensure argv ends in NULL. */ 823 argvp = cmd_copy_argv(wp->argc, wp->argv); 824 execvp(argvp[0], argvp); 825 fatal("execvp failed"); 826 } 827 first = wp->argv[0]; 828 829 if (ptr != NULL && *(ptr + 1) != '\0') 830 xasprintf(&argv0, "%s", ptr + 1); 831 else 832 xasprintf(&argv0, "%s", wp->shell); 833 execl(wp->shell, argv0, "-c", first, (char *)NULL); 834 fatal("execl failed"); 835 } 836 if (ptr != NULL && *(ptr + 1) != '\0') 837 xasprintf(&argv0, "-%s", ptr + 1); 838 else 839 xasprintf(&argv0, "-%s", wp->shell); 840 execl(wp->shell, argv0, (char *)NULL); 841 fatal("execl failed"); 842 } 843 844 setblocking(wp->fd, 0); 845 846 wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, 847 window_pane_error_callback, wp); 848 bufferevent_enable(wp->event, EV_READ|EV_WRITE); 849 850 free(cmd); 851 return (0); 852 } 853 854 void 855 window_pane_timer_start(struct window_pane *wp) 856 { 857 struct timeval tv; 858 859 tv.tv_sec = 0; 860 tv.tv_usec = 1000; 861 862 evtimer_del(&wp->changes_timer); 863 evtimer_set(&wp->changes_timer, window_pane_timer_callback, wp); 864 evtimer_add(&wp->changes_timer, &tv); 865 } 866 867 void 868 window_pane_timer_callback(unused int fd, unused short events, void *data) 869 { 870 struct window_pane *wp = data; 871 struct window *w = wp->window; 872 u_int interval, trigger; 873 874 interval = options_get_number(&w->options, "c0-change-interval"); 875 trigger = options_get_number(&w->options, "c0-change-trigger"); 876 877 if (wp->changes_redraw++ == interval) { 878 wp->flags |= PANE_REDRAW; 879 wp->changes_redraw = 0; 880 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 (!window_pane_visible(wp)) 1056 return; 1057 1058 if (wp->mode != NULL) { 1059 if (wp->mode->key != NULL) 1060 wp->mode->key(wp, sess, key); 1061 return; 1062 } 1063 1064 if (wp->fd == -1) 1065 return; 1066 input_key(wp, key); 1067 if (options_get_number(&wp->window->options, "synchronize-panes")) { 1068 TAILQ_FOREACH(wp2, &wp->window->panes, entry) { 1069 if (wp2 == wp || wp2->mode != NULL) 1070 continue; 1071 if (wp2->fd != -1 && window_pane_visible(wp2)) 1072 input_key(wp2, key); 1073 } 1074 } 1075 } 1076 1077 void 1078 window_pane_mouse( 1079 struct window_pane *wp, struct session *sess, struct mouse_event *m) 1080 { 1081 if (!window_pane_visible(wp)) 1082 return; 1083 1084 if (m->x < wp->xoff || m->x >= wp->xoff + wp->sx) 1085 return; 1086 if (m->y < wp->yoff || m->y >= wp->yoff + wp->sy) 1087 return; 1088 m->x -= wp->xoff; 1089 m->y -= wp->yoff; 1090 1091 if (wp->mode != NULL) { 1092 if (wp->mode->mouse != NULL && 1093 options_get_number(&wp->window->options, "mode-mouse")) 1094 wp->mode->mouse(wp, sess, m); 1095 } else if (wp->fd != -1) 1096 input_mouse(wp, sess, m); 1097 } 1098 1099 int 1100 window_pane_visible(struct window_pane *wp) 1101 { 1102 struct window *w = wp->window; 1103 1104 if (wp->layout_cell == NULL) 1105 return (0); 1106 if (wp->xoff >= w->sx || wp->yoff >= w->sy) 1107 return (0); 1108 if (wp->xoff + wp->sx > w->sx || wp->yoff + wp->sy > w->sy) 1109 return (0); 1110 return (1); 1111 } 1112 1113 char * 1114 window_pane_search(struct window_pane *wp, const char *searchstr, 1115 u_int *lineno) 1116 { 1117 struct screen *s = &wp->base; 1118 char *newsearchstr, *line, *msg; 1119 u_int i; 1120 1121 msg = NULL; 1122 xasprintf(&newsearchstr, "*%s*", searchstr); 1123 1124 for (i = 0; i < screen_size_y(s); i++) { 1125 line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s)); 1126 if (fnmatch(newsearchstr, line, 0) == 0) { 1127 msg = line; 1128 if (lineno != NULL) 1129 *lineno = i; 1130 break; 1131 } 1132 free(line); 1133 } 1134 1135 free(newsearchstr); 1136 return (msg); 1137 } 1138 1139 /* Get MRU pane from a list. */ 1140 struct window_pane * 1141 window_pane_choose_best(struct window_pane_list *list) 1142 { 1143 struct window_pane *next, *best; 1144 u_int i; 1145 1146 if (ARRAY_LENGTH(list) == 0) 1147 return (NULL); 1148 1149 best = ARRAY_FIRST(list); 1150 for (i = 1; i < ARRAY_LENGTH(list); i++) { 1151 next = ARRAY_ITEM(list, i); 1152 if (next->active_point > best->active_point) 1153 best = next; 1154 } 1155 return (best); 1156 } 1157 1158 /* 1159 * Find the pane directly above another. We build a list of those adjacent to 1160 * top edge and then choose the best. 1161 */ 1162 struct window_pane * 1163 window_pane_find_up(struct window_pane *wp) 1164 { 1165 struct window_pane *next, *best; 1166 u_int edge, left, right, end; 1167 struct window_pane_list list; 1168 int found; 1169 1170 if (wp == NULL || !window_pane_visible(wp)) 1171 return (NULL); 1172 ARRAY_INIT(&list); 1173 1174 edge = wp->yoff; 1175 if (edge == 0) 1176 edge = wp->window->sy + 1; 1177 1178 left = wp->xoff; 1179 right = wp->xoff + wp->sx; 1180 1181 TAILQ_FOREACH(next, &wp->window->panes, entry) { 1182 if (next == wp || !window_pane_visible(next)) 1183 continue; 1184 if (next->yoff + next->sy + 1 != edge) 1185 continue; 1186 end = next->xoff + next->sx - 1; 1187 1188 found = 0; 1189 if (next->xoff < left && end > right) 1190 found = 1; 1191 else if (next->xoff >= left && next->xoff <= right) 1192 found = 1; 1193 else if (end >= left && end <= right) 1194 found = 1; 1195 if (found) 1196 ARRAY_ADD(&list, next); 1197 } 1198 1199 best = window_pane_choose_best(&list); 1200 ARRAY_FREE(&list); 1201 return (best); 1202 } 1203 1204 /* Find the pane directly below another. */ 1205 struct window_pane * 1206 window_pane_find_down(struct window_pane *wp) 1207 { 1208 struct window_pane *next, *best; 1209 u_int edge, left, right, end; 1210 struct window_pane_list list; 1211 int found; 1212 1213 if (wp == NULL || !window_pane_visible(wp)) 1214 return (NULL); 1215 ARRAY_INIT(&list); 1216 1217 edge = wp->yoff + wp->sy + 1; 1218 if (edge >= wp->window->sy) 1219 edge = 0; 1220 1221 left = wp->xoff; 1222 right = wp->xoff + wp->sx; 1223 1224 TAILQ_FOREACH(next, &wp->window->panes, entry) { 1225 if (next == wp || !window_pane_visible(next)) 1226 continue; 1227 if (next->yoff != edge) 1228 continue; 1229 end = next->xoff + next->sx - 1; 1230 1231 found = 0; 1232 if (next->xoff < left && end > right) 1233 found = 1; 1234 else if (next->xoff >= left && next->xoff <= right) 1235 found = 1; 1236 else if (end >= left && end <= right) 1237 found = 1; 1238 if (found) 1239 ARRAY_ADD(&list, next); 1240 } 1241 1242 best = window_pane_choose_best(&list); 1243 ARRAY_FREE(&list); 1244 return (best); 1245 } 1246 1247 /* Find the pane directly to the left of another. */ 1248 struct window_pane * 1249 window_pane_find_left(struct window_pane *wp) 1250 { 1251 struct window_pane *next, *best; 1252 u_int edge, top, bottom, end; 1253 struct window_pane_list list; 1254 int found; 1255 1256 if (wp == NULL || !window_pane_visible(wp)) 1257 return (NULL); 1258 ARRAY_INIT(&list); 1259 1260 edge = wp->xoff; 1261 if (edge == 0) 1262 edge = wp->window->sx + 1; 1263 1264 top = wp->yoff; 1265 bottom = wp->yoff + wp->sy; 1266 1267 TAILQ_FOREACH(next, &wp->window->panes, entry) { 1268 if (next == wp || !window_pane_visible(next)) 1269 continue; 1270 if (next->xoff + next->sx + 1 != edge) 1271 continue; 1272 end = next->yoff + next->sy - 1; 1273 1274 found = 0; 1275 if (next->yoff < top && end > bottom) 1276 found = 1; 1277 else if (next->yoff >= top && next->yoff <= bottom) 1278 found = 1; 1279 else if (end >= top && end <= bottom) 1280 found = 1; 1281 if (found) 1282 ARRAY_ADD(&list, next); 1283 } 1284 1285 best = window_pane_choose_best(&list); 1286 ARRAY_FREE(&list); 1287 return (best); 1288 } 1289 1290 /* Find the pane directly to the right of another. */ 1291 struct window_pane * 1292 window_pane_find_right(struct window_pane *wp) 1293 { 1294 struct window_pane *next, *best; 1295 u_int edge, top, bottom, end; 1296 struct window_pane_list list; 1297 int found; 1298 1299 if (wp == NULL || !window_pane_visible(wp)) 1300 return (NULL); 1301 ARRAY_INIT(&list); 1302 1303 edge = wp->xoff + wp->sx + 1; 1304 if (edge >= wp->window->sx) 1305 edge = 0; 1306 1307 top = wp->yoff; 1308 bottom = wp->yoff + wp->sy; 1309 1310 TAILQ_FOREACH(next, &wp->window->panes, entry) { 1311 if (next == wp || !window_pane_visible(next)) 1312 continue; 1313 if (next->xoff != edge) 1314 continue; 1315 end = next->yoff + next->sy - 1; 1316 1317 found = 0; 1318 if (next->yoff < top && end > bottom) 1319 found = 1; 1320 else if (next->yoff >= top && next->yoff <= bottom) 1321 found = 1; 1322 else if (end >= top && end <= bottom) 1323 found = 1; 1324 if (found) 1325 ARRAY_ADD(&list, next); 1326 } 1327 1328 best = window_pane_choose_best(&list); 1329 ARRAY_FREE(&list); 1330 return (best); 1331 } 1332 1333 /* Clear alert flags for a winlink */ 1334 void 1335 winlink_clear_flags(struct winlink *wl) 1336 { 1337 struct winlink *wm; 1338 struct session *s; 1339 struct window *w; 1340 u_int i; 1341 1342 for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 1343 if ((w = ARRAY_ITEM(&windows, i)) == NULL) 1344 continue; 1345 1346 RB_FOREACH(s, sessions, &sessions) { 1347 if ((wm = session_has(s, w)) == NULL) 1348 continue; 1349 1350 if (wm->window != wl->window) 1351 continue; 1352 if ((wm->flags & WINLINK_ALERTFLAGS) == 0) 1353 continue; 1354 1355 wm->flags &= ~WINLINK_ALERTFLAGS; 1356 wm->window->flags &= ~WINDOW_ALERTFLAGS; 1357 server_status_session(s); 1358 } 1359 } 1360 } 1361