1 /* $OpenBSD$ */ 2 3 /* 4 * Copyright (c) 2017 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 21 #include <ctype.h> 22 #include <stdlib.h> 23 #include <string.h> 24 25 #include "tmux.h" 26 27 static struct screen *window_tree_init(struct window_pane *, 28 struct cmd_find_state *, struct args *); 29 static void window_tree_free(struct window_pane *); 30 static void window_tree_resize(struct window_pane *, u_int, u_int); 31 static void window_tree_key(struct window_pane *, 32 struct client *, struct session *, key_code, 33 struct mouse_event *); 34 35 #define WINDOW_TREE_DEFAULT_COMMAND "switch-client -t '%%'" 36 37 #define WINDOW_TREE_DEFAULT_FORMAT \ 38 "#{?pane_format," \ 39 "#{pane_current_command} \"#{pane_title}\"" \ 40 "," \ 41 "#{?window_format," \ 42 "#{window_name}#{window_flags} " \ 43 "(#{window_panes} panes)" \ 44 "#{?#{==:#{window_panes},1}, \"#{pane_title}\",}" \ 45 "," \ 46 "#{session_windows} windows" \ 47 "#{?session_grouped, " \ 48 "(group #{session_group}: " \ 49 "#{session_group_list})," \ 50 "}" \ 51 "#{?session_attached, (attached),}" \ 52 "}" \ 53 "}" 54 55 const struct window_mode window_tree_mode = { 56 .name = "tree-mode", 57 58 .init = window_tree_init, 59 .free = window_tree_free, 60 .resize = window_tree_resize, 61 .key = window_tree_key, 62 }; 63 64 enum window_tree_sort_type { 65 WINDOW_TREE_BY_INDEX, 66 WINDOW_TREE_BY_NAME, 67 WINDOW_TREE_BY_TIME, 68 }; 69 static const char *window_tree_sort_list[] = { 70 "index", 71 "name", 72 "time" 73 }; 74 75 enum window_tree_type { 76 WINDOW_TREE_NONE, 77 WINDOW_TREE_SESSION, 78 WINDOW_TREE_WINDOW, 79 WINDOW_TREE_PANE, 80 }; 81 82 struct window_tree_itemdata { 83 enum window_tree_type type; 84 int session; 85 int winlink; 86 int pane; 87 }; 88 89 struct window_tree_modedata { 90 struct window_pane *wp; 91 int dead; 92 int references; 93 94 struct mode_tree_data *data; 95 char *format; 96 char *command; 97 int squash_groups; 98 99 struct window_tree_itemdata **item_list; 100 u_int item_size; 101 102 const char *entered; 103 104 struct cmd_find_state fs; 105 enum window_tree_type type; 106 107 int offset; 108 109 int left; 110 int right; 111 u_int start; 112 u_int end; 113 u_int each; 114 }; 115 116 static void 117 window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp, 118 struct winlink **wlp, struct window_pane **wp) 119 { 120 *wp = NULL; 121 *wlp = NULL; 122 *sp = session_find_by_id(item->session); 123 if (*sp == NULL) 124 return; 125 if (item->type == WINDOW_TREE_SESSION) { 126 *wlp = (*sp)->curw; 127 *wp = (*wlp)->window->active; 128 return; 129 } 130 131 *wlp = winlink_find_by_index(&(*sp)->windows, item->winlink); 132 if (*wlp == NULL) { 133 *sp = NULL; 134 return; 135 } 136 if (item->type == WINDOW_TREE_WINDOW) { 137 *wp = (*wlp)->window->active; 138 return; 139 } 140 141 *wp = window_pane_find_by_id(item->pane); 142 if (!window_has_pane((*wlp)->window, *wp)) 143 *wp = NULL; 144 if (*wp == NULL) { 145 *sp = NULL; 146 *wlp = NULL; 147 return; 148 } 149 } 150 151 static struct window_tree_itemdata * 152 window_tree_add_item(struct window_tree_modedata *data) 153 { 154 struct window_tree_itemdata *item; 155 156 data->item_list = xreallocarray(data->item_list, data->item_size + 1, 157 sizeof *data->item_list); 158 item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item); 159 return (item); 160 } 161 162 static void 163 window_tree_free_item(struct window_tree_itemdata *item) 164 { 165 free(item); 166 } 167 168 static int 169 window_tree_cmp_session_name(const void *a0, const void *b0) 170 { 171 const struct session *const *a = a0; 172 const struct session *const *b = b0; 173 174 return (strcmp((*a)->name, (*b)->name)); 175 } 176 177 static int 178 window_tree_cmp_session_time(const void *a0, const void *b0) 179 { 180 const struct session *const *a = a0; 181 const struct session *const *b = b0; 182 183 if (timercmp(&(*a)->activity_time, &(*b)->activity_time, >)) 184 return (-1); 185 if (timercmp(&(*a)->activity_time, &(*b)->activity_time, <)) 186 return (1); 187 return (strcmp((*a)->name, (*b)->name)); 188 } 189 190 static int 191 window_tree_cmp_window_name(const void *a0, const void *b0) 192 { 193 const struct winlink *const *a = a0; 194 const struct winlink *const *b = b0; 195 196 return (strcmp((*a)->window->name, (*b)->window->name)); 197 } 198 199 static int 200 window_tree_cmp_window_time(const void *a0, const void *b0) 201 { 202 const struct winlink *const *a = a0; 203 const struct winlink *const *b = b0; 204 205 if (timercmp(&(*a)->window->activity_time, 206 &(*b)->window->activity_time, >)) 207 return (-1); 208 if (timercmp(&(*a)->window->activity_time, 209 &(*b)->window->activity_time, <)) 210 return (1); 211 return (strcmp((*a)->window->name, (*b)->window->name)); 212 } 213 214 static int 215 window_tree_cmp_pane_time(const void *a0, const void *b0) 216 { 217 const struct window_pane *const *a = a0; 218 const struct window_pane *const *b = b0; 219 220 if ((*a)->active_point < (*b)->active_point) 221 return (-1); 222 if ((*a)->active_point > (*b)->active_point) 223 return (1); 224 return (0); 225 } 226 227 static void 228 window_tree_build_pane(struct session *s, struct winlink *wl, 229 struct window_pane *wp, void *modedata, struct mode_tree_item *parent) 230 { 231 struct window_tree_modedata *data = modedata; 232 struct window_tree_itemdata *item; 233 char *name, *text; 234 u_int idx; 235 236 window_pane_index(wp, &idx); 237 238 item = window_tree_add_item(data); 239 item->type = WINDOW_TREE_PANE; 240 item->session = s->id; 241 item->winlink = wl->idx; 242 item->pane = wp->id; 243 244 text = format_single(NULL, data->format, NULL, s, wl, wp); 245 xasprintf(&name, "%u", idx); 246 247 mode_tree_add(data->data, parent, item, (uintptr_t)wp, name, text, -1); 248 free(text); 249 free(name); 250 } 251 252 static int 253 window_tree_filter_pane(struct session *s, struct winlink *wl, 254 struct window_pane *wp, const char *filter) 255 { 256 char *cp; 257 int result; 258 259 if (filter == NULL) 260 return (1); 261 262 cp = format_single(NULL, filter, NULL, s, wl, wp); 263 result = format_true(cp); 264 free(cp); 265 266 return (result); 267 } 268 269 static int 270 window_tree_build_window(struct session *s, struct winlink *wl, void* modedata, 271 u_int sort_type, struct mode_tree_item *parent, const char *filter) 272 { 273 struct window_tree_modedata *data = modedata; 274 struct window_tree_itemdata *item; 275 struct mode_tree_item *mti; 276 char *name, *text; 277 struct window_pane *wp, **l; 278 u_int n, i; 279 int expanded; 280 281 item = window_tree_add_item(data); 282 item->type = WINDOW_TREE_WINDOW; 283 item->session = s->id; 284 item->winlink = wl->idx; 285 item->pane = -1; 286 287 text = format_single(NULL, data->format, NULL, s, wl, NULL); 288 xasprintf(&name, "%u", wl->idx); 289 290 if (data->type == WINDOW_TREE_SESSION || 291 data->type == WINDOW_TREE_WINDOW) 292 expanded = 0; 293 else 294 expanded = 1; 295 mti = mode_tree_add(data->data, parent, item, (uintptr_t)wl, name, text, 296 expanded); 297 free(text); 298 free(name); 299 300 if ((wp = TAILQ_FIRST(&wl->window->panes)) == NULL) 301 goto empty; 302 if (TAILQ_NEXT(wp, entry) == NULL) { 303 if (!window_tree_filter_pane(s, wl, wp, filter)) 304 goto empty; 305 return (1); 306 } 307 308 l = NULL; 309 n = 0; 310 311 TAILQ_FOREACH(wp, &wl->window->panes, entry) { 312 if (!window_tree_filter_pane(s, wl, wp, filter)) 313 continue; 314 l = xreallocarray(l, n + 1, sizeof *l); 315 l[n++] = wp; 316 } 317 if (n == 0) 318 goto empty; 319 320 switch (sort_type) { 321 case WINDOW_TREE_BY_INDEX: 322 break; 323 case WINDOW_TREE_BY_NAME: 324 /* Panes don't have names, so leave in number order. */ 325 break; 326 case WINDOW_TREE_BY_TIME: 327 qsort(l, n, sizeof *l, window_tree_cmp_pane_time); 328 break; 329 } 330 331 for (i = 0; i < n; i++) 332 window_tree_build_pane(s, wl, l[i], modedata, mti); 333 free(l); 334 return (1); 335 336 empty: 337 window_tree_free_item(item); 338 data->item_size--; 339 mode_tree_remove(data->data, mti); 340 return (0); 341 } 342 343 static void 344 window_tree_build_session(struct session *s, void* modedata, 345 u_int sort_type, const char *filter) 346 { 347 struct window_tree_modedata *data = modedata; 348 struct window_tree_itemdata *item; 349 struct mode_tree_item *mti; 350 char *text; 351 struct winlink *wl, **l; 352 u_int n, i, empty; 353 int expanded; 354 355 item = window_tree_add_item(data); 356 item->type = WINDOW_TREE_SESSION; 357 item->session = s->id; 358 item->winlink = -1; 359 item->pane = -1; 360 361 text = format_single(NULL, data->format, NULL, s, NULL, NULL); 362 363 if (data->type == WINDOW_TREE_SESSION) 364 expanded = 0; 365 else 366 expanded = 1; 367 mti = mode_tree_add(data->data, NULL, item, (uintptr_t)s, s->name, text, 368 expanded); 369 free(text); 370 371 l = NULL; 372 n = 0; 373 RB_FOREACH(wl, winlinks, &s->windows) { 374 l = xreallocarray(l, n + 1, sizeof *l); 375 l[n++] = wl; 376 } 377 switch (sort_type) { 378 case WINDOW_TREE_BY_INDEX: 379 break; 380 case WINDOW_TREE_BY_NAME: 381 qsort(l, n, sizeof *l, window_tree_cmp_window_name); 382 break; 383 case WINDOW_TREE_BY_TIME: 384 qsort(l, n, sizeof *l, window_tree_cmp_window_time); 385 break; 386 } 387 388 empty = 0; 389 for (i = 0; i < n; i++) { 390 if (!window_tree_build_window(s, l[i], modedata, sort_type, mti, 391 filter)) 392 empty++; 393 } 394 if (empty == n) { 395 window_tree_free_item(item); 396 data->item_size--; 397 mode_tree_remove(data->data, mti); 398 } 399 free(l); 400 } 401 402 static void 403 window_tree_build(void *modedata, u_int sort_type, uint64_t *tag, 404 const char *filter) 405 { 406 struct window_tree_modedata *data = modedata; 407 struct session *s, **l; 408 struct session_group *sg, *current; 409 u_int n, i; 410 411 current = session_group_contains(data->fs.s); 412 413 for (i = 0; i < data->item_size; i++) 414 window_tree_free_item(data->item_list[i]); 415 free(data->item_list); 416 data->item_list = NULL; 417 data->item_size = 0; 418 419 l = NULL; 420 n = 0; 421 RB_FOREACH(s, sessions, &sessions) { 422 if (data->squash_groups && 423 (sg = session_group_contains(s)) != NULL) { 424 if ((sg == current && s != data->fs.s) || 425 (sg != current && s != TAILQ_FIRST(&sg->sessions))) 426 continue; 427 } 428 l = xreallocarray(l, n + 1, sizeof *l); 429 l[n++] = s; 430 } 431 switch (sort_type) { 432 case WINDOW_TREE_BY_INDEX: 433 break; 434 case WINDOW_TREE_BY_NAME: 435 qsort(l, n, sizeof *l, window_tree_cmp_session_name); 436 break; 437 case WINDOW_TREE_BY_TIME: 438 qsort(l, n, sizeof *l, window_tree_cmp_session_time); 439 break; 440 } 441 442 for (i = 0; i < n; i++) 443 window_tree_build_session(l[i], modedata, sort_type, filter); 444 free(l); 445 446 switch (data->type) { 447 case WINDOW_TREE_NONE: 448 break; 449 case WINDOW_TREE_SESSION: 450 *tag = (uintptr_t)data->fs.s; 451 break; 452 case WINDOW_TREE_WINDOW: 453 *tag = (uintptr_t)data->fs.wl; 454 break; 455 case WINDOW_TREE_PANE: 456 if (window_count_panes(data->fs.wl->window) == 1) 457 *tag = (uintptr_t)data->fs.wl; 458 else 459 *tag = (uintptr_t)data->fs.wp; 460 break; 461 } 462 } 463 464 465 static void 466 window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py, 467 u_int sx, u_int sy, const struct grid_cell *gc, const char *label) 468 { 469 size_t len; 470 u_int ox, oy; 471 472 len = strlen(label); 473 if (sx == 0 || sy == 1 || len > sx) 474 return; 475 ox = (sx - len + 1) / 2; 476 oy = (sy + 1) / 2; 477 478 if (ox > 1 && ox + len < sx - 1 && sy >= 3) { 479 screen_write_cursormove(ctx, px + ox - 1, py + oy - 1); 480 screen_write_box(ctx, len + 2, 3); 481 } 482 screen_write_cursormove(ctx, px + ox, py + oy); 483 screen_write_puts(ctx, gc, "%s", label); 484 } 485 486 static void 487 window_tree_draw_session(struct window_tree_modedata *data, struct session *s, 488 struct screen_write_ctx *ctx, u_int sx, u_int sy) 489 { 490 struct options *oo = s->options; 491 struct winlink *wl; 492 struct window *w; 493 u_int cx = ctx->s->cx, cy = ctx->s->cy; 494 u_int loop, total, visible, each, width, offset; 495 u_int current, start, end, remaining, i; 496 struct grid_cell gc; 497 int colour, active_colour, left, right; 498 char *label; 499 500 total = winlink_count(&s->windows); 501 502 memcpy(&gc, &grid_default_cell, sizeof gc); 503 colour = options_get_number(oo, "display-panes-colour"); 504 active_colour = options_get_number(oo, "display-panes-active-colour"); 505 506 if (sx / total < 24) { 507 visible = sx / 24; 508 if (visible == 0) 509 visible = 1; 510 } else 511 visible = total; 512 513 current = 0; 514 RB_FOREACH(wl, winlinks, &s->windows) { 515 if (wl == s->curw) 516 break; 517 current++; 518 } 519 520 if (current < visible) { 521 start = 0; 522 end = visible; 523 } else if (current >= total - visible) { 524 start = total - visible; 525 end = total; 526 } else { 527 start = current - (visible / 2); 528 end = start + visible; 529 } 530 531 if (data->offset < -(int)start) 532 data->offset = -(int)start; 533 if (data->offset > (int)(total - end)) 534 data->offset = (int)(total - end); 535 start += data->offset; 536 end += data->offset; 537 538 left = (start != 0); 539 right = (end != total); 540 if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) 541 left = right = 0; 542 if (left && right) { 543 each = (sx - 6) / visible; 544 remaining = (sx - 6) - (visible * each); 545 } else if (left || right) { 546 each = (sx - 3) / visible; 547 remaining = (sx - 3) - (visible * each); 548 } else { 549 each = sx / visible; 550 remaining = sx - (visible * each); 551 } 552 if (each == 0) 553 return; 554 555 if (left) { 556 data->left = cx + 2; 557 screen_write_cursormove(ctx, cx + 2, cy); 558 screen_write_vline(ctx, sy, 0, 0); 559 screen_write_cursormove(ctx, cx, cy + sy / 2); 560 screen_write_puts(ctx, &grid_default_cell, "<"); 561 } else 562 data->left = -1; 563 if (right) { 564 data->right = cx + sx - 3; 565 screen_write_cursormove(ctx, cx + sx - 3, cy); 566 screen_write_vline(ctx, sy, 0, 0); 567 screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2); 568 screen_write_puts(ctx, &grid_default_cell, ">"); 569 } else 570 data->right = -1; 571 572 data->start = start; 573 data->end = end; 574 data->each = each; 575 576 i = loop = 0; 577 RB_FOREACH(wl, winlinks, &s->windows) { 578 if (loop == end) 579 break; 580 if (loop < start) { 581 loop++; 582 continue; 583 } 584 w = wl->window; 585 586 if (wl == s->curw) 587 gc.fg = active_colour; 588 else 589 gc.fg = colour; 590 591 if (left) 592 offset = 3 + (i * each); 593 else 594 offset = (i * each); 595 if (loop == end - 1) 596 width = each + remaining; 597 else 598 width = each - 1; 599 600 screen_write_cursormove(ctx, cx + offset, cy); 601 screen_write_preview(ctx, &w->active->base, width, sy); 602 603 xasprintf(&label, " %u:%s ", wl->idx, w->name); 604 if (strlen(label) > width) 605 xasprintf(&label, " %u ", wl->idx); 606 window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc, 607 label); 608 free(label); 609 610 if (loop != end - 1) { 611 screen_write_cursormove(ctx, cx + offset + width, cy); 612 screen_write_vline(ctx, sy, 0, 0); 613 } 614 loop++; 615 616 i++; 617 } 618 } 619 620 static void 621 window_tree_draw_window(struct window_tree_modedata *data, struct session *s, 622 struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy) 623 { 624 struct options *oo = s->options; 625 struct window_pane *wp; 626 u_int cx = ctx->s->cx, cy = ctx->s->cy; 627 u_int loop, total, visible, each, width, offset; 628 u_int current, start, end, remaining, i; 629 u_int pane_idx; 630 struct grid_cell gc; 631 int colour, active_colour, left, right; 632 char *label; 633 634 total = window_count_panes(w); 635 636 memcpy(&gc, &grid_default_cell, sizeof gc); 637 colour = options_get_number(oo, "display-panes-colour"); 638 active_colour = options_get_number(oo, "display-panes-active-colour"); 639 640 if (sx / total < 24) { 641 visible = sx / 24; 642 if (visible == 0) 643 visible = 1; 644 } else 645 visible = total; 646 647 current = 0; 648 TAILQ_FOREACH(wp, &w->panes, entry) { 649 if (wp == w->active) 650 break; 651 current++; 652 } 653 654 if (current < visible) { 655 start = 0; 656 end = visible; 657 } else if (current >= total - visible) { 658 start = total - visible; 659 end = total; 660 } else { 661 start = current - (visible / 2); 662 end = start + visible; 663 } 664 665 if (data->offset < -(int)start) 666 data->offset = -(int)start; 667 if (data->offset > (int)(total - end)) 668 data->offset = (int)(total - end); 669 start += data->offset; 670 end += data->offset; 671 672 left = (start != 0); 673 right = (end != total); 674 if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) 675 left = right = 0; 676 if (left && right) { 677 each = (sx - 6) / visible; 678 remaining = (sx - 6) - (visible * each); 679 } else if (left || right) { 680 each = (sx - 3) / visible; 681 remaining = (sx - 3) - (visible * each); 682 } else { 683 each = sx / visible; 684 remaining = sx - (visible * each); 685 } 686 if (each == 0) 687 return; 688 689 if (left) { 690 data->left = cx + 2; 691 screen_write_cursormove(ctx, cx + 2, cy); 692 screen_write_vline(ctx, sy, 0, 0); 693 screen_write_cursormove(ctx, cx, cy + sy / 2); 694 screen_write_puts(ctx, &grid_default_cell, "<"); 695 } else 696 data->left = -1; 697 if (right) { 698 data->right = cx + sx - 3; 699 screen_write_cursormove(ctx, cx + sx - 3, cy); 700 screen_write_vline(ctx, sy, 0, 0); 701 screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2); 702 screen_write_puts(ctx, &grid_default_cell, ">"); 703 } else 704 data->right = -1; 705 706 data->start = start; 707 data->end = end; 708 data->each = each; 709 710 i = loop = 0; 711 TAILQ_FOREACH(wp, &w->panes, entry) { 712 if (loop == end) 713 break; 714 if (loop < start) { 715 loop++; 716 continue; 717 } 718 719 if (wp == w->active) 720 gc.fg = active_colour; 721 else 722 gc.fg = colour; 723 724 if (left) 725 offset = 3 + (i * each); 726 else 727 offset = (i * each); 728 if (loop == end - 1) 729 width = each + remaining; 730 else 731 width = each - 1; 732 733 screen_write_cursormove(ctx, cx + offset, cy); 734 screen_write_preview(ctx, &wp->base, width, sy); 735 736 if (window_pane_index(wp, &pane_idx) != 0) 737 pane_idx = loop; 738 xasprintf(&label, " %u ", pane_idx); 739 window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc, 740 label); 741 free(label); 742 743 if (loop != end - 1) { 744 screen_write_cursormove(ctx, cx + offset + width, cy); 745 screen_write_vline(ctx, sy, 0, 0); 746 } 747 loop++; 748 749 i++; 750 } 751 } 752 753 static void 754 window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, 755 u_int sx, u_int sy) 756 { 757 struct window_tree_itemdata *item = itemdata; 758 struct session *sp; 759 struct winlink *wlp; 760 struct window_pane *wp; 761 762 window_tree_pull_item(item, &sp, &wlp, &wp); 763 if (wp == NULL) 764 return; 765 766 switch (item->type) { 767 case WINDOW_TREE_NONE: 768 break; 769 case WINDOW_TREE_SESSION: 770 window_tree_draw_session(modedata, sp, ctx, sx, sy); 771 break; 772 case WINDOW_TREE_WINDOW: 773 window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy); 774 break; 775 case WINDOW_TREE_PANE: 776 screen_write_preview(ctx, &wp->base, sx, sy); 777 break; 778 } 779 } 780 781 static int 782 window_tree_search(__unused void *modedata, void *itemdata, const char *ss) 783 { 784 struct window_tree_itemdata *item = itemdata; 785 struct session *s; 786 struct winlink *wl; 787 struct window_pane *wp; 788 const char *cmd; 789 790 window_tree_pull_item(item, &s, &wl, &wp); 791 792 switch (item->type) { 793 case WINDOW_TREE_NONE: 794 return (0); 795 case WINDOW_TREE_SESSION: 796 if (s == NULL) 797 return (0); 798 return (strstr(s->name, ss) != NULL); 799 case WINDOW_TREE_WINDOW: 800 if (s == NULL || wl == NULL) 801 return (0); 802 return (strstr(wl->window->name, ss) != NULL); 803 case WINDOW_TREE_PANE: 804 if (s == NULL || wl == NULL || wp == NULL) 805 break; 806 cmd = osdep_get_name(wp->fd, wp->tty); 807 if (cmd == NULL || *cmd == '\0') 808 return (0); 809 return (strstr(cmd, ss) != NULL); 810 } 811 return (0); 812 } 813 814 static struct screen * 815 window_tree_init(struct window_pane *wp, struct cmd_find_state *fs, 816 struct args *args) 817 { 818 struct window_tree_modedata *data; 819 struct screen *s; 820 821 wp->modedata = data = xcalloc(1, sizeof *data); 822 823 if (args_has(args, 's')) 824 data->type = WINDOW_TREE_SESSION; 825 else if (args_has(args, 'w')) 826 data->type = WINDOW_TREE_WINDOW; 827 else 828 data->type = WINDOW_TREE_PANE; 829 memcpy(&data->fs, fs, sizeof data->fs); 830 831 data->wp = wp; 832 data->references = 1; 833 834 if (args == NULL || !args_has(args, 'F')) 835 data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT); 836 else 837 data->format = xstrdup(args_get(args, 'F')); 838 if (args == NULL || args->argc == 0) 839 data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND); 840 else 841 data->command = xstrdup(args->argv[0]); 842 data->squash_groups = !args_has(args, 'G'); 843 844 data->data = mode_tree_start(wp, args, window_tree_build, 845 window_tree_draw, window_tree_search, data, window_tree_sort_list, 846 nitems(window_tree_sort_list), &s); 847 mode_tree_zoom(data->data, args); 848 849 mode_tree_build(data->data); 850 mode_tree_draw(data->data); 851 852 data->type = WINDOW_TREE_NONE; 853 854 return (s); 855 } 856 857 static void 858 window_tree_destroy(struct window_tree_modedata *data) 859 { 860 u_int i; 861 862 if (--data->references != 0) 863 return; 864 865 for (i = 0; i < data->item_size; i++) 866 window_tree_free_item(data->item_list[i]); 867 free(data->item_list); 868 869 free(data->format); 870 free(data->command); 871 872 free(data); 873 } 874 875 static void 876 window_tree_free(struct window_pane *wp) 877 { 878 struct window_tree_modedata *data = wp->modedata; 879 880 if (data == NULL) 881 return; 882 883 data->dead = 1; 884 mode_tree_free(data->data); 885 window_tree_destroy(data); 886 } 887 888 static void 889 window_tree_resize(struct window_pane *wp, u_int sx, u_int sy) 890 { 891 struct window_tree_modedata *data = wp->modedata; 892 893 mode_tree_resize(data->data, sx, sy); 894 } 895 896 static char * 897 window_tree_get_target(struct window_tree_itemdata *item, 898 struct cmd_find_state *fs) 899 { 900 struct session *s; 901 struct winlink *wl; 902 struct window_pane *wp; 903 char *target; 904 905 window_tree_pull_item(item, &s, &wl, &wp); 906 907 target = NULL; 908 switch (item->type) { 909 case WINDOW_TREE_NONE: 910 break; 911 case WINDOW_TREE_SESSION: 912 if (s == NULL) 913 break; 914 xasprintf(&target, "=%s:", s->name); 915 break; 916 case WINDOW_TREE_WINDOW: 917 if (s == NULL || wl == NULL) 918 break; 919 xasprintf(&target, "=%s:%u.", s->name, wl->idx); 920 break; 921 case WINDOW_TREE_PANE: 922 if (s == NULL || wl == NULL || wp == NULL) 923 break; 924 xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id); 925 break; 926 } 927 if (target == NULL) 928 cmd_find_clear_state(fs, 0); 929 else 930 cmd_find_from_winlink_pane(fs, wl, wp, 0); 931 return (target); 932 } 933 934 static void 935 window_tree_command_each(void* modedata, void* itemdata, struct client *c, 936 __unused key_code key) 937 { 938 struct window_tree_modedata *data = modedata; 939 struct window_tree_itemdata *item = itemdata; 940 char *name; 941 struct cmd_find_state fs; 942 943 name = window_tree_get_target(item, &fs); 944 if (name != NULL) 945 mode_tree_run_command(c, &fs, data->entered, name); 946 free(name); 947 } 948 949 static enum cmd_retval 950 window_tree_command_done(__unused struct cmdq_item *item, void *modedata) 951 { 952 struct window_tree_modedata *data = modedata; 953 954 if (!data->dead) { 955 mode_tree_build(data->data); 956 mode_tree_draw(data->data); 957 data->wp->flags |= PANE_REDRAW; 958 } 959 window_tree_destroy(data); 960 return (CMD_RETURN_NORMAL); 961 } 962 963 static int 964 window_tree_command_callback(struct client *c, void *modedata, const char *s, 965 __unused int done) 966 { 967 struct window_tree_modedata *data = modedata; 968 969 if (s == NULL || *s == '\0' || data->dead) 970 return (0); 971 972 data->entered = s; 973 mode_tree_each_tagged(data->data, window_tree_command_each, c, 974 KEYC_NONE, 1); 975 data->entered = NULL; 976 977 data->references++; 978 cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 979 980 return (0); 981 } 982 983 static void 984 window_tree_command_free(void *modedata) 985 { 986 struct window_tree_modedata *data = modedata; 987 988 window_tree_destroy(data); 989 } 990 991 static void 992 window_tree_kill_each(__unused void* modedata, void* itemdata, 993 __unused struct client *c, __unused key_code key) 994 { 995 struct window_tree_itemdata *item = itemdata; 996 struct session *s; 997 struct winlink *wl; 998 struct window_pane *wp; 999 1000 window_tree_pull_item(item, &s, &wl, &wp); 1001 1002 switch (item->type) { 1003 case WINDOW_TREE_NONE: 1004 break; 1005 case WINDOW_TREE_SESSION: 1006 if (s != NULL) { 1007 server_destroy_session(s); 1008 session_destroy(s, __func__); 1009 } 1010 break; 1011 case WINDOW_TREE_WINDOW: 1012 if (wl != NULL) 1013 server_kill_window(wl->window); 1014 break; 1015 case WINDOW_TREE_PANE: 1016 if (wp != NULL) 1017 server_kill_pane(wp); 1018 break; 1019 } 1020 } 1021 1022 static int 1023 window_tree_kill_current_callback(struct client *c, void *modedata, 1024 const char *s, __unused int done) 1025 { 1026 struct window_tree_modedata *data = modedata; 1027 struct mode_tree_data *mtd = data->data; 1028 1029 if (s == NULL || *s == '\0' || data->dead) 1030 return (0); 1031 if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') 1032 return (0); 1033 1034 window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE); 1035 1036 data->references++; 1037 cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1038 1039 return (0); 1040 } 1041 1042 static int 1043 window_tree_kill_tagged_callback(struct client *c, void *modedata, 1044 const char *s, __unused int done) 1045 { 1046 struct window_tree_modedata *data = modedata; 1047 struct mode_tree_data *mtd = data->data; 1048 1049 if (s == NULL || *s == '\0' || data->dead) 1050 return (0); 1051 if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') 1052 return (0); 1053 1054 mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1); 1055 1056 data->references++; 1057 cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1058 1059 return (0); 1060 } 1061 1062 static key_code 1063 window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x, 1064 struct window_tree_itemdata *item) 1065 { 1066 struct session *s; 1067 struct winlink *wl; 1068 struct window_pane *wp; 1069 u_int loop; 1070 1071 if (key != KEYC_MOUSEDOWN1_PANE) 1072 return (KEYC_NONE); 1073 1074 if (data->left != -1 && x <= (u_int)data->left) 1075 return ('<'); 1076 if (data->right != -1 && x >= (u_int)data->right) 1077 return ('>'); 1078 1079 if (data->left != -1) 1080 x -= data->left; 1081 else if (x != 0) 1082 x--; 1083 if (x == 0 || data->end == 0) 1084 x = 0; 1085 else { 1086 x = x / data->each; 1087 if (data->start + x >= data->end) 1088 x = data->end - 1; 1089 } 1090 1091 window_tree_pull_item(item, &s, &wl, &wp); 1092 if (item->type == WINDOW_TREE_SESSION) { 1093 if (s == NULL) 1094 return (KEYC_NONE); 1095 mode_tree_expand_current(data->data); 1096 loop = 0; 1097 RB_FOREACH(wl, winlinks, &s->windows) { 1098 if (loop == data->start + x) 1099 break; 1100 loop++; 1101 } 1102 if (wl != NULL) 1103 mode_tree_set_current(data->data, (uintptr_t)wl); 1104 return ('\r'); 1105 } 1106 if (item->type == WINDOW_TREE_WINDOW) { 1107 if (wl == NULL) 1108 return (KEYC_NONE); 1109 mode_tree_expand_current(data->data); 1110 loop = 0; 1111 TAILQ_FOREACH(wp, &wl->window->panes, entry) { 1112 if (loop == data->start + x) 1113 break; 1114 loop++; 1115 } 1116 if (wp != NULL) 1117 mode_tree_set_current(data->data, (uintptr_t)wp); 1118 return ('\r'); 1119 } 1120 return (KEYC_NONE); 1121 } 1122 1123 static void 1124 window_tree_key(struct window_pane *wp, struct client *c, 1125 __unused struct session *s, key_code key, struct mouse_event *m) 1126 { 1127 struct window_tree_modedata *data = wp->modedata; 1128 struct window_tree_itemdata *item, *new_item; 1129 char *name, *prompt = NULL; 1130 struct cmd_find_state fs; 1131 int finished; 1132 u_int tagged, x, y, idx; 1133 struct session *ns; 1134 struct winlink *nwl; 1135 struct window_pane *nwp; 1136 1137 item = mode_tree_get_current(data->data); 1138 finished = mode_tree_key(data->data, c, &key, m, &x, &y); 1139 if (item != (new_item = mode_tree_get_current(data->data))) { 1140 item = new_item; 1141 data->offset = 0; 1142 } 1143 if (KEYC_IS_MOUSE(key)) 1144 key = window_tree_mouse(data, key, x, item); 1145 switch (key) { 1146 case '<': 1147 data->offset--; 1148 break; 1149 case '>': 1150 data->offset++; 1151 break; 1152 case 'x': 1153 window_tree_pull_item(item, &ns, &nwl, &nwp); 1154 switch (item->type) { 1155 case WINDOW_TREE_NONE: 1156 break; 1157 case WINDOW_TREE_SESSION: 1158 if (ns == NULL) 1159 break; 1160 xasprintf(&prompt, "Kill session %s? ", ns->name); 1161 break; 1162 case WINDOW_TREE_WINDOW: 1163 if (nwl == NULL) 1164 break; 1165 xasprintf(&prompt, "Kill window %u? ", nwl->idx); 1166 break; 1167 case WINDOW_TREE_PANE: 1168 if (nwp == NULL || window_pane_index(nwp, &idx) != 0) 1169 break; 1170 xasprintf(&prompt, "Kill pane %u? ", idx); 1171 break; 1172 } 1173 if (prompt == NULL) 1174 break; 1175 data->references++; 1176 status_prompt_set(c, prompt, "", 1177 window_tree_kill_current_callback, window_tree_command_free, 1178 data, PROMPT_SINGLE|PROMPT_NOFORMAT); 1179 free(prompt); 1180 break; 1181 case 'X': 1182 tagged = mode_tree_count_tagged(data->data); 1183 if (tagged == 0) 1184 break; 1185 xasprintf(&prompt, "Kill %u tagged? ", tagged); 1186 data->references++; 1187 status_prompt_set(c, prompt, "", 1188 window_tree_kill_tagged_callback, window_tree_command_free, 1189 data, PROMPT_SINGLE|PROMPT_NOFORMAT); 1190 free(prompt); 1191 break; 1192 case ':': 1193 tagged = mode_tree_count_tagged(data->data); 1194 if (tagged != 0) 1195 xasprintf(&prompt, "(%u tagged) ", tagged); 1196 else 1197 xasprintf(&prompt, "(current) "); 1198 data->references++; 1199 status_prompt_set(c, prompt, "", window_tree_command_callback, 1200 window_tree_command_free, data, PROMPT_NOFORMAT); 1201 free(prompt); 1202 break; 1203 case '\r': 1204 item = mode_tree_get_current(data->data); 1205 name = window_tree_get_target(item, &fs); 1206 if (name != NULL) 1207 mode_tree_run_command(c, NULL, data->command, name); 1208 finished = 1; 1209 free(name); 1210 break; 1211 } 1212 if (finished) 1213 window_pane_reset_mode(wp); 1214 else { 1215 mode_tree_draw(data->data); 1216 wp->flags |= PANE_REDRAW; 1217 } 1218 } 1219