1 /* $Id: window.c,v 1.4 2011/09/17 01:50:09 christos 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 #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; 59 60 void window_pane_read_callback(struct bufferevent *, void *); 61 void window_pane_error_callback(struct bufferevent *, short, void *); 62 63 RB_GENERATE(winlinks, winlink, entry, winlink_cmp); 64 65 int 66 winlink_cmp(struct winlink *wl1, struct winlink *wl2) 67 { 68 return (wl1->idx - wl2->idx); 69 } 70 71 RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp); 72 73 int 74 window_pane_cmp(struct window_pane *wp1, struct window_pane *wp2) 75 { 76 return (wp1->id - wp2->id); 77 } 78 79 struct winlink * 80 winlink_find_by_window(struct winlinks *wwl, struct window *w) 81 { 82 struct winlink *wl; 83 84 RB_FOREACH(wl, winlinks, wwl) { 85 if (wl->window == w) 86 return (wl); 87 } 88 89 return (NULL); 90 } 91 92 struct winlink * 93 winlink_find_by_index(struct winlinks *wwl, int idx) 94 { 95 struct winlink wl; 96 97 if (idx < 0) 98 fatalx("bad index"); 99 100 wl.idx = idx; 101 return (RB_FIND(winlinks, wwl, &wl)); 102 } 103 104 int 105 winlink_next_index(struct winlinks *wwl, int idx) 106 { 107 int i; 108 109 i = idx; 110 do { 111 if (winlink_find_by_index(wwl, i) == NULL) 112 return (i); 113 if (i == INT_MAX) 114 i = 0; 115 else 116 i++; 117 } while (i != idx); 118 return (-1); 119 } 120 121 u_int 122 winlink_count(struct winlinks *wwl) 123 { 124 struct winlink *wl; 125 u_int n; 126 127 n = 0; 128 RB_FOREACH(wl, winlinks, wwl) 129 n++; 130 131 return (n); 132 } 133 134 struct winlink * 135 winlink_add(struct winlinks *wwl, int idx) 136 { 137 struct winlink *wl; 138 139 if (idx < 0) { 140 if ((idx = winlink_next_index(wwl, -idx - 1)) == -1) 141 return (NULL); 142 } else if (winlink_find_by_index(wwl, idx) != NULL) 143 return (NULL); 144 145 wl = xcalloc(1, sizeof *wl); 146 wl->idx = idx; 147 RB_INSERT(winlinks, wwl, wl); 148 149 return (wl); 150 } 151 152 void 153 winlink_set_window(struct winlink *wl, struct window *w) 154 { 155 wl->window = w; 156 w->references++; 157 } 158 159 void 160 winlink_remove(struct winlinks *wwl, struct winlink *wl) 161 { 162 struct window *w = wl->window; 163 164 RB_REMOVE(winlinks, wwl, wl); 165 if (wl->status_text != NULL) 166 xfree(wl->status_text); 167 xfree(wl); 168 169 if (w != NULL) { 170 if (w->references == 0) 171 fatal("bad reference count"); 172 w->references--; 173 if (w->references == 0) 174 window_destroy(w); 175 } 176 } 177 178 struct winlink * 179 winlink_next(struct winlink *wl) 180 { 181 return (RB_NEXT(winlinks, wwl, wl)); 182 } 183 184 struct winlink * 185 winlink_previous(struct winlink *wl) 186 { 187 return (RB_PREV(winlinks, wwl, wl)); 188 } 189 190 struct winlink * 191 winlink_next_by_number(struct winlink *wl, struct session *s, int n) 192 { 193 for (; n > 0; n--) { 194 if ((wl = RB_NEXT(winlinks, wwl, wl)) == NULL) 195 wl = RB_MIN(winlinks, &s->windows); 196 } 197 198 return (wl); 199 } 200 201 struct winlink * 202 winlink_previous_by_number(struct winlink *wl, struct session *s, int n) 203 { 204 for (; n > 0; n--) { 205 if ((wl = RB_PREV(winlinks, wwl, wl)) == NULL) 206 wl = RB_MAX(winlinks, &s->windows); 207 } 208 209 return (wl); 210 } 211 212 void 213 winlink_stack_push(struct winlink_stack *stack, struct winlink *wl) 214 { 215 if (wl == NULL) 216 return; 217 218 winlink_stack_remove(stack, wl); 219 TAILQ_INSERT_HEAD(stack, wl, sentry); 220 } 221 222 void 223 winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl) 224 { 225 struct winlink *wl2; 226 227 if (wl == NULL) 228 return; 229 230 TAILQ_FOREACH(wl2, stack, sentry) { 231 if (wl2 == wl) { 232 TAILQ_REMOVE(stack, wl, sentry); 233 return; 234 } 235 } 236 } 237 238 int 239 window_index(struct window *s, u_int *i) 240 { 241 for (*i = 0; *i < ARRAY_LENGTH(&windows); (*i)++) { 242 if (s == ARRAY_ITEM(&windows, *i)) 243 return (0); 244 } 245 return (-1); 246 } 247 248 struct window * 249 window_create1(u_int sx, u_int sy) 250 { 251 struct window *w; 252 u_int i; 253 254 w = xcalloc(1, sizeof *w); 255 w->name = NULL; 256 w->flags = 0; 257 258 TAILQ_INIT(&w->panes); 259 w->active = NULL; 260 261 w->lastlayout = -1; 262 w->layout_root = NULL; 263 264 w->sx = sx; 265 w->sy = sy; 266 267 queue_window_name(w); 268 269 options_init(&w->options, &global_w_options); 270 271 for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 272 if (ARRAY_ITEM(&windows, i) == NULL) { 273 ARRAY_SET(&windows, i, w); 274 break; 275 } 276 } 277 if (i == ARRAY_LENGTH(&windows)) 278 ARRAY_ADD(&windows, w); 279 w->references = 0; 280 281 return (w); 282 } 283 284 struct window * 285 window_create(const char *name, const char *cmd, const char *shell, 286 const char *cwd, struct environ *env, struct termios *tio, 287 u_int sx, u_int sy, u_int hlimit,char **cause) 288 { 289 struct window *w; 290 struct window_pane *wp; 291 292 w = window_create1(sx, sy); 293 wp = window_add_pane(w, hlimit); 294 layout_init(w); 295 if (window_pane_spawn(wp, cmd, shell, cwd, env, tio, cause) != 0) { 296 window_destroy(w); 297 return (NULL); 298 } 299 w->active = TAILQ_FIRST(&w->panes); 300 if (name != NULL) { 301 w->name = xstrdup(name); 302 options_set_number(&w->options, "automatic-rename", 0); 303 } else 304 w->name = default_window_name(w); 305 return (w); 306 } 307 308 void 309 window_destroy(struct window *w) 310 { 311 u_int i; 312 313 if (window_index(w, &i) != 0) 314 fatalx("index not found"); 315 ARRAY_SET(&windows, i, NULL); 316 while (!ARRAY_EMPTY(&windows) && ARRAY_LAST(&windows) == NULL) 317 ARRAY_TRUNC(&windows, 1); 318 319 if (w->layout_root != NULL) 320 layout_free(w); 321 322 evtimer_del(&w->name_timer); 323 324 options_free(&w->options); 325 326 window_destroy_panes(w); 327 328 if (w->name != NULL) 329 xfree(w->name); 330 xfree(w); 331 } 332 333 void 334 window_resize(struct window *w, u_int sx, u_int sy) 335 { 336 w->sx = sx; 337 w->sy = sy; 338 } 339 340 void 341 window_set_active_pane(struct window *w, struct window_pane *wp) 342 { 343 if (wp == w->active) 344 return; 345 w->last = w->active; 346 w->active = wp; 347 while (!window_pane_visible(w->active)) { 348 w->active = TAILQ_PREV(w->active, window_panes, entry); 349 if (w->active == NULL) 350 w->active = TAILQ_LAST(&w->panes, window_panes); 351 if (w->active == wp) 352 return; 353 } 354 } 355 356 struct window_pane * 357 window_get_active_at(struct window *w, u_int x, u_int y) 358 { 359 struct window_pane *wp; 360 361 TAILQ_FOREACH(wp, &w->panes, entry) { 362 if (!window_pane_visible(wp)) 363 continue; 364 if (x < wp->xoff || x > wp->xoff + wp->sx) 365 continue; 366 if (y < wp->yoff || y > wp->yoff + wp->sy) 367 continue; 368 return (wp); 369 } 370 return (NULL); 371 } 372 373 void 374 window_set_active_at(struct window *w, u_int x, u_int y) 375 { 376 struct window_pane *wp; 377 378 wp = window_get_active_at(w, x, y); 379 if (wp != NULL && wp != w->active) 380 window_set_active_pane(w, wp); 381 } 382 383 struct window_pane * 384 window_find_string(struct window *w, const char *s) 385 { 386 u_int x, y; 387 388 x = w->sx / 2; 389 y = w->sy / 2; 390 391 if (strcasecmp(s, "top") == 0) 392 y = 0; 393 else if (strcasecmp(s, "bottom") == 0) 394 y = w->sy - 1; 395 else if (strcasecmp(s, "left") == 0) 396 x = 0; 397 else if (strcasecmp(s, "right") == 0) 398 x = w->sx - 1; 399 else if (strcasecmp(s, "top-left") == 0) { 400 x = 0; 401 y = 0; 402 } else if (strcasecmp(s, "top-right") == 0) { 403 x = w->sx - 1; 404 y = 0; 405 } else if (strcasecmp(s, "bottom-left") == 0) { 406 x = 0; 407 y = w->sy - 1; 408 } else if (strcasecmp(s, "bottom-right") == 0) { 409 x = w->sx - 1; 410 y = w->sy - 1; 411 } else 412 return (NULL); 413 414 return (window_get_active_at(w, x, y)); 415 } 416 417 struct window_pane * 418 window_add_pane(struct window *w, u_int hlimit) 419 { 420 struct window_pane *wp; 421 422 wp = window_pane_create(w, w->sx, w->sy, hlimit); 423 if (TAILQ_EMPTY(&w->panes)) 424 TAILQ_INSERT_HEAD(&w->panes, wp, entry); 425 else 426 TAILQ_INSERT_AFTER(&w->panes, w->active, wp, entry); 427 return (wp); 428 } 429 430 void 431 window_remove_pane(struct window *w, struct window_pane *wp) 432 { 433 if (wp == w->active) { 434 w->active = w->last; 435 w->last = NULL; 436 if (w->active == NULL) { 437 w->active = TAILQ_PREV(wp, window_panes, entry); 438 if (w->active == NULL) 439 w->active = TAILQ_NEXT(wp, entry); 440 } 441 } else if (wp == w->last) 442 w->last = NULL; 443 444 TAILQ_REMOVE(&w->panes, wp, entry); 445 window_pane_destroy(wp); 446 } 447 448 struct window_pane * 449 window_pane_at_index(struct window *w, u_int idx) 450 { 451 struct window_pane *wp; 452 u_int n; 453 454 n = 0; 455 TAILQ_FOREACH(wp, &w->panes, entry) { 456 if (n == idx) 457 return (wp); 458 n++; 459 } 460 return (NULL); 461 } 462 463 struct window_pane * 464 window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n) 465 { 466 for (; n > 0; n--) { 467 if ((wp = TAILQ_NEXT(wp, entry)) == NULL) 468 wp = TAILQ_FIRST(&w->panes); 469 } 470 471 return (wp); 472 } 473 474 struct window_pane * 475 window_pane_previous_by_number(struct window *w, struct window_pane *wp, 476 u_int n) 477 { 478 for (; n > 0; n--) { 479 if ((wp = TAILQ_PREV(wp, window_panes, entry)) == NULL) 480 wp = TAILQ_LAST(&w->panes, window_panes); 481 } 482 483 return (wp); 484 } 485 486 u_int 487 window_pane_index(struct window *w, struct window_pane *wp) 488 { 489 struct window_pane *wq; 490 u_int n; 491 492 n = 0; 493 TAILQ_FOREACH(wq, &w->panes, entry) { 494 if (wp == wq) 495 break; 496 n++; 497 } 498 return (n); 499 } 500 501 u_int 502 window_count_panes(struct window *w) 503 { 504 struct window_pane *wp; 505 u_int n; 506 507 n = 0; 508 TAILQ_FOREACH(wp, &w->panes, entry) 509 n++; 510 return (n); 511 } 512 513 void 514 window_destroy_panes(struct window *w) 515 { 516 struct window_pane *wp; 517 518 while (!TAILQ_EMPTY(&w->panes)) { 519 wp = TAILQ_FIRST(&w->panes); 520 TAILQ_REMOVE(&w->panes, wp, entry); 521 window_pane_destroy(wp); 522 } 523 } 524 525 /* Return list of printable window flag symbols. No flags is just a space. */ 526 char * 527 window_printable_flags(struct session *s, struct winlink *wl) 528 { 529 char flags[BUFSIZ]; 530 int pos; 531 532 pos = 0; 533 if (wl->flags & WINLINK_ACTIVITY) 534 flags[pos++] = '#'; 535 if (wl->flags & WINLINK_BELL) 536 flags[pos++] = '!'; 537 if (wl->flags & WINLINK_CONTENT) 538 flags[pos++] = '+'; 539 if (wl->flags & WINLINK_SILENCE) 540 flags[pos++] = '~'; 541 if (wl == s->curw) 542 flags[pos++] = '*'; 543 if (wl == TAILQ_FIRST(&s->lastw)) 544 flags[pos++] = '-'; 545 if (pos == 0) 546 flags[pos++] = ' '; 547 flags[pos] = '\0'; 548 return (xstrdup(flags)); 549 } 550 551 /* Find pane in global tree by id. */ 552 struct window_pane * 553 window_pane_find_by_id(u_int id) 554 { 555 struct window_pane wp; 556 557 wp.id = id; 558 return (RB_FIND(window_pane_tree, &all_window_panes, &wp)); 559 } 560 561 struct window_pane * 562 window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) 563 { 564 struct window_pane *wp; 565 566 wp = xcalloc(1, sizeof *wp); 567 wp->window = w; 568 569 wp->id = next_window_pane++; 570 RB_INSERT(window_pane_tree, &all_window_panes, wp); 571 572 wp->cmd = NULL; 573 wp->shell = NULL; 574 wp->cwd = NULL; 575 576 wp->fd = -1; 577 wp->event = NULL; 578 579 wp->mode = NULL; 580 581 wp->layout_cell = NULL; 582 583 wp->xoff = 0; 584 wp->yoff = 0; 585 586 wp->sx = sx; 587 wp->sy = sy; 588 589 wp->pipe_fd = -1; 590 wp->pipe_off = 0; 591 wp->pipe_event = NULL; 592 593 wp->saved_grid = NULL; 594 595 screen_init(&wp->base, sx, sy, hlimit); 596 wp->screen = &wp->base; 597 598 input_init(wp); 599 600 return (wp); 601 } 602 603 void 604 window_pane_destroy(struct window_pane *wp) 605 { 606 window_pane_reset_mode(wp); 607 608 if (wp->fd != -1) { 609 close(wp->fd); 610 bufferevent_free(wp->event); 611 } 612 613 input_free(wp); 614 615 screen_free(&wp->base); 616 if (wp->saved_grid != NULL) 617 grid_destroy(wp->saved_grid); 618 619 if (wp->pipe_fd != -1) { 620 close(wp->pipe_fd); 621 bufferevent_free(wp->pipe_event); 622 } 623 624 RB_REMOVE(window_pane_tree, &all_window_panes, wp); 625 626 if (wp->cwd != NULL) 627 xfree(wp->cwd); 628 if (wp->shell != NULL) 629 xfree(wp->shell); 630 if (wp->cmd != NULL) 631 xfree(wp->cmd); 632 utmp_destroy(wp->utmp); 633 xfree(wp); 634 } 635 636 int 637 window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, 638 const char *cwd, struct environ *env, struct termios *tio, char **cause) 639 { 640 struct winsize ws; 641 char *argv0, paneid[16]; 642 const char *ptr; 643 struct termios tio2; 644 645 if (wp->fd != -1) { 646 close(wp->fd); 647 bufferevent_free(wp->event); 648 } 649 if (cmd != NULL) { 650 if (wp->cmd != NULL) 651 xfree(wp->cmd); 652 wp->cmd = xstrdup(cmd); 653 } 654 if (shell != NULL) { 655 if (wp->shell != NULL) 656 xfree(wp->shell); 657 wp->shell = xstrdup(shell); 658 } 659 if (cwd != NULL) { 660 if (wp->cwd != NULL) 661 xfree(wp->cwd); 662 wp->cwd = xstrdup(cwd); 663 } 664 665 memset(&ws, 0, sizeof ws); 666 ws.ws_col = screen_size_x(&wp->base); 667 ws.ws_row = screen_size_y(&wp->base); 668 669 switch (wp->pid = forkpty(&wp->fd, wp->tty, NULL, &ws)) { 670 case -1: 671 wp->fd = -1; 672 xasprintf(cause, "%s: %s", cmd, strerror(errno)); 673 return (-1); 674 case 0: 675 if (chdir(wp->cwd) != 0) 676 chdir("/"); 677 678 if (tcgetattr(STDIN_FILENO, &tio2) != 0) 679 fatal("tcgetattr failed"); 680 if (tio != NULL) 681 memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc); 682 tio2.c_cc[VERASE] = '\177'; 683 if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0) 684 fatal("tcgetattr failed"); 685 686 closefrom(STDERR_FILENO + 1); 687 688 xsnprintf(paneid, sizeof paneid, "%%%u", wp->id); 689 environ_set(env, "TMUX_PANE", paneid); 690 environ_push(env); 691 692 clear_signals(1); 693 log_close(); 694 695 if (*wp->cmd != '\0') { 696 /* Set SHELL but only if it is currently not useful. */ 697 shell = getenv("SHELL"); 698 if (shell == NULL || *shell == '\0' || areshell(shell)) 699 setenv("SHELL", wp->shell, 1); 700 701 execl(_PATH_BSHELL, "sh", "-c", wp->cmd, (char *) NULL); 702 fatal("execl failed"); 703 } 704 705 /* No command; fork a login shell. */ 706 ptr = strrchr(wp->shell, '/'); 707 if (ptr != NULL && *(ptr + 1) != '\0') 708 xasprintf(&argv0, "-%s", ptr + 1); 709 else 710 xasprintf(&argv0, "-%s", wp->shell); 711 setenv("SHELL", wp->shell, 1); 712 execl(wp->shell, argv0, (char *) NULL); 713 fatal("execl failed"); 714 } 715 716 setblocking(wp->fd, 0); 717 718 wp->event = bufferevent_new(wp->fd, 719 window_pane_read_callback, NULL, window_pane_error_callback, wp); 720 bufferevent_enable(wp->event, EV_READ|EV_WRITE); 721 722 wp->utmp = utmp_create(wp->tty); 723 724 return (0); 725 } 726 727 /* ARGSUSED */ 728 void 729 window_pane_read_callback(unused struct bufferevent *bufev, void *data) 730 { 731 struct window_pane *wp = data; 732 char *new_data; 733 size_t new_size; 734 735 new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off; 736 if (wp->pipe_fd != -1 && new_size > 0) { 737 new_data = (char *)EVBUFFER_DATA(wp->event->input); 738 bufferevent_write(wp->pipe_event, new_data, new_size); 739 } 740 741 input_parse(wp); 742 743 wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); 744 745 /* 746 * If we get here, we're not outputting anymore, so set the silence 747 * flag on the window. 748 */ 749 wp->window->flags |= WINDOW_SILENCE; 750 if (gettimeofday(&wp->window->silence_timer, NULL) != 0) 751 fatal("gettimeofday failed."); 752 } 753 754 /* ARGSUSED */ 755 void 756 window_pane_error_callback( 757 unused struct bufferevent *bufev, unused short what, void *data) 758 { 759 struct window_pane *wp = data; 760 761 server_destroy_pane(wp); 762 } 763 764 void 765 window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) 766 { 767 struct winsize ws; 768 769 if (sx == wp->sx && sy == wp->sy) 770 return; 771 wp->sx = sx; 772 wp->sy = sy; 773 774 memset(&ws, 0, sizeof ws); 775 ws.ws_col = sx; 776 ws.ws_row = sy; 777 778 screen_resize(&wp->base, sx, sy); 779 if (wp->mode != NULL) 780 wp->mode->resize(wp, sx, sy); 781 782 if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) 783 #ifdef __sun 784 /* 785 * Some versions of Solaris apparently can return an error when 786 * resizing; don't know why this happens, can't reproduce on 787 * other platforms and ignoring it doesn't seem to cause any 788 * issues. 789 */ 790 if (errno != EINVAL) 791 #endif 792 fatal("ioctl failed"); 793 } 794 795 /* 796 * Enter alternative screen mode. A copy of the visible screen is saved and the 797 * history is not updated 798 */ 799 void 800 window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc) 801 { 802 struct screen *s = &wp->base; 803 u_int sx, sy; 804 805 if (wp->saved_grid != NULL) 806 return; 807 if (!options_get_number(&wp->window->options, "alternate-screen")) 808 return; 809 sx = screen_size_x(s); 810 sy = screen_size_y(s); 811 812 wp->saved_grid = grid_create(sx, sy, 0); 813 grid_duplicate_lines(wp->saved_grid, 0, s->grid, screen_hsize(s), sy); 814 wp->saved_cx = s->cx; 815 wp->saved_cy = s->cy; 816 memcpy(&wp->saved_cell, gc, sizeof wp->saved_cell); 817 818 grid_view_clear(s->grid, 0, 0, sx, sy); 819 820 wp->base.grid->flags &= ~GRID_HISTORY; 821 822 wp->flags |= PANE_REDRAW; 823 } 824 825 /* Exit alternate screen mode and restore the copied grid. */ 826 void 827 window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc) 828 { 829 struct screen *s = &wp->base; 830 u_int sx, sy; 831 832 if (wp->saved_grid == NULL) 833 return; 834 if (!options_get_number(&wp->window->options, "alternate-screen")) 835 return; 836 sx = screen_size_x(s); 837 sy = screen_size_y(s); 838 839 /* 840 * If the current size is bigger, temporarily resize to the old size 841 * before copying back. 842 */ 843 if (sy > wp->saved_grid->sy) 844 screen_resize(s, sx, wp->saved_grid->sy); 845 846 /* Restore the grid, cursor position and cell. */ 847 grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy); 848 s->cx = wp->saved_cx; 849 if (s->cx > screen_size_x(s) - 1) 850 s->cx = screen_size_x(s) - 1; 851 s->cy = wp->saved_cy; 852 if (s->cy > screen_size_y(s) - 1) 853 s->cy = screen_size_y(s) - 1; 854 memcpy(gc, &wp->saved_cell, sizeof *gc); 855 856 /* 857 * Turn history back on (so resize can use it) and then resize back to 858 * the current size. 859 */ 860 wp->base.grid->flags |= GRID_HISTORY; 861 if (sy > wp->saved_grid->sy) 862 screen_resize(s, sx, sy); 863 864 grid_destroy(wp->saved_grid); 865 wp->saved_grid = NULL; 866 867 wp->flags |= PANE_REDRAW; 868 } 869 870 int 871 window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode) 872 { 873 struct screen *s; 874 875 if (wp->mode != NULL) 876 return (1); 877 wp->mode = mode; 878 879 if ((s = wp->mode->init(wp)) != NULL) 880 wp->screen = s; 881 wp->flags |= PANE_REDRAW; 882 return (0); 883 } 884 885 void 886 window_pane_reset_mode(struct window_pane *wp) 887 { 888 if (wp->mode == NULL) 889 return; 890 891 wp->mode->free(wp); 892 wp->mode = NULL; 893 894 wp->screen = &wp->base; 895 wp->flags |= PANE_REDRAW; 896 } 897 898 void 899 window_pane_key(struct window_pane *wp, struct session *sess, int key) 900 { 901 struct window_pane *wp2; 902 903 if (!window_pane_visible(wp)) 904 return; 905 906 if (wp->mode != NULL) { 907 if (wp->mode->key != NULL) 908 wp->mode->key(wp, sess, key); 909 return; 910 } 911 912 if (wp->fd == -1) 913 return; 914 input_key(wp, key); 915 if (options_get_number(&wp->window->options, "synchronize-panes")) { 916 TAILQ_FOREACH(wp2, &wp->window->panes, entry) { 917 if (wp2 == wp || wp2->mode != NULL) 918 continue; 919 if (wp2->fd != -1 && window_pane_visible(wp2)) 920 input_key(wp2, key); 921 } 922 } 923 } 924 925 void 926 window_pane_mouse( 927 struct window_pane *wp, struct session *sess, struct mouse_event *m) 928 { 929 if (!window_pane_visible(wp)) 930 return; 931 932 if (m->x < wp->xoff || m->x >= wp->xoff + wp->sx) 933 return; 934 if (m->y < wp->yoff || m->y >= wp->yoff + wp->sy) 935 return; 936 m->x -= wp->xoff; 937 m->y -= wp->yoff; 938 939 if (wp->mode != NULL) { 940 if (wp->mode->mouse != NULL && 941 options_get_number(&wp->window->options, "mode-mouse")) 942 wp->mode->mouse(wp, sess, m); 943 } else if (wp->fd != -1) 944 input_mouse(wp, m); 945 } 946 947 int 948 window_pane_visible(struct window_pane *wp) 949 { 950 struct window *w = wp->window; 951 952 if (wp->xoff >= w->sx || wp->yoff >= w->sy) 953 return (0); 954 if (wp->xoff + wp->sx > w->sx || wp->yoff + wp->sy > w->sy) 955 return (0); 956 return (1); 957 } 958 959 char * 960 window_pane_search(struct window_pane *wp, const char *searchstr, u_int *lineno) 961 { 962 struct screen *s = &wp->base; 963 char *newsearchstr, *line, *msg; 964 u_int i; 965 966 msg = NULL; 967 xasprintf(&newsearchstr, "*%s*", searchstr); 968 969 for (i = 0; i < screen_size_y(s); i++) { 970 line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s)); 971 if (fnmatch(newsearchstr, line, 0) == 0) { 972 msg = line; 973 if (lineno != NULL) 974 *lineno = i; 975 break; 976 } 977 xfree(line); 978 } 979 980 xfree(newsearchstr); 981 return (msg); 982 } 983 984 /* Find the pane directly above another. */ 985 struct window_pane * 986 window_pane_find_up(struct window_pane *wp) 987 { 988 struct window_pane *wp2; 989 u_int left, top; 990 991 if (wp == NULL || !window_pane_visible(wp)) 992 return (NULL); 993 994 top = wp->yoff; 995 if (top == 0) 996 top = wp->window->sy + 1; 997 left = wp->xoff; 998 999 TAILQ_FOREACH(wp2, &wp->window->panes, entry) { 1000 if (!window_pane_visible(wp2)) 1001 continue; 1002 if (wp2->yoff + wp2->sy + 1 != top) 1003 continue; 1004 if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx) 1005 return (wp2); 1006 } 1007 return (NULL); 1008 } 1009 1010 /* Find the pane directly below another. */ 1011 struct window_pane * 1012 window_pane_find_down(struct window_pane *wp) 1013 { 1014 struct window_pane *wp2; 1015 u_int left, bottom; 1016 1017 if (wp == NULL || !window_pane_visible(wp)) 1018 return (NULL); 1019 1020 bottom = wp->yoff + wp->sy + 1; 1021 if (bottom >= wp->window->sy) 1022 bottom = 0; 1023 left = wp->xoff; 1024 1025 TAILQ_FOREACH(wp2, &wp->window->panes, entry) { 1026 if (!window_pane_visible(wp2)) 1027 continue; 1028 if (wp2->yoff != bottom) 1029 continue; 1030 if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx) 1031 return (wp2); 1032 } 1033 return (NULL); 1034 } 1035 1036 /* 1037 * Find the pane directly to the left of another, adjacent to the left side and 1038 * containing the top edge. 1039 */ 1040 struct window_pane * 1041 window_pane_find_left(struct window_pane *wp) 1042 { 1043 struct window_pane *wp2; 1044 u_int left, top; 1045 1046 if (wp == NULL || !window_pane_visible(wp)) 1047 return (NULL); 1048 1049 left = wp->xoff; 1050 if (left == 0) 1051 left = wp->window->sx + 1; 1052 top = wp->yoff; 1053 1054 TAILQ_FOREACH(wp2, &wp->window->panes, entry) { 1055 if (!window_pane_visible(wp2)) 1056 continue; 1057 if (wp2->xoff + wp2->sx + 1 != left) 1058 continue; 1059 if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy) 1060 return (wp2); 1061 } 1062 return (NULL); 1063 } 1064 1065 /* 1066 * Find the pane directly to the right of another, that is adjacent to the 1067 * right edge and including the top edge. 1068 */ 1069 struct window_pane * 1070 window_pane_find_right(struct window_pane *wp) 1071 { 1072 struct window_pane *wp2; 1073 u_int right, top; 1074 1075 if (wp == NULL || !window_pane_visible(wp)) 1076 return (NULL); 1077 1078 right = wp->xoff + wp->sx + 1; 1079 if (right >= wp->window->sx) 1080 right = 0; 1081 top = wp->yoff; 1082 1083 TAILQ_FOREACH(wp2, &wp->window->panes, entry) { 1084 if (!window_pane_visible(wp2)) 1085 continue; 1086 if (wp2->xoff != right) 1087 continue; 1088 if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy) 1089 return (wp2); 1090 } 1091 return (NULL); 1092 } 1093