1 /* $OpenBSD$ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott (at) gmail.com> 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 <ctype.h> 23 #include <errno.h> 24 #include <fcntl.h> 25 #include <fnmatch.h> 26 #include <regex.h> 27 #include <signal.h> 28 #include <stdint.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <time.h> 32 #include <unistd.h> 33 34 #include "tmux.h" 35 36 /* 37 * Each window is attached to a number of panes, each of which is a pty. This 38 * file contains code to handle them. 39 * 40 * A pane has two buffers attached, these are filled and emptied by the main 41 * server poll loop. Output data is received from pty's in screen format, 42 * translated and returned as a series of escape sequences and strings via 43 * input_parse (in input.c). Input data is received as key codes and written 44 * directly via input_key. 45 * 46 * Each pane also has a "virtual" screen (screen.c) which contains the current 47 * state and is redisplayed when the window is reattached to a client. 48 * 49 * Windows are stored directly on a global array and wrapped in any number of 50 * winlink structs to be linked onto local session RB trees. A reference count 51 * is maintained and a window removed from the global list and destroyed when 52 * it reaches zero. 53 */ 54 55 /* Global window list. */ 56 struct windows windows; 57 58 /* Global panes tree. */ 59 struct window_pane_tree all_window_panes; 60 static u_int next_window_pane_id; 61 static u_int next_window_id; 62 static u_int next_active_point; 63 64 struct window_pane_input_data { 65 struct cmdq_item *item; 66 u_int wp; 67 struct client_file *file; 68 }; 69 70 static struct window_pane *window_pane_create(struct window *, u_int, u_int, 71 u_int); 72 static void window_pane_destroy(struct window_pane *); 73 static void window_pane_full_size_offset(struct window_pane *wp, 74 u_int *xoff, u_int *yoff, u_int *sx, u_int *sy); 75 76 RB_GENERATE(windows, window, entry, window_cmp); 77 RB_GENERATE(winlinks, winlink, entry, winlink_cmp); 78 RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp); 79 80 int 81 window_cmp(struct window *w1, struct window *w2) 82 { 83 return (w1->id - w2->id); 84 } 85 86 int 87 winlink_cmp(struct winlink *wl1, struct winlink *wl2) 88 { 89 return (wl1->idx - wl2->idx); 90 } 91 92 int 93 window_pane_cmp(struct window_pane *wp1, struct window_pane *wp2) 94 { 95 return (wp1->id - wp2->id); 96 } 97 98 struct winlink * 99 winlink_find_by_window(struct winlinks *wwl, struct window *w) 100 { 101 struct winlink *wl; 102 103 RB_FOREACH(wl, winlinks, wwl) { 104 if (wl->window == w) 105 return (wl); 106 } 107 108 return (NULL); 109 } 110 111 struct winlink * 112 winlink_find_by_index(struct winlinks *wwl, int idx) 113 { 114 struct winlink wl; 115 116 if (idx < 0) 117 fatalx("bad index"); 118 119 wl.idx = idx; 120 return (RB_FIND(winlinks, wwl, &wl)); 121 } 122 123 struct winlink * 124 winlink_find_by_window_id(struct winlinks *wwl, u_int id) 125 { 126 struct winlink *wl; 127 128 RB_FOREACH(wl, winlinks, wwl) { 129 if (wl->window->id == id) 130 return (wl); 131 } 132 return (NULL); 133 } 134 135 static int 136 winlink_next_index(struct winlinks *wwl, int idx) 137 { 138 int i; 139 140 i = idx; 141 do { 142 if (winlink_find_by_index(wwl, i) == NULL) 143 return (i); 144 if (i == INT_MAX) 145 i = 0; 146 else 147 i++; 148 } while (i != idx); 149 return (-1); 150 } 151 152 u_int 153 winlink_count(struct winlinks *wwl) 154 { 155 struct winlink *wl; 156 u_int n; 157 158 n = 0; 159 RB_FOREACH(wl, winlinks, wwl) 160 n++; 161 162 return (n); 163 } 164 165 struct winlink * 166 winlink_add(struct winlinks *wwl, int idx) 167 { 168 struct winlink *wl; 169 170 if (idx < 0) { 171 if ((idx = winlink_next_index(wwl, -idx - 1)) == -1) 172 return (NULL); 173 } else if (winlink_find_by_index(wwl, idx) != NULL) 174 return (NULL); 175 176 wl = xcalloc(1, sizeof *wl); 177 wl->idx = idx; 178 RB_INSERT(winlinks, wwl, wl); 179 180 return (wl); 181 } 182 183 void 184 winlink_set_window(struct winlink *wl, struct window *w) 185 { 186 if (wl->window != NULL) { 187 TAILQ_REMOVE(&wl->window->winlinks, wl, wentry); 188 window_remove_ref(wl->window, __func__); 189 } 190 TAILQ_INSERT_TAIL(&w->winlinks, wl, wentry); 191 wl->window = w; 192 window_add_ref(w, __func__); 193 } 194 195 void 196 winlink_remove(struct winlinks *wwl, struct winlink *wl) 197 { 198 struct window *w = wl->window; 199 200 if (w != NULL) { 201 TAILQ_REMOVE(&w->winlinks, wl, wentry); 202 window_remove_ref(w, __func__); 203 } 204 205 RB_REMOVE(winlinks, wwl, wl); 206 free(wl); 207 } 208 209 struct winlink * 210 winlink_next(struct winlink *wl) 211 { 212 return (RB_NEXT(winlinks, wwl, wl)); 213 } 214 215 struct winlink * 216 winlink_previous(struct winlink *wl) 217 { 218 return (RB_PREV(winlinks, wwl, wl)); 219 } 220 221 struct winlink * 222 winlink_next_by_number(struct winlink *wl, struct session *s, int n) 223 { 224 for (; n > 0; n--) { 225 if ((wl = RB_NEXT(winlinks, wwl, wl)) == NULL) 226 wl = RB_MIN(winlinks, &s->windows); 227 } 228 229 return (wl); 230 } 231 232 struct winlink * 233 winlink_previous_by_number(struct winlink *wl, struct session *s, int n) 234 { 235 for (; n > 0; n--) { 236 if ((wl = RB_PREV(winlinks, wwl, wl)) == NULL) 237 wl = RB_MAX(winlinks, &s->windows); 238 } 239 240 return (wl); 241 } 242 243 void 244 winlink_stack_push(struct winlink_stack *stack, struct winlink *wl) 245 { 246 if (wl == NULL) 247 return; 248 249 winlink_stack_remove(stack, wl); 250 TAILQ_INSERT_HEAD(stack, wl, sentry); 251 wl->flags |= WINLINK_VISITED; 252 } 253 254 void 255 winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl) 256 { 257 if (wl != NULL && (wl->flags & WINLINK_VISITED)) { 258 TAILQ_REMOVE(stack, wl, sentry); 259 wl->flags &= ~WINLINK_VISITED; 260 } 261 } 262 263 struct window * 264 window_find_by_id_str(const char *s) 265 { 266 const char *errstr; 267 u_int id; 268 269 if (*s != '@') 270 return (NULL); 271 272 id = strtonum(s + 1, 0, UINT_MAX, &errstr); 273 if (errstr != NULL) 274 return (NULL); 275 return (window_find_by_id(id)); 276 } 277 278 struct window * 279 window_find_by_id(u_int id) 280 { 281 struct window w; 282 283 w.id = id; 284 return (RB_FIND(windows, &windows, &w)); 285 } 286 287 void 288 window_update_activity(struct window *w) 289 { 290 gettimeofday(&w->activity_time, NULL); 291 alerts_queue(w, WINDOW_ACTIVITY); 292 } 293 294 struct window * 295 window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel) 296 { 297 struct window *w; 298 299 if (xpixel == 0) 300 xpixel = DEFAULT_XPIXEL; 301 if (ypixel == 0) 302 ypixel = DEFAULT_YPIXEL; 303 304 w = xcalloc(1, sizeof *w); 305 w->name = xstrdup(""); 306 w->flags = 0; 307 308 TAILQ_INIT(&w->panes); 309 TAILQ_INIT(&w->last_panes); 310 w->active = NULL; 311 312 w->lastlayout = -1; 313 w->layout_root = NULL; 314 315 w->sx = sx; 316 w->sy = sy; 317 w->manual_sx = sx; 318 w->manual_sy = sy; 319 w->xpixel = xpixel; 320 w->ypixel = ypixel; 321 322 w->options = options_create(global_w_options); 323 324 w->references = 0; 325 TAILQ_INIT(&w->winlinks); 326 327 w->id = next_window_id++; 328 RB_INSERT(windows, &windows, w); 329 330 window_set_fill_character(w); 331 window_update_activity(w); 332 333 log_debug("%s: @%u create %ux%u (%ux%u)", __func__, w->id, sx, sy, 334 w->xpixel, w->ypixel); 335 return (w); 336 } 337 338 static void 339 window_destroy(struct window *w) 340 { 341 log_debug("window @%u destroyed (%d references)", w->id, w->references); 342 343 window_unzoom(w, 0); 344 RB_REMOVE(windows, &windows, w); 345 346 if (w->layout_root != NULL) 347 layout_free_cell(w->layout_root); 348 if (w->saved_layout_root != NULL) 349 layout_free_cell(w->saved_layout_root); 350 free(w->old_layout); 351 352 window_destroy_panes(w); 353 354 if (event_initialized(&w->name_event)) 355 evtimer_del(&w->name_event); 356 357 if (event_initialized(&w->alerts_timer)) 358 evtimer_del(&w->alerts_timer); 359 if (event_initialized(&w->offset_timer)) 360 event_del(&w->offset_timer); 361 362 options_free(w->options); 363 free(w->fill_character); 364 365 free(w->name); 366 free(w); 367 } 368 369 int 370 window_pane_destroy_ready(struct window_pane *wp) 371 { 372 int n; 373 374 if (wp->pipe_fd != -1) { 375 if (EVBUFFER_LENGTH(wp->pipe_event->output) != 0) 376 return (0); 377 if (ioctl(wp->fd, FIONREAD, &n) != -1 && n > 0) 378 return (0); 379 } 380 381 if (~wp->flags & PANE_EXITED) 382 return (0); 383 return (1); 384 } 385 386 void 387 window_add_ref(struct window *w, const char *from) 388 { 389 w->references++; 390 log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references); 391 } 392 393 void 394 window_remove_ref(struct window *w, const char *from) 395 { 396 w->references--; 397 log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references); 398 399 if (w->references == 0) 400 window_destroy(w); 401 } 402 403 void 404 window_set_name(struct window *w, const char *new_name) 405 { 406 free(w->name); 407 utf8_stravis(&w->name, new_name, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); 408 notify_window("window-renamed", w); 409 } 410 411 void 412 window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) 413 { 414 if (xpixel == 0) 415 xpixel = DEFAULT_XPIXEL; 416 if (ypixel == 0) 417 ypixel = DEFAULT_YPIXEL; 418 419 log_debug("%s: @%u resize %ux%u (%ux%u)", __func__, w->id, sx, sy, 420 xpixel == -1 ? w->xpixel : (u_int)xpixel, 421 ypixel == -1 ? w->ypixel : (u_int)ypixel); 422 w->sx = sx; 423 w->sy = sy; 424 if (xpixel != -1) 425 w->xpixel = xpixel; 426 if (ypixel != -1) 427 w->ypixel = ypixel; 428 } 429 430 void 431 window_pane_send_resize(struct window_pane *wp, u_int sx, u_int sy) 432 { 433 struct window *w = wp->window; 434 struct winsize ws; 435 436 if (wp->fd == -1) 437 return; 438 439 log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, sx, sy); 440 441 memset(&ws, 0, sizeof ws); 442 ws.ws_col = sx; 443 ws.ws_row = sy; 444 ws.ws_xpixel = w->xpixel * ws.ws_col; 445 ws.ws_ypixel = w->ypixel * ws.ws_row; 446 if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) 447 #ifdef __sun 448 /* 449 * Some versions of Solaris apparently can return an error when 450 * resizing; don't know why this happens, can't reproduce on 451 * other platforms and ignoring it doesn't seem to cause any 452 * issues. 453 */ 454 if (errno != EINVAL && errno != ENXIO) 455 #endif 456 fatal("ioctl failed"); 457 } 458 459 int 460 window_has_pane(struct window *w, struct window_pane *wp) 461 { 462 struct window_pane *wp1; 463 464 TAILQ_FOREACH(wp1, &w->panes, entry) { 465 if (wp1 == wp) 466 return (1); 467 } 468 return (0); 469 } 470 471 void 472 window_update_focus(struct window *w) 473 { 474 if (w != NULL) { 475 log_debug("%s: @%u", __func__, w->id); 476 window_pane_update_focus(w->active); 477 } 478 } 479 480 void 481 window_pane_update_focus(struct window_pane *wp) 482 { 483 struct client *c; 484 int focused = 0; 485 486 if (wp != NULL && (~wp->flags & PANE_EXITED)) { 487 if (wp != wp->window->active) 488 focused = 0; 489 else { 490 TAILQ_FOREACH(c, &clients, entry) { 491 if (c->session != NULL && 492 c->session->attached != 0 && 493 (c->flags & CLIENT_FOCUSED) && 494 c->session->curw->window == wp->window && 495 c->overlay_draw == NULL) { 496 focused = 1; 497 break; 498 } 499 } 500 } 501 if (!focused && (wp->flags & PANE_FOCUSED)) { 502 log_debug("%s: %%%u focus out", __func__, wp->id); 503 if (wp->base.mode & MODE_FOCUSON) 504 bufferevent_write(wp->event, "\033[O", 3); 505 notify_pane("pane-focus-out", wp); 506 wp->flags &= ~PANE_FOCUSED; 507 } else if (focused && (~wp->flags & PANE_FOCUSED)) { 508 log_debug("%s: %%%u focus in", __func__, wp->id); 509 if (wp->base.mode & MODE_FOCUSON) 510 bufferevent_write(wp->event, "\033[I", 3); 511 notify_pane("pane-focus-in", wp); 512 wp->flags |= PANE_FOCUSED; 513 } else 514 log_debug("%s: %%%u focus unchanged", __func__, wp->id); 515 } 516 } 517 518 int 519 window_set_active_pane(struct window *w, struct window_pane *wp, int notify) 520 { 521 struct window_pane *lastwp; 522 523 log_debug("%s: pane %%%u", __func__, wp->id); 524 525 if (wp == w->active) 526 return (0); 527 lastwp = w->active; 528 529 window_pane_stack_remove(&w->last_panes, wp); 530 window_pane_stack_push(&w->last_panes, lastwp); 531 532 w->active = wp; 533 w->active->active_point = next_active_point++; 534 w->active->flags |= PANE_CHANGED; 535 536 if (options_get_number(global_options, "focus-events")) { 537 window_pane_update_focus(lastwp); 538 window_pane_update_focus(w->active); 539 } 540 541 tty_update_window_offset(w); 542 543 if (notify) 544 notify_window("window-pane-changed", w); 545 return (1); 546 } 547 548 static int 549 window_pane_get_palette(struct window_pane *wp, int c) 550 { 551 if (wp == NULL) 552 return (-1); 553 return (colour_palette_get(&wp->palette, c)); 554 } 555 556 void 557 window_redraw_active_switch(struct window *w, struct window_pane *wp) 558 { 559 struct grid_cell *gc1, *gc2; 560 int c1, c2; 561 562 if (wp == w->active) 563 return; 564 565 for (;;) { 566 /* 567 * If the active and inactive styles or palettes are different, 568 * need to redraw the panes. 569 */ 570 gc1 = &wp->cached_gc; 571 gc2 = &wp->cached_active_gc; 572 if (!grid_cells_look_equal(gc1, gc2)) 573 wp->flags |= PANE_REDRAW; 574 else { 575 c1 = window_pane_get_palette(wp, gc1->fg); 576 c2 = window_pane_get_palette(wp, gc2->fg); 577 if (c1 != c2) 578 wp->flags |= PANE_REDRAW; 579 else { 580 c1 = window_pane_get_palette(wp, gc1->bg); 581 c2 = window_pane_get_palette(wp, gc2->bg); 582 if (c1 != c2) 583 wp->flags |= PANE_REDRAW; 584 } 585 } 586 if (wp == w->active) 587 break; 588 wp = w->active; 589 } 590 } 591 592 struct window_pane * 593 window_get_active_at(struct window *w, u_int x, u_int y) 594 { 595 struct window_pane *wp; 596 u_int xoff, yoff, sx, sy; 597 598 TAILQ_FOREACH(wp, &w->panes, entry) { 599 if (!window_pane_visible(wp)) 600 continue; 601 window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); 602 if (x < xoff || x > xoff + sx) 603 continue; 604 if (y < yoff || y > yoff + sy) 605 continue; 606 return (wp); 607 } 608 return (NULL); 609 } 610 611 struct window_pane * 612 window_find_string(struct window *w, const char *s) 613 { 614 u_int x, y, top = 0, bottom = w->sy - 1; 615 int status; 616 617 x = w->sx / 2; 618 y = w->sy / 2; 619 620 status = options_get_number(w->options, "pane-border-status"); 621 if (status == PANE_STATUS_TOP) 622 top++; 623 else if (status == PANE_STATUS_BOTTOM) 624 bottom--; 625 626 if (strcasecmp(s, "top") == 0) 627 y = top; 628 else if (strcasecmp(s, "bottom") == 0) 629 y = bottom; 630 else if (strcasecmp(s, "left") == 0) 631 x = 0; 632 else if (strcasecmp(s, "right") == 0) 633 x = w->sx - 1; 634 else if (strcasecmp(s, "top-left") == 0) { 635 x = 0; 636 y = top; 637 } else if (strcasecmp(s, "top-right") == 0) { 638 x = w->sx - 1; 639 y = top; 640 } else if (strcasecmp(s, "bottom-left") == 0) { 641 x = 0; 642 y = bottom; 643 } else if (strcasecmp(s, "bottom-right") == 0) { 644 x = w->sx - 1; 645 y = bottom; 646 } else 647 return (NULL); 648 649 return (window_get_active_at(w, x, y)); 650 } 651 652 int 653 window_zoom(struct window_pane *wp) 654 { 655 struct window *w = wp->window; 656 struct window_pane *wp1; 657 658 if (w->flags & WINDOW_ZOOMED) 659 return (-1); 660 661 if (window_count_panes(w) == 1) 662 return (-1); 663 664 if (w->active != wp) 665 window_set_active_pane(w, wp, 1); 666 667 TAILQ_FOREACH(wp1, &w->panes, entry) { 668 wp1->saved_layout_cell = wp1->layout_cell; 669 wp1->layout_cell = NULL; 670 } 671 672 w->saved_layout_root = w->layout_root; 673 layout_init(w, wp); 674 w->flags |= WINDOW_ZOOMED; 675 notify_window("window-layout-changed", w); 676 677 return (0); 678 } 679 680 int 681 window_unzoom(struct window *w, int notify) 682 { 683 struct window_pane *wp; 684 685 if (!(w->flags & WINDOW_ZOOMED)) 686 return (-1); 687 688 w->flags &= ~WINDOW_ZOOMED; 689 layout_free(w); 690 w->layout_root = w->saved_layout_root; 691 w->saved_layout_root = NULL; 692 693 TAILQ_FOREACH(wp, &w->panes, entry) { 694 wp->layout_cell = wp->saved_layout_cell; 695 wp->saved_layout_cell = NULL; 696 } 697 layout_fix_panes(w, NULL); 698 699 if (notify) 700 notify_window("window-layout-changed", w); 701 702 return (0); 703 } 704 705 int 706 window_push_zoom(struct window *w, int always, int flag) 707 { 708 log_debug("%s: @%u %d", __func__, w->id, 709 flag && (w->flags & WINDOW_ZOOMED)); 710 if (flag && (always || (w->flags & WINDOW_ZOOMED))) 711 w->flags |= WINDOW_WASZOOMED; 712 else 713 w->flags &= ~WINDOW_WASZOOMED; 714 return (window_unzoom(w, 1) == 0); 715 } 716 717 int 718 window_pop_zoom(struct window *w) 719 { 720 log_debug("%s: @%u %d", __func__, w->id, 721 !!(w->flags & WINDOW_WASZOOMED)); 722 if (w->flags & WINDOW_WASZOOMED) 723 return (window_zoom(w->active) == 0); 724 return (0); 725 } 726 727 struct window_pane * 728 window_add_pane(struct window *w, struct window_pane *other, u_int hlimit, 729 int flags) 730 { 731 struct window_pane *wp; 732 733 if (other == NULL) 734 other = w->active; 735 736 wp = window_pane_create(w, w->sx, w->sy, hlimit); 737 if (TAILQ_EMPTY(&w->panes)) { 738 log_debug("%s: @%u at start", __func__, w->id); 739 TAILQ_INSERT_HEAD(&w->panes, wp, entry); 740 } else if (flags & SPAWN_BEFORE) { 741 log_debug("%s: @%u before %%%u", __func__, w->id, wp->id); 742 if (flags & SPAWN_FULLSIZE) 743 TAILQ_INSERT_HEAD(&w->panes, wp, entry); 744 else 745 TAILQ_INSERT_BEFORE(other, wp, entry); 746 } else { 747 log_debug("%s: @%u after %%%u", __func__, w->id, wp->id); 748 if (flags & SPAWN_FULLSIZE) 749 TAILQ_INSERT_TAIL(&w->panes, wp, entry); 750 else 751 TAILQ_INSERT_AFTER(&w->panes, other, wp, entry); 752 } 753 return (wp); 754 } 755 756 void 757 window_lost_pane(struct window *w, struct window_pane *wp) 758 { 759 log_debug("%s: @%u pane %%%u", __func__, w->id, wp->id); 760 761 if (wp == marked_pane.wp) 762 server_clear_marked(); 763 764 window_pane_stack_remove(&w->last_panes, wp); 765 if (wp == w->active) { 766 w->active = TAILQ_FIRST(&w->last_panes); 767 if (w->active == NULL) { 768 w->active = TAILQ_PREV(wp, window_panes, entry); 769 if (w->active == NULL) 770 w->active = TAILQ_NEXT(wp, entry); 771 } 772 if (w->active != NULL) { 773 window_pane_stack_remove(&w->last_panes, w->active); 774 w->active->flags |= PANE_CHANGED; 775 notify_window("window-pane-changed", w); 776 window_update_focus(w); 777 } 778 } 779 } 780 781 void 782 window_remove_pane(struct window *w, struct window_pane *wp) 783 { 784 window_lost_pane(w, wp); 785 786 TAILQ_REMOVE(&w->panes, wp, entry); 787 window_pane_destroy(wp); 788 } 789 790 struct window_pane * 791 window_pane_at_index(struct window *w, u_int idx) 792 { 793 struct window_pane *wp; 794 u_int n; 795 796 n = options_get_number(w->options, "pane-base-index"); 797 TAILQ_FOREACH(wp, &w->panes, entry) { 798 if (n == idx) 799 return (wp); 800 n++; 801 } 802 return (NULL); 803 } 804 805 struct window_pane * 806 window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n) 807 { 808 for (; n > 0; n--) { 809 if ((wp = TAILQ_NEXT(wp, entry)) == NULL) 810 wp = TAILQ_FIRST(&w->panes); 811 } 812 813 return (wp); 814 } 815 816 struct window_pane * 817 window_pane_previous_by_number(struct window *w, struct window_pane *wp, 818 u_int n) 819 { 820 for (; n > 0; n--) { 821 if ((wp = TAILQ_PREV(wp, window_panes, entry)) == NULL) 822 wp = TAILQ_LAST(&w->panes, window_panes); 823 } 824 825 return (wp); 826 } 827 828 int 829 window_pane_index(struct window_pane *wp, u_int *i) 830 { 831 struct window_pane *wq; 832 struct window *w = wp->window; 833 834 *i = options_get_number(w->options, "pane-base-index"); 835 TAILQ_FOREACH(wq, &w->panes, entry) { 836 if (wp == wq) { 837 return (0); 838 } 839 (*i)++; 840 } 841 842 return (-1); 843 } 844 845 u_int 846 window_count_panes(struct window *w) 847 { 848 struct window_pane *wp; 849 u_int n; 850 851 n = 0; 852 TAILQ_FOREACH(wp, &w->panes, entry) 853 n++; 854 return (n); 855 } 856 857 void 858 window_destroy_panes(struct window *w) 859 { 860 struct window_pane *wp; 861 862 while (!TAILQ_EMPTY(&w->last_panes)) { 863 wp = TAILQ_FIRST(&w->last_panes); 864 window_pane_stack_remove(&w->last_panes, wp); 865 } 866 867 while (!TAILQ_EMPTY(&w->panes)) { 868 wp = TAILQ_FIRST(&w->panes); 869 TAILQ_REMOVE(&w->panes, wp, entry); 870 window_pane_destroy(wp); 871 } 872 } 873 874 const char * 875 window_printable_flags(struct winlink *wl, int escape) 876 { 877 struct session *s = wl->session; 878 static char flags[32]; 879 int pos; 880 881 pos = 0; 882 if (wl->flags & WINLINK_ACTIVITY) { 883 flags[pos++] = '#'; 884 if (escape) 885 flags[pos++] = '#'; 886 } 887 if (wl->flags & WINLINK_BELL) 888 flags[pos++] = '!'; 889 if (wl->flags & WINLINK_SILENCE) 890 flags[pos++] = '~'; 891 if (wl == s->curw) 892 flags[pos++] = '*'; 893 if (wl == TAILQ_FIRST(&s->lastw)) 894 flags[pos++] = '-'; 895 if (server_check_marked() && wl == marked_pane.wl) 896 flags[pos++] = 'M'; 897 if (wl->window->flags & WINDOW_ZOOMED) 898 flags[pos++] = 'Z'; 899 flags[pos] = '\0'; 900 return (flags); 901 } 902 903 struct window_pane * 904 window_pane_find_by_id_str(const char *s) 905 { 906 const char *errstr; 907 u_int id; 908 909 if (*s != '%') 910 return (NULL); 911 912 id = strtonum(s + 1, 0, UINT_MAX, &errstr); 913 if (errstr != NULL) 914 return (NULL); 915 return (window_pane_find_by_id(id)); 916 } 917 918 struct window_pane * 919 window_pane_find_by_id(u_int id) 920 { 921 struct window_pane wp; 922 923 wp.id = id; 924 return (RB_FIND(window_pane_tree, &all_window_panes, &wp)); 925 } 926 927 static struct window_pane * 928 window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) 929 { 930 struct window_pane *wp; 931 char host[HOST_NAME_MAX + 1]; 932 933 wp = xcalloc(1, sizeof *wp); 934 wp->window = w; 935 wp->options = options_create(w->options); 936 wp->flags = (PANE_STYLECHANGED|PANE_THEMECHANGED); 937 938 wp->id = next_window_pane_id++; 939 RB_INSERT(window_pane_tree, &all_window_panes, wp); 940 941 wp->fd = -1; 942 943 TAILQ_INIT(&wp->modes); 944 945 TAILQ_INIT (&wp->resize_queue); 946 947 wp->sx = sx; 948 wp->sy = sy; 949 950 wp->pipe_fd = -1; 951 952 wp->control_bg = -1; 953 wp->control_fg = -1; 954 955 style_set_scrollbar_style_from_option(&wp->scrollbar_style, 956 wp->options); 957 958 colour_palette_init(&wp->palette); 959 colour_palette_from_option(&wp->palette, wp->options); 960 961 screen_init(&wp->base, sx, sy, hlimit); 962 wp->screen = &wp->base; 963 window_pane_default_cursor(wp); 964 965 screen_init(&wp->status_screen, 1, 1, 0); 966 967 if (gethostname(host, sizeof host) == 0) 968 screen_set_title(&wp->base, host); 969 970 return (wp); 971 } 972 973 static void 974 window_pane_destroy(struct window_pane *wp) 975 { 976 struct window_pane_resize *r; 977 struct window_pane_resize *r1; 978 979 window_pane_reset_mode_all(wp); 980 free(wp->searchstr); 981 982 if (wp->fd != -1) { 983 #ifdef HAVE_UTEMPTER 984 utempter_remove_record(wp->fd); 985 kill(getpid(), SIGCHLD); 986 #endif 987 bufferevent_free(wp->event); 988 close(wp->fd); 989 } 990 if (wp->ictx != NULL) 991 input_free(wp->ictx); 992 993 screen_free(&wp->status_screen); 994 995 screen_free(&wp->base); 996 997 if (wp->pipe_fd != -1) { 998 bufferevent_free(wp->pipe_event); 999 close(wp->pipe_fd); 1000 } 1001 1002 if (event_initialized(&wp->resize_timer)) 1003 event_del(&wp->resize_timer); 1004 TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) { 1005 TAILQ_REMOVE(&wp->resize_queue, r, entry); 1006 free(r); 1007 } 1008 1009 RB_REMOVE(window_pane_tree, &all_window_panes, wp); 1010 1011 options_free(wp->options); 1012 free(__UNCONST(wp->cwd)); 1013 free(wp->shell); 1014 cmd_free_argv(wp->argc, wp->argv); 1015 colour_palette_free(&wp->palette); 1016 free(wp); 1017 } 1018 1019 static void 1020 window_pane_read_callback(__unused struct bufferevent *bufev, void *data) 1021 { 1022 struct window_pane *wp = data; 1023 struct evbuffer *evb = wp->event->input; 1024 struct window_pane_offset *wpo = &wp->pipe_offset; 1025 size_t size = EVBUFFER_LENGTH(evb); 1026 char *new_data; 1027 size_t new_size; 1028 struct client *c; 1029 1030 if (wp->pipe_fd != -1) { 1031 new_data = window_pane_get_new_data(wp, wpo, &new_size); 1032 if (new_size > 0) { 1033 bufferevent_write(wp->pipe_event, new_data, new_size); 1034 window_pane_update_used_data(wp, wpo, new_size); 1035 } 1036 } 1037 1038 log_debug("%%%u has %zu bytes", wp->id, size); 1039 TAILQ_FOREACH(c, &clients, entry) { 1040 if (c->session != NULL && (c->flags & CLIENT_CONTROL)) 1041 control_write_output(c, wp); 1042 } 1043 input_parse_pane(wp); 1044 bufferevent_disable(wp->event, EV_READ); 1045 } 1046 1047 static void 1048 window_pane_error_callback(__unused struct bufferevent *bufev, 1049 __unused short what, void *data) 1050 { 1051 struct window_pane *wp = data; 1052 1053 log_debug("%%%u error", wp->id); 1054 wp->flags |= PANE_EXITED; 1055 1056 if (window_pane_destroy_ready(wp)) 1057 server_destroy_pane(wp, 1); 1058 } 1059 1060 void 1061 window_pane_set_event(struct window_pane *wp) 1062 { 1063 setblocking(wp->fd, 0); 1064 1065 wp->event = bufferevent_new(wp->fd, window_pane_read_callback, 1066 NULL, window_pane_error_callback, wp); 1067 if (wp->event == NULL) 1068 fatalx("out of memory"); 1069 wp->ictx = input_init(wp, wp->event, &wp->palette); 1070 1071 bufferevent_enable(wp->event, EV_READ|EV_WRITE); 1072 } 1073 1074 void 1075 window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) 1076 { 1077 struct window_mode_entry *wme; 1078 struct window_pane_resize *r; 1079 1080 if (sx == wp->sx && sy == wp->sy) 1081 return; 1082 1083 r = xmalloc(sizeof *r); 1084 r->sx = sx; 1085 r->sy = sy; 1086 r->osx = wp->sx; 1087 r->osy = wp->sy; 1088 TAILQ_INSERT_TAIL (&wp->resize_queue, r, entry); 1089 1090 wp->sx = sx; 1091 wp->sy = sy; 1092 1093 log_debug("%s: %%%u resize %ux%u", __func__, wp->id, sx, sy); 1094 screen_resize(&wp->base, sx, sy, wp->base.saved_grid == NULL); 1095 1096 wme = TAILQ_FIRST(&wp->modes); 1097 if (wme != NULL && wme->mode->resize != NULL) 1098 wme->mode->resize(wme, sx, sy); 1099 } 1100 1101 int 1102 window_pane_set_mode(struct window_pane *wp, struct window_pane *swp, 1103 const struct window_mode *mode, struct cmd_find_state *fs, 1104 struct args *args) 1105 { 1106 struct window_mode_entry *wme; 1107 struct window *w = wp->window; 1108 1109 if (!TAILQ_EMPTY(&wp->modes) && TAILQ_FIRST(&wp->modes)->mode == mode) 1110 return (1); 1111 1112 TAILQ_FOREACH(wme, &wp->modes, entry) { 1113 if (wme->mode == mode) 1114 break; 1115 } 1116 if (wme != NULL) { 1117 TAILQ_REMOVE(&wp->modes, wme, entry); 1118 TAILQ_INSERT_HEAD(&wp->modes, wme, entry); 1119 } else { 1120 wme = xcalloc(1, sizeof *wme); 1121 wme->wp = wp; 1122 wme->swp = swp; 1123 wme->mode = mode; 1124 wme->prefix = 1; 1125 TAILQ_INSERT_HEAD(&wp->modes, wme, entry); 1126 wme->screen = wme->mode->init(wme, fs, args); 1127 } 1128 wp->screen = wme->screen; 1129 1130 wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED); 1131 layout_fix_panes(w, NULL); 1132 1133 server_redraw_window_borders(wp->window); 1134 server_status_window(wp->window); 1135 notify_pane("pane-mode-changed", wp); 1136 1137 return (0); 1138 } 1139 1140 void 1141 window_pane_reset_mode(struct window_pane *wp) 1142 { 1143 struct window_mode_entry *wme, *next; 1144 struct window *w = wp->window; 1145 1146 if (TAILQ_EMPTY(&wp->modes)) 1147 return; 1148 1149 wme = TAILQ_FIRST(&wp->modes); 1150 TAILQ_REMOVE(&wp->modes, wme, entry); 1151 wme->mode->free(wme); 1152 free(wme); 1153 1154 next = TAILQ_FIRST(&wp->modes); 1155 if (next == NULL) { 1156 wp->flags &= ~PANE_UNSEENCHANGES; 1157 log_debug("%s: no next mode", __func__); 1158 wp->screen = &wp->base; 1159 } else { 1160 log_debug("%s: next mode is %s", __func__, next->mode->name); 1161 wp->screen = next->screen; 1162 if (next->mode->resize != NULL) 1163 next->mode->resize(next, wp->sx, wp->sy); 1164 } 1165 1166 wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED); 1167 layout_fix_panes(w, NULL); 1168 1169 server_redraw_window_borders(wp->window); 1170 server_status_window(wp->window); 1171 notify_pane("pane-mode-changed", wp); 1172 } 1173 1174 void 1175 window_pane_reset_mode_all(struct window_pane *wp) 1176 { 1177 while (!TAILQ_EMPTY(&wp->modes)) 1178 window_pane_reset_mode(wp); 1179 } 1180 1181 static void 1182 window_pane_copy_paste(struct window_pane *wp, char *buf, size_t len) 1183 { 1184 struct window_pane *loop; 1185 1186 TAILQ_FOREACH(loop, &wp->window->panes, entry) { 1187 if (loop != wp && 1188 TAILQ_EMPTY(&loop->modes) && 1189 loop->fd != -1 && 1190 (~loop->flags & PANE_INPUTOFF) && 1191 window_pane_visible(loop) && 1192 options_get_number(loop->options, "synchronize-panes")) { 1193 log_debug("%s: %.*s", __func__, (int)len, buf); 1194 bufferevent_write(loop->event, buf, len); 1195 } 1196 } 1197 } 1198 1199 static void 1200 window_pane_copy_key(struct window_pane *wp, key_code key) 1201 { 1202 struct window_pane *loop; 1203 1204 TAILQ_FOREACH(loop, &wp->window->panes, entry) { 1205 if (loop != wp && 1206 TAILQ_EMPTY(&loop->modes) && 1207 loop->fd != -1 && 1208 (~loop->flags & PANE_INPUTOFF) && 1209 window_pane_visible(loop) && 1210 options_get_number(loop->options, "synchronize-panes")) 1211 input_key_pane(loop, key, NULL); 1212 } 1213 } 1214 1215 void 1216 window_pane_paste(struct window_pane *wp, key_code key, char *buf, size_t len) 1217 { 1218 if (!TAILQ_EMPTY(&wp->modes)) 1219 return; 1220 1221 if (wp->fd == -1 || wp->flags & PANE_INPUTOFF) 1222 return; 1223 1224 if (KEYC_IS_PASTE(key) && (~wp->screen->mode & MODE_BRACKETPASTE)) 1225 return; 1226 1227 log_debug("%s: %.*s", __func__, (int)len, buf); 1228 bufferevent_write(wp->event, buf, len); 1229 1230 if (options_get_number(wp->options, "synchronize-panes")) 1231 window_pane_copy_paste(wp, buf, len); 1232 } 1233 1234 int 1235 window_pane_key(struct window_pane *wp, struct client *c, struct session *s, 1236 struct winlink *wl, key_code key, struct mouse_event *m) 1237 { 1238 struct window_mode_entry *wme; 1239 1240 if (KEYC_IS_MOUSE(key) && m == NULL) 1241 return (-1); 1242 1243 wme = TAILQ_FIRST(&wp->modes); 1244 if (wme != NULL) { 1245 if (wme->mode->key != NULL && c != NULL) { 1246 key &= ~KEYC_MASK_FLAGS; 1247 wme->mode->key(wme, c, s, wl, key, m); 1248 } 1249 return (0); 1250 } 1251 1252 if (wp->fd == -1 || wp->flags & PANE_INPUTOFF) 1253 return (0); 1254 1255 if (input_key_pane(wp, key, m) != 0) 1256 return (-1); 1257 1258 if (KEYC_IS_MOUSE(key)) 1259 return (0); 1260 if (options_get_number(wp->options, "synchronize-panes")) 1261 window_pane_copy_key(wp, key); 1262 return (0); 1263 } 1264 1265 int 1266 window_pane_visible(struct window_pane *wp) 1267 { 1268 if (~wp->window->flags & WINDOW_ZOOMED) 1269 return (1); 1270 return (wp == wp->window->active); 1271 } 1272 1273 int 1274 window_pane_exited(struct window_pane *wp) 1275 { 1276 return (wp->fd == -1 || (wp->flags & PANE_EXITED)); 1277 } 1278 1279 u_int 1280 window_pane_search(struct window_pane *wp, const char *term, int regex, 1281 int ignore) 1282 { 1283 struct screen *s = &wp->base; 1284 regex_t r; 1285 char *new = NULL, *line; 1286 u_int i; 1287 int flags = 0, found; 1288 size_t n; 1289 1290 if (!regex) { 1291 if (ignore) 1292 flags |= FNM_CASEFOLD; 1293 xasprintf(&new, "*%s*", term); 1294 } else { 1295 if (ignore) 1296 flags |= REG_ICASE; 1297 if (regcomp(&r, term, flags|REG_EXTENDED) != 0) 1298 return (0); 1299 } 1300 1301 for (i = 0; i < screen_size_y(s); i++) { 1302 line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s)); 1303 for (n = strlen(line); n > 0; n--) { 1304 if (!isspace((u_char)line[n - 1])) 1305 break; 1306 line[n - 1] = '\0'; 1307 } 1308 log_debug("%s: %s", __func__, line); 1309 if (!regex) 1310 found = (fnmatch(new, line, flags) == 0); 1311 else 1312 found = (regexec(&r, line, 0, NULL, 0) == 0); 1313 free(line); 1314 if (found) 1315 break; 1316 } 1317 if (!regex) 1318 free(new); 1319 else 1320 regfree(&r); 1321 1322 if (i == screen_size_y(s)) 1323 return (0); 1324 return (i + 1); 1325 } 1326 1327 /* Get MRU pane from a list. */ 1328 static struct window_pane * 1329 window_pane_choose_best(struct window_pane **list, u_int size) 1330 { 1331 struct window_pane *next, *best; 1332 u_int i; 1333 1334 if (size == 0) 1335 return (NULL); 1336 1337 best = list[0]; 1338 for (i = 1; i < size; i++) { 1339 next = list[i]; 1340 if (next->active_point > best->active_point) 1341 best = next; 1342 } 1343 return (best); 1344 } 1345 1346 /* 1347 * Get full size and offset of a window pane including the area of the 1348 * scrollbars if they were visible but not including the border(s). 1349 */ 1350 static void 1351 window_pane_full_size_offset(struct window_pane *wp, u_int *xoff, u_int *yoff, 1352 u_int *sx, u_int *sy) 1353 { 1354 struct window *w = wp->window; 1355 int pane_scrollbars; 1356 u_int sb_w, sb_pos; 1357 1358 pane_scrollbars = options_get_number(w->options, "pane-scrollbars"); 1359 sb_pos = options_get_number(w->options, "pane-scrollbars-position"); 1360 1361 if (window_pane_show_scrollbar(wp, pane_scrollbars)) 1362 sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; 1363 else 1364 sb_w = 0; 1365 if (sb_pos == PANE_SCROLLBARS_LEFT) { 1366 *xoff = wp->xoff - sb_w; 1367 *sx = wp->sx + sb_w; 1368 } else { /* sb_pos == PANE_SCROLLBARS_RIGHT */ 1369 *xoff = wp->xoff; 1370 *sx = wp->sx + sb_w; 1371 } 1372 *yoff = wp->yoff; 1373 *sy = wp->sy; 1374 } 1375 1376 /* 1377 * Find the pane directly above another. We build a list of those adjacent to 1378 * top edge and then choose the best. 1379 */ 1380 struct window_pane * 1381 window_pane_find_up(struct window_pane *wp) 1382 { 1383 struct window *w; 1384 struct window_pane *next, *best, **list; 1385 u_int edge, left, right, end, size; 1386 int status, found; 1387 u_int xoff, yoff, sx, sy; 1388 1389 if (wp == NULL) 1390 return (NULL); 1391 w = wp->window; 1392 status = options_get_number(w->options, "pane-border-status"); 1393 1394 list = NULL; 1395 size = 0; 1396 1397 window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); 1398 1399 edge = yoff; 1400 if (status == PANE_STATUS_TOP) { 1401 if (edge == 1) 1402 edge = w->sy + 1; 1403 } else if (status == PANE_STATUS_BOTTOM) { 1404 if (edge == 0) 1405 edge = w->sy; 1406 } else { 1407 if (edge == 0) 1408 edge = w->sy + 1; 1409 } 1410 1411 left = xoff; 1412 right = xoff + sx; 1413 1414 TAILQ_FOREACH(next, &w->panes, entry) { 1415 window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy); 1416 if (next == wp) 1417 continue; 1418 if (yoff + sy + 1 != edge) 1419 continue; 1420 end = xoff + sx - 1; 1421 1422 found = 0; 1423 if (xoff < left && end > right) 1424 found = 1; 1425 else if (xoff >= left && xoff <= right) 1426 found = 1; 1427 else if (end >= left && end <= right) 1428 found = 1; 1429 if (!found) 1430 continue; 1431 list = xreallocarray(list, size + 1, sizeof *list); 1432 list[size++] = next; 1433 } 1434 1435 best = window_pane_choose_best(list, size); 1436 free(list); 1437 return (best); 1438 } 1439 1440 /* Find the pane directly below another. */ 1441 struct window_pane * 1442 window_pane_find_down(struct window_pane *wp) 1443 { 1444 struct window *w; 1445 struct window_pane *next, *best, **list; 1446 u_int edge, left, right, end, size; 1447 int status, found; 1448 u_int xoff, yoff, sx, sy; 1449 1450 if (wp == NULL) 1451 return (NULL); 1452 w = wp->window; 1453 status = options_get_number(w->options, "pane-border-status"); 1454 1455 list = NULL; 1456 size = 0; 1457 1458 window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); 1459 1460 edge = yoff + sy + 1; 1461 if (status == PANE_STATUS_TOP) { 1462 if (edge >= w->sy) 1463 edge = 1; 1464 } else if (status == PANE_STATUS_BOTTOM) { 1465 if (edge >= w->sy - 1) 1466 edge = 0; 1467 } else { 1468 if (edge >= w->sy) 1469 edge = 0; 1470 } 1471 1472 left = wp->xoff; 1473 right = wp->xoff + wp->sx; 1474 1475 TAILQ_FOREACH(next, &w->panes, entry) { 1476 window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy); 1477 if (next == wp) 1478 continue; 1479 if (yoff != edge) 1480 continue; 1481 end = xoff + sx - 1; 1482 1483 found = 0; 1484 if (xoff < left && end > right) 1485 found = 1; 1486 else if (xoff >= left && xoff <= right) 1487 found = 1; 1488 else if (end >= left && end <= right) 1489 found = 1; 1490 if (!found) 1491 continue; 1492 list = xreallocarray(list, size + 1, sizeof *list); 1493 list[size++] = next; 1494 } 1495 1496 best = window_pane_choose_best(list, size); 1497 free(list); 1498 return (best); 1499 } 1500 1501 /* Find the pane directly to the left of another. */ 1502 struct window_pane * 1503 window_pane_find_left(struct window_pane *wp) 1504 { 1505 struct window *w; 1506 struct window_pane *next, *best, **list; 1507 u_int edge, top, bottom, end, size; 1508 int found; 1509 u_int xoff, yoff, sx, sy; 1510 1511 if (wp == NULL) 1512 return (NULL); 1513 w = wp->window; 1514 1515 list = NULL; 1516 size = 0; 1517 1518 window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); 1519 1520 edge = xoff; 1521 if (edge == 0) 1522 edge = w->sx + 1; 1523 1524 top = yoff; 1525 bottom = yoff + sy; 1526 1527 TAILQ_FOREACH(next, &w->panes, entry) { 1528 window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy); 1529 if (next == wp) 1530 continue; 1531 if (xoff + sx + 1 != edge) 1532 continue; 1533 end = yoff + sy - 1; 1534 1535 found = 0; 1536 if (yoff < top && end > bottom) 1537 found = 1; 1538 else if (yoff >= top && yoff <= bottom) 1539 found = 1; 1540 else if (end >= top && end <= bottom) 1541 found = 1; 1542 if (!found) 1543 continue; 1544 list = xreallocarray(list, size + 1, sizeof *list); 1545 list[size++] = next; 1546 } 1547 1548 best = window_pane_choose_best(list, size); 1549 free(list); 1550 return (best); 1551 } 1552 1553 /* Find the pane directly to the right of another. */ 1554 struct window_pane * 1555 window_pane_find_right(struct window_pane *wp) 1556 { 1557 struct window *w; 1558 struct window_pane *next, *best, **list; 1559 u_int edge, top, bottom, end, size; 1560 int found; 1561 u_int xoff, yoff, sx, sy; 1562 1563 if (wp == NULL) 1564 return (NULL); 1565 w = wp->window; 1566 1567 list = NULL; 1568 size = 0; 1569 1570 window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); 1571 1572 edge = xoff + sx + 1; 1573 if (edge >= w->sx) 1574 edge = 0; 1575 1576 top = wp->yoff; 1577 bottom = wp->yoff + wp->sy; 1578 1579 TAILQ_FOREACH(next, &w->panes, entry) { 1580 window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy); 1581 if (next == wp) 1582 continue; 1583 if (xoff != edge) 1584 continue; 1585 end = yoff + sy - 1; 1586 1587 found = 0; 1588 if (yoff < top && end > bottom) 1589 found = 1; 1590 else if (yoff >= top && yoff <= bottom) 1591 found = 1; 1592 else if (end >= top && end <= bottom) 1593 found = 1; 1594 if (!found) 1595 continue; 1596 list = xreallocarray(list, size + 1, sizeof *list); 1597 list[size++] = next; 1598 } 1599 1600 best = window_pane_choose_best(list, size); 1601 free(list); 1602 return (best); 1603 } 1604 1605 void 1606 window_pane_stack_push(struct window_panes *stack, struct window_pane *wp) 1607 { 1608 if (wp != NULL) { 1609 window_pane_stack_remove(stack, wp); 1610 TAILQ_INSERT_HEAD(stack, wp, sentry); 1611 wp->flags |= PANE_VISITED; 1612 } 1613 } 1614 1615 void 1616 window_pane_stack_remove(struct window_panes *stack, struct window_pane *wp) 1617 { 1618 if (wp != NULL && (wp->flags & PANE_VISITED)) { 1619 TAILQ_REMOVE(stack, wp, sentry); 1620 wp->flags &= ~PANE_VISITED; 1621 } 1622 } 1623 1624 /* Clear alert flags for a winlink */ 1625 void 1626 winlink_clear_flags(struct winlink *wl) 1627 { 1628 struct winlink *loop; 1629 1630 wl->window->flags &= ~WINDOW_ALERTFLAGS; 1631 TAILQ_FOREACH(loop, &wl->window->winlinks, wentry) { 1632 if ((loop->flags & WINLINK_ALERTFLAGS) != 0) { 1633 loop->flags &= ~WINLINK_ALERTFLAGS; 1634 server_status_session(loop->session); 1635 } 1636 } 1637 } 1638 1639 /* Shuffle window indexes up. */ 1640 int 1641 winlink_shuffle_up(struct session *s, struct winlink *wl, int before) 1642 { 1643 int idx, last; 1644 1645 if (wl == NULL) 1646 return (-1); 1647 if (before) 1648 idx = wl->idx; 1649 else 1650 idx = wl->idx + 1; 1651 1652 /* Find the next free index. */ 1653 for (last = idx; last < INT_MAX; last++) { 1654 if (winlink_find_by_index(&s->windows, last) == NULL) 1655 break; 1656 } 1657 if (last == INT_MAX) 1658 return (-1); 1659 1660 /* Move everything from last - 1 to idx up a bit. */ 1661 for (; last > idx; last--) { 1662 wl = winlink_find_by_index(&s->windows, last - 1); 1663 RB_REMOVE(winlinks, &s->windows, wl); 1664 wl->idx++; 1665 RB_INSERT(winlinks, &s->windows, wl); 1666 } 1667 1668 return (idx); 1669 } 1670 1671 static void 1672 window_pane_input_callback(struct client *c, __unused const char *path, 1673 int error, int closed, struct evbuffer *buffer, void *data) 1674 { 1675 struct window_pane_input_data *cdata = data; 1676 struct window_pane *wp; 1677 u_char *buf = EVBUFFER_DATA(buffer); 1678 size_t len = EVBUFFER_LENGTH(buffer); 1679 1680 wp = window_pane_find_by_id(cdata->wp); 1681 if (cdata->file != NULL && (wp == NULL || c->flags & CLIENT_DEAD)) { 1682 if (wp == NULL) { 1683 c->retval = 1; 1684 c->flags |= CLIENT_EXIT; 1685 } 1686 file_cancel(cdata->file); 1687 } else if (cdata->file == NULL || closed || error != 0) { 1688 cmdq_continue(cdata->item); 1689 server_client_unref(c); 1690 free(cdata); 1691 } else 1692 input_parse_buffer(wp, buf, len); 1693 evbuffer_drain(buffer, len); 1694 } 1695 1696 int 1697 window_pane_start_input(struct window_pane *wp, struct cmdq_item *item, 1698 char **cause) 1699 { 1700 struct client *c = cmdq_get_client(item); 1701 struct window_pane_input_data *cdata; 1702 1703 if (~wp->flags & PANE_EMPTY) { 1704 *cause = xstrdup("pane is not empty"); 1705 return (-1); 1706 } 1707 if (c->flags & (CLIENT_DEAD|CLIENT_EXITED)) 1708 return (1); 1709 if (c->session != NULL) 1710 return (1); 1711 1712 cdata = xmalloc(sizeof *cdata); 1713 cdata->item = item; 1714 cdata->wp = wp->id; 1715 cdata->file = file_read(c, "-", window_pane_input_callback, cdata); 1716 c->references++; 1717 1718 return (0); 1719 } 1720 1721 void * 1722 window_pane_get_new_data(struct window_pane *wp, 1723 struct window_pane_offset *wpo, size_t *size) 1724 { 1725 size_t used = wpo->used - wp->base_offset; 1726 1727 *size = EVBUFFER_LENGTH(wp->event->input) - used; 1728 return (EVBUFFER_DATA(wp->event->input) + used); 1729 } 1730 1731 void 1732 window_pane_update_used_data(struct window_pane *wp, 1733 struct window_pane_offset *wpo, size_t size) 1734 { 1735 size_t used = wpo->used - wp->base_offset; 1736 1737 if (size > EVBUFFER_LENGTH(wp->event->input) - used) 1738 size = EVBUFFER_LENGTH(wp->event->input) - used; 1739 wpo->used += size; 1740 } 1741 1742 void 1743 window_set_fill_character(struct window *w) 1744 { 1745 const char *value; 1746 struct utf8_data *ud; 1747 1748 free(w->fill_character); 1749 w->fill_character = NULL; 1750 1751 value = options_get_string(w->options, "fill-character"); 1752 if (*value != '\0' && utf8_isvalid(value)) { 1753 ud = utf8_fromcstr(value); 1754 if (ud != NULL && ud[0].width == 1) 1755 w->fill_character = ud; 1756 else 1757 free(ud); 1758 } 1759 } 1760 1761 void 1762 window_pane_default_cursor(struct window_pane *wp) 1763 { 1764 screen_set_default_cursor(wp->screen, wp->options); 1765 } 1766 1767 int 1768 window_pane_mode(struct window_pane *wp) 1769 { 1770 if (TAILQ_FIRST(&wp->modes) != NULL) { 1771 if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode) 1772 return (WINDOW_PANE_COPY_MODE); 1773 if (TAILQ_FIRST(&wp->modes)->mode == &window_view_mode) 1774 return (WINDOW_PANE_VIEW_MODE); 1775 } 1776 return (WINDOW_PANE_NO_MODE); 1777 } 1778 1779 /* Return 1 if scrollbar is or should be displayed. */ 1780 int 1781 window_pane_show_scrollbar(struct window_pane *wp, int sb_option) 1782 { 1783 if (SCREEN_IS_ALTERNATE(wp->screen)) 1784 return (0); 1785 if (sb_option == PANE_SCROLLBARS_ALWAYS || 1786 (sb_option == PANE_SCROLLBARS_MODAL && 1787 window_pane_mode(wp) != WINDOW_PANE_NO_MODE)) 1788 return (1); 1789 return (0); 1790 } 1791 1792 int 1793 window_pane_get_bg(struct window_pane *wp) 1794 { 1795 int c; 1796 struct grid_cell defaults; 1797 1798 c = window_pane_get_bg_control_client(wp); 1799 if (c == -1) { 1800 tty_default_colours(&defaults, wp); 1801 if (COLOUR_DEFAULT(defaults.bg)) 1802 c = window_get_bg_client(wp); 1803 else 1804 c = defaults.bg; 1805 } 1806 return (c); 1807 } 1808 1809 /* Get a client with a background for the pane. */ 1810 int 1811 window_get_bg_client(struct window_pane *wp) 1812 { 1813 struct window *w = wp->window; 1814 struct client *loop; 1815 1816 TAILQ_FOREACH(loop, &clients, entry) { 1817 if (loop->flags & CLIENT_UNATTACHEDFLAGS) 1818 continue; 1819 if (loop->session == NULL || !session_has(loop->session, w)) 1820 continue; 1821 if (loop->tty.bg == -1) 1822 continue; 1823 return (loop->tty.bg); 1824 } 1825 return (-1); 1826 } 1827 1828 /* 1829 * If any control mode client exists that has provided a bg color, return it. 1830 * Otherwise, return -1. 1831 */ 1832 int 1833 window_pane_get_bg_control_client(struct window_pane *wp) 1834 { 1835 struct client *c; 1836 1837 if (wp->control_bg == -1) 1838 return (-1); 1839 1840 TAILQ_FOREACH(c, &clients, entry) { 1841 if (c->flags & CLIENT_CONTROL) 1842 return (wp->control_bg); 1843 } 1844 return (-1); 1845 } 1846 1847 /* 1848 * Get a client with a foreground for the pane. There isn't much to choose 1849 * between them so just use the first. 1850 */ 1851 int 1852 window_pane_get_fg(struct window_pane *wp) 1853 { 1854 struct window *w = wp->window; 1855 struct client *loop; 1856 1857 TAILQ_FOREACH(loop, &clients, entry) { 1858 if (loop->flags & CLIENT_UNATTACHEDFLAGS) 1859 continue; 1860 if (loop->session == NULL || !session_has(loop->session, w)) 1861 continue; 1862 if (loop->tty.fg == -1) 1863 continue; 1864 return (loop->tty.fg); 1865 } 1866 return (-1); 1867 } 1868 1869 /* 1870 * If any control mode client exists that has provided a fg color, return it. 1871 * Otherwise, return -1. 1872 */ 1873 int 1874 window_pane_get_fg_control_client(struct window_pane *wp) 1875 { 1876 struct client *c; 1877 1878 if (wp->control_fg == -1) 1879 return (-1); 1880 1881 TAILQ_FOREACH(c, &clients, entry) { 1882 if (c->flags & CLIENT_CONTROL) 1883 return (wp->control_fg); 1884 } 1885 return (-1); 1886 } 1887 1888 enum client_theme 1889 window_pane_get_theme(struct window_pane *wp) 1890 { 1891 struct window *w; 1892 struct client *loop; 1893 enum client_theme theme; 1894 int found_light = 0, found_dark = 0; 1895 1896 if (wp == NULL) 1897 return (THEME_UNKNOWN); 1898 w = wp->window; 1899 1900 /* 1901 * Derive theme from pane background color, if it's not the default 1902 * colour. 1903 */ 1904 theme = colour_totheme(window_pane_get_bg(wp)); 1905 if (theme != THEME_UNKNOWN) 1906 return (theme); 1907 1908 /* Try to find a client that has a theme. */ 1909 TAILQ_FOREACH(loop, &clients, entry) { 1910 if (loop->flags & CLIENT_UNATTACHEDFLAGS) 1911 continue; 1912 if (loop->session == NULL || !session_has(loop->session, w)) 1913 continue; 1914 switch (loop->theme) { 1915 case THEME_LIGHT: 1916 found_light = 1; 1917 break; 1918 case THEME_DARK: 1919 found_dark = 1; 1920 break; 1921 case THEME_UNKNOWN: 1922 break; 1923 } 1924 } 1925 1926 if (found_dark && !found_light) 1927 return (THEME_DARK); 1928 if (found_light && !found_dark) 1929 return (THEME_LIGHT); 1930 return (THEME_UNKNOWN); 1931 } 1932 1933 void 1934 window_pane_send_theme_update(struct window_pane *wp) 1935 { 1936 if (wp == NULL || window_pane_exited(wp)) 1937 return; 1938 if (~wp->flags & PANE_THEMECHANGED) 1939 return; 1940 if (~wp->screen->mode & MODE_THEME_UPDATES) 1941 return; 1942 1943 switch (window_pane_get_theme(wp)) { 1944 case THEME_LIGHT: 1945 input_key_pane(wp, KEYC_REPORT_LIGHT_THEME, NULL); 1946 break; 1947 case THEME_DARK: 1948 input_key_pane(wp, KEYC_REPORT_DARK_THEME, NULL); 1949 break; 1950 case THEME_UNKNOWN: 1951 break; 1952 } 1953 1954 wp->flags &= ~PANE_THEMECHANGED; 1955 } 1956