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