1 /* $OpenBSD: window-tree.c,v 1.30 2018/04/10 10:48: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 *, 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, (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 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 struct grid_cell gc; 630 int colour, active_colour, left, right, pane_idx; 631 char *label; 632 633 total = window_count_panes(w); 634 635 memcpy(&gc, &grid_default_cell, sizeof gc); 636 colour = options_get_number(oo, "display-panes-colour"); 637 active_colour = options_get_number(oo, "display-panes-active-colour"); 638 639 if (sx / total < 24) { 640 visible = sx / 24; 641 if (visible == 0) 642 visible = 1; 643 } else 644 visible = total; 645 646 current = 0; 647 TAILQ_FOREACH(wp, &w->panes, entry) { 648 if (wp == w->active) 649 break; 650 current++; 651 } 652 653 if (current < visible) { 654 start = 0; 655 end = visible; 656 } else if (current >= total - visible) { 657 start = total - visible; 658 end = total; 659 } else { 660 start = current - (visible / 2); 661 end = start + visible; 662 } 663 664 if (data->offset < -(int)start) 665 data->offset = -(int)start; 666 if (data->offset > (int)(total - end)) 667 data->offset = (int)(total - end); 668 start += data->offset; 669 end += data->offset; 670 671 left = (start != 0); 672 right = (end != total); 673 if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) 674 left = right = 0; 675 if (left && right) { 676 each = (sx - 6) / visible; 677 remaining = (sx - 6) - (visible * each); 678 } else if (left || right) { 679 each = (sx - 3) / visible; 680 remaining = (sx - 3) - (visible * each); 681 } else { 682 each = sx / visible; 683 remaining = sx - (visible * each); 684 } 685 if (each == 0) 686 return; 687 688 if (left) { 689 data->left = cx + 2; 690 screen_write_cursormove(ctx, cx + 2, cy); 691 screen_write_vline(ctx, sy, 0, 0); 692 screen_write_cursormove(ctx, cx, cy + sy / 2); 693 screen_write_puts(ctx, &grid_default_cell, "<"); 694 } else 695 data->left = -1; 696 if (right) { 697 data->right = cx + sx - 3; 698 screen_write_cursormove(ctx, cx + sx - 3, cy); 699 screen_write_vline(ctx, sy, 0, 0); 700 screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2); 701 screen_write_puts(ctx, &grid_default_cell, ">"); 702 } else 703 data->right = -1; 704 705 data->start = start; 706 data->end = end; 707 data->each = each; 708 709 i = loop = 0; 710 TAILQ_FOREACH(wp, &w->panes, entry) { 711 if (loop == end) 712 break; 713 if (loop < start) { 714 loop++; 715 continue; 716 } 717 718 if (wp == w->active) 719 gc.fg = active_colour; 720 else 721 gc.fg = colour; 722 723 if (left) 724 offset = 3 + (i * each); 725 else 726 offset = (i * each); 727 if (loop == end - 1) 728 width = each + remaining; 729 else 730 width = each - 1; 731 732 screen_write_cursormove(ctx, cx + offset, cy); 733 screen_write_preview(ctx, &wp->base, width, sy); 734 735 if (window_pane_index(wp, &pane_idx) != 0) 736 pane_idx = loop; 737 xasprintf(&label, " %u ", pane_idx); 738 window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc, 739 label); 740 free(label); 741 742 if (loop != end - 1) { 743 screen_write_cursormove(ctx, cx + offset + width, cy); 744 screen_write_vline(ctx, sy, 0, 0); 745 } 746 loop++; 747 748 i++; 749 } 750 } 751 752 static void 753 window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, 754 u_int sx, u_int sy) 755 { 756 struct window_tree_itemdata *item = itemdata; 757 struct session *sp; 758 struct winlink *wlp; 759 struct window_pane *wp; 760 761 window_tree_pull_item(item, &sp, &wlp, &wp); 762 if (wp == NULL) 763 return; 764 765 switch (item->type) { 766 case WINDOW_TREE_NONE: 767 break; 768 case WINDOW_TREE_SESSION: 769 window_tree_draw_session(modedata, sp, ctx, sx, sy); 770 break; 771 case WINDOW_TREE_WINDOW: 772 window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy); 773 break; 774 case WINDOW_TREE_PANE: 775 screen_write_preview(ctx, &wp->base, sx, sy); 776 break; 777 } 778 } 779 780 static int 781 window_tree_search(__unused void *modedata, void *itemdata, const char *ss) 782 { 783 struct window_tree_itemdata *item = itemdata; 784 struct session *s; 785 struct winlink *wl; 786 struct window_pane *wp; 787 const char *cmd; 788 789 window_tree_pull_item(item, &s, &wl, &wp); 790 791 switch (item->type) { 792 case WINDOW_TREE_NONE: 793 return (0); 794 case WINDOW_TREE_SESSION: 795 if (s == NULL) 796 return (0); 797 return (strstr(s->name, ss) != NULL); 798 case WINDOW_TREE_WINDOW: 799 if (s == NULL || wl == NULL) 800 return (0); 801 return (strstr(wl->window->name, ss) != NULL); 802 case WINDOW_TREE_PANE: 803 if (s == NULL || wl == NULL || wp == NULL) 804 break; 805 cmd = get_proc_name(wp->fd, wp->tty); 806 if (cmd == NULL || *cmd == '\0') 807 return (0); 808 return (strstr(cmd, ss) != NULL); 809 } 810 return (0); 811 } 812 813 static struct screen * 814 window_tree_init(struct window_pane *wp, struct cmd_find_state *fs, 815 struct args *args) 816 { 817 struct window_tree_modedata *data; 818 struct screen *s; 819 820 wp->modedata = data = xcalloc(1, sizeof *data); 821 822 if (args_has(args, 's')) 823 data->type = WINDOW_TREE_SESSION; 824 else if (args_has(args, 'w')) 825 data->type = WINDOW_TREE_WINDOW; 826 else 827 data->type = WINDOW_TREE_PANE; 828 memcpy(&data->fs, fs, sizeof data->fs); 829 830 data->wp = wp; 831 data->references = 1; 832 833 if (args == NULL || !args_has(args, 'F')) 834 data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT); 835 else 836 data->format = xstrdup(args_get(args, 'F')); 837 if (args == NULL || args->argc == 0) 838 data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND); 839 else 840 data->command = xstrdup(args->argv[0]); 841 data->squash_groups = !args_has(args, 'G'); 842 843 data->data = mode_tree_start(wp, args, window_tree_build, 844 window_tree_draw, window_tree_search, data, window_tree_sort_list, 845 nitems(window_tree_sort_list), &s); 846 mode_tree_zoom(data->data, args); 847 848 mode_tree_build(data->data); 849 mode_tree_draw(data->data); 850 851 data->type = WINDOW_TREE_NONE; 852 853 return (s); 854 } 855 856 static void 857 window_tree_destroy(struct window_tree_modedata *data) 858 { 859 u_int i; 860 861 if (--data->references != 0) 862 return; 863 864 for (i = 0; i < data->item_size; i++) 865 window_tree_free_item(data->item_list[i]); 866 free(data->item_list); 867 868 free(data->format); 869 free(data->command); 870 871 free(data); 872 } 873 874 static void 875 window_tree_free(struct window_pane *wp) 876 { 877 struct window_tree_modedata *data = wp->modedata; 878 879 if (data == NULL) 880 return; 881 882 data->dead = 1; 883 mode_tree_free(data->data); 884 window_tree_destroy(data); 885 } 886 887 static void 888 window_tree_resize(struct window_pane *wp, u_int sx, u_int sy) 889 { 890 struct window_tree_modedata *data = wp->modedata; 891 892 mode_tree_resize(data->data, sx, sy); 893 } 894 895 static char * 896 window_tree_get_target(struct window_tree_itemdata *item, 897 struct cmd_find_state *fs) 898 { 899 struct session *s; 900 struct winlink *wl; 901 struct window_pane *wp; 902 char *target; 903 904 window_tree_pull_item(item, &s, &wl, &wp); 905 906 target = NULL; 907 switch (item->type) { 908 case WINDOW_TREE_NONE: 909 break; 910 case WINDOW_TREE_SESSION: 911 if (s == NULL) 912 break; 913 xasprintf(&target, "=%s:", s->name); 914 break; 915 case WINDOW_TREE_WINDOW: 916 if (s == NULL || wl == NULL) 917 break; 918 xasprintf(&target, "=%s:%u.", s->name, wl->idx); 919 break; 920 case WINDOW_TREE_PANE: 921 if (s == NULL || wl == NULL || wp == NULL) 922 break; 923 xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id); 924 break; 925 } 926 if (target == NULL) 927 cmd_find_clear_state(fs, 0); 928 else 929 cmd_find_from_winlink_pane(fs, wl, wp, 0); 930 return (target); 931 } 932 933 static void 934 window_tree_command_each(void* modedata, void* itemdata, struct client *c, 935 __unused key_code key) 936 { 937 struct window_tree_modedata *data = modedata; 938 struct window_tree_itemdata *item = itemdata; 939 char *name; 940 struct cmd_find_state fs; 941 942 name = window_tree_get_target(item, &fs); 943 if (name != NULL) 944 mode_tree_run_command(c, &fs, data->entered, name); 945 free(name); 946 } 947 948 static enum cmd_retval 949 window_tree_command_done(__unused struct cmdq_item *item, void *modedata) 950 { 951 struct window_tree_modedata *data = modedata; 952 953 if (!data->dead) { 954 mode_tree_build(data->data); 955 mode_tree_draw(data->data); 956 data->wp->flags |= PANE_REDRAW; 957 } 958 window_tree_destroy(data); 959 return (CMD_RETURN_NORMAL); 960 } 961 962 static int 963 window_tree_command_callback(struct client *c, void *modedata, const char *s, 964 __unused int done) 965 { 966 struct window_tree_modedata *data = modedata; 967 968 if (s == NULL || *s == '\0' || data->dead) 969 return (0); 970 971 data->entered = s; 972 mode_tree_each_tagged(data->data, window_tree_command_each, c, 973 KEYC_NONE, 1); 974 data->entered = NULL; 975 976 data->references++; 977 cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 978 979 return (0); 980 } 981 982 static void 983 window_tree_command_free(void *modedata) 984 { 985 struct window_tree_modedata *data = modedata; 986 987 window_tree_destroy(data); 988 } 989 990 static void 991 window_tree_kill_each(__unused void* modedata, void* itemdata, 992 __unused struct client *c, __unused key_code key) 993 { 994 struct window_tree_itemdata *item = itemdata; 995 struct session *s; 996 struct winlink *wl; 997 struct window_pane *wp; 998 999 window_tree_pull_item(item, &s, &wl, &wp); 1000 1001 switch (item->type) { 1002 case WINDOW_TREE_NONE: 1003 break; 1004 case WINDOW_TREE_SESSION: 1005 if (s != NULL) { 1006 server_destroy_session(s); 1007 session_destroy(s, __func__); 1008 } 1009 break; 1010 case WINDOW_TREE_WINDOW: 1011 if (wl != NULL) 1012 server_kill_window(wl->window); 1013 break; 1014 case WINDOW_TREE_PANE: 1015 if (wp != NULL) 1016 server_kill_pane(wp); 1017 break; 1018 } 1019 } 1020 1021 static int 1022 window_tree_kill_current_callback(struct client *c, void *modedata, 1023 const char *s, __unused int done) 1024 { 1025 struct window_tree_modedata *data = modedata; 1026 struct mode_tree_data *mtd = data->data; 1027 1028 if (s == NULL || *s == '\0' || data->dead) 1029 return (0); 1030 if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') 1031 return (0); 1032 1033 window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE); 1034 1035 data->references++; 1036 cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1037 1038 return (0); 1039 } 1040 1041 static int 1042 window_tree_kill_tagged_callback(struct client *c, void *modedata, 1043 const char *s, __unused int done) 1044 { 1045 struct window_tree_modedata *data = modedata; 1046 struct mode_tree_data *mtd = data->data; 1047 1048 if (s == NULL || *s == '\0' || data->dead) 1049 return (0); 1050 if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') 1051 return (0); 1052 1053 mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1); 1054 1055 data->references++; 1056 cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1057 1058 return (0); 1059 } 1060 1061 static key_code 1062 window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x, 1063 struct window_tree_itemdata *item) 1064 { 1065 struct session *s; 1066 struct winlink *wl; 1067 struct window_pane *wp; 1068 u_int loop; 1069 1070 if (key != KEYC_MOUSEDOWN1_PANE) 1071 return (KEYC_NONE); 1072 1073 if (data->left != -1 && x <= (u_int)data->left) 1074 return ('<'); 1075 if (data->right != -1 && x >= (u_int)data->right) 1076 return ('>'); 1077 1078 if (data->left != -1) 1079 x -= data->left; 1080 else if (x != 0) 1081 x--; 1082 if (x == 0 || data->end == 0) 1083 x = 0; 1084 else { 1085 x = x / data->each; 1086 if (data->start + x >= data->end) 1087 x = data->end - 1; 1088 } 1089 1090 window_tree_pull_item(item, &s, &wl, &wp); 1091 if (item->type == WINDOW_TREE_SESSION) { 1092 if (s == NULL) 1093 return (KEYC_NONE); 1094 mode_tree_expand_current(data->data); 1095 loop = 0; 1096 RB_FOREACH(wl, winlinks, &s->windows) { 1097 if (loop == data->start + x) 1098 break; 1099 loop++; 1100 } 1101 if (wl != NULL) 1102 mode_tree_set_current(data->data, (uint64_t)wl); 1103 return ('\r'); 1104 } 1105 if (item->type == WINDOW_TREE_WINDOW) { 1106 if (wl == NULL) 1107 return (KEYC_NONE); 1108 mode_tree_expand_current(data->data); 1109 loop = 0; 1110 TAILQ_FOREACH(wp, &wl->window->panes, entry) { 1111 if (loop == data->start + x) 1112 break; 1113 loop++; 1114 } 1115 if (wp != NULL) 1116 mode_tree_set_current(data->data, (uint64_t)wp); 1117 return ('\r'); 1118 } 1119 return (KEYC_NONE); 1120 } 1121 1122 static void 1123 window_tree_key(struct window_pane *wp, struct client *c, 1124 __unused struct session *s, key_code key, 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