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