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