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