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