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