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