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