1*af11b61dSnicm /* $OpenBSD: window-tree.c,v 1.64 2024/10/04 19:16:13 nicm Exp $ */ 2a42faf7dSnicm 3a42faf7dSnicm /* 4a42faf7dSnicm * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com> 5a42faf7dSnicm * 6a42faf7dSnicm * Permission to use, copy, modify, and distribute this software for any 7a42faf7dSnicm * purpose with or without fee is hereby granted, provided that the above 8a42faf7dSnicm * copyright notice and this permission notice appear in all copies. 9a42faf7dSnicm * 10a42faf7dSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11a42faf7dSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12a42faf7dSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13a42faf7dSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14a42faf7dSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15a42faf7dSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16a42faf7dSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17a42faf7dSnicm */ 18a42faf7dSnicm 19a42faf7dSnicm #include <sys/types.h> 20a42faf7dSnicm 21d0ca3a30Snicm #include <ctype.h> 22a42faf7dSnicm #include <stdlib.h> 23a42faf7dSnicm #include <string.h> 24a42faf7dSnicm 25a42faf7dSnicm #include "tmux.h" 26a42faf7dSnicm 2730a94f45Snicm static struct screen *window_tree_init(struct window_mode_entry *, 28a42faf7dSnicm struct cmd_find_state *, struct args *); 2930a94f45Snicm static void window_tree_free(struct window_mode_entry *); 3030a94f45Snicm static void window_tree_resize(struct window_mode_entry *, u_int, 3130a94f45Snicm u_int); 32734f37e4Snicm static void window_tree_update(struct window_mode_entry *); 3330a94f45Snicm static void window_tree_key(struct window_mode_entry *, 34bf52409eSnicm struct client *, struct session *, 35bf52409eSnicm struct winlink *, key_code, struct mouse_event *); 36a42faf7dSnicm 37ec12dec9Snicm #define WINDOW_TREE_DEFAULT_COMMAND "switch-client -Zt '%%'" 38a42faf7dSnicm 39bf38e336Snicm #define WINDOW_TREE_DEFAULT_FORMAT \ 40bf38e336Snicm "#{?pane_format," \ 417d0fd907Snicm "#{?pane_marked,#[reverse],}" \ 426c6f347cSnicm "#{pane_current_command}#{?pane_active,*,}#{?pane_marked,M,}" \ 436c6f347cSnicm "#{?#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}},: \"#{pane_title}\",}" \ 44bf38e336Snicm "," \ 45bf38e336Snicm "#{?window_format," \ 467d0fd907Snicm "#{?window_marked_flag,#[reverse],}" \ 47bf38e336Snicm "#{window_name}#{window_flags}" \ 486c6f347cSnicm "#{?#{&&:#{==:#{window_panes},1},#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}}},: \"#{pane_title}\",}" \ 49bf38e336Snicm "," \ 50bf38e336Snicm "#{session_windows} windows" \ 51988c4c87Snicm "#{?session_grouped, " \ 5234f4cdc8Snicm "(group #{session_group}: " \ 5334f4cdc8Snicm "#{session_group_list})," \ 54988c4c87Snicm "}" \ 55bf38e336Snicm "#{?session_attached, (attached),}" \ 56bf38e336Snicm "}" \ 57bf38e336Snicm "}" 58bf38e336Snicm 59438eed14Snicm #define WINDOW_TREE_DEFAULT_KEY_FORMAT \ 60438eed14Snicm "#{?#{e|<:#{line},10}," \ 61438eed14Snicm "#{line}" \ 62438eed14Snicm "," \ 63438eed14Snicm "#{?#{e|<:#{line},36}," \ 64438eed14Snicm "M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \ 65438eed14Snicm "," \ 66438eed14Snicm "" \ 67438eed14Snicm "}" \ 68438eed14Snicm "}" 69438eed14Snicm 701335341aSnicm static const struct menu_item window_tree_menu_items[] = { 71e38aac43Snicm { "Select", '\r', NULL }, 72e38aac43Snicm { "Expand", KEYC_RIGHT, NULL }, 737d0fd907Snicm { "Mark", 'm', NULL }, 741335341aSnicm { "", KEYC_NONE, NULL }, 751335341aSnicm { "Tag", 't', NULL }, 761335341aSnicm { "Tag All", '\024', NULL }, 771335341aSnicm { "Tag None", 'T', NULL }, 781335341aSnicm { "", KEYC_NONE, NULL }, 791335341aSnicm { "Kill", 'x', NULL }, 801335341aSnicm { "Kill Tagged", 'X', NULL }, 811335341aSnicm { "", KEYC_NONE, NULL }, 821335341aSnicm { "Cancel", 'q', NULL }, 831335341aSnicm 841335341aSnicm { NULL, KEYC_NONE, NULL } 851335341aSnicm }; 86f43bc87cSnicm 87a42faf7dSnicm const struct window_mode window_tree_mode = { 88a42faf7dSnicm .name = "tree-mode", 8971431f24Snicm .default_format = WINDOW_TREE_DEFAULT_FORMAT, 90a42faf7dSnicm 91a42faf7dSnicm .init = window_tree_init, 92a42faf7dSnicm .free = window_tree_free, 93a42faf7dSnicm .resize = window_tree_resize, 94734f37e4Snicm .update = window_tree_update, 95a42faf7dSnicm .key = window_tree_key, 96a42faf7dSnicm }; 97a42faf7dSnicm 98a42faf7dSnicm enum window_tree_sort_type { 99a42faf7dSnicm WINDOW_TREE_BY_INDEX, 100a42faf7dSnicm WINDOW_TREE_BY_NAME, 101a42faf7dSnicm WINDOW_TREE_BY_TIME, 102a42faf7dSnicm }; 103a42faf7dSnicm static const char *window_tree_sort_list[] = { 104a42faf7dSnicm "index", 105a42faf7dSnicm "name", 106a42faf7dSnicm "time" 107a42faf7dSnicm }; 1083f6f9f7dSnicm static struct mode_tree_sort_criteria *window_tree_sort; 109a42faf7dSnicm 110a42faf7dSnicm enum window_tree_type { 111a42faf7dSnicm WINDOW_TREE_NONE, 112a42faf7dSnicm WINDOW_TREE_SESSION, 113a42faf7dSnicm WINDOW_TREE_WINDOW, 114a42faf7dSnicm WINDOW_TREE_PANE, 115a42faf7dSnicm }; 116a42faf7dSnicm 117a42faf7dSnicm struct window_tree_itemdata { 118a42faf7dSnicm enum window_tree_type type; 119a42faf7dSnicm int session; 120a42faf7dSnicm int winlink; 121a42faf7dSnicm int pane; 122a42faf7dSnicm }; 123a42faf7dSnicm 124a42faf7dSnicm struct window_tree_modedata { 125a42faf7dSnicm struct window_pane *wp; 126a42faf7dSnicm int dead; 127a42faf7dSnicm int references; 128a42faf7dSnicm 129a42faf7dSnicm struct mode_tree_data *data; 130bf38e336Snicm char *format; 131438eed14Snicm char *key_format; 132a42faf7dSnicm char *command; 133988c4c87Snicm int squash_groups; 134*af11b61dSnicm int prompt_flags; 135a42faf7dSnicm 136a42faf7dSnicm struct window_tree_itemdata **item_list; 137a42faf7dSnicm u_int item_size; 138a42faf7dSnicm 139a42faf7dSnicm const char *entered; 140a42faf7dSnicm 141a42faf7dSnicm struct cmd_find_state fs; 142a42faf7dSnicm enum window_tree_type type; 14305d44586Snicm 14405d44586Snicm int offset; 14563c9949dSnicm 14663c9949dSnicm int left; 14763c9949dSnicm int right; 14863c9949dSnicm u_int start; 14963c9949dSnicm u_int end; 15063c9949dSnicm u_int each; 151a42faf7dSnicm }; 152a42faf7dSnicm 153a42faf7dSnicm static void 154a42faf7dSnicm window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp, 155a42faf7dSnicm struct winlink **wlp, struct window_pane **wp) 156a42faf7dSnicm { 157a42faf7dSnicm *wp = NULL; 158a42faf7dSnicm *wlp = NULL; 159a42faf7dSnicm *sp = session_find_by_id(item->session); 160a42faf7dSnicm if (*sp == NULL) 161a42faf7dSnicm return; 162a42faf7dSnicm if (item->type == WINDOW_TREE_SESSION) { 163a42faf7dSnicm *wlp = (*sp)->curw; 164a42faf7dSnicm *wp = (*wlp)->window->active; 165a42faf7dSnicm return; 166a42faf7dSnicm } 167a42faf7dSnicm 168a42faf7dSnicm *wlp = winlink_find_by_index(&(*sp)->windows, item->winlink); 169a42faf7dSnicm if (*wlp == NULL) { 170a42faf7dSnicm *sp = NULL; 171a42faf7dSnicm return; 172a42faf7dSnicm } 173a42faf7dSnicm if (item->type == WINDOW_TREE_WINDOW) { 174a42faf7dSnicm *wp = (*wlp)->window->active; 175a42faf7dSnicm return; 176a42faf7dSnicm } 177a42faf7dSnicm 178a42faf7dSnicm *wp = window_pane_find_by_id(item->pane); 179a42faf7dSnicm if (!window_has_pane((*wlp)->window, *wp)) 180a42faf7dSnicm *wp = NULL; 181a42faf7dSnicm if (*wp == NULL) { 182a42faf7dSnicm *sp = NULL; 183a42faf7dSnicm *wlp = NULL; 184a42faf7dSnicm return; 185a42faf7dSnicm } 186a42faf7dSnicm } 187a42faf7dSnicm 188a42faf7dSnicm static struct window_tree_itemdata * 189a42faf7dSnicm window_tree_add_item(struct window_tree_modedata *data) 190a42faf7dSnicm { 191a42faf7dSnicm struct window_tree_itemdata *item; 192a42faf7dSnicm 193a42faf7dSnicm data->item_list = xreallocarray(data->item_list, data->item_size + 1, 194a42faf7dSnicm sizeof *data->item_list); 195a42faf7dSnicm item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item); 196a42faf7dSnicm return (item); 197a42faf7dSnicm } 198a42faf7dSnicm 199a42faf7dSnicm static void 200a42faf7dSnicm window_tree_free_item(struct window_tree_itemdata *item) 201a42faf7dSnicm { 202a42faf7dSnicm free(item); 203a42faf7dSnicm } 204a42faf7dSnicm 205a42faf7dSnicm static int 2063f6f9f7dSnicm window_tree_cmp_session(const void *a0, const void *b0) 207a42faf7dSnicm { 208a42faf7dSnicm const struct session *const *a = a0; 209a42faf7dSnicm const struct session *const *b = b0; 2103f6f9f7dSnicm const struct session *sa = *a; 2113f6f9f7dSnicm const struct session *sb = *b; 21276e38b2dSnicm int result = 0; 213a42faf7dSnicm 2143f6f9f7dSnicm switch (window_tree_sort->field) { 2153f6f9f7dSnicm case WINDOW_TREE_BY_INDEX: 2163f6f9f7dSnicm result = sa->id - sb->id; 2173f6f9f7dSnicm break; 2183f6f9f7dSnicm case WINDOW_TREE_BY_TIME: 2193f6f9f7dSnicm if (timercmp(&sa->activity_time, &sb->activity_time, >)) { 2203f6f9f7dSnicm result = -1; 2213f6f9f7dSnicm break; 2223f6f9f7dSnicm } 2233f6f9f7dSnicm if (timercmp(&sa->activity_time, &sb->activity_time, <)) { 2243f6f9f7dSnicm result = 1; 2253f6f9f7dSnicm break; 2263f6f9f7dSnicm } 2273f6f9f7dSnicm /* FALLTHROUGH */ 2283f6f9f7dSnicm case WINDOW_TREE_BY_NAME: 2293f6f9f7dSnicm result = strcmp(sa->name, sb->name); 2303f6f9f7dSnicm break; 2313f6f9f7dSnicm } 2323f6f9f7dSnicm 2333f6f9f7dSnicm if (window_tree_sort->reversed) 2343f6f9f7dSnicm result = -result; 2353f6f9f7dSnicm return (result); 236a42faf7dSnicm } 237a42faf7dSnicm 238a42faf7dSnicm static int 2393f6f9f7dSnicm window_tree_cmp_window(const void *a0, const void *b0) 240a42faf7dSnicm { 241a42faf7dSnicm const struct winlink *const *a = a0; 242a42faf7dSnicm const struct winlink *const *b = b0; 2433f6f9f7dSnicm const struct winlink *wla = *a; 2443f6f9f7dSnicm const struct winlink *wlb = *b; 2453f6f9f7dSnicm struct window *wa = wla->window; 2463f6f9f7dSnicm struct window *wb = wlb->window; 24776e38b2dSnicm int result = 0; 248a42faf7dSnicm 2493f6f9f7dSnicm switch (window_tree_sort->field) { 2503f6f9f7dSnicm case WINDOW_TREE_BY_INDEX: 2513f6f9f7dSnicm result = wla->idx - wlb->idx; 2523f6f9f7dSnicm break; 2533f6f9f7dSnicm case WINDOW_TREE_BY_TIME: 2543f6f9f7dSnicm if (timercmp(&wa->activity_time, &wb->activity_time, >)) { 2553f6f9f7dSnicm result = -1; 2563f6f9f7dSnicm break; 2573f6f9f7dSnicm } 2583f6f9f7dSnicm if (timercmp(&wa->activity_time, &wb->activity_time, <)) { 2593f6f9f7dSnicm result = 1; 2603f6f9f7dSnicm break; 2613f6f9f7dSnicm } 2623f6f9f7dSnicm /* FALLTHROUGH */ 2633f6f9f7dSnicm case WINDOW_TREE_BY_NAME: 2643f6f9f7dSnicm result = strcmp(wa->name, wb->name); 2653f6f9f7dSnicm break; 2663f6f9f7dSnicm } 2673f6f9f7dSnicm 2683f6f9f7dSnicm if (window_tree_sort->reversed) 2693f6f9f7dSnicm result = -result; 2703f6f9f7dSnicm return (result); 271a42faf7dSnicm } 272a42faf7dSnicm 273a42faf7dSnicm static int 2743f6f9f7dSnicm window_tree_cmp_pane(const void *a0, const void *b0) 275a42faf7dSnicm { 2760344f191Snicm struct window_pane **a = (struct window_pane **)a0; 2770344f191Snicm struct window_pane **b = (struct window_pane **)b0; 2783f6f9f7dSnicm int result; 2790344f191Snicm u_int ai, bi; 280a42faf7dSnicm 2813f6f9f7dSnicm if (window_tree_sort->field == WINDOW_TREE_BY_TIME) 2823f6f9f7dSnicm result = (*a)->active_point - (*b)->active_point; 2833f6f9f7dSnicm else { 2843f6f9f7dSnicm /* 2853f6f9f7dSnicm * Panes don't have names, so use number order for any other 2863f6f9f7dSnicm * sort field. 2873f6f9f7dSnicm */ 2880344f191Snicm window_pane_index(*a, &ai); 2890344f191Snicm window_pane_index(*b, &bi); 2900344f191Snicm result = ai - bi; 2913f6f9f7dSnicm } 2923f6f9f7dSnicm if (window_tree_sort->reversed) 2932d71b92eSnicm result = -result; 2943f6f9f7dSnicm return (result); 295a42faf7dSnicm } 296a42faf7dSnicm 297a42faf7dSnicm static void 298a42faf7dSnicm window_tree_build_pane(struct session *s, struct winlink *wl, 299a42faf7dSnicm struct window_pane *wp, void *modedata, struct mode_tree_item *parent) 300a42faf7dSnicm { 301a42faf7dSnicm struct window_tree_modedata *data = modedata; 302a42faf7dSnicm struct window_tree_itemdata *item; 303a42faf7dSnicm char *name, *text; 304a42faf7dSnicm u_int idx; 305a42faf7dSnicm 306a42faf7dSnicm window_pane_index(wp, &idx); 307a42faf7dSnicm 308a42faf7dSnicm item = window_tree_add_item(data); 309a42faf7dSnicm item->type = WINDOW_TREE_PANE; 310a42faf7dSnicm item->session = s->id; 311a42faf7dSnicm item->winlink = wl->idx; 312a42faf7dSnicm item->pane = wp->id; 313a42faf7dSnicm 314bf38e336Snicm text = format_single(NULL, data->format, NULL, s, wl, wp); 315a42faf7dSnicm xasprintf(&name, "%u", idx); 316a42faf7dSnicm 317a42faf7dSnicm mode_tree_add(data->data, parent, item, (uint64_t)wp, name, text, -1); 318a42faf7dSnicm free(text); 319a42faf7dSnicm free(name); 320a42faf7dSnicm } 321a42faf7dSnicm 322a42faf7dSnicm static int 323f5b43402Snicm window_tree_filter_pane(struct session *s, struct winlink *wl, 324f5b43402Snicm struct window_pane *wp, const char *filter) 325f5b43402Snicm { 326f5b43402Snicm char *cp; 327f5b43402Snicm int result; 328f5b43402Snicm 329f5b43402Snicm if (filter == NULL) 330f5b43402Snicm return (1); 331f5b43402Snicm 332f5b43402Snicm cp = format_single(NULL, filter, NULL, s, wl, wp); 333f5b43402Snicm result = format_true(cp); 334f5b43402Snicm free(cp); 335f5b43402Snicm 336f5b43402Snicm return (result); 337f5b43402Snicm } 338f5b43402Snicm 339f5b43402Snicm static int 3403f6f9f7dSnicm window_tree_build_window(struct session *s, struct winlink *wl, 3413f6f9f7dSnicm void *modedata, struct mode_tree_sort_criteria *sort_crit, 3423f6f9f7dSnicm struct mode_tree_item *parent, const char *filter) 343a42faf7dSnicm { 344a42faf7dSnicm struct window_tree_modedata *data = modedata; 345a42faf7dSnicm struct window_tree_itemdata *item; 346a42faf7dSnicm struct mode_tree_item *mti; 347f5b43402Snicm char *name, *text; 348a42faf7dSnicm struct window_pane *wp, **l; 349a42faf7dSnicm u_int n, i; 350a42faf7dSnicm int expanded; 351a42faf7dSnicm 352a42faf7dSnicm item = window_tree_add_item(data); 353a42faf7dSnicm item->type = WINDOW_TREE_WINDOW; 354a42faf7dSnicm item->session = s->id; 355a42faf7dSnicm item->winlink = wl->idx; 356a42faf7dSnicm item->pane = -1; 357a42faf7dSnicm 358bf38e336Snicm text = format_single(NULL, data->format, NULL, s, wl, NULL); 359a42faf7dSnicm xasprintf(&name, "%u", wl->idx); 360a42faf7dSnicm 361a42faf7dSnicm if (data->type == WINDOW_TREE_SESSION || 362a42faf7dSnicm data->type == WINDOW_TREE_WINDOW) 363a42faf7dSnicm expanded = 0; 364a42faf7dSnicm else 365a42faf7dSnicm expanded = 1; 366a42faf7dSnicm mti = mode_tree_add(data->data, parent, item, (uint64_t)wl, name, text, 367a42faf7dSnicm expanded); 368a42faf7dSnicm free(text); 369a42faf7dSnicm free(name); 370a42faf7dSnicm 371a8b654cbSnicm if ((wp = TAILQ_FIRST(&wl->window->panes)) == NULL) 372a8b654cbSnicm goto empty; 373f5b43402Snicm if (TAILQ_NEXT(wp, entry) == NULL) { 374f5b43402Snicm if (!window_tree_filter_pane(s, wl, wp, filter)) 375f5b43402Snicm goto empty; 3762920087aSnicm return (1); 377f5b43402Snicm } 3782920087aSnicm 379a42faf7dSnicm l = NULL; 380a42faf7dSnicm n = 0; 3812920087aSnicm 382a42faf7dSnicm TAILQ_FOREACH(wp, &wl->window->panes, entry) { 383f5b43402Snicm if (!window_tree_filter_pane(s, wl, wp, filter)) 384a42faf7dSnicm continue; 385a42faf7dSnicm l = xreallocarray(l, n + 1, sizeof *l); 386a42faf7dSnicm l[n++] = wp; 387a42faf7dSnicm } 388f5b43402Snicm if (n == 0) 389f5b43402Snicm goto empty; 390a42faf7dSnicm 3913f6f9f7dSnicm window_tree_sort = sort_crit; 3923f6f9f7dSnicm qsort(l, n, sizeof *l, window_tree_cmp_pane); 393a42faf7dSnicm 394a42faf7dSnicm for (i = 0; i < n; i++) 395a42faf7dSnicm window_tree_build_pane(s, wl, l[i], modedata, mti); 396a42faf7dSnicm free(l); 397a42faf7dSnicm return (1); 398f5b43402Snicm 399f5b43402Snicm empty: 400f5b43402Snicm window_tree_free_item(item); 401f5b43402Snicm data->item_size--; 402f5b43402Snicm mode_tree_remove(data->data, mti); 403f5b43402Snicm return (0); 404a42faf7dSnicm } 405a42faf7dSnicm 406a42faf7dSnicm static void 407a42faf7dSnicm window_tree_build_session(struct session *s, void *modedata, 4083f6f9f7dSnicm struct mode_tree_sort_criteria *sort_crit, const char *filter) 409a42faf7dSnicm { 410a42faf7dSnicm struct window_tree_modedata *data = modedata; 411a42faf7dSnicm struct window_tree_itemdata *item; 412a42faf7dSnicm struct mode_tree_item *mti; 413a42faf7dSnicm char *text; 414a42faf7dSnicm struct winlink *wl, **l; 415a42faf7dSnicm u_int n, i, empty; 416a42faf7dSnicm int expanded; 417a42faf7dSnicm 418a42faf7dSnicm item = window_tree_add_item(data); 419a42faf7dSnicm item->type = WINDOW_TREE_SESSION; 420a42faf7dSnicm item->session = s->id; 421a42faf7dSnicm item->winlink = -1; 422a42faf7dSnicm item->pane = -1; 423a42faf7dSnicm 424bf38e336Snicm text = format_single(NULL, data->format, NULL, s, NULL, NULL); 425a42faf7dSnicm 426a42faf7dSnicm if (data->type == WINDOW_TREE_SESSION) 427a42faf7dSnicm expanded = 0; 428a42faf7dSnicm else 429a42faf7dSnicm expanded = 1; 430a42faf7dSnicm mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text, 431a42faf7dSnicm expanded); 432a42faf7dSnicm free(text); 433a42faf7dSnicm 434a42faf7dSnicm l = NULL; 435a42faf7dSnicm n = 0; 436a42faf7dSnicm RB_FOREACH(wl, winlinks, &s->windows) { 437a42faf7dSnicm l = xreallocarray(l, n + 1, sizeof *l); 438a42faf7dSnicm l[n++] = wl; 439a42faf7dSnicm } 4403f6f9f7dSnicm window_tree_sort = sort_crit; 4413f6f9f7dSnicm qsort(l, n, sizeof *l, window_tree_cmp_window); 442a42faf7dSnicm 443a42faf7dSnicm empty = 0; 444a42faf7dSnicm for (i = 0; i < n; i++) { 4453f6f9f7dSnicm if (!window_tree_build_window(s, l[i], modedata, sort_crit, mti, 446024c311aSnicm filter)) 447a42faf7dSnicm empty++; 448a42faf7dSnicm } 449a42faf7dSnicm if (empty == n) { 450a42faf7dSnicm window_tree_free_item(item); 451a42faf7dSnicm data->item_size--; 452a42faf7dSnicm mode_tree_remove(data->data, mti); 453a42faf7dSnicm } 454a42faf7dSnicm free(l); 455a42faf7dSnicm } 456a42faf7dSnicm 457a42faf7dSnicm static void 4583f6f9f7dSnicm window_tree_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, 4593f6f9f7dSnicm uint64_t *tag, const char *filter) 460a42faf7dSnicm { 461a42faf7dSnicm struct window_tree_modedata *data = modedata; 462a42faf7dSnicm struct session *s, **l; 463f443747aSnicm struct session_group *sg, *current; 464a42faf7dSnicm u_int n, i; 465a42faf7dSnicm 466f443747aSnicm current = session_group_contains(data->fs.s); 467f443747aSnicm 468a42faf7dSnicm for (i = 0; i < data->item_size; i++) 469a42faf7dSnicm window_tree_free_item(data->item_list[i]); 470a42faf7dSnicm free(data->item_list); 471a42faf7dSnicm data->item_list = NULL; 472a42faf7dSnicm data->item_size = 0; 473a42faf7dSnicm 474a42faf7dSnicm l = NULL; 475a42faf7dSnicm n = 0; 476a42faf7dSnicm RB_FOREACH(s, sessions, &sessions) { 477988c4c87Snicm if (data->squash_groups && 478f443747aSnicm (sg = session_group_contains(s)) != NULL) { 479f443747aSnicm if ((sg == current && s != data->fs.s) || 480f443747aSnicm (sg != current && s != TAILQ_FIRST(&sg->sessions))) 481988c4c87Snicm continue; 482f443747aSnicm } 483a42faf7dSnicm l = xreallocarray(l, n + 1, sizeof *l); 484a42faf7dSnicm l[n++] = s; 485a42faf7dSnicm } 4863f6f9f7dSnicm window_tree_sort = sort_crit; 4873f6f9f7dSnicm qsort(l, n, sizeof *l, window_tree_cmp_session); 488a42faf7dSnicm 489a42faf7dSnicm for (i = 0; i < n; i++) 4903f6f9f7dSnicm window_tree_build_session(l[i], modedata, sort_crit, filter); 491a42faf7dSnicm free(l); 492a42faf7dSnicm 493a42faf7dSnicm switch (data->type) { 494a42faf7dSnicm case WINDOW_TREE_NONE: 495a42faf7dSnicm break; 496a42faf7dSnicm case WINDOW_TREE_SESSION: 497a42faf7dSnicm *tag = (uint64_t)data->fs.s; 498a42faf7dSnicm break; 499a42faf7dSnicm case WINDOW_TREE_WINDOW: 500a42faf7dSnicm *tag = (uint64_t)data->fs.wl; 501a42faf7dSnicm break; 502a42faf7dSnicm case WINDOW_TREE_PANE: 5039e3444daSnicm if (window_count_panes(data->fs.wl->window) == 1) 5049e3444daSnicm *tag = (uint64_t)data->fs.wl; 5059e3444daSnicm else 506a42faf7dSnicm *tag = (uint64_t)data->fs.wp; 507a42faf7dSnicm break; 508a42faf7dSnicm } 509a42faf7dSnicm } 510a42faf7dSnicm 5110088ef99Snicm static void 5120088ef99Snicm window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py, 5130088ef99Snicm u_int sx, u_int sy, const struct grid_cell *gc, const char *label) 5140088ef99Snicm { 5150088ef99Snicm size_t len; 5160088ef99Snicm u_int ox, oy; 5170088ef99Snicm 5180088ef99Snicm len = strlen(label); 5190088ef99Snicm if (sx == 0 || sy == 1 || len > sx) 5200088ef99Snicm return; 5210088ef99Snicm ox = (sx - len + 1) / 2; 5220088ef99Snicm oy = (sy + 1) / 2; 5230088ef99Snicm 5240088ef99Snicm if (ox > 1 && ox + len < sx - 1 && sy >= 3) { 5255c6c7001Snicm screen_write_cursormove(ctx, px + ox - 1, py + oy - 1, 0); 526dad8e5c0Snicm screen_write_box(ctx, len + 2, 3, BOX_LINES_DEFAULT, NULL, 527dad8e5c0Snicm NULL); 5280088ef99Snicm } 5295c6c7001Snicm screen_write_cursormove(ctx, px + ox, py + oy, 0); 5300088ef99Snicm screen_write_puts(ctx, gc, "%s", label); 5310088ef99Snicm } 5320088ef99Snicm 533367b65a4Snicm static void 53405d44586Snicm window_tree_draw_session(struct window_tree_modedata *data, struct session *s, 53505d44586Snicm struct screen_write_ctx *ctx, u_int sx, u_int sy) 536367b65a4Snicm { 537367b65a4Snicm struct options *oo = s->options; 538367b65a4Snicm struct winlink *wl; 539367b65a4Snicm struct window *w; 5402b7e51f7Snicm u_int cx = ctx->s->cx, cy = ctx->s->cy; 54124a42803Snicm u_int loop, total, visible, each, width, offset; 54224a42803Snicm u_int current, start, end, remaining, i; 543367b65a4Snicm struct grid_cell gc; 54424a42803Snicm int colour, active_colour, left, right; 545367b65a4Snicm char *label; 546367b65a4Snicm 54724a42803Snicm total = winlink_count(&s->windows); 548367b65a4Snicm 549367b65a4Snicm memcpy(&gc, &grid_default_cell, sizeof gc); 550367b65a4Snicm colour = options_get_number(oo, "display-panes-colour"); 551367b65a4Snicm active_colour = options_get_number(oo, "display-panes-active-colour"); 552367b65a4Snicm 55324a42803Snicm if (sx / total < 24) { 55424a42803Snicm visible = sx / 24; 55524a42803Snicm if (visible == 0) 55624a42803Snicm visible = 1; 55724a42803Snicm } else 55824a42803Snicm visible = total; 559367b65a4Snicm 56024a42803Snicm current = 0; 56124a42803Snicm RB_FOREACH(wl, winlinks, &s->windows) { 56224a42803Snicm if (wl == s->curw) 56324a42803Snicm break; 56424a42803Snicm current++; 56524a42803Snicm } 566367b65a4Snicm 56724a42803Snicm if (current < visible) { 56824a42803Snicm start = 0; 56924a42803Snicm end = visible; 57024a42803Snicm } else if (current >= total - visible) { 57124a42803Snicm start = total - visible; 57224a42803Snicm end = total; 57324a42803Snicm } else { 57424a42803Snicm start = current - (visible / 2); 57524a42803Snicm end = start + visible; 57624a42803Snicm } 57724a42803Snicm 57805d44586Snicm if (data->offset < -(int)start) 57905d44586Snicm data->offset = -(int)start; 58005d44586Snicm if (data->offset > (int)(total - end)) 58105d44586Snicm data->offset = (int)(total - end); 58205d44586Snicm start += data->offset; 58305d44586Snicm end += data->offset; 58405d44586Snicm 58524a42803Snicm left = (start != 0); 58624a42803Snicm right = (end != total); 58724a42803Snicm if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) 58824a42803Snicm left = right = 0; 58924a42803Snicm if (left && right) { 59024a42803Snicm each = (sx - 6) / visible; 59124a42803Snicm remaining = (sx - 6) - (visible * each); 59224a42803Snicm } else if (left || right) { 59324a42803Snicm each = (sx - 3) / visible; 59424a42803Snicm remaining = (sx - 3) - (visible * each); 59524a42803Snicm } else { 59624a42803Snicm each = sx / visible; 59724a42803Snicm remaining = sx - (visible * each); 59824a42803Snicm } 599c224fd4bSnicm if (each == 0) 600367b65a4Snicm return; 601367b65a4Snicm 60224a42803Snicm if (left) { 60363c9949dSnicm data->left = cx + 2; 6045c6c7001Snicm screen_write_cursormove(ctx, cx + 2, cy, 0); 60524a42803Snicm screen_write_vline(ctx, sy, 0, 0); 6065c6c7001Snicm screen_write_cursormove(ctx, cx, cy + sy / 2, 0); 60724a42803Snicm screen_write_puts(ctx, &grid_default_cell, "<"); 60863c9949dSnicm } else 60963c9949dSnicm data->left = -1; 61024a42803Snicm if (right) { 61163c9949dSnicm data->right = cx + sx - 3; 6125c6c7001Snicm screen_write_cursormove(ctx, cx + sx - 3, cy, 0); 61324a42803Snicm screen_write_vline(ctx, sy, 0, 0); 6145c6c7001Snicm screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0); 61524a42803Snicm screen_write_puts(ctx, &grid_default_cell, ">"); 61663c9949dSnicm } else 61763c9949dSnicm data->right = -1; 61863c9949dSnicm 61963c9949dSnicm data->start = start; 62063c9949dSnicm data->end = end; 62163c9949dSnicm data->each = each; 62224a42803Snicm 62324a42803Snicm i = loop = 0; 62424a42803Snicm RB_FOREACH(wl, winlinks, &s->windows) { 62524a42803Snicm if (loop == end) 62624a42803Snicm break; 62724a42803Snicm if (loop < start) { 62824a42803Snicm loop++; 62924a42803Snicm continue; 63024a42803Snicm } 63124a42803Snicm w = wl->window; 63224a42803Snicm 633367b65a4Snicm if (wl == s->curw) 634367b65a4Snicm gc.fg = active_colour; 635367b65a4Snicm else 636367b65a4Snicm gc.fg = colour; 63724a42803Snicm 63824a42803Snicm if (left) 63924a42803Snicm offset = 3 + (i * each); 64024a42803Snicm else 64124a42803Snicm offset = (i * each); 64224a42803Snicm if (loop == end - 1) 64303471407Snicm width = each + remaining; 644367b65a4Snicm else 645367b65a4Snicm width = each - 1; 646367b65a4Snicm 6475c6c7001Snicm screen_write_cursormove(ctx, cx + offset, cy, 0); 648367b65a4Snicm screen_write_preview(ctx, &w->active->base, width, sy); 649367b65a4Snicm 650367b65a4Snicm xasprintf(&label, " %u:%s ", wl->idx, w->name); 651367b65a4Snicm if (strlen(label) > width) 652367b65a4Snicm xasprintf(&label, " %u ", wl->idx); 6532b7e51f7Snicm window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc, 6542b7e51f7Snicm label); 655367b65a4Snicm free(label); 656367b65a4Snicm 65724a42803Snicm if (loop != end - 1) { 6585c6c7001Snicm screen_write_cursormove(ctx, cx + offset + width, cy, 0); 659367b65a4Snicm screen_write_vline(ctx, sy, 0, 0); 660367b65a4Snicm } 66124a42803Snicm loop++; 66224a42803Snicm 66324a42803Snicm i++; 664367b65a4Snicm } 665367b65a4Snicm } 666367b65a4Snicm 667367b65a4Snicm static void 66805d44586Snicm window_tree_draw_window(struct window_tree_modedata *data, struct session *s, 66905d44586Snicm struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy) 670367b65a4Snicm { 671367b65a4Snicm struct options *oo = s->options; 672367b65a4Snicm struct window_pane *wp; 6732b7e51f7Snicm u_int cx = ctx->s->cx, cy = ctx->s->cy; 67424a42803Snicm u_int loop, total, visible, each, width, offset; 6756af87e9aSnicm u_int current, start, end, remaining, i, pane_idx; 676367b65a4Snicm struct grid_cell gc; 6776af87e9aSnicm int colour, active_colour, left, right; 678367b65a4Snicm char *label; 679367b65a4Snicm 68024a42803Snicm total = window_count_panes(w); 681367b65a4Snicm 682367b65a4Snicm memcpy(&gc, &grid_default_cell, sizeof gc); 683367b65a4Snicm colour = options_get_number(oo, "display-panes-colour"); 684367b65a4Snicm active_colour = options_get_number(oo, "display-panes-active-colour"); 685367b65a4Snicm 68624a42803Snicm if (sx / total < 24) { 68724a42803Snicm visible = sx / 24; 68824a42803Snicm if (visible == 0) 68924a42803Snicm visible = 1; 69024a42803Snicm } else 69124a42803Snicm visible = total; 692367b65a4Snicm 69324a42803Snicm current = 0; 69424a42803Snicm TAILQ_FOREACH(wp, &w->panes, entry) { 69524a42803Snicm if (wp == w->active) 69624a42803Snicm break; 69724a42803Snicm current++; 69824a42803Snicm } 699367b65a4Snicm 70024a42803Snicm if (current < visible) { 70124a42803Snicm start = 0; 70224a42803Snicm end = visible; 70324a42803Snicm } else if (current >= total - visible) { 70424a42803Snicm start = total - visible; 70524a42803Snicm end = total; 70624a42803Snicm } else { 70724a42803Snicm start = current - (visible / 2); 70824a42803Snicm end = start + visible; 70924a42803Snicm } 71024a42803Snicm 71105d44586Snicm if (data->offset < -(int)start) 71205d44586Snicm data->offset = -(int)start; 71305d44586Snicm if (data->offset > (int)(total - end)) 71405d44586Snicm data->offset = (int)(total - end); 71505d44586Snicm start += data->offset; 71605d44586Snicm end += data->offset; 71705d44586Snicm 71824a42803Snicm left = (start != 0); 71924a42803Snicm right = (end != total); 72024a42803Snicm if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) 72124a42803Snicm left = right = 0; 72224a42803Snicm if (left && right) { 72324a42803Snicm each = (sx - 6) / visible; 72424a42803Snicm remaining = (sx - 6) - (visible * each); 72524a42803Snicm } else if (left || right) { 72624a42803Snicm each = (sx - 3) / visible; 72724a42803Snicm remaining = (sx - 3) - (visible * each); 72824a42803Snicm } else { 72924a42803Snicm each = sx / visible; 73024a42803Snicm remaining = sx - (visible * each); 73124a42803Snicm } 732c224fd4bSnicm if (each == 0) 733367b65a4Snicm return; 734367b65a4Snicm 73524a42803Snicm if (left) { 73663c9949dSnicm data->left = cx + 2; 7375c6c7001Snicm screen_write_cursormove(ctx, cx + 2, cy, 0); 73824a42803Snicm screen_write_vline(ctx, sy, 0, 0); 7395c6c7001Snicm screen_write_cursormove(ctx, cx, cy + sy / 2, 0); 74024a42803Snicm screen_write_puts(ctx, &grid_default_cell, "<"); 74163c9949dSnicm } else 74263c9949dSnicm data->left = -1; 74324a42803Snicm if (right) { 74463c9949dSnicm data->right = cx + sx - 3; 7455c6c7001Snicm screen_write_cursormove(ctx, cx + sx - 3, cy, 0); 74624a42803Snicm screen_write_vline(ctx, sy, 0, 0); 7475c6c7001Snicm screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0); 74824a42803Snicm screen_write_puts(ctx, &grid_default_cell, ">"); 74963c9949dSnicm } else 75063c9949dSnicm data->right = -1; 75163c9949dSnicm 75263c9949dSnicm data->start = start; 75363c9949dSnicm data->end = end; 75463c9949dSnicm data->each = each; 75524a42803Snicm 75624a42803Snicm i = loop = 0; 75724a42803Snicm TAILQ_FOREACH(wp, &w->panes, entry) { 75824a42803Snicm if (loop == end) 75924a42803Snicm break; 76024a42803Snicm if (loop < start) { 76124a42803Snicm loop++; 76224a42803Snicm continue; 76324a42803Snicm } 76424a42803Snicm 765367b65a4Snicm if (wp == w->active) 766367b65a4Snicm gc.fg = active_colour; 767367b65a4Snicm else 768367b65a4Snicm gc.fg = colour; 76924a42803Snicm 77024a42803Snicm if (left) 77124a42803Snicm offset = 3 + (i * each); 77224a42803Snicm else 77324a42803Snicm offset = (i * each); 77424a42803Snicm if (loop == end - 1) 77503471407Snicm width = each + remaining; 776367b65a4Snicm else 777367b65a4Snicm width = each - 1; 778367b65a4Snicm 7795c6c7001Snicm screen_write_cursormove(ctx, cx + offset, cy, 0); 780367b65a4Snicm screen_write_preview(ctx, &wp->base, width, sy); 781367b65a4Snicm 7828807a41eSnicm if (window_pane_index(wp, &pane_idx) != 0) 7838807a41eSnicm pane_idx = loop; 7848807a41eSnicm xasprintf(&label, " %u ", pane_idx); 7852b7e51f7Snicm window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc, 7862b7e51f7Snicm label); 787367b65a4Snicm free(label); 788367b65a4Snicm 78924a42803Snicm if (loop != end - 1) { 7905c6c7001Snicm screen_write_cursormove(ctx, cx + offset + width, cy, 0); 791367b65a4Snicm screen_write_vline(ctx, sy, 0, 0); 792367b65a4Snicm } 79324a42803Snicm loop++; 79424a42803Snicm 79524a42803Snicm i++; 796367b65a4Snicm } 797367b65a4Snicm } 798367b65a4Snicm 7992b7e51f7Snicm static void 8002b7e51f7Snicm window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, 8012b7e51f7Snicm u_int sx, u_int sy) 802a42faf7dSnicm { 803a42faf7dSnicm struct window_tree_itemdata *item = itemdata; 804a42faf7dSnicm struct session *sp; 805a42faf7dSnicm struct winlink *wlp; 806a42faf7dSnicm struct window_pane *wp; 807a42faf7dSnicm 808a42faf7dSnicm window_tree_pull_item(item, &sp, &wlp, &wp); 809a42faf7dSnicm if (wp == NULL) 8102b7e51f7Snicm return; 811a42faf7dSnicm 812367b65a4Snicm switch (item->type) { 813367b65a4Snicm case WINDOW_TREE_NONE: 8142b7e51f7Snicm break; 815367b65a4Snicm case WINDOW_TREE_SESSION: 8162b7e51f7Snicm window_tree_draw_session(modedata, sp, ctx, sx, sy); 817367b65a4Snicm break; 818367b65a4Snicm case WINDOW_TREE_WINDOW: 8192b7e51f7Snicm window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy); 820367b65a4Snicm break; 821367b65a4Snicm case WINDOW_TREE_PANE: 8222b7e51f7Snicm screen_write_preview(ctx, &wp->base, sx, sy); 823367b65a4Snicm break; 824367b65a4Snicm } 825a42faf7dSnicm } 826a42faf7dSnicm 827943a08b1Snicm static int 828943a08b1Snicm window_tree_search(__unused void *modedata, void *itemdata, const char *ss) 829943a08b1Snicm { 830943a08b1Snicm struct window_tree_itemdata *item = itemdata; 831943a08b1Snicm struct session *s; 832943a08b1Snicm struct winlink *wl; 833943a08b1Snicm struct window_pane *wp; 8340a4d943dSnicm char *cmd; 8350a4d943dSnicm int retval; 836943a08b1Snicm 837943a08b1Snicm window_tree_pull_item(item, &s, &wl, &wp); 838943a08b1Snicm 839943a08b1Snicm switch (item->type) { 840943a08b1Snicm case WINDOW_TREE_NONE: 841943a08b1Snicm return (0); 842943a08b1Snicm case WINDOW_TREE_SESSION: 843943a08b1Snicm if (s == NULL) 844943a08b1Snicm return (0); 845943a08b1Snicm return (strstr(s->name, ss) != NULL); 846943a08b1Snicm case WINDOW_TREE_WINDOW: 847943a08b1Snicm if (s == NULL || wl == NULL) 848943a08b1Snicm return (0); 849943a08b1Snicm return (strstr(wl->window->name, ss) != NULL); 850943a08b1Snicm case WINDOW_TREE_PANE: 851943a08b1Snicm if (s == NULL || wl == NULL || wp == NULL) 852943a08b1Snicm break; 853943a08b1Snicm cmd = get_proc_name(wp->fd, wp->tty); 854943a08b1Snicm if (cmd == NULL || *cmd == '\0') 855943a08b1Snicm return (0); 8560a4d943dSnicm retval = (strstr(cmd, ss) != NULL); 8570a4d943dSnicm free(cmd); 858acf6cf7cSnicm return (retval); 859943a08b1Snicm } 860943a08b1Snicm return (0); 861943a08b1Snicm } 862943a08b1Snicm 863f43bc87cSnicm static void 864f43bc87cSnicm window_tree_menu(void *modedata, struct client *c, key_code key) 865f43bc87cSnicm { 866f43bc87cSnicm struct window_tree_modedata *data = modedata; 867f43bc87cSnicm struct window_pane *wp = data->wp; 868f43bc87cSnicm struct window_mode_entry *wme; 869f43bc87cSnicm 870f43bc87cSnicm wme = TAILQ_FIRST(&wp->modes); 871f43bc87cSnicm if (wme == NULL || wme->data != modedata) 872f43bc87cSnicm return; 873f43bc87cSnicm window_tree_key(wme, c, NULL, NULL, key, NULL); 874f43bc87cSnicm } 875f43bc87cSnicm 876438eed14Snicm static key_code 877438eed14Snicm window_tree_get_key(void *modedata, void *itemdata, u_int line) 878438eed14Snicm { 879438eed14Snicm struct window_tree_modedata *data = modedata; 880438eed14Snicm struct window_tree_itemdata *item = itemdata; 881438eed14Snicm struct format_tree *ft; 882438eed14Snicm struct session *s; 883438eed14Snicm struct winlink *wl; 884438eed14Snicm struct window_pane *wp; 885438eed14Snicm char *expanded; 886438eed14Snicm key_code key; 887438eed14Snicm 888438eed14Snicm ft = format_create(NULL, NULL, FORMAT_NONE, 0); 889438eed14Snicm window_tree_pull_item(item, &s, &wl, &wp); 890438eed14Snicm if (item->type == WINDOW_TREE_SESSION) 891438eed14Snicm format_defaults(ft, NULL, s, NULL, NULL); 892438eed14Snicm else if (item->type == WINDOW_TREE_WINDOW) 893438eed14Snicm format_defaults(ft, NULL, s, wl, NULL); 894438eed14Snicm else 895438eed14Snicm format_defaults(ft, NULL, s, wl, wp); 896438eed14Snicm format_add(ft, "line", "%u", line); 897438eed14Snicm 898438eed14Snicm expanded = format_expand(ft, data->key_format); 899438eed14Snicm key = key_string_lookup_string(expanded); 900438eed14Snicm free(expanded); 901438eed14Snicm format_free(ft); 9023e8355bdSnicm return (key); 903438eed14Snicm } 904438eed14Snicm 905a42faf7dSnicm static struct screen * 90630a94f45Snicm window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, 907a42faf7dSnicm struct args *args) 908a42faf7dSnicm { 90930a94f45Snicm struct window_pane *wp = wme->wp; 910a42faf7dSnicm struct window_tree_modedata *data; 911a42faf7dSnicm struct screen *s; 912a42faf7dSnicm 91330a94f45Snicm wme->data = data = xcalloc(1, sizeof *data); 914f43bc87cSnicm data->wp = wp; 915f43bc87cSnicm data->references = 1; 916a42faf7dSnicm 917a42faf7dSnicm if (args_has(args, 's')) 918a42faf7dSnicm data->type = WINDOW_TREE_SESSION; 919a42faf7dSnicm else if (args_has(args, 'w')) 920a42faf7dSnicm data->type = WINDOW_TREE_WINDOW; 921a42faf7dSnicm else 922a42faf7dSnicm data->type = WINDOW_TREE_PANE; 923a42faf7dSnicm memcpy(&data->fs, fs, sizeof data->fs); 924a42faf7dSnicm 925bf38e336Snicm if (args == NULL || !args_has(args, 'F')) 926bf38e336Snicm data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT); 927bf38e336Snicm else 928bf38e336Snicm data->format = xstrdup(args_get(args, 'F')); 929438eed14Snicm if (args == NULL || !args_has(args, 'K')) 930438eed14Snicm data->key_format = xstrdup(WINDOW_TREE_DEFAULT_KEY_FORMAT); 931438eed14Snicm else 932438eed14Snicm data->key_format = xstrdup(args_get(args, 'K')); 9331693b10bSnicm if (args == NULL || args_count(args) == 0) 934a42faf7dSnicm data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND); 935a42faf7dSnicm else 9361693b10bSnicm data->command = xstrdup(args_string(args, 0)); 937988c4c87Snicm data->squash_groups = !args_has(args, 'G'); 938*af11b61dSnicm if (args_has(args, 'y')) 939*af11b61dSnicm data->prompt_flags = PROMPT_ACCEPT; 940a42faf7dSnicm 941b38aa712Snicm data->data = mode_tree_start(wp, args, window_tree_build, 942438eed14Snicm window_tree_draw, window_tree_search, window_tree_menu, NULL, 943438eed14Snicm window_tree_get_key, data, window_tree_menu_items, 944438eed14Snicm window_tree_sort_list, nitems(window_tree_sort_list), &s); 9454f5e4c93Snicm mode_tree_zoom(data->data, args); 946a42faf7dSnicm 947a42faf7dSnicm mode_tree_build(data->data); 948a42faf7dSnicm mode_tree_draw(data->data); 949a42faf7dSnicm 950a42faf7dSnicm data->type = WINDOW_TREE_NONE; 951a42faf7dSnicm 952a42faf7dSnicm return (s); 953a42faf7dSnicm } 954a42faf7dSnicm 955a42faf7dSnicm static void 956a42faf7dSnicm window_tree_destroy(struct window_tree_modedata *data) 957a42faf7dSnicm { 958a42faf7dSnicm u_int i; 959a42faf7dSnicm 960a42faf7dSnicm if (--data->references != 0) 961a42faf7dSnicm return; 962a42faf7dSnicm 963a42faf7dSnicm for (i = 0; i < data->item_size; i++) 964a42faf7dSnicm window_tree_free_item(data->item_list[i]); 965a42faf7dSnicm free(data->item_list); 966a42faf7dSnicm 967bf38e336Snicm free(data->format); 968438eed14Snicm free(data->key_format); 969a42faf7dSnicm free(data->command); 970bf38e336Snicm 971a42faf7dSnicm free(data); 972a42faf7dSnicm } 973a42faf7dSnicm 974a42faf7dSnicm static void 97530a94f45Snicm window_tree_free(struct window_mode_entry *wme) 976a42faf7dSnicm { 97730a94f45Snicm struct window_tree_modedata *data = wme->data; 978a42faf7dSnicm 979a42faf7dSnicm if (data == NULL) 980a42faf7dSnicm return; 981a42faf7dSnicm 982a42faf7dSnicm data->dead = 1; 983d0ca3a30Snicm mode_tree_free(data->data); 984a42faf7dSnicm window_tree_destroy(data); 985a42faf7dSnicm } 986a42faf7dSnicm 987a42faf7dSnicm static void 98830a94f45Snicm window_tree_resize(struct window_mode_entry *wme, u_int sx, u_int sy) 989a42faf7dSnicm { 99030a94f45Snicm struct window_tree_modedata *data = wme->data; 991a42faf7dSnicm 992a42faf7dSnicm mode_tree_resize(data->data, sx, sy); 993a42faf7dSnicm } 994a42faf7dSnicm 995734f37e4Snicm static void 996734f37e4Snicm window_tree_update(struct window_mode_entry *wme) 997734f37e4Snicm { 998734f37e4Snicm struct window_tree_modedata *data = wme->data; 999734f37e4Snicm 1000734f37e4Snicm mode_tree_build(data->data); 1001734f37e4Snicm mode_tree_draw(data->data); 1002734f37e4Snicm data->wp->flags |= PANE_REDRAW; 1003734f37e4Snicm } 1004734f37e4Snicm 1005a42faf7dSnicm static char * 1006a42faf7dSnicm window_tree_get_target(struct window_tree_itemdata *item, 1007a42faf7dSnicm struct cmd_find_state *fs) 1008a42faf7dSnicm { 1009a42faf7dSnicm struct session *s; 1010a42faf7dSnicm struct winlink *wl; 1011a42faf7dSnicm struct window_pane *wp; 1012a42faf7dSnicm char *target; 1013a42faf7dSnicm 1014a42faf7dSnicm window_tree_pull_item(item, &s, &wl, &wp); 1015a42faf7dSnicm 1016a42faf7dSnicm target = NULL; 1017a42faf7dSnicm switch (item->type) { 1018a42faf7dSnicm case WINDOW_TREE_NONE: 1019a42faf7dSnicm break; 1020a42faf7dSnicm case WINDOW_TREE_SESSION: 1021a42faf7dSnicm if (s == NULL) 1022a42faf7dSnicm break; 1023a42faf7dSnicm xasprintf(&target, "=%s:", s->name); 1024a42faf7dSnicm break; 1025a42faf7dSnicm case WINDOW_TREE_WINDOW: 1026a42faf7dSnicm if (s == NULL || wl == NULL) 1027a42faf7dSnicm break; 1028a42faf7dSnicm xasprintf(&target, "=%s:%u.", s->name, wl->idx); 1029a42faf7dSnicm break; 1030a42faf7dSnicm case WINDOW_TREE_PANE: 1031a42faf7dSnicm if (s == NULL || wl == NULL || wp == NULL) 1032a42faf7dSnicm break; 1033a42faf7dSnicm xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id); 1034a42faf7dSnicm break; 1035a42faf7dSnicm } 1036a42faf7dSnicm if (target == NULL) 1037a42faf7dSnicm cmd_find_clear_state(fs, 0); 1038a42faf7dSnicm else 10390772530eSnicm cmd_find_from_winlink_pane(fs, wl, wp, 0); 1040a42faf7dSnicm return (target); 1041a42faf7dSnicm } 1042a42faf7dSnicm 1043a42faf7dSnicm static void 1044d7af2c28Snicm window_tree_command_each(void *modedata, void *itemdata, struct client *c, 1045d7af2c28Snicm __unused key_code key) 1046a42faf7dSnicm { 1047a42faf7dSnicm struct window_tree_modedata *data = modedata; 1048a42faf7dSnicm struct window_tree_itemdata *item = itemdata; 1049a42faf7dSnicm char *name; 1050a42faf7dSnicm struct cmd_find_state fs; 1051a42faf7dSnicm 1052a42faf7dSnicm name = window_tree_get_target(item, &fs); 1053a42faf7dSnicm if (name != NULL) 1054d7af2c28Snicm mode_tree_run_command(c, &fs, data->entered, name); 1055a42faf7dSnicm free(name); 1056a42faf7dSnicm } 1057a42faf7dSnicm 1058a42faf7dSnicm static enum cmd_retval 1059a42faf7dSnicm window_tree_command_done(__unused struct cmdq_item *item, void *modedata) 1060a42faf7dSnicm { 1061a42faf7dSnicm struct window_tree_modedata *data = modedata; 1062a42faf7dSnicm 1063a42faf7dSnicm if (!data->dead) { 1064a42faf7dSnicm mode_tree_build(data->data); 1065a42faf7dSnicm mode_tree_draw(data->data); 1066a42faf7dSnicm data->wp->flags |= PANE_REDRAW; 1067a42faf7dSnicm } 1068a42faf7dSnicm window_tree_destroy(data); 1069a42faf7dSnicm return (CMD_RETURN_NORMAL); 1070a42faf7dSnicm } 1071a42faf7dSnicm 1072a42faf7dSnicm static int 1073a42faf7dSnicm window_tree_command_callback(struct client *c, void *modedata, const char *s, 1074a42faf7dSnicm __unused int done) 1075a42faf7dSnicm { 1076a42faf7dSnicm struct window_tree_modedata *data = modedata; 1077a42faf7dSnicm 1078d0ca3a30Snicm if (s == NULL || *s == '\0' || data->dead) 1079a42faf7dSnicm return (0); 1080a42faf7dSnicm 1081a42faf7dSnicm data->entered = s; 1082d7af2c28Snicm mode_tree_each_tagged(data->data, window_tree_command_each, c, 1083d7af2c28Snicm KEYC_NONE, 1); 1084a42faf7dSnicm data->entered = NULL; 1085a42faf7dSnicm 1086a42faf7dSnicm data->references++; 1087a42faf7dSnicm cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1088a42faf7dSnicm 1089a42faf7dSnicm return (0); 1090a42faf7dSnicm } 1091a42faf7dSnicm 1092a42faf7dSnicm static void 1093a42faf7dSnicm window_tree_command_free(void *modedata) 1094a42faf7dSnicm { 1095a42faf7dSnicm struct window_tree_modedata *data = modedata; 1096a42faf7dSnicm 1097a42faf7dSnicm window_tree_destroy(data); 1098a42faf7dSnicm } 1099a42faf7dSnicm 1100d0ca3a30Snicm static void 1101d0ca3a30Snicm window_tree_kill_each(__unused void *modedata, void *itemdata, 1102d0ca3a30Snicm __unused struct client *c, __unused key_code key) 1103d0ca3a30Snicm { 1104d0ca3a30Snicm struct window_tree_itemdata *item = itemdata; 1105d0ca3a30Snicm struct session *s; 1106d0ca3a30Snicm struct winlink *wl; 1107d0ca3a30Snicm struct window_pane *wp; 1108d0ca3a30Snicm 1109d0ca3a30Snicm window_tree_pull_item(item, &s, &wl, &wp); 1110d0ca3a30Snicm 1111d0ca3a30Snicm switch (item->type) { 1112d0ca3a30Snicm case WINDOW_TREE_NONE: 1113d0ca3a30Snicm break; 1114d0ca3a30Snicm case WINDOW_TREE_SESSION: 1115d0ca3a30Snicm if (s != NULL) { 1116d0ca3a30Snicm server_destroy_session(s); 1117c26c4f79Snicm session_destroy(s, 1, __func__); 1118d0ca3a30Snicm } 1119d0ca3a30Snicm break; 1120d0ca3a30Snicm case WINDOW_TREE_WINDOW: 1121d0ca3a30Snicm if (wl != NULL) 1122e8b59d3cSnicm server_kill_window(wl->window, 0); 1123d0ca3a30Snicm break; 1124d0ca3a30Snicm case WINDOW_TREE_PANE: 1125d0ca3a30Snicm if (wp != NULL) 1126d0ca3a30Snicm server_kill_pane(wp); 1127d0ca3a30Snicm break; 1128d0ca3a30Snicm } 1129d0ca3a30Snicm } 1130d0ca3a30Snicm 1131d0ca3a30Snicm static int 1132d0ca3a30Snicm window_tree_kill_current_callback(struct client *c, void *modedata, 1133d0ca3a30Snicm const char *s, __unused int done) 1134d0ca3a30Snicm { 1135d0ca3a30Snicm struct window_tree_modedata *data = modedata; 1136d0ca3a30Snicm struct mode_tree_data *mtd = data->data; 1137d0ca3a30Snicm 1138d0ca3a30Snicm if (s == NULL || *s == '\0' || data->dead) 1139d0ca3a30Snicm return (0); 1140d0ca3a30Snicm if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') 1141d0ca3a30Snicm return (0); 1142d0ca3a30Snicm 1143d0ca3a30Snicm window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE); 1144e8b59d3cSnicm server_renumber_all(); 1145d0ca3a30Snicm 1146d0ca3a30Snicm data->references++; 1147d0ca3a30Snicm cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1148d0ca3a30Snicm 1149d0ca3a30Snicm return (0); 1150d0ca3a30Snicm } 1151d0ca3a30Snicm 1152d0ca3a30Snicm static int 1153d0ca3a30Snicm window_tree_kill_tagged_callback(struct client *c, void *modedata, 1154d0ca3a30Snicm const char *s, __unused int done) 1155d0ca3a30Snicm { 1156d0ca3a30Snicm struct window_tree_modedata *data = modedata; 1157d0ca3a30Snicm struct mode_tree_data *mtd = data->data; 1158d0ca3a30Snicm 1159d0ca3a30Snicm if (s == NULL || *s == '\0' || data->dead) 1160d0ca3a30Snicm return (0); 1161d0ca3a30Snicm if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') 1162d0ca3a30Snicm return (0); 1163d0ca3a30Snicm 1164d0ca3a30Snicm mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1); 1165e8b59d3cSnicm server_renumber_all(); 1166d0ca3a30Snicm 1167d0ca3a30Snicm data->references++; 1168d0ca3a30Snicm cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1169d0ca3a30Snicm 1170d0ca3a30Snicm return (0); 1171d0ca3a30Snicm } 1172d0ca3a30Snicm 117363c9949dSnicm static key_code 117463c9949dSnicm window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x, 117563c9949dSnicm struct window_tree_itemdata *item) 117663c9949dSnicm { 117763c9949dSnicm struct session *s; 117863c9949dSnicm struct winlink *wl; 117963c9949dSnicm struct window_pane *wp; 118063c9949dSnicm u_int loop; 118163c9949dSnicm 118263c9949dSnicm if (key != KEYC_MOUSEDOWN1_PANE) 118363c9949dSnicm return (KEYC_NONE); 118463c9949dSnicm 118563c9949dSnicm if (data->left != -1 && x <= (u_int)data->left) 118663c9949dSnicm return ('<'); 118763c9949dSnicm if (data->right != -1 && x >= (u_int)data->right) 118863c9949dSnicm return ('>'); 118963c9949dSnicm 119063c9949dSnicm if (data->left != -1) 119163c9949dSnicm x -= data->left; 119263c9949dSnicm else if (x != 0) 119363c9949dSnicm x--; 119463c9949dSnicm if (x == 0 || data->end == 0) 119563c9949dSnicm x = 0; 119663c9949dSnicm else { 119763c9949dSnicm x = x / data->each; 119863c9949dSnicm if (data->start + x >= data->end) 119963c9949dSnicm x = data->end - 1; 120063c9949dSnicm } 120163c9949dSnicm 120263c9949dSnicm window_tree_pull_item(item, &s, &wl, &wp); 120363c9949dSnicm if (item->type == WINDOW_TREE_SESSION) { 120463c9949dSnicm if (s == NULL) 120563c9949dSnicm return (KEYC_NONE); 120663c9949dSnicm mode_tree_expand_current(data->data); 120763c9949dSnicm loop = 0; 120863c9949dSnicm RB_FOREACH(wl, winlinks, &s->windows) { 120963c9949dSnicm if (loop == data->start + x) 121063c9949dSnicm break; 121163c9949dSnicm loop++; 121263c9949dSnicm } 121363c9949dSnicm if (wl != NULL) 121463c9949dSnicm mode_tree_set_current(data->data, (uint64_t)wl); 121563c9949dSnicm return ('\r'); 121663c9949dSnicm } 121763c9949dSnicm if (item->type == WINDOW_TREE_WINDOW) { 121863c9949dSnicm if (wl == NULL) 121963c9949dSnicm return (KEYC_NONE); 122063c9949dSnicm mode_tree_expand_current(data->data); 122163c9949dSnicm loop = 0; 122263c9949dSnicm TAILQ_FOREACH(wp, &wl->window->panes, entry) { 122363c9949dSnicm if (loop == data->start + x) 122463c9949dSnicm break; 122563c9949dSnicm loop++; 122663c9949dSnicm } 122763c9949dSnicm if (wp != NULL) 122863c9949dSnicm mode_tree_set_current(data->data, (uint64_t)wp); 122963c9949dSnicm return ('\r'); 123063c9949dSnicm } 123163c9949dSnicm return (KEYC_NONE); 123263c9949dSnicm } 123363c9949dSnicm 1234a42faf7dSnicm static void 123530a94f45Snicm window_tree_key(struct window_mode_entry *wme, struct client *c, 1236bf52409eSnicm __unused struct session *s, __unused struct winlink *wl, key_code key, 1237bf52409eSnicm struct mouse_event *m) 1238a42faf7dSnicm { 123930a94f45Snicm struct window_pane *wp = wme->wp; 124030a94f45Snicm struct window_tree_modedata *data = wme->data; 124163c9949dSnicm struct window_tree_itemdata *item, *new_item; 1242d0ca3a30Snicm char *name, *prompt = NULL; 12437d0fd907Snicm struct cmd_find_state fs, *fsp = &data->fs; 1244a42faf7dSnicm int finished; 1245d0ca3a30Snicm u_int tagged, x, y, idx; 1246d0ca3a30Snicm struct session *ns; 1247d0ca3a30Snicm struct winlink *nwl; 1248d0ca3a30Snicm struct window_pane *nwp; 1249a42faf7dSnicm 125005d44586Snicm item = mode_tree_get_current(data->data); 125163c9949dSnicm finished = mode_tree_key(data->data, c, &key, m, &x, &y); 1252ba7a0894Snicm 1253ba7a0894Snicm again: 125463c9949dSnicm if (item != (new_item = mode_tree_get_current(data->data))) { 125563c9949dSnicm item = new_item; 125605d44586Snicm data->offset = 0; 125763c9949dSnicm } 1258ba7a0894Snicm if (KEYC_IS_MOUSE(key) && m != NULL) { 125963c9949dSnicm key = window_tree_mouse(data, key, x, item); 1260ba7a0894Snicm goto again; 1261ba7a0894Snicm } 1262ba7a0894Snicm 1263a42faf7dSnicm switch (key) { 126405d44586Snicm case '<': 126505d44586Snicm data->offset--; 126605d44586Snicm break; 126705d44586Snicm case '>': 126805d44586Snicm data->offset++; 126905d44586Snicm break; 12707d0fd907Snicm case 'H': 12717d0fd907Snicm mode_tree_expand(data->data, (uint64_t)fsp->s); 12727d0fd907Snicm mode_tree_expand(data->data, (uint64_t)fsp->wl); 12737d0fd907Snicm if (!mode_tree_set_current(data->data, (uint64_t)wme->wp)) 12747d0fd907Snicm mode_tree_set_current(data->data, (uint64_t)fsp->wl); 12757d0fd907Snicm break; 12767d0fd907Snicm case 'm': 12777d0fd907Snicm window_tree_pull_item(item, &ns, &nwl, &nwp); 12787d0fd907Snicm server_set_marked(ns, nwl, nwp); 12797d0fd907Snicm mode_tree_build(data->data); 12807d0fd907Snicm break; 12817d0fd907Snicm case 'M': 12827d0fd907Snicm server_clear_marked(); 12837d0fd907Snicm mode_tree_build(data->data); 12847d0fd907Snicm break; 1285d0ca3a30Snicm case 'x': 1286d0ca3a30Snicm window_tree_pull_item(item, &ns, &nwl, &nwp); 1287d0ca3a30Snicm switch (item->type) { 1288d0ca3a30Snicm case WINDOW_TREE_NONE: 1289d0ca3a30Snicm break; 1290d0ca3a30Snicm case WINDOW_TREE_SESSION: 1291d0ca3a30Snicm if (ns == NULL) 1292d0ca3a30Snicm break; 1293d0ca3a30Snicm xasprintf(&prompt, "Kill session %s? ", ns->name); 1294d0ca3a30Snicm break; 1295d0ca3a30Snicm case WINDOW_TREE_WINDOW: 1296d0ca3a30Snicm if (nwl == NULL) 1297d0ca3a30Snicm break; 1298d0ca3a30Snicm xasprintf(&prompt, "Kill window %u? ", nwl->idx); 1299d0ca3a30Snicm break; 1300d0ca3a30Snicm case WINDOW_TREE_PANE: 1301d0ca3a30Snicm if (nwp == NULL || window_pane_index(nwp, &idx) != 0) 1302d0ca3a30Snicm break; 1303d0ca3a30Snicm xasprintf(&prompt, "Kill pane %u? ", idx); 1304d0ca3a30Snicm break; 1305d0ca3a30Snicm } 1306d0ca3a30Snicm if (prompt == NULL) 1307d0ca3a30Snicm break; 1308d0ca3a30Snicm data->references++; 130994adf770Snicm status_prompt_set(c, NULL, prompt, "", 1310d0ca3a30Snicm window_tree_kill_current_callback, window_tree_command_free, 1311*af11b61dSnicm data, PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags, 1312*af11b61dSnicm PROMPT_TYPE_COMMAND); 1313d0ca3a30Snicm free(prompt); 1314d0ca3a30Snicm break; 1315d0ca3a30Snicm case 'X': 1316d0ca3a30Snicm tagged = mode_tree_count_tagged(data->data); 1317d0ca3a30Snicm if (tagged == 0) 1318d0ca3a30Snicm break; 1319d0ca3a30Snicm xasprintf(&prompt, "Kill %u tagged? ", tagged); 1320d0ca3a30Snicm data->references++; 132194adf770Snicm status_prompt_set(c, NULL, prompt, "", 1322d0ca3a30Snicm window_tree_kill_tagged_callback, window_tree_command_free, 1323*af11b61dSnicm data, PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags, 1324*af11b61dSnicm PROMPT_TYPE_COMMAND); 1325d0ca3a30Snicm free(prompt); 1326d0ca3a30Snicm break; 1327a42faf7dSnicm case ':': 1328a42faf7dSnicm tagged = mode_tree_count_tagged(data->data); 1329a42faf7dSnicm if (tagged != 0) 1330a42faf7dSnicm xasprintf(&prompt, "(%u tagged) ", tagged); 1331a42faf7dSnicm else 1332a42faf7dSnicm xasprintf(&prompt, "(current) "); 1333a42faf7dSnicm data->references++; 133494adf770Snicm status_prompt_set(c, NULL, prompt, "", 133594adf770Snicm window_tree_command_callback, window_tree_command_free, 1336bc5a8fc2Snicm data, PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); 1337a42faf7dSnicm free(prompt); 1338a42faf7dSnicm break; 1339a42faf7dSnicm case '\r': 1340a42faf7dSnicm name = window_tree_get_target(item, &fs); 1341a42faf7dSnicm if (name != NULL) 1342d7af2c28Snicm mode_tree_run_command(c, NULL, data->command, name); 1343d7af2c28Snicm finished = 1; 1344a42faf7dSnicm free(name); 1345d7af2c28Snicm break; 1346a42faf7dSnicm } 1347a42faf7dSnicm if (finished) 1348a42faf7dSnicm window_pane_reset_mode(wp); 1349a42faf7dSnicm else { 1350a42faf7dSnicm mode_tree_draw(data->data); 1351a42faf7dSnicm wp->flags |= PANE_REDRAW; 1352a42faf7dSnicm } 1353a42faf7dSnicm } 1354