1*438eed14Snicm /* $OpenBSD: window-tree.c,v 1.54 2021/04/12 06:50:25 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 59*438eed14Snicm #define WINDOW_TREE_DEFAULT_KEY_FORMAT \ 60*438eed14Snicm "#{?#{e|<:#{line},10}," \ 61*438eed14Snicm "#{line}" \ 62*438eed14Snicm "," \ 63*438eed14Snicm "#{?#{e|<:#{line},36}," \ 64*438eed14Snicm "M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \ 65*438eed14Snicm "," \ 66*438eed14Snicm "" \ 67*438eed14Snicm "}" \ 68*438eed14Snicm "}" 69*438eed14Snicm 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; 131*438eed14Snicm char *key_format; 132a42faf7dSnicm char *command; 133988c4c87Snicm int squash_groups; 134a42faf7dSnicm 135a42faf7dSnicm struct window_tree_itemdata **item_list; 136a42faf7dSnicm u_int item_size; 137a42faf7dSnicm 138a42faf7dSnicm const char *entered; 139a42faf7dSnicm 140a42faf7dSnicm struct cmd_find_state fs; 141a42faf7dSnicm enum window_tree_type type; 14205d44586Snicm 14305d44586Snicm int offset; 14463c9949dSnicm 14563c9949dSnicm int left; 14663c9949dSnicm int right; 14763c9949dSnicm u_int start; 14863c9949dSnicm u_int end; 14963c9949dSnicm u_int each; 150a42faf7dSnicm }; 151a42faf7dSnicm 152a42faf7dSnicm static void 153a42faf7dSnicm window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp, 154a42faf7dSnicm struct winlink **wlp, struct window_pane **wp) 155a42faf7dSnicm { 156a42faf7dSnicm *wp = NULL; 157a42faf7dSnicm *wlp = NULL; 158a42faf7dSnicm *sp = session_find_by_id(item->session); 159a42faf7dSnicm if (*sp == NULL) 160a42faf7dSnicm return; 161a42faf7dSnicm if (item->type == WINDOW_TREE_SESSION) { 162a42faf7dSnicm *wlp = (*sp)->curw; 163a42faf7dSnicm *wp = (*wlp)->window->active; 164a42faf7dSnicm return; 165a42faf7dSnicm } 166a42faf7dSnicm 167a42faf7dSnicm *wlp = winlink_find_by_index(&(*sp)->windows, item->winlink); 168a42faf7dSnicm if (*wlp == NULL) { 169a42faf7dSnicm *sp = NULL; 170a42faf7dSnicm return; 171a42faf7dSnicm } 172a42faf7dSnicm if (item->type == WINDOW_TREE_WINDOW) { 173a42faf7dSnicm *wp = (*wlp)->window->active; 174a42faf7dSnicm return; 175a42faf7dSnicm } 176a42faf7dSnicm 177a42faf7dSnicm *wp = window_pane_find_by_id(item->pane); 178a42faf7dSnicm if (!window_has_pane((*wlp)->window, *wp)) 179a42faf7dSnicm *wp = NULL; 180a42faf7dSnicm if (*wp == NULL) { 181a42faf7dSnicm *sp = NULL; 182a42faf7dSnicm *wlp = NULL; 183a42faf7dSnicm return; 184a42faf7dSnicm } 185a42faf7dSnicm } 186a42faf7dSnicm 187a42faf7dSnicm static struct window_tree_itemdata * 188a42faf7dSnicm window_tree_add_item(struct window_tree_modedata *data) 189a42faf7dSnicm { 190a42faf7dSnicm struct window_tree_itemdata *item; 191a42faf7dSnicm 192a42faf7dSnicm data->item_list = xreallocarray(data->item_list, data->item_size + 1, 193a42faf7dSnicm sizeof *data->item_list); 194a42faf7dSnicm item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item); 195a42faf7dSnicm return (item); 196a42faf7dSnicm } 197a42faf7dSnicm 198a42faf7dSnicm static void 199a42faf7dSnicm window_tree_free_item(struct window_tree_itemdata *item) 200a42faf7dSnicm { 201a42faf7dSnicm free(item); 202a42faf7dSnicm } 203a42faf7dSnicm 204a42faf7dSnicm static int 2053f6f9f7dSnicm window_tree_cmp_session(const void *a0, const void *b0) 206a42faf7dSnicm { 207a42faf7dSnicm const struct session *const *a = a0; 208a42faf7dSnicm const struct session *const *b = b0; 2093f6f9f7dSnicm const struct session *sa = *a; 2103f6f9f7dSnicm const struct session *sb = *b; 21176e38b2dSnicm int result = 0; 212a42faf7dSnicm 2133f6f9f7dSnicm switch (window_tree_sort->field) { 2143f6f9f7dSnicm case WINDOW_TREE_BY_INDEX: 2153f6f9f7dSnicm result = sa->id - sb->id; 2163f6f9f7dSnicm break; 2173f6f9f7dSnicm case WINDOW_TREE_BY_TIME: 2183f6f9f7dSnicm if (timercmp(&sa->activity_time, &sb->activity_time, >)) { 2193f6f9f7dSnicm result = -1; 2203f6f9f7dSnicm break; 2213f6f9f7dSnicm } 2223f6f9f7dSnicm if (timercmp(&sa->activity_time, &sb->activity_time, <)) { 2233f6f9f7dSnicm result = 1; 2243f6f9f7dSnicm break; 2253f6f9f7dSnicm } 2263f6f9f7dSnicm /* FALLTHROUGH */ 2273f6f9f7dSnicm case WINDOW_TREE_BY_NAME: 2283f6f9f7dSnicm result = strcmp(sa->name, sb->name); 2293f6f9f7dSnicm break; 2303f6f9f7dSnicm } 2313f6f9f7dSnicm 2323f6f9f7dSnicm if (window_tree_sort->reversed) 2333f6f9f7dSnicm result = -result; 2343f6f9f7dSnicm return (result); 235a42faf7dSnicm } 236a42faf7dSnicm 237a42faf7dSnicm static int 2383f6f9f7dSnicm window_tree_cmp_window(const void *a0, const void *b0) 239a42faf7dSnicm { 240a42faf7dSnicm const struct winlink *const *a = a0; 241a42faf7dSnicm const struct winlink *const *b = b0; 2423f6f9f7dSnicm const struct winlink *wla = *a; 2433f6f9f7dSnicm const struct winlink *wlb = *b; 2443f6f9f7dSnicm struct window *wa = wla->window; 2453f6f9f7dSnicm struct window *wb = wlb->window; 24676e38b2dSnicm int result = 0; 247a42faf7dSnicm 2483f6f9f7dSnicm switch (window_tree_sort->field) { 2493f6f9f7dSnicm case WINDOW_TREE_BY_INDEX: 2503f6f9f7dSnicm result = wla->idx - wlb->idx; 2513f6f9f7dSnicm break; 2523f6f9f7dSnicm case WINDOW_TREE_BY_TIME: 2533f6f9f7dSnicm if (timercmp(&wa->activity_time, &wb->activity_time, >)) { 2543f6f9f7dSnicm result = -1; 2553f6f9f7dSnicm break; 2563f6f9f7dSnicm } 2573f6f9f7dSnicm if (timercmp(&wa->activity_time, &wb->activity_time, <)) { 2583f6f9f7dSnicm result = 1; 2593f6f9f7dSnicm break; 2603f6f9f7dSnicm } 2613f6f9f7dSnicm /* FALLTHROUGH */ 2623f6f9f7dSnicm case WINDOW_TREE_BY_NAME: 2633f6f9f7dSnicm result = strcmp(wa->name, wb->name); 2643f6f9f7dSnicm break; 2653f6f9f7dSnicm } 2663f6f9f7dSnicm 2673f6f9f7dSnicm if (window_tree_sort->reversed) 2683f6f9f7dSnicm result = -result; 2693f6f9f7dSnicm return (result); 270a42faf7dSnicm } 271a42faf7dSnicm 272a42faf7dSnicm static int 2733f6f9f7dSnicm window_tree_cmp_pane(const void *a0, const void *b0) 274a42faf7dSnicm { 275a42faf7dSnicm const struct window_pane *const *a = a0; 276a42faf7dSnicm const struct window_pane *const *b = b0; 2773f6f9f7dSnicm int result; 278a42faf7dSnicm 2793f6f9f7dSnicm if (window_tree_sort->field == WINDOW_TREE_BY_TIME) 2803f6f9f7dSnicm result = (*a)->active_point - (*b)->active_point; 2813f6f9f7dSnicm else { 2823f6f9f7dSnicm /* 2833f6f9f7dSnicm * Panes don't have names, so use number order for any other 2843f6f9f7dSnicm * sort field. 2853f6f9f7dSnicm */ 2863f6f9f7dSnicm result = (*a)->id - (*b)->id; 2873f6f9f7dSnicm } 2883f6f9f7dSnicm if (window_tree_sort->reversed) 2892d71b92eSnicm result = -result; 2903f6f9f7dSnicm return (result); 291a42faf7dSnicm } 292a42faf7dSnicm 293a42faf7dSnicm static void 294a42faf7dSnicm window_tree_build_pane(struct session *s, struct winlink *wl, 295a42faf7dSnicm struct window_pane *wp, void *modedata, struct mode_tree_item *parent) 296a42faf7dSnicm { 297a42faf7dSnicm struct window_tree_modedata *data = modedata; 298a42faf7dSnicm struct window_tree_itemdata *item; 299a42faf7dSnicm char *name, *text; 300a42faf7dSnicm u_int idx; 301a42faf7dSnicm 302a42faf7dSnicm window_pane_index(wp, &idx); 303a42faf7dSnicm 304a42faf7dSnicm item = window_tree_add_item(data); 305a42faf7dSnicm item->type = WINDOW_TREE_PANE; 306a42faf7dSnicm item->session = s->id; 307a42faf7dSnicm item->winlink = wl->idx; 308a42faf7dSnicm item->pane = wp->id; 309a42faf7dSnicm 310bf38e336Snicm text = format_single(NULL, data->format, NULL, s, wl, wp); 311a42faf7dSnicm xasprintf(&name, "%u", idx); 312a42faf7dSnicm 313a42faf7dSnicm mode_tree_add(data->data, parent, item, (uint64_t)wp, name, text, -1); 314a42faf7dSnicm free(text); 315a42faf7dSnicm free(name); 316a42faf7dSnicm } 317a42faf7dSnicm 318a42faf7dSnicm static int 319f5b43402Snicm window_tree_filter_pane(struct session *s, struct winlink *wl, 320f5b43402Snicm struct window_pane *wp, const char *filter) 321f5b43402Snicm { 322f5b43402Snicm char *cp; 323f5b43402Snicm int result; 324f5b43402Snicm 325f5b43402Snicm if (filter == NULL) 326f5b43402Snicm return (1); 327f5b43402Snicm 328f5b43402Snicm cp = format_single(NULL, filter, NULL, s, wl, wp); 329f5b43402Snicm result = format_true(cp); 330f5b43402Snicm free(cp); 331f5b43402Snicm 332f5b43402Snicm return (result); 333f5b43402Snicm } 334f5b43402Snicm 335f5b43402Snicm static int 3363f6f9f7dSnicm window_tree_build_window(struct session *s, struct winlink *wl, 3373f6f9f7dSnicm void *modedata, struct mode_tree_sort_criteria *sort_crit, 3383f6f9f7dSnicm struct mode_tree_item *parent, const char *filter) 339a42faf7dSnicm { 340a42faf7dSnicm struct window_tree_modedata *data = modedata; 341a42faf7dSnicm struct window_tree_itemdata *item; 342a42faf7dSnicm struct mode_tree_item *mti; 343f5b43402Snicm char *name, *text; 344a42faf7dSnicm struct window_pane *wp, **l; 345a42faf7dSnicm u_int n, i; 346a42faf7dSnicm int expanded; 347a42faf7dSnicm 348a42faf7dSnicm item = window_tree_add_item(data); 349a42faf7dSnicm item->type = WINDOW_TREE_WINDOW; 350a42faf7dSnicm item->session = s->id; 351a42faf7dSnicm item->winlink = wl->idx; 352a42faf7dSnicm item->pane = -1; 353a42faf7dSnicm 354bf38e336Snicm text = format_single(NULL, data->format, NULL, s, wl, NULL); 355a42faf7dSnicm xasprintf(&name, "%u", wl->idx); 356a42faf7dSnicm 357a42faf7dSnicm if (data->type == WINDOW_TREE_SESSION || 358a42faf7dSnicm data->type == WINDOW_TREE_WINDOW) 359a42faf7dSnicm expanded = 0; 360a42faf7dSnicm else 361a42faf7dSnicm expanded = 1; 362a42faf7dSnicm mti = mode_tree_add(data->data, parent, item, (uint64_t)wl, name, text, 363a42faf7dSnicm expanded); 364a42faf7dSnicm free(text); 365a42faf7dSnicm free(name); 366a42faf7dSnicm 367a8b654cbSnicm if ((wp = TAILQ_FIRST(&wl->window->panes)) == NULL) 368a8b654cbSnicm goto empty; 369f5b43402Snicm if (TAILQ_NEXT(wp, entry) == NULL) { 370f5b43402Snicm if (!window_tree_filter_pane(s, wl, wp, filter)) 371f5b43402Snicm goto empty; 3722920087aSnicm return (1); 373f5b43402Snicm } 3742920087aSnicm 375a42faf7dSnicm l = NULL; 376a42faf7dSnicm n = 0; 3772920087aSnicm 378a42faf7dSnicm TAILQ_FOREACH(wp, &wl->window->panes, entry) { 379f5b43402Snicm if (!window_tree_filter_pane(s, wl, wp, filter)) 380a42faf7dSnicm continue; 381a42faf7dSnicm l = xreallocarray(l, n + 1, sizeof *l); 382a42faf7dSnicm l[n++] = wp; 383a42faf7dSnicm } 384f5b43402Snicm if (n == 0) 385f5b43402Snicm goto empty; 386a42faf7dSnicm 3873f6f9f7dSnicm window_tree_sort = sort_crit; 3883f6f9f7dSnicm qsort(l, n, sizeof *l, window_tree_cmp_pane); 389a42faf7dSnicm 390a42faf7dSnicm for (i = 0; i < n; i++) 391a42faf7dSnicm window_tree_build_pane(s, wl, l[i], modedata, mti); 392a42faf7dSnicm free(l); 393a42faf7dSnicm return (1); 394f5b43402Snicm 395f5b43402Snicm empty: 396f5b43402Snicm window_tree_free_item(item); 397f5b43402Snicm data->item_size--; 398f5b43402Snicm mode_tree_remove(data->data, mti); 399f5b43402Snicm return (0); 400a42faf7dSnicm } 401a42faf7dSnicm 402a42faf7dSnicm static void 403a42faf7dSnicm window_tree_build_session(struct session *s, void *modedata, 4043f6f9f7dSnicm struct mode_tree_sort_criteria *sort_crit, const char *filter) 405a42faf7dSnicm { 406a42faf7dSnicm struct window_tree_modedata *data = modedata; 407a42faf7dSnicm struct window_tree_itemdata *item; 408a42faf7dSnicm struct mode_tree_item *mti; 409a42faf7dSnicm char *text; 410a42faf7dSnicm struct winlink *wl, **l; 411a42faf7dSnicm u_int n, i, empty; 412a42faf7dSnicm int expanded; 413a42faf7dSnicm 414a42faf7dSnicm item = window_tree_add_item(data); 415a42faf7dSnicm item->type = WINDOW_TREE_SESSION; 416a42faf7dSnicm item->session = s->id; 417a42faf7dSnicm item->winlink = -1; 418a42faf7dSnicm item->pane = -1; 419a42faf7dSnicm 420bf38e336Snicm text = format_single(NULL, data->format, NULL, s, NULL, NULL); 421a42faf7dSnicm 422a42faf7dSnicm if (data->type == WINDOW_TREE_SESSION) 423a42faf7dSnicm expanded = 0; 424a42faf7dSnicm else 425a42faf7dSnicm expanded = 1; 426a42faf7dSnicm mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text, 427a42faf7dSnicm expanded); 428a42faf7dSnicm free(text); 429a42faf7dSnicm 430a42faf7dSnicm l = NULL; 431a42faf7dSnicm n = 0; 432a42faf7dSnicm RB_FOREACH(wl, winlinks, &s->windows) { 433a42faf7dSnicm l = xreallocarray(l, n + 1, sizeof *l); 434a42faf7dSnicm l[n++] = wl; 435a42faf7dSnicm } 4363f6f9f7dSnicm window_tree_sort = sort_crit; 4373f6f9f7dSnicm qsort(l, n, sizeof *l, window_tree_cmp_window); 438a42faf7dSnicm 439a42faf7dSnicm empty = 0; 440a42faf7dSnicm for (i = 0; i < n; i++) { 4413f6f9f7dSnicm if (!window_tree_build_window(s, l[i], modedata, sort_crit, mti, 442024c311aSnicm filter)) 443a42faf7dSnicm empty++; 444a42faf7dSnicm } 445a42faf7dSnicm if (empty == n) { 446a42faf7dSnicm window_tree_free_item(item); 447a42faf7dSnicm data->item_size--; 448a42faf7dSnicm mode_tree_remove(data->data, mti); 449a42faf7dSnicm } 450a42faf7dSnicm free(l); 451a42faf7dSnicm } 452a42faf7dSnicm 453a42faf7dSnicm static void 4543f6f9f7dSnicm window_tree_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, 4553f6f9f7dSnicm uint64_t *tag, const char *filter) 456a42faf7dSnicm { 457a42faf7dSnicm struct window_tree_modedata *data = modedata; 458a42faf7dSnicm struct session *s, **l; 459f443747aSnicm struct session_group *sg, *current; 460a42faf7dSnicm u_int n, i; 461a42faf7dSnicm 462f443747aSnicm current = session_group_contains(data->fs.s); 463f443747aSnicm 464a42faf7dSnicm for (i = 0; i < data->item_size; i++) 465a42faf7dSnicm window_tree_free_item(data->item_list[i]); 466a42faf7dSnicm free(data->item_list); 467a42faf7dSnicm data->item_list = NULL; 468a42faf7dSnicm data->item_size = 0; 469a42faf7dSnicm 470a42faf7dSnicm l = NULL; 471a42faf7dSnicm n = 0; 472a42faf7dSnicm RB_FOREACH(s, sessions, &sessions) { 473988c4c87Snicm if (data->squash_groups && 474f443747aSnicm (sg = session_group_contains(s)) != NULL) { 475f443747aSnicm if ((sg == current && s != data->fs.s) || 476f443747aSnicm (sg != current && s != TAILQ_FIRST(&sg->sessions))) 477988c4c87Snicm continue; 478f443747aSnicm } 479a42faf7dSnicm l = xreallocarray(l, n + 1, sizeof *l); 480a42faf7dSnicm l[n++] = s; 481a42faf7dSnicm } 4823f6f9f7dSnicm window_tree_sort = sort_crit; 4833f6f9f7dSnicm qsort(l, n, sizeof *l, window_tree_cmp_session); 484a42faf7dSnicm 485a42faf7dSnicm for (i = 0; i < n; i++) 4863f6f9f7dSnicm window_tree_build_session(l[i], modedata, sort_crit, filter); 487a42faf7dSnicm free(l); 488a42faf7dSnicm 489a42faf7dSnicm switch (data->type) { 490a42faf7dSnicm case WINDOW_TREE_NONE: 491a42faf7dSnicm break; 492a42faf7dSnicm case WINDOW_TREE_SESSION: 493a42faf7dSnicm *tag = (uint64_t)data->fs.s; 494a42faf7dSnicm break; 495a42faf7dSnicm case WINDOW_TREE_WINDOW: 496a42faf7dSnicm *tag = (uint64_t)data->fs.wl; 497a42faf7dSnicm break; 498a42faf7dSnicm case WINDOW_TREE_PANE: 4999e3444daSnicm if (window_count_panes(data->fs.wl->window) == 1) 5009e3444daSnicm *tag = (uint64_t)data->fs.wl; 5019e3444daSnicm else 502a42faf7dSnicm *tag = (uint64_t)data->fs.wp; 503a42faf7dSnicm break; 504a42faf7dSnicm } 505a42faf7dSnicm } 506a42faf7dSnicm 5070088ef99Snicm static void 5080088ef99Snicm window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py, 5090088ef99Snicm u_int sx, u_int sy, const struct grid_cell *gc, const char *label) 5100088ef99Snicm { 5110088ef99Snicm size_t len; 5120088ef99Snicm u_int ox, oy; 5130088ef99Snicm 5140088ef99Snicm len = strlen(label); 5150088ef99Snicm if (sx == 0 || sy == 1 || len > sx) 5160088ef99Snicm return; 5170088ef99Snicm ox = (sx - len + 1) / 2; 5180088ef99Snicm oy = (sy + 1) / 2; 5190088ef99Snicm 5200088ef99Snicm if (ox > 1 && ox + len < sx - 1 && sy >= 3) { 5215c6c7001Snicm screen_write_cursormove(ctx, px + ox - 1, py + oy - 1, 0); 5220088ef99Snicm screen_write_box(ctx, len + 2, 3); 5230088ef99Snicm } 5245c6c7001Snicm screen_write_cursormove(ctx, px + ox, py + oy, 0); 5250088ef99Snicm screen_write_puts(ctx, gc, "%s", label); 5260088ef99Snicm } 5270088ef99Snicm 528367b65a4Snicm static void 52905d44586Snicm window_tree_draw_session(struct window_tree_modedata *data, struct session *s, 53005d44586Snicm struct screen_write_ctx *ctx, u_int sx, u_int sy) 531367b65a4Snicm { 532367b65a4Snicm struct options *oo = s->options; 533367b65a4Snicm struct winlink *wl; 534367b65a4Snicm struct window *w; 5352b7e51f7Snicm u_int cx = ctx->s->cx, cy = ctx->s->cy; 53624a42803Snicm u_int loop, total, visible, each, width, offset; 53724a42803Snicm u_int current, start, end, remaining, i; 538367b65a4Snicm struct grid_cell gc; 53924a42803Snicm int colour, active_colour, left, right; 540367b65a4Snicm char *label; 541367b65a4Snicm 54224a42803Snicm total = winlink_count(&s->windows); 543367b65a4Snicm 544367b65a4Snicm memcpy(&gc, &grid_default_cell, sizeof gc); 545367b65a4Snicm colour = options_get_number(oo, "display-panes-colour"); 546367b65a4Snicm active_colour = options_get_number(oo, "display-panes-active-colour"); 547367b65a4Snicm 54824a42803Snicm if (sx / total < 24) { 54924a42803Snicm visible = sx / 24; 55024a42803Snicm if (visible == 0) 55124a42803Snicm visible = 1; 55224a42803Snicm } else 55324a42803Snicm visible = total; 554367b65a4Snicm 55524a42803Snicm current = 0; 55624a42803Snicm RB_FOREACH(wl, winlinks, &s->windows) { 55724a42803Snicm if (wl == s->curw) 55824a42803Snicm break; 55924a42803Snicm current++; 56024a42803Snicm } 561367b65a4Snicm 56224a42803Snicm if (current < visible) { 56324a42803Snicm start = 0; 56424a42803Snicm end = visible; 56524a42803Snicm } else if (current >= total - visible) { 56624a42803Snicm start = total - visible; 56724a42803Snicm end = total; 56824a42803Snicm } else { 56924a42803Snicm start = current - (visible / 2); 57024a42803Snicm end = start + visible; 57124a42803Snicm } 57224a42803Snicm 57305d44586Snicm if (data->offset < -(int)start) 57405d44586Snicm data->offset = -(int)start; 57505d44586Snicm if (data->offset > (int)(total - end)) 57605d44586Snicm data->offset = (int)(total - end); 57705d44586Snicm start += data->offset; 57805d44586Snicm end += data->offset; 57905d44586Snicm 58024a42803Snicm left = (start != 0); 58124a42803Snicm right = (end != total); 58224a42803Snicm if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) 58324a42803Snicm left = right = 0; 58424a42803Snicm if (left && right) { 58524a42803Snicm each = (sx - 6) / visible; 58624a42803Snicm remaining = (sx - 6) - (visible * each); 58724a42803Snicm } else if (left || right) { 58824a42803Snicm each = (sx - 3) / visible; 58924a42803Snicm remaining = (sx - 3) - (visible * each); 59024a42803Snicm } else { 59124a42803Snicm each = sx / visible; 59224a42803Snicm remaining = sx - (visible * each); 59324a42803Snicm } 594c224fd4bSnicm if (each == 0) 595367b65a4Snicm return; 596367b65a4Snicm 59724a42803Snicm if (left) { 59863c9949dSnicm data->left = cx + 2; 5995c6c7001Snicm screen_write_cursormove(ctx, cx + 2, cy, 0); 60024a42803Snicm screen_write_vline(ctx, sy, 0, 0); 6015c6c7001Snicm screen_write_cursormove(ctx, cx, cy + sy / 2, 0); 60224a42803Snicm screen_write_puts(ctx, &grid_default_cell, "<"); 60363c9949dSnicm } else 60463c9949dSnicm data->left = -1; 60524a42803Snicm if (right) { 60663c9949dSnicm data->right = cx + sx - 3; 6075c6c7001Snicm screen_write_cursormove(ctx, cx + sx - 3, cy, 0); 60824a42803Snicm screen_write_vline(ctx, sy, 0, 0); 6095c6c7001Snicm screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0); 61024a42803Snicm screen_write_puts(ctx, &grid_default_cell, ">"); 61163c9949dSnicm } else 61263c9949dSnicm data->right = -1; 61363c9949dSnicm 61463c9949dSnicm data->start = start; 61563c9949dSnicm data->end = end; 61663c9949dSnicm data->each = each; 61724a42803Snicm 61824a42803Snicm i = loop = 0; 61924a42803Snicm RB_FOREACH(wl, winlinks, &s->windows) { 62024a42803Snicm if (loop == end) 62124a42803Snicm break; 62224a42803Snicm if (loop < start) { 62324a42803Snicm loop++; 62424a42803Snicm continue; 62524a42803Snicm } 62624a42803Snicm w = wl->window; 62724a42803Snicm 628367b65a4Snicm if (wl == s->curw) 629367b65a4Snicm gc.fg = active_colour; 630367b65a4Snicm else 631367b65a4Snicm gc.fg = colour; 63224a42803Snicm 63324a42803Snicm if (left) 63424a42803Snicm offset = 3 + (i * each); 63524a42803Snicm else 63624a42803Snicm offset = (i * each); 63724a42803Snicm if (loop == end - 1) 63803471407Snicm width = each + remaining; 639367b65a4Snicm else 640367b65a4Snicm width = each - 1; 641367b65a4Snicm 6425c6c7001Snicm screen_write_cursormove(ctx, cx + offset, cy, 0); 643367b65a4Snicm screen_write_preview(ctx, &w->active->base, width, sy); 644367b65a4Snicm 645367b65a4Snicm xasprintf(&label, " %u:%s ", wl->idx, w->name); 646367b65a4Snicm if (strlen(label) > width) 647367b65a4Snicm xasprintf(&label, " %u ", wl->idx); 6482b7e51f7Snicm window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc, 6492b7e51f7Snicm label); 650367b65a4Snicm free(label); 651367b65a4Snicm 65224a42803Snicm if (loop != end - 1) { 6535c6c7001Snicm screen_write_cursormove(ctx, cx + offset + width, cy, 0); 654367b65a4Snicm screen_write_vline(ctx, sy, 0, 0); 655367b65a4Snicm } 65624a42803Snicm loop++; 65724a42803Snicm 65824a42803Snicm i++; 659367b65a4Snicm } 660367b65a4Snicm } 661367b65a4Snicm 662367b65a4Snicm static void 66305d44586Snicm window_tree_draw_window(struct window_tree_modedata *data, struct session *s, 66405d44586Snicm struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy) 665367b65a4Snicm { 666367b65a4Snicm struct options *oo = s->options; 667367b65a4Snicm struct window_pane *wp; 6682b7e51f7Snicm u_int cx = ctx->s->cx, cy = ctx->s->cy; 66924a42803Snicm u_int loop, total, visible, each, width, offset; 67024a42803Snicm u_int current, start, end, remaining, i; 671367b65a4Snicm struct grid_cell gc; 6728807a41eSnicm int colour, active_colour, left, right, pane_idx; 673367b65a4Snicm char *label; 674367b65a4Snicm 67524a42803Snicm total = window_count_panes(w); 676367b65a4Snicm 677367b65a4Snicm memcpy(&gc, &grid_default_cell, sizeof gc); 678367b65a4Snicm colour = options_get_number(oo, "display-panes-colour"); 679367b65a4Snicm active_colour = options_get_number(oo, "display-panes-active-colour"); 680367b65a4Snicm 68124a42803Snicm if (sx / total < 24) { 68224a42803Snicm visible = sx / 24; 68324a42803Snicm if (visible == 0) 68424a42803Snicm visible = 1; 68524a42803Snicm } else 68624a42803Snicm visible = total; 687367b65a4Snicm 68824a42803Snicm current = 0; 68924a42803Snicm TAILQ_FOREACH(wp, &w->panes, entry) { 69024a42803Snicm if (wp == w->active) 69124a42803Snicm break; 69224a42803Snicm current++; 69324a42803Snicm } 694367b65a4Snicm 69524a42803Snicm if (current < visible) { 69624a42803Snicm start = 0; 69724a42803Snicm end = visible; 69824a42803Snicm } else if (current >= total - visible) { 69924a42803Snicm start = total - visible; 70024a42803Snicm end = total; 70124a42803Snicm } else { 70224a42803Snicm start = current - (visible / 2); 70324a42803Snicm end = start + visible; 70424a42803Snicm } 70524a42803Snicm 70605d44586Snicm if (data->offset < -(int)start) 70705d44586Snicm data->offset = -(int)start; 70805d44586Snicm if (data->offset > (int)(total - end)) 70905d44586Snicm data->offset = (int)(total - end); 71005d44586Snicm start += data->offset; 71105d44586Snicm end += data->offset; 71205d44586Snicm 71324a42803Snicm left = (start != 0); 71424a42803Snicm right = (end != total); 71524a42803Snicm if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) 71624a42803Snicm left = right = 0; 71724a42803Snicm if (left && right) { 71824a42803Snicm each = (sx - 6) / visible; 71924a42803Snicm remaining = (sx - 6) - (visible * each); 72024a42803Snicm } else if (left || right) { 72124a42803Snicm each = (sx - 3) / visible; 72224a42803Snicm remaining = (sx - 3) - (visible * each); 72324a42803Snicm } else { 72424a42803Snicm each = sx / visible; 72524a42803Snicm remaining = sx - (visible * each); 72624a42803Snicm } 727c224fd4bSnicm if (each == 0) 728367b65a4Snicm return; 729367b65a4Snicm 73024a42803Snicm if (left) { 73163c9949dSnicm data->left = cx + 2; 7325c6c7001Snicm screen_write_cursormove(ctx, cx + 2, cy, 0); 73324a42803Snicm screen_write_vline(ctx, sy, 0, 0); 7345c6c7001Snicm screen_write_cursormove(ctx, cx, cy + sy / 2, 0); 73524a42803Snicm screen_write_puts(ctx, &grid_default_cell, "<"); 73663c9949dSnicm } else 73763c9949dSnicm data->left = -1; 73824a42803Snicm if (right) { 73963c9949dSnicm data->right = cx + sx - 3; 7405c6c7001Snicm screen_write_cursormove(ctx, cx + sx - 3, cy, 0); 74124a42803Snicm screen_write_vline(ctx, sy, 0, 0); 7425c6c7001Snicm screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0); 74324a42803Snicm screen_write_puts(ctx, &grid_default_cell, ">"); 74463c9949dSnicm } else 74563c9949dSnicm data->right = -1; 74663c9949dSnicm 74763c9949dSnicm data->start = start; 74863c9949dSnicm data->end = end; 74963c9949dSnicm data->each = each; 75024a42803Snicm 75124a42803Snicm i = loop = 0; 75224a42803Snicm TAILQ_FOREACH(wp, &w->panes, entry) { 75324a42803Snicm if (loop == end) 75424a42803Snicm break; 75524a42803Snicm if (loop < start) { 75624a42803Snicm loop++; 75724a42803Snicm continue; 75824a42803Snicm } 75924a42803Snicm 760367b65a4Snicm if (wp == w->active) 761367b65a4Snicm gc.fg = active_colour; 762367b65a4Snicm else 763367b65a4Snicm gc.fg = colour; 76424a42803Snicm 76524a42803Snicm if (left) 76624a42803Snicm offset = 3 + (i * each); 76724a42803Snicm else 76824a42803Snicm offset = (i * each); 76924a42803Snicm if (loop == end - 1) 77003471407Snicm width = each + remaining; 771367b65a4Snicm else 772367b65a4Snicm width = each - 1; 773367b65a4Snicm 7745c6c7001Snicm screen_write_cursormove(ctx, cx + offset, cy, 0); 775367b65a4Snicm screen_write_preview(ctx, &wp->base, width, sy); 776367b65a4Snicm 7778807a41eSnicm if (window_pane_index(wp, &pane_idx) != 0) 7788807a41eSnicm pane_idx = loop; 7798807a41eSnicm xasprintf(&label, " %u ", pane_idx); 7802b7e51f7Snicm window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc, 7812b7e51f7Snicm label); 782367b65a4Snicm free(label); 783367b65a4Snicm 78424a42803Snicm if (loop != end - 1) { 7855c6c7001Snicm screen_write_cursormove(ctx, cx + offset + width, cy, 0); 786367b65a4Snicm screen_write_vline(ctx, sy, 0, 0); 787367b65a4Snicm } 78824a42803Snicm loop++; 78924a42803Snicm 79024a42803Snicm i++; 791367b65a4Snicm } 792367b65a4Snicm } 793367b65a4Snicm 7942b7e51f7Snicm static void 7952b7e51f7Snicm window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, 7962b7e51f7Snicm u_int sx, u_int sy) 797a42faf7dSnicm { 798a42faf7dSnicm struct window_tree_itemdata *item = itemdata; 799a42faf7dSnicm struct session *sp; 800a42faf7dSnicm struct winlink *wlp; 801a42faf7dSnicm struct window_pane *wp; 802a42faf7dSnicm 803a42faf7dSnicm window_tree_pull_item(item, &sp, &wlp, &wp); 804a42faf7dSnicm if (wp == NULL) 8052b7e51f7Snicm return; 806a42faf7dSnicm 807367b65a4Snicm switch (item->type) { 808367b65a4Snicm case WINDOW_TREE_NONE: 8092b7e51f7Snicm break; 810367b65a4Snicm case WINDOW_TREE_SESSION: 8112b7e51f7Snicm window_tree_draw_session(modedata, sp, ctx, sx, sy); 812367b65a4Snicm break; 813367b65a4Snicm case WINDOW_TREE_WINDOW: 8142b7e51f7Snicm window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy); 815367b65a4Snicm break; 816367b65a4Snicm case WINDOW_TREE_PANE: 8172b7e51f7Snicm screen_write_preview(ctx, &wp->base, sx, sy); 818367b65a4Snicm break; 819367b65a4Snicm } 820a42faf7dSnicm } 821a42faf7dSnicm 822943a08b1Snicm static int 823943a08b1Snicm window_tree_search(__unused void *modedata, void *itemdata, const char *ss) 824943a08b1Snicm { 825943a08b1Snicm struct window_tree_itemdata *item = itemdata; 826943a08b1Snicm struct session *s; 827943a08b1Snicm struct winlink *wl; 828943a08b1Snicm struct window_pane *wp; 8290a4d943dSnicm char *cmd; 8300a4d943dSnicm int retval; 831943a08b1Snicm 832943a08b1Snicm window_tree_pull_item(item, &s, &wl, &wp); 833943a08b1Snicm 834943a08b1Snicm switch (item->type) { 835943a08b1Snicm case WINDOW_TREE_NONE: 836943a08b1Snicm return (0); 837943a08b1Snicm case WINDOW_TREE_SESSION: 838943a08b1Snicm if (s == NULL) 839943a08b1Snicm return (0); 840943a08b1Snicm return (strstr(s->name, ss) != NULL); 841943a08b1Snicm case WINDOW_TREE_WINDOW: 842943a08b1Snicm if (s == NULL || wl == NULL) 843943a08b1Snicm return (0); 844943a08b1Snicm return (strstr(wl->window->name, ss) != NULL); 845943a08b1Snicm case WINDOW_TREE_PANE: 846943a08b1Snicm if (s == NULL || wl == NULL || wp == NULL) 847943a08b1Snicm break; 848943a08b1Snicm cmd = get_proc_name(wp->fd, wp->tty); 849943a08b1Snicm if (cmd == NULL || *cmd == '\0') 850943a08b1Snicm return (0); 8510a4d943dSnicm retval = (strstr(cmd, ss) != NULL); 8520a4d943dSnicm free(cmd); 853acf6cf7cSnicm return (retval); 854943a08b1Snicm } 855943a08b1Snicm return (0); 856943a08b1Snicm } 857943a08b1Snicm 858f43bc87cSnicm static void 859f43bc87cSnicm window_tree_menu(void *modedata, struct client *c, key_code key) 860f43bc87cSnicm { 861f43bc87cSnicm struct window_tree_modedata *data = modedata; 862f43bc87cSnicm struct window_pane *wp = data->wp; 863f43bc87cSnicm struct window_mode_entry *wme; 864f43bc87cSnicm 865f43bc87cSnicm wme = TAILQ_FIRST(&wp->modes); 866f43bc87cSnicm if (wme == NULL || wme->data != modedata) 867f43bc87cSnicm return; 868f43bc87cSnicm window_tree_key(wme, c, NULL, NULL, key, NULL); 869f43bc87cSnicm } 870f43bc87cSnicm 871*438eed14Snicm static key_code 872*438eed14Snicm window_tree_get_key(void *modedata, void *itemdata, u_int line) 873*438eed14Snicm { 874*438eed14Snicm struct window_tree_modedata *data = modedata; 875*438eed14Snicm struct window_tree_itemdata *item = itemdata; 876*438eed14Snicm struct format_tree *ft; 877*438eed14Snicm struct session *s; 878*438eed14Snicm struct winlink *wl; 879*438eed14Snicm struct window_pane *wp; 880*438eed14Snicm char *expanded; 881*438eed14Snicm key_code key; 882*438eed14Snicm 883*438eed14Snicm ft = format_create(NULL, NULL, FORMAT_NONE, 0); 884*438eed14Snicm window_tree_pull_item(item, &s, &wl, &wp); 885*438eed14Snicm if (item->type == WINDOW_TREE_SESSION) 886*438eed14Snicm format_defaults(ft, NULL, s, NULL, NULL); 887*438eed14Snicm else if (item->type == WINDOW_TREE_WINDOW) 888*438eed14Snicm format_defaults(ft, NULL, s, wl, NULL); 889*438eed14Snicm else 890*438eed14Snicm format_defaults(ft, NULL, s, wl, wp); 891*438eed14Snicm format_add(ft, "line", "%u", line); 892*438eed14Snicm 893*438eed14Snicm expanded = format_expand(ft, data->key_format); 894*438eed14Snicm key = key_string_lookup_string(expanded); 895*438eed14Snicm free(expanded); 896*438eed14Snicm format_free(ft); 897*438eed14Snicm return key; 898*438eed14Snicm } 899*438eed14Snicm 900a42faf7dSnicm static struct screen * 90130a94f45Snicm window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, 902a42faf7dSnicm struct args *args) 903a42faf7dSnicm { 90430a94f45Snicm struct window_pane *wp = wme->wp; 905a42faf7dSnicm struct window_tree_modedata *data; 906a42faf7dSnicm struct screen *s; 907a42faf7dSnicm 90830a94f45Snicm wme->data = data = xcalloc(1, sizeof *data); 909f43bc87cSnicm data->wp = wp; 910f43bc87cSnicm data->references = 1; 911a42faf7dSnicm 912a42faf7dSnicm if (args_has(args, 's')) 913a42faf7dSnicm data->type = WINDOW_TREE_SESSION; 914a42faf7dSnicm else if (args_has(args, 'w')) 915a42faf7dSnicm data->type = WINDOW_TREE_WINDOW; 916a42faf7dSnicm else 917a42faf7dSnicm data->type = WINDOW_TREE_PANE; 918a42faf7dSnicm memcpy(&data->fs, fs, sizeof data->fs); 919a42faf7dSnicm 920bf38e336Snicm if (args == NULL || !args_has(args, 'F')) 921bf38e336Snicm data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT); 922bf38e336Snicm else 923bf38e336Snicm data->format = xstrdup(args_get(args, 'F')); 924*438eed14Snicm if (args == NULL || !args_has(args, 'K')) 925*438eed14Snicm data->key_format = xstrdup(WINDOW_TREE_DEFAULT_KEY_FORMAT); 926*438eed14Snicm else 927*438eed14Snicm data->key_format = xstrdup(args_get(args, 'K')); 928a42faf7dSnicm if (args == NULL || args->argc == 0) 929a42faf7dSnicm data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND); 930a42faf7dSnicm else 931a42faf7dSnicm data->command = xstrdup(args->argv[0]); 932988c4c87Snicm data->squash_groups = !args_has(args, 'G'); 933a42faf7dSnicm 934b38aa712Snicm data->data = mode_tree_start(wp, args, window_tree_build, 935*438eed14Snicm window_tree_draw, window_tree_search, window_tree_menu, NULL, 936*438eed14Snicm window_tree_get_key, data, window_tree_menu_items, 937*438eed14Snicm window_tree_sort_list, nitems(window_tree_sort_list), &s); 9384f5e4c93Snicm mode_tree_zoom(data->data, args); 939a42faf7dSnicm 940a42faf7dSnicm mode_tree_build(data->data); 941a42faf7dSnicm mode_tree_draw(data->data); 942a42faf7dSnicm 943a42faf7dSnicm data->type = WINDOW_TREE_NONE; 944a42faf7dSnicm 945a42faf7dSnicm return (s); 946a42faf7dSnicm } 947a42faf7dSnicm 948a42faf7dSnicm static void 949a42faf7dSnicm window_tree_destroy(struct window_tree_modedata *data) 950a42faf7dSnicm { 951a42faf7dSnicm u_int i; 952a42faf7dSnicm 953a42faf7dSnicm if (--data->references != 0) 954a42faf7dSnicm return; 955a42faf7dSnicm 956a42faf7dSnicm for (i = 0; i < data->item_size; i++) 957a42faf7dSnicm window_tree_free_item(data->item_list[i]); 958a42faf7dSnicm free(data->item_list); 959a42faf7dSnicm 960bf38e336Snicm free(data->format); 961*438eed14Snicm free(data->key_format); 962a42faf7dSnicm free(data->command); 963bf38e336Snicm 964a42faf7dSnicm free(data); 965a42faf7dSnicm } 966a42faf7dSnicm 967a42faf7dSnicm static void 96830a94f45Snicm window_tree_free(struct window_mode_entry *wme) 969a42faf7dSnicm { 97030a94f45Snicm struct window_tree_modedata *data = wme->data; 971a42faf7dSnicm 972a42faf7dSnicm if (data == NULL) 973a42faf7dSnicm return; 974a42faf7dSnicm 975a42faf7dSnicm data->dead = 1; 976d0ca3a30Snicm mode_tree_free(data->data); 977a42faf7dSnicm window_tree_destroy(data); 978a42faf7dSnicm } 979a42faf7dSnicm 980a42faf7dSnicm static void 98130a94f45Snicm window_tree_resize(struct window_mode_entry *wme, u_int sx, u_int sy) 982a42faf7dSnicm { 98330a94f45Snicm struct window_tree_modedata *data = wme->data; 984a42faf7dSnicm 985a42faf7dSnicm mode_tree_resize(data->data, sx, sy); 986a42faf7dSnicm } 987a42faf7dSnicm 988734f37e4Snicm static void 989734f37e4Snicm window_tree_update(struct window_mode_entry *wme) 990734f37e4Snicm { 991734f37e4Snicm struct window_tree_modedata *data = wme->data; 992734f37e4Snicm 993734f37e4Snicm mode_tree_build(data->data); 994734f37e4Snicm mode_tree_draw(data->data); 995734f37e4Snicm data->wp->flags |= PANE_REDRAW; 996734f37e4Snicm } 997734f37e4Snicm 998a42faf7dSnicm static char * 999a42faf7dSnicm window_tree_get_target(struct window_tree_itemdata *item, 1000a42faf7dSnicm struct cmd_find_state *fs) 1001a42faf7dSnicm { 1002a42faf7dSnicm struct session *s; 1003a42faf7dSnicm struct winlink *wl; 1004a42faf7dSnicm struct window_pane *wp; 1005a42faf7dSnicm char *target; 1006a42faf7dSnicm 1007a42faf7dSnicm window_tree_pull_item(item, &s, &wl, &wp); 1008a42faf7dSnicm 1009a42faf7dSnicm target = NULL; 1010a42faf7dSnicm switch (item->type) { 1011a42faf7dSnicm case WINDOW_TREE_NONE: 1012a42faf7dSnicm break; 1013a42faf7dSnicm case WINDOW_TREE_SESSION: 1014a42faf7dSnicm if (s == NULL) 1015a42faf7dSnicm break; 1016a42faf7dSnicm xasprintf(&target, "=%s:", s->name); 1017a42faf7dSnicm break; 1018a42faf7dSnicm case WINDOW_TREE_WINDOW: 1019a42faf7dSnicm if (s == NULL || wl == NULL) 1020a42faf7dSnicm break; 1021a42faf7dSnicm xasprintf(&target, "=%s:%u.", s->name, wl->idx); 1022a42faf7dSnicm break; 1023a42faf7dSnicm case WINDOW_TREE_PANE: 1024a42faf7dSnicm if (s == NULL || wl == NULL || wp == NULL) 1025a42faf7dSnicm break; 1026a42faf7dSnicm xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id); 1027a42faf7dSnicm break; 1028a42faf7dSnicm } 1029a42faf7dSnicm if (target == NULL) 1030a42faf7dSnicm cmd_find_clear_state(fs, 0); 1031a42faf7dSnicm else 10320772530eSnicm cmd_find_from_winlink_pane(fs, wl, wp, 0); 1033a42faf7dSnicm return (target); 1034a42faf7dSnicm } 1035a42faf7dSnicm 1036a42faf7dSnicm static void 1037d7af2c28Snicm window_tree_command_each(void *modedata, void *itemdata, struct client *c, 1038d7af2c28Snicm __unused key_code key) 1039a42faf7dSnicm { 1040a42faf7dSnicm struct window_tree_modedata *data = modedata; 1041a42faf7dSnicm struct window_tree_itemdata *item = itemdata; 1042a42faf7dSnicm char *name; 1043a42faf7dSnicm struct cmd_find_state fs; 1044a42faf7dSnicm 1045a42faf7dSnicm name = window_tree_get_target(item, &fs); 1046a42faf7dSnicm if (name != NULL) 1047d7af2c28Snicm mode_tree_run_command(c, &fs, data->entered, name); 1048a42faf7dSnicm free(name); 1049a42faf7dSnicm } 1050a42faf7dSnicm 1051a42faf7dSnicm static enum cmd_retval 1052a42faf7dSnicm window_tree_command_done(__unused struct cmdq_item *item, void *modedata) 1053a42faf7dSnicm { 1054a42faf7dSnicm struct window_tree_modedata *data = modedata; 1055a42faf7dSnicm 1056a42faf7dSnicm if (!data->dead) { 1057a42faf7dSnicm mode_tree_build(data->data); 1058a42faf7dSnicm mode_tree_draw(data->data); 1059a42faf7dSnicm data->wp->flags |= PANE_REDRAW; 1060a42faf7dSnicm } 1061a42faf7dSnicm window_tree_destroy(data); 1062a42faf7dSnicm return (CMD_RETURN_NORMAL); 1063a42faf7dSnicm } 1064a42faf7dSnicm 1065a42faf7dSnicm static int 1066a42faf7dSnicm window_tree_command_callback(struct client *c, void *modedata, const char *s, 1067a42faf7dSnicm __unused int done) 1068a42faf7dSnicm { 1069a42faf7dSnicm struct window_tree_modedata *data = modedata; 1070a42faf7dSnicm 1071d0ca3a30Snicm if (s == NULL || *s == '\0' || data->dead) 1072a42faf7dSnicm return (0); 1073a42faf7dSnicm 1074a42faf7dSnicm data->entered = s; 1075d7af2c28Snicm mode_tree_each_tagged(data->data, window_tree_command_each, c, 1076d7af2c28Snicm KEYC_NONE, 1); 1077a42faf7dSnicm data->entered = NULL; 1078a42faf7dSnicm 1079a42faf7dSnicm data->references++; 1080a42faf7dSnicm cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1081a42faf7dSnicm 1082a42faf7dSnicm return (0); 1083a42faf7dSnicm } 1084a42faf7dSnicm 1085a42faf7dSnicm static void 1086a42faf7dSnicm window_tree_command_free(void *modedata) 1087a42faf7dSnicm { 1088a42faf7dSnicm struct window_tree_modedata *data = modedata; 1089a42faf7dSnicm 1090a42faf7dSnicm window_tree_destroy(data); 1091a42faf7dSnicm } 1092a42faf7dSnicm 1093d0ca3a30Snicm static void 1094d0ca3a30Snicm window_tree_kill_each(__unused void *modedata, void *itemdata, 1095d0ca3a30Snicm __unused struct client *c, __unused key_code key) 1096d0ca3a30Snicm { 1097d0ca3a30Snicm struct window_tree_itemdata *item = itemdata; 1098d0ca3a30Snicm struct session *s; 1099d0ca3a30Snicm struct winlink *wl; 1100d0ca3a30Snicm struct window_pane *wp; 1101d0ca3a30Snicm 1102d0ca3a30Snicm window_tree_pull_item(item, &s, &wl, &wp); 1103d0ca3a30Snicm 1104d0ca3a30Snicm switch (item->type) { 1105d0ca3a30Snicm case WINDOW_TREE_NONE: 1106d0ca3a30Snicm break; 1107d0ca3a30Snicm case WINDOW_TREE_SESSION: 1108d0ca3a30Snicm if (s != NULL) { 1109d0ca3a30Snicm server_destroy_session(s); 1110c26c4f79Snicm session_destroy(s, 1, __func__); 1111d0ca3a30Snicm } 1112d0ca3a30Snicm break; 1113d0ca3a30Snicm case WINDOW_TREE_WINDOW: 1114d0ca3a30Snicm if (wl != NULL) 1115e8b59d3cSnicm server_kill_window(wl->window, 0); 1116d0ca3a30Snicm break; 1117d0ca3a30Snicm case WINDOW_TREE_PANE: 1118d0ca3a30Snicm if (wp != NULL) 1119d0ca3a30Snicm server_kill_pane(wp); 1120d0ca3a30Snicm break; 1121d0ca3a30Snicm } 1122d0ca3a30Snicm } 1123d0ca3a30Snicm 1124d0ca3a30Snicm static int 1125d0ca3a30Snicm window_tree_kill_current_callback(struct client *c, void *modedata, 1126d0ca3a30Snicm const char *s, __unused int done) 1127d0ca3a30Snicm { 1128d0ca3a30Snicm struct window_tree_modedata *data = modedata; 1129d0ca3a30Snicm struct mode_tree_data *mtd = data->data; 1130d0ca3a30Snicm 1131d0ca3a30Snicm if (s == NULL || *s == '\0' || data->dead) 1132d0ca3a30Snicm return (0); 1133d0ca3a30Snicm if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') 1134d0ca3a30Snicm return (0); 1135d0ca3a30Snicm 1136d0ca3a30Snicm window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE); 1137e8b59d3cSnicm server_renumber_all(); 1138d0ca3a30Snicm 1139d0ca3a30Snicm data->references++; 1140d0ca3a30Snicm cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1141d0ca3a30Snicm 1142d0ca3a30Snicm return (0); 1143d0ca3a30Snicm } 1144d0ca3a30Snicm 1145d0ca3a30Snicm static int 1146d0ca3a30Snicm window_tree_kill_tagged_callback(struct client *c, void *modedata, 1147d0ca3a30Snicm const char *s, __unused int done) 1148d0ca3a30Snicm { 1149d0ca3a30Snicm struct window_tree_modedata *data = modedata; 1150d0ca3a30Snicm struct mode_tree_data *mtd = data->data; 1151d0ca3a30Snicm 1152d0ca3a30Snicm if (s == NULL || *s == '\0' || data->dead) 1153d0ca3a30Snicm return (0); 1154d0ca3a30Snicm if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') 1155d0ca3a30Snicm return (0); 1156d0ca3a30Snicm 1157d0ca3a30Snicm mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1); 1158e8b59d3cSnicm server_renumber_all(); 1159d0ca3a30Snicm 1160d0ca3a30Snicm data->references++; 1161d0ca3a30Snicm cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1162d0ca3a30Snicm 1163d0ca3a30Snicm return (0); 1164d0ca3a30Snicm } 1165d0ca3a30Snicm 116663c9949dSnicm static key_code 116763c9949dSnicm window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x, 116863c9949dSnicm struct window_tree_itemdata *item) 116963c9949dSnicm { 117063c9949dSnicm struct session *s; 117163c9949dSnicm struct winlink *wl; 117263c9949dSnicm struct window_pane *wp; 117363c9949dSnicm u_int loop; 117463c9949dSnicm 117563c9949dSnicm if (key != KEYC_MOUSEDOWN1_PANE) 117663c9949dSnicm return (KEYC_NONE); 117763c9949dSnicm 117863c9949dSnicm if (data->left != -1 && x <= (u_int)data->left) 117963c9949dSnicm return ('<'); 118063c9949dSnicm if (data->right != -1 && x >= (u_int)data->right) 118163c9949dSnicm return ('>'); 118263c9949dSnicm 118363c9949dSnicm if (data->left != -1) 118463c9949dSnicm x -= data->left; 118563c9949dSnicm else if (x != 0) 118663c9949dSnicm x--; 118763c9949dSnicm if (x == 0 || data->end == 0) 118863c9949dSnicm x = 0; 118963c9949dSnicm else { 119063c9949dSnicm x = x / data->each; 119163c9949dSnicm if (data->start + x >= data->end) 119263c9949dSnicm x = data->end - 1; 119363c9949dSnicm } 119463c9949dSnicm 119563c9949dSnicm window_tree_pull_item(item, &s, &wl, &wp); 119663c9949dSnicm if (item->type == WINDOW_TREE_SESSION) { 119763c9949dSnicm if (s == NULL) 119863c9949dSnicm return (KEYC_NONE); 119963c9949dSnicm mode_tree_expand_current(data->data); 120063c9949dSnicm loop = 0; 120163c9949dSnicm RB_FOREACH(wl, winlinks, &s->windows) { 120263c9949dSnicm if (loop == data->start + x) 120363c9949dSnicm break; 120463c9949dSnicm loop++; 120563c9949dSnicm } 120663c9949dSnicm if (wl != NULL) 120763c9949dSnicm mode_tree_set_current(data->data, (uint64_t)wl); 120863c9949dSnicm return ('\r'); 120963c9949dSnicm } 121063c9949dSnicm if (item->type == WINDOW_TREE_WINDOW) { 121163c9949dSnicm if (wl == NULL) 121263c9949dSnicm return (KEYC_NONE); 121363c9949dSnicm mode_tree_expand_current(data->data); 121463c9949dSnicm loop = 0; 121563c9949dSnicm TAILQ_FOREACH(wp, &wl->window->panes, entry) { 121663c9949dSnicm if (loop == data->start + x) 121763c9949dSnicm break; 121863c9949dSnicm loop++; 121963c9949dSnicm } 122063c9949dSnicm if (wp != NULL) 122163c9949dSnicm mode_tree_set_current(data->data, (uint64_t)wp); 122263c9949dSnicm return ('\r'); 122363c9949dSnicm } 122463c9949dSnicm return (KEYC_NONE); 122563c9949dSnicm } 122663c9949dSnicm 1227a42faf7dSnicm static void 122830a94f45Snicm window_tree_key(struct window_mode_entry *wme, struct client *c, 1229bf52409eSnicm __unused struct session *s, __unused struct winlink *wl, key_code key, 1230bf52409eSnicm struct mouse_event *m) 1231a42faf7dSnicm { 123230a94f45Snicm struct window_pane *wp = wme->wp; 123330a94f45Snicm struct window_tree_modedata *data = wme->data; 123463c9949dSnicm struct window_tree_itemdata *item, *new_item; 1235d0ca3a30Snicm char *name, *prompt = NULL; 12367d0fd907Snicm struct cmd_find_state fs, *fsp = &data->fs; 1237a42faf7dSnicm int finished; 1238d0ca3a30Snicm u_int tagged, x, y, idx; 1239d0ca3a30Snicm struct session *ns; 1240d0ca3a30Snicm struct winlink *nwl; 1241d0ca3a30Snicm struct window_pane *nwp; 1242a42faf7dSnicm 124305d44586Snicm item = mode_tree_get_current(data->data); 124463c9949dSnicm finished = mode_tree_key(data->data, c, &key, m, &x, &y); 124563c9949dSnicm if (item != (new_item = mode_tree_get_current(data->data))) { 124663c9949dSnicm item = new_item; 124705d44586Snicm data->offset = 0; 124863c9949dSnicm } 1249f43bc87cSnicm if (KEYC_IS_MOUSE(key) && m != NULL) 125063c9949dSnicm key = window_tree_mouse(data, key, x, item); 1251a42faf7dSnicm switch (key) { 125205d44586Snicm case '<': 125305d44586Snicm data->offset--; 125405d44586Snicm break; 125505d44586Snicm case '>': 125605d44586Snicm data->offset++; 125705d44586Snicm break; 12587d0fd907Snicm case 'H': 12597d0fd907Snicm mode_tree_expand(data->data, (uint64_t)fsp->s); 12607d0fd907Snicm mode_tree_expand(data->data, (uint64_t)fsp->wl); 12617d0fd907Snicm if (!mode_tree_set_current(data->data, (uint64_t)wme->wp)) 12627d0fd907Snicm mode_tree_set_current(data->data, (uint64_t)fsp->wl); 12637d0fd907Snicm break; 12647d0fd907Snicm case 'm': 12657d0fd907Snicm window_tree_pull_item(item, &ns, &nwl, &nwp); 12667d0fd907Snicm server_set_marked(ns, nwl, nwp); 12677d0fd907Snicm mode_tree_build(data->data); 12687d0fd907Snicm break; 12697d0fd907Snicm case 'M': 12707d0fd907Snicm server_clear_marked(); 12717d0fd907Snicm mode_tree_build(data->data); 12727d0fd907Snicm break; 1273d0ca3a30Snicm case 'x': 1274d0ca3a30Snicm window_tree_pull_item(item, &ns, &nwl, &nwp); 1275d0ca3a30Snicm switch (item->type) { 1276d0ca3a30Snicm case WINDOW_TREE_NONE: 1277d0ca3a30Snicm break; 1278d0ca3a30Snicm case WINDOW_TREE_SESSION: 1279d0ca3a30Snicm if (ns == NULL) 1280d0ca3a30Snicm break; 1281d0ca3a30Snicm xasprintf(&prompt, "Kill session %s? ", ns->name); 1282d0ca3a30Snicm break; 1283d0ca3a30Snicm case WINDOW_TREE_WINDOW: 1284d0ca3a30Snicm if (nwl == NULL) 1285d0ca3a30Snicm break; 1286d0ca3a30Snicm xasprintf(&prompt, "Kill window %u? ", nwl->idx); 1287d0ca3a30Snicm break; 1288d0ca3a30Snicm case WINDOW_TREE_PANE: 1289d0ca3a30Snicm if (nwp == NULL || window_pane_index(nwp, &idx) != 0) 1290d0ca3a30Snicm break; 1291d0ca3a30Snicm xasprintf(&prompt, "Kill pane %u? ", idx); 1292d0ca3a30Snicm break; 1293d0ca3a30Snicm } 1294d0ca3a30Snicm if (prompt == NULL) 1295d0ca3a30Snicm break; 1296d0ca3a30Snicm data->references++; 129794adf770Snicm status_prompt_set(c, NULL, prompt, "", 1298d0ca3a30Snicm window_tree_kill_current_callback, window_tree_command_free, 1299d0ca3a30Snicm data, PROMPT_SINGLE|PROMPT_NOFORMAT); 1300d0ca3a30Snicm free(prompt); 1301d0ca3a30Snicm break; 1302d0ca3a30Snicm case 'X': 1303d0ca3a30Snicm tagged = mode_tree_count_tagged(data->data); 1304d0ca3a30Snicm if (tagged == 0) 1305d0ca3a30Snicm break; 1306d0ca3a30Snicm xasprintf(&prompt, "Kill %u tagged? ", tagged); 1307d0ca3a30Snicm data->references++; 130894adf770Snicm status_prompt_set(c, NULL, prompt, "", 1309d0ca3a30Snicm window_tree_kill_tagged_callback, window_tree_command_free, 1310d0ca3a30Snicm data, PROMPT_SINGLE|PROMPT_NOFORMAT); 1311d0ca3a30Snicm free(prompt); 1312d0ca3a30Snicm break; 1313a42faf7dSnicm case ':': 1314a42faf7dSnicm tagged = mode_tree_count_tagged(data->data); 1315a42faf7dSnicm if (tagged != 0) 1316a42faf7dSnicm xasprintf(&prompt, "(%u tagged) ", tagged); 1317a42faf7dSnicm else 1318a42faf7dSnicm xasprintf(&prompt, "(current) "); 1319a42faf7dSnicm data->references++; 132094adf770Snicm status_prompt_set(c, NULL, prompt, "", 132194adf770Snicm window_tree_command_callback, window_tree_command_free, 132294adf770Snicm data, PROMPT_NOFORMAT); 1323a42faf7dSnicm free(prompt); 1324a42faf7dSnicm break; 1325a42faf7dSnicm case '\r': 1326a42faf7dSnicm name = window_tree_get_target(item, &fs); 1327a42faf7dSnicm if (name != NULL) 1328d7af2c28Snicm mode_tree_run_command(c, NULL, data->command, name); 1329d7af2c28Snicm finished = 1; 1330a42faf7dSnicm free(name); 1331d7af2c28Snicm break; 1332a42faf7dSnicm } 1333a42faf7dSnicm if (finished) 1334a42faf7dSnicm window_pane_reset_mode(wp); 1335a42faf7dSnicm else { 1336a42faf7dSnicm mode_tree_draw(data->data); 1337a42faf7dSnicm wp->flags |= PANE_REDRAW; 1338a42faf7dSnicm } 1339a42faf7dSnicm } 1340