1 /* $OpenBSD: window-tree.c,v 1.32 2018/12/18 13:20:44 nicm Exp $ */ 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 *, 33 struct winlink *, key_code, 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, (uint64_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, (uint64_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, (uint64_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 = (uint64_t)data->fs.s; 451 break; 452 case WINDOW_TREE_WINDOW: 453 *tag = (uint64_t)data->fs.wl; 454 break; 455 case WINDOW_TREE_PANE: 456 if (window_count_panes(data->fs.wl->window) == 1) 457 *tag = (uint64_t)data->fs.wl; 458 else 459 *tag = (uint64_t)data->fs.wp; 460 break; 461 } 462 } 463 464 static void 465 window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py, 466 u_int sx, u_int sy, const struct grid_cell *gc, const char *label) 467 { 468 size_t len; 469 u_int ox, oy; 470 471 len = strlen(label); 472 if (sx == 0 || sy == 1 || len > sx) 473 return; 474 ox = (sx - len + 1) / 2; 475 oy = (sy + 1) / 2; 476 477 if (ox > 1 && ox + len < sx - 1 && sy >= 3) { 478 screen_write_cursormove(ctx, px + ox - 1, py + oy - 1); 479 screen_write_box(ctx, len + 2, 3); 480 } 481 screen_write_cursormove(ctx, px + ox, py + oy); 482 screen_write_puts(ctx, gc, "%s", label); 483 } 484 485 static void 486 window_tree_draw_session(struct window_tree_modedata *data, struct session *s, 487 struct screen_write_ctx *ctx, u_int sx, u_int sy) 488 { 489 struct options *oo = s->options; 490 struct winlink *wl; 491 struct window *w; 492 u_int cx = ctx->s->cx, cy = ctx->s->cy; 493 u_int loop, total, visible, each, width, offset; 494 u_int current, start, end, remaining, i; 495 struct grid_cell gc; 496 int colour, active_colour, left, right; 497 char *label; 498 499 total = winlink_count(&s->windows); 500 501 memcpy(&gc, &grid_default_cell, sizeof gc); 502 colour = options_get_number(oo, "display-panes-colour"); 503 active_colour = options_get_number(oo, "display-panes-active-colour"); 504 505 if (sx / total < 24) { 506 visible = sx / 24; 507 if (visible == 0) 508 visible = 1; 509 } else 510 visible = total; 511 512 current = 0; 513 RB_FOREACH(wl, winlinks, &s->windows) { 514 if (wl == s->curw) 515 break; 516 current++; 517 } 518 519 if (current < visible) { 520 start = 0; 521 end = visible; 522 } else if (current >= total - visible) { 523 start = total - visible; 524 end = total; 525 } else { 526 start = current - (visible / 2); 527 end = start + visible; 528 } 529 530 if (data->offset < -(int)start) 531 data->offset = -(int)start; 532 if (data->offset > (int)(total - end)) 533 data->offset = (int)(total - end); 534 start += data->offset; 535 end += data->offset; 536 537 left = (start != 0); 538 right = (end != total); 539 if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) 540 left = right = 0; 541 if (left && right) { 542 each = (sx - 6) / visible; 543 remaining = (sx - 6) - (visible * each); 544 } else if (left || right) { 545 each = (sx - 3) / visible; 546 remaining = (sx - 3) - (visible * each); 547 } else { 548 each = sx / visible; 549 remaining = sx - (visible * each); 550 } 551 if (each == 0) 552 return; 553 554 if (left) { 555 data->left = cx + 2; 556 screen_write_cursormove(ctx, cx + 2, cy); 557 screen_write_vline(ctx, sy, 0, 0); 558 screen_write_cursormove(ctx, cx, cy + sy / 2); 559 screen_write_puts(ctx, &grid_default_cell, "<"); 560 } else 561 data->left = -1; 562 if (right) { 563 data->right = cx + sx - 3; 564 screen_write_cursormove(ctx, cx + sx - 3, cy); 565 screen_write_vline(ctx, sy, 0, 0); 566 screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2); 567 screen_write_puts(ctx, &grid_default_cell, ">"); 568 } else 569 data->right = -1; 570 571 data->start = start; 572 data->end = end; 573 data->each = each; 574 575 i = loop = 0; 576 RB_FOREACH(wl, winlinks, &s->windows) { 577 if (loop == end) 578 break; 579 if (loop < start) { 580 loop++; 581 continue; 582 } 583 w = wl->window; 584 585 if (wl == s->curw) 586 gc.fg = active_colour; 587 else 588 gc.fg = colour; 589 590 if (left) 591 offset = 3 + (i * each); 592 else 593 offset = (i * each); 594 if (loop == end - 1) 595 width = each + remaining; 596 else 597 width = each - 1; 598 599 screen_write_cursormove(ctx, cx + offset, cy); 600 screen_write_preview(ctx, &w->active->base, width, sy); 601 602 xasprintf(&label, " %u:%s ", wl->idx, w->name); 603 if (strlen(label) > width) 604 xasprintf(&label, " %u ", wl->idx); 605 window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc, 606 label); 607 free(label); 608 609 if (loop != end - 1) { 610 screen_write_cursormove(ctx, cx + offset + width, cy); 611 screen_write_vline(ctx, sy, 0, 0); 612 } 613 loop++; 614 615 i++; 616 } 617 } 618 619 static void 620 window_tree_draw_window(struct window_tree_modedata *data, struct session *s, 621 struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy) 622 { 623 struct options *oo = s->options; 624 struct window_pane *wp; 625 u_int cx = ctx->s->cx, cy = ctx->s->cy; 626 u_int loop, total, visible, each, width, offset; 627 u_int current, start, end, remaining, i; 628 struct grid_cell gc; 629 int colour, active_colour, left, right, pane_idx; 630 char *label; 631 632 total = window_count_panes(w); 633 634 memcpy(&gc, &grid_default_cell, sizeof gc); 635 colour = options_get_number(oo, "display-panes-colour"); 636 active_colour = options_get_number(oo, "display-panes-active-colour"); 637 638 if (sx / total < 24) { 639 visible = sx / 24; 640 if (visible == 0) 641 visible = 1; 642 } else 643 visible = total; 644 645 current = 0; 646 TAILQ_FOREACH(wp, &w->panes, entry) { 647 if (wp == w->active) 648 break; 649 current++; 650 } 651 652 if (current < visible) { 653 start = 0; 654 end = visible; 655 } else if (current >= total - visible) { 656 start = total - visible; 657 end = total; 658 } else { 659 start = current - (visible / 2); 660 end = start + visible; 661 } 662 663 if (data->offset < -(int)start) 664 data->offset = -(int)start; 665 if (data->offset > (int)(total - end)) 666 data->offset = (int)(total - end); 667 start += data->offset; 668 end += data->offset; 669 670 left = (start != 0); 671 right = (end != total); 672 if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) 673 left = right = 0; 674 if (left && right) { 675 each = (sx - 6) / visible; 676 remaining = (sx - 6) - (visible * each); 677 } else if (left || right) { 678 each = (sx - 3) / visible; 679 remaining = (sx - 3) - (visible * each); 680 } else { 681 each = sx / visible; 682 remaining = sx - (visible * each); 683 } 684 if (each == 0) 685 return; 686 687 if (left) { 688 data->left = cx + 2; 689 screen_write_cursormove(ctx, cx + 2, cy); 690 screen_write_vline(ctx, sy, 0, 0); 691 screen_write_cursormove(ctx, cx, cy + sy / 2); 692 screen_write_puts(ctx, &grid_default_cell, "<"); 693 } else 694 data->left = -1; 695 if (right) { 696 data->right = cx + sx - 3; 697 screen_write_cursormove(ctx, cx + sx - 3, cy); 698 screen_write_vline(ctx, sy, 0, 0); 699 screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2); 700 screen_write_puts(ctx, &grid_default_cell, ">"); 701 } else 702 data->right = -1; 703 704 data->start = start; 705 data->end = end; 706 data->each = each; 707 708 i = loop = 0; 709 TAILQ_FOREACH(wp, &w->panes, entry) { 710 if (loop == end) 711 break; 712 if (loop < start) { 713 loop++; 714 continue; 715 } 716 717 if (wp == w->active) 718 gc.fg = active_colour; 719 else 720 gc.fg = colour; 721 722 if (left) 723 offset = 3 + (i * each); 724 else 725 offset = (i * each); 726 if (loop == end - 1) 727 width = each + remaining; 728 else 729 width = each - 1; 730 731 screen_write_cursormove(ctx, cx + offset, cy); 732 screen_write_preview(ctx, &wp->base, width, sy); 733 734 if (window_pane_index(wp, &pane_idx) != 0) 735 pane_idx = loop; 736 xasprintf(&label, " %u ", pane_idx); 737 window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc, 738 label); 739 free(label); 740 741 if (loop != end - 1) { 742 screen_write_cursormove(ctx, cx + offset + width, cy); 743 screen_write_vline(ctx, sy, 0, 0); 744 } 745 loop++; 746 747 i++; 748 } 749 } 750 751 static void 752 window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, 753 u_int sx, u_int sy) 754 { 755 struct window_tree_itemdata *item = itemdata; 756 struct session *sp; 757 struct winlink *wlp; 758 struct window_pane *wp; 759 760 window_tree_pull_item(item, &sp, &wlp, &wp); 761 if (wp == NULL) 762 return; 763 764 switch (item->type) { 765 case WINDOW_TREE_NONE: 766 break; 767 case WINDOW_TREE_SESSION: 768 window_tree_draw_session(modedata, sp, ctx, sx, sy); 769 break; 770 case WINDOW_TREE_WINDOW: 771 window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy); 772 break; 773 case WINDOW_TREE_PANE: 774 screen_write_preview(ctx, &wp->base, sx, sy); 775 break; 776 } 777 } 778 779 static int 780 window_tree_search(__unused void *modedata, void *itemdata, const char *ss) 781 { 782 struct window_tree_itemdata *item = itemdata; 783 struct session *s; 784 struct winlink *wl; 785 struct window_pane *wp; 786 const char *cmd; 787 788 window_tree_pull_item(item, &s, &wl, &wp); 789 790 switch (item->type) { 791 case WINDOW_TREE_NONE: 792 return (0); 793 case WINDOW_TREE_SESSION: 794 if (s == NULL) 795 return (0); 796 return (strstr(s->name, ss) != NULL); 797 case WINDOW_TREE_WINDOW: 798 if (s == NULL || wl == NULL) 799 return (0); 800 return (strstr(wl->window->name, ss) != NULL); 801 case WINDOW_TREE_PANE: 802 if (s == NULL || wl == NULL || wp == NULL) 803 break; 804 cmd = get_proc_name(wp->fd, wp->tty); 805 if (cmd == NULL || *cmd == '\0') 806 return (0); 807 return (strstr(cmd, ss) != NULL); 808 } 809 return (0); 810 } 811 812 static struct screen * 813 window_tree_init(struct window_pane *wp, struct cmd_find_state *fs, 814 struct args *args) 815 { 816 struct window_tree_modedata *data; 817 struct screen *s; 818 819 wp->modedata = data = xcalloc(1, sizeof *data); 820 821 if (args_has(args, 's')) 822 data->type = WINDOW_TREE_SESSION; 823 else if (args_has(args, 'w')) 824 data->type = WINDOW_TREE_WINDOW; 825 else 826 data->type = WINDOW_TREE_PANE; 827 memcpy(&data->fs, fs, sizeof data->fs); 828 829 data->wp = wp; 830 data->references = 1; 831 832 if (args == NULL || !args_has(args, 'F')) 833 data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT); 834 else 835 data->format = xstrdup(args_get(args, 'F')); 836 if (args == NULL || args->argc == 0) 837 data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND); 838 else 839 data->command = xstrdup(args->argv[0]); 840 data->squash_groups = !args_has(args, 'G'); 841 842 data->data = mode_tree_start(wp, args, window_tree_build, 843 window_tree_draw, window_tree_search, data, window_tree_sort_list, 844 nitems(window_tree_sort_list), &s); 845 mode_tree_zoom(data->data, args); 846 847 mode_tree_build(data->data); 848 mode_tree_draw(data->data); 849 850 data->type = WINDOW_TREE_NONE; 851 852 return (s); 853 } 854 855 static void 856 window_tree_destroy(struct window_tree_modedata *data) 857 { 858 u_int i; 859 860 if (--data->references != 0) 861 return; 862 863 for (i = 0; i < data->item_size; i++) 864 window_tree_free_item(data->item_list[i]); 865 free(data->item_list); 866 867 free(data->format); 868 free(data->command); 869 870 free(data); 871 } 872 873 static void 874 window_tree_free(struct window_pane *wp) 875 { 876 struct window_tree_modedata *data = wp->modedata; 877 878 if (data == NULL) 879 return; 880 881 data->dead = 1; 882 mode_tree_free(data->data); 883 window_tree_destroy(data); 884 } 885 886 static void 887 window_tree_resize(struct window_pane *wp, u_int sx, u_int sy) 888 { 889 struct window_tree_modedata *data = wp->modedata; 890 891 mode_tree_resize(data->data, sx, sy); 892 } 893 894 static char * 895 window_tree_get_target(struct window_tree_itemdata *item, 896 struct cmd_find_state *fs) 897 { 898 struct session *s; 899 struct winlink *wl; 900 struct window_pane *wp; 901 char *target; 902 903 window_tree_pull_item(item, &s, &wl, &wp); 904 905 target = NULL; 906 switch (item->type) { 907 case WINDOW_TREE_NONE: 908 break; 909 case WINDOW_TREE_SESSION: 910 if (s == NULL) 911 break; 912 xasprintf(&target, "=%s:", s->name); 913 break; 914 case WINDOW_TREE_WINDOW: 915 if (s == NULL || wl == NULL) 916 break; 917 xasprintf(&target, "=%s:%u.", s->name, wl->idx); 918 break; 919 case WINDOW_TREE_PANE: 920 if (s == NULL || wl == NULL || wp == NULL) 921 break; 922 xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id); 923 break; 924 } 925 if (target == NULL) 926 cmd_find_clear_state(fs, 0); 927 else 928 cmd_find_from_winlink_pane(fs, wl, wp, 0); 929 return (target); 930 } 931 932 static void 933 window_tree_command_each(void* modedata, void* itemdata, struct client *c, 934 __unused key_code key) 935 { 936 struct window_tree_modedata *data = modedata; 937 struct window_tree_itemdata *item = itemdata; 938 char *name; 939 struct cmd_find_state fs; 940 941 name = window_tree_get_target(item, &fs); 942 if (name != NULL) 943 mode_tree_run_command(c, &fs, data->entered, name); 944 free(name); 945 } 946 947 static enum cmd_retval 948 window_tree_command_done(__unused struct cmdq_item *item, void *modedata) 949 { 950 struct window_tree_modedata *data = modedata; 951 952 if (!data->dead) { 953 mode_tree_build(data->data); 954 mode_tree_draw(data->data); 955 data->wp->flags |= PANE_REDRAW; 956 } 957 window_tree_destroy(data); 958 return (CMD_RETURN_NORMAL); 959 } 960 961 static int 962 window_tree_command_callback(struct client *c, void *modedata, const char *s, 963 __unused int done) 964 { 965 struct window_tree_modedata *data = modedata; 966 967 if (s == NULL || *s == '\0' || data->dead) 968 return (0); 969 970 data->entered = s; 971 mode_tree_each_tagged(data->data, window_tree_command_each, c, 972 KEYC_NONE, 1); 973 data->entered = NULL; 974 975 data->references++; 976 cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 977 978 return (0); 979 } 980 981 static void 982 window_tree_command_free(void *modedata) 983 { 984 struct window_tree_modedata *data = modedata; 985 986 window_tree_destroy(data); 987 } 988 989 static void 990 window_tree_kill_each(__unused void* modedata, void* itemdata, 991 __unused struct client *c, __unused key_code key) 992 { 993 struct window_tree_itemdata *item = itemdata; 994 struct session *s; 995 struct winlink *wl; 996 struct window_pane *wp; 997 998 window_tree_pull_item(item, &s, &wl, &wp); 999 1000 switch (item->type) { 1001 case WINDOW_TREE_NONE: 1002 break; 1003 case WINDOW_TREE_SESSION: 1004 if (s != NULL) { 1005 server_destroy_session(s); 1006 session_destroy(s, __func__); 1007 } 1008 break; 1009 case WINDOW_TREE_WINDOW: 1010 if (wl != NULL) 1011 server_kill_window(wl->window); 1012 break; 1013 case WINDOW_TREE_PANE: 1014 if (wp != NULL) 1015 server_kill_pane(wp); 1016 break; 1017 } 1018 } 1019 1020 static int 1021 window_tree_kill_current_callback(struct client *c, void *modedata, 1022 const char *s, __unused int done) 1023 { 1024 struct window_tree_modedata *data = modedata; 1025 struct mode_tree_data *mtd = data->data; 1026 1027 if (s == NULL || *s == '\0' || data->dead) 1028 return (0); 1029 if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') 1030 return (0); 1031 1032 window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE); 1033 1034 data->references++; 1035 cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1036 1037 return (0); 1038 } 1039 1040 static int 1041 window_tree_kill_tagged_callback(struct client *c, void *modedata, 1042 const char *s, __unused int done) 1043 { 1044 struct window_tree_modedata *data = modedata; 1045 struct mode_tree_data *mtd = data->data; 1046 1047 if (s == NULL || *s == '\0' || data->dead) 1048 return (0); 1049 if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') 1050 return (0); 1051 1052 mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1); 1053 1054 data->references++; 1055 cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1056 1057 return (0); 1058 } 1059 1060 static key_code 1061 window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x, 1062 struct window_tree_itemdata *item) 1063 { 1064 struct session *s; 1065 struct winlink *wl; 1066 struct window_pane *wp; 1067 u_int loop; 1068 1069 if (key != KEYC_MOUSEDOWN1_PANE) 1070 return (KEYC_NONE); 1071 1072 if (data->left != -1 && x <= (u_int)data->left) 1073 return ('<'); 1074 if (data->right != -1 && x >= (u_int)data->right) 1075 return ('>'); 1076 1077 if (data->left != -1) 1078 x -= data->left; 1079 else if (x != 0) 1080 x--; 1081 if (x == 0 || data->end == 0) 1082 x = 0; 1083 else { 1084 x = x / data->each; 1085 if (data->start + x >= data->end) 1086 x = data->end - 1; 1087 } 1088 1089 window_tree_pull_item(item, &s, &wl, &wp); 1090 if (item->type == WINDOW_TREE_SESSION) { 1091 if (s == NULL) 1092 return (KEYC_NONE); 1093 mode_tree_expand_current(data->data); 1094 loop = 0; 1095 RB_FOREACH(wl, winlinks, &s->windows) { 1096 if (loop == data->start + x) 1097 break; 1098 loop++; 1099 } 1100 if (wl != NULL) 1101 mode_tree_set_current(data->data, (uint64_t)wl); 1102 return ('\r'); 1103 } 1104 if (item->type == WINDOW_TREE_WINDOW) { 1105 if (wl == NULL) 1106 return (KEYC_NONE); 1107 mode_tree_expand_current(data->data); 1108 loop = 0; 1109 TAILQ_FOREACH(wp, &wl->window->panes, entry) { 1110 if (loop == data->start + x) 1111 break; 1112 loop++; 1113 } 1114 if (wp != NULL) 1115 mode_tree_set_current(data->data, (uint64_t)wp); 1116 return ('\r'); 1117 } 1118 return (KEYC_NONE); 1119 } 1120 1121 static void 1122 window_tree_key(struct window_pane *wp, struct client *c, 1123 __unused struct session *s, __unused struct winlink *wl, key_code key, 1124 struct mouse_event *m) 1125 { 1126 struct window_tree_modedata *data = wp->modedata; 1127 struct window_tree_itemdata *item, *new_item; 1128 char *name, *prompt = NULL; 1129 struct cmd_find_state fs; 1130 int finished; 1131 u_int tagged, x, y, idx; 1132 struct session *ns; 1133 struct winlink *nwl; 1134 struct window_pane *nwp; 1135 1136 item = mode_tree_get_current(data->data); 1137 finished = mode_tree_key(data->data, c, &key, m, &x, &y); 1138 if (item != (new_item = mode_tree_get_current(data->data))) { 1139 item = new_item; 1140 data->offset = 0; 1141 } 1142 if (KEYC_IS_MOUSE(key)) 1143 key = window_tree_mouse(data, key, x, item); 1144 switch (key) { 1145 case '<': 1146 data->offset--; 1147 break; 1148 case '>': 1149 data->offset++; 1150 break; 1151 case 'x': 1152 window_tree_pull_item(item, &ns, &nwl, &nwp); 1153 switch (item->type) { 1154 case WINDOW_TREE_NONE: 1155 break; 1156 case WINDOW_TREE_SESSION: 1157 if (ns == NULL) 1158 break; 1159 xasprintf(&prompt, "Kill session %s? ", ns->name); 1160 break; 1161 case WINDOW_TREE_WINDOW: 1162 if (nwl == NULL) 1163 break; 1164 xasprintf(&prompt, "Kill window %u? ", nwl->idx); 1165 break; 1166 case WINDOW_TREE_PANE: 1167 if (nwp == NULL || window_pane_index(nwp, &idx) != 0) 1168 break; 1169 xasprintf(&prompt, "Kill pane %u? ", idx); 1170 break; 1171 } 1172 if (prompt == NULL) 1173 break; 1174 data->references++; 1175 status_prompt_set(c, prompt, "", 1176 window_tree_kill_current_callback, window_tree_command_free, 1177 data, PROMPT_SINGLE|PROMPT_NOFORMAT); 1178 free(prompt); 1179 break; 1180 case 'X': 1181 tagged = mode_tree_count_tagged(data->data); 1182 if (tagged == 0) 1183 break; 1184 xasprintf(&prompt, "Kill %u tagged? ", tagged); 1185 data->references++; 1186 status_prompt_set(c, prompt, "", 1187 window_tree_kill_tagged_callback, window_tree_command_free, 1188 data, PROMPT_SINGLE|PROMPT_NOFORMAT); 1189 free(prompt); 1190 break; 1191 case ':': 1192 tagged = mode_tree_count_tagged(data->data); 1193 if (tagged != 0) 1194 xasprintf(&prompt, "(%u tagged) ", tagged); 1195 else 1196 xasprintf(&prompt, "(current) "); 1197 data->references++; 1198 status_prompt_set(c, prompt, "", window_tree_command_callback, 1199 window_tree_command_free, data, PROMPT_NOFORMAT); 1200 free(prompt); 1201 break; 1202 case '\r': 1203 item = mode_tree_get_current(data->data); 1204 name = window_tree_get_target(item, &fs); 1205 if (name != NULL) 1206 mode_tree_run_command(c, NULL, data->command, name); 1207 finished = 1; 1208 free(name); 1209 break; 1210 } 1211 if (finished) 1212 window_pane_reset_mode(wp); 1213 else { 1214 mode_tree_draw(data->data); 1215 wp->flags |= PANE_REDRAW; 1216 } 1217 } 1218