1*ec12dec9Snicm /* $OpenBSD: window-tree.c,v 1.44 2020/01/08 06:38:55 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); 3230a94f45Snicm static void window_tree_key(struct window_mode_entry *, 33bf52409eSnicm struct client *, struct session *, 34bf52409eSnicm struct winlink *, key_code, struct mouse_event *); 35a42faf7dSnicm 36*ec12dec9Snicm #define WINDOW_TREE_DEFAULT_COMMAND "switch-client -Zt '%%'" 37a42faf7dSnicm 38bf38e336Snicm #define WINDOW_TREE_DEFAULT_FORMAT \ 39bf38e336Snicm "#{?pane_format," \ 40bf38e336Snicm "#{pane_current_command} \"#{pane_title}\"" \ 41bf38e336Snicm "," \ 42bf38e336Snicm "#{?window_format," \ 43bf38e336Snicm "#{window_name}#{window_flags} " \ 44bf38e336Snicm "(#{window_panes} panes)" \ 45bf38e336Snicm "#{?#{==:#{window_panes},1}, \"#{pane_title}\",}" \ 46bf38e336Snicm "," \ 47bf38e336Snicm "#{session_windows} windows" \ 48988c4c87Snicm "#{?session_grouped, " \ 4934f4cdc8Snicm "(group #{session_group}: " \ 5034f4cdc8Snicm "#{session_group_list})," \ 51988c4c87Snicm "}" \ 52bf38e336Snicm "#{?session_attached, (attached),}" \ 53bf38e336Snicm "}" \ 54bf38e336Snicm "}" 55bf38e336Snicm 561335341aSnicm static const struct menu_item window_tree_menu_items[] = { 571335341aSnicm { "Select", 'E', NULL }, 581335341aSnicm { "Expand", 'R', NULL }, 591335341aSnicm { "", KEYC_NONE, NULL }, 601335341aSnicm { "Tag", 't', NULL }, 611335341aSnicm { "Tag All", '\024', NULL }, 621335341aSnicm { "Tag None", 'T', NULL }, 631335341aSnicm { "", KEYC_NONE, NULL }, 641335341aSnicm { "Kill", 'x', NULL }, 651335341aSnicm { "Kill Tagged", 'X', NULL }, 661335341aSnicm { "", KEYC_NONE, NULL }, 671335341aSnicm { "Cancel", 'q', NULL }, 681335341aSnicm 691335341aSnicm { NULL, KEYC_NONE, NULL } 701335341aSnicm }; 71f43bc87cSnicm 72a42faf7dSnicm const struct window_mode window_tree_mode = { 73a42faf7dSnicm .name = "tree-mode", 7471431f24Snicm .default_format = WINDOW_TREE_DEFAULT_FORMAT, 75a42faf7dSnicm 76a42faf7dSnicm .init = window_tree_init, 77a42faf7dSnicm .free = window_tree_free, 78a42faf7dSnicm .resize = window_tree_resize, 79a42faf7dSnicm .key = window_tree_key, 80a42faf7dSnicm }; 81a42faf7dSnicm 82a42faf7dSnicm enum window_tree_sort_type { 83a42faf7dSnicm WINDOW_TREE_BY_INDEX, 84a42faf7dSnicm WINDOW_TREE_BY_NAME, 85a42faf7dSnicm WINDOW_TREE_BY_TIME, 86a42faf7dSnicm }; 87a42faf7dSnicm static const char *window_tree_sort_list[] = { 88a42faf7dSnicm "index", 89a42faf7dSnicm "name", 90a42faf7dSnicm "time" 91a42faf7dSnicm }; 923f6f9f7dSnicm static struct mode_tree_sort_criteria *window_tree_sort; 93a42faf7dSnicm 94a42faf7dSnicm enum window_tree_type { 95a42faf7dSnicm WINDOW_TREE_NONE, 96a42faf7dSnicm WINDOW_TREE_SESSION, 97a42faf7dSnicm WINDOW_TREE_WINDOW, 98a42faf7dSnicm WINDOW_TREE_PANE, 99a42faf7dSnicm }; 100a42faf7dSnicm 101a42faf7dSnicm struct window_tree_itemdata { 102a42faf7dSnicm enum window_tree_type type; 103a42faf7dSnicm int session; 104a42faf7dSnicm int winlink; 105a42faf7dSnicm int pane; 106a42faf7dSnicm }; 107a42faf7dSnicm 108a42faf7dSnicm struct window_tree_modedata { 109a42faf7dSnicm struct window_pane *wp; 110a42faf7dSnicm int dead; 111a42faf7dSnicm int references; 112a42faf7dSnicm 113a42faf7dSnicm struct mode_tree_data *data; 114bf38e336Snicm char *format; 115a42faf7dSnicm char *command; 116988c4c87Snicm int squash_groups; 117a42faf7dSnicm 118a42faf7dSnicm struct window_tree_itemdata **item_list; 119a42faf7dSnicm u_int item_size; 120a42faf7dSnicm 121a42faf7dSnicm const char *entered; 122a42faf7dSnicm 123a42faf7dSnicm struct cmd_find_state fs; 124a42faf7dSnicm enum window_tree_type type; 12505d44586Snicm 12605d44586Snicm int offset; 12763c9949dSnicm 12863c9949dSnicm int left; 12963c9949dSnicm int right; 13063c9949dSnicm u_int start; 13163c9949dSnicm u_int end; 13263c9949dSnicm u_int each; 133a42faf7dSnicm }; 134a42faf7dSnicm 135a42faf7dSnicm static void 136a42faf7dSnicm window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp, 137a42faf7dSnicm struct winlink **wlp, struct window_pane **wp) 138a42faf7dSnicm { 139a42faf7dSnicm *wp = NULL; 140a42faf7dSnicm *wlp = NULL; 141a42faf7dSnicm *sp = session_find_by_id(item->session); 142a42faf7dSnicm if (*sp == NULL) 143a42faf7dSnicm return; 144a42faf7dSnicm if (item->type == WINDOW_TREE_SESSION) { 145a42faf7dSnicm *wlp = (*sp)->curw; 146a42faf7dSnicm *wp = (*wlp)->window->active; 147a42faf7dSnicm return; 148a42faf7dSnicm } 149a42faf7dSnicm 150a42faf7dSnicm *wlp = winlink_find_by_index(&(*sp)->windows, item->winlink); 151a42faf7dSnicm if (*wlp == NULL) { 152a42faf7dSnicm *sp = NULL; 153a42faf7dSnicm return; 154a42faf7dSnicm } 155a42faf7dSnicm if (item->type == WINDOW_TREE_WINDOW) { 156a42faf7dSnicm *wp = (*wlp)->window->active; 157a42faf7dSnicm return; 158a42faf7dSnicm } 159a42faf7dSnicm 160a42faf7dSnicm *wp = window_pane_find_by_id(item->pane); 161a42faf7dSnicm if (!window_has_pane((*wlp)->window, *wp)) 162a42faf7dSnicm *wp = NULL; 163a42faf7dSnicm if (*wp == NULL) { 164a42faf7dSnicm *sp = NULL; 165a42faf7dSnicm *wlp = NULL; 166a42faf7dSnicm return; 167a42faf7dSnicm } 168a42faf7dSnicm } 169a42faf7dSnicm 170a42faf7dSnicm static struct window_tree_itemdata * 171a42faf7dSnicm window_tree_add_item(struct window_tree_modedata *data) 172a42faf7dSnicm { 173a42faf7dSnicm struct window_tree_itemdata *item; 174a42faf7dSnicm 175a42faf7dSnicm data->item_list = xreallocarray(data->item_list, data->item_size + 1, 176a42faf7dSnicm sizeof *data->item_list); 177a42faf7dSnicm item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item); 178a42faf7dSnicm return (item); 179a42faf7dSnicm } 180a42faf7dSnicm 181a42faf7dSnicm static void 182a42faf7dSnicm window_tree_free_item(struct window_tree_itemdata *item) 183a42faf7dSnicm { 184a42faf7dSnicm free(item); 185a42faf7dSnicm } 186a42faf7dSnicm 187a42faf7dSnicm static int 1883f6f9f7dSnicm window_tree_cmp_session(const void *a0, const void *b0) 189a42faf7dSnicm { 190a42faf7dSnicm const struct session *const *a = a0; 191a42faf7dSnicm const struct session *const *b = b0; 1923f6f9f7dSnicm const struct session *sa = *a; 1933f6f9f7dSnicm const struct session *sb = *b; 19476e38b2dSnicm int result = 0; 195a42faf7dSnicm 1963f6f9f7dSnicm switch (window_tree_sort->field) { 1973f6f9f7dSnicm case WINDOW_TREE_BY_INDEX: 1983f6f9f7dSnicm result = sa->id - sb->id; 1993f6f9f7dSnicm break; 2003f6f9f7dSnicm case WINDOW_TREE_BY_TIME: 2013f6f9f7dSnicm if (timercmp(&sa->activity_time, &sb->activity_time, >)) { 2023f6f9f7dSnicm result = -1; 2033f6f9f7dSnicm break; 2043f6f9f7dSnicm } 2053f6f9f7dSnicm if (timercmp(&sa->activity_time, &sb->activity_time, <)) { 2063f6f9f7dSnicm result = 1; 2073f6f9f7dSnicm break; 2083f6f9f7dSnicm } 2093f6f9f7dSnicm /* FALLTHROUGH */ 2103f6f9f7dSnicm case WINDOW_TREE_BY_NAME: 2113f6f9f7dSnicm result = strcmp(sa->name, sb->name); 2123f6f9f7dSnicm break; 2133f6f9f7dSnicm } 2143f6f9f7dSnicm 2153f6f9f7dSnicm if (window_tree_sort->reversed) 2163f6f9f7dSnicm result = -result; 2173f6f9f7dSnicm return (result); 218a42faf7dSnicm } 219a42faf7dSnicm 220a42faf7dSnicm static int 2213f6f9f7dSnicm window_tree_cmp_window(const void *a0, const void *b0) 222a42faf7dSnicm { 223a42faf7dSnicm const struct winlink *const *a = a0; 224a42faf7dSnicm const struct winlink *const *b = b0; 2253f6f9f7dSnicm const struct winlink *wla = *a; 2263f6f9f7dSnicm const struct winlink *wlb = *b; 2273f6f9f7dSnicm struct window *wa = wla->window; 2283f6f9f7dSnicm struct window *wb = wlb->window; 22976e38b2dSnicm int result = 0; 230a42faf7dSnicm 2313f6f9f7dSnicm switch (window_tree_sort->field) { 2323f6f9f7dSnicm case WINDOW_TREE_BY_INDEX: 2333f6f9f7dSnicm result = wla->idx - wlb->idx; 2343f6f9f7dSnicm break; 2353f6f9f7dSnicm case WINDOW_TREE_BY_TIME: 2363f6f9f7dSnicm if (timercmp(&wa->activity_time, &wb->activity_time, >)) { 2373f6f9f7dSnicm result = -1; 2383f6f9f7dSnicm break; 2393f6f9f7dSnicm } 2403f6f9f7dSnicm if (timercmp(&wa->activity_time, &wb->activity_time, <)) { 2413f6f9f7dSnicm result = 1; 2423f6f9f7dSnicm break; 2433f6f9f7dSnicm } 2443f6f9f7dSnicm /* FALLTHROUGH */ 2453f6f9f7dSnicm case WINDOW_TREE_BY_NAME: 2463f6f9f7dSnicm result = strcmp(wa->name, wb->name); 2473f6f9f7dSnicm break; 2483f6f9f7dSnicm } 2493f6f9f7dSnicm 2503f6f9f7dSnicm if (window_tree_sort->reversed) 2513f6f9f7dSnicm result = -result; 2523f6f9f7dSnicm return (result); 253a42faf7dSnicm } 254a42faf7dSnicm 255a42faf7dSnicm static int 2563f6f9f7dSnicm window_tree_cmp_pane(const void *a0, const void *b0) 257a42faf7dSnicm { 258a42faf7dSnicm const struct window_pane *const *a = a0; 259a42faf7dSnicm const struct window_pane *const *b = b0; 2603f6f9f7dSnicm int result; 261a42faf7dSnicm 2623f6f9f7dSnicm if (window_tree_sort->field == WINDOW_TREE_BY_TIME) 2633f6f9f7dSnicm result = (*a)->active_point - (*b)->active_point; 2643f6f9f7dSnicm else { 2653f6f9f7dSnicm /* 2663f6f9f7dSnicm * Panes don't have names, so use number order for any other 2673f6f9f7dSnicm * sort field. 2683f6f9f7dSnicm */ 2693f6f9f7dSnicm result = (*a)->id - (*b)->id; 2703f6f9f7dSnicm } 2713f6f9f7dSnicm if (window_tree_sort->reversed) 2722d71b92eSnicm result = -result; 2733f6f9f7dSnicm return (result); 274a42faf7dSnicm } 275a42faf7dSnicm 276a42faf7dSnicm static void 277a42faf7dSnicm window_tree_build_pane(struct session *s, struct winlink *wl, 278a42faf7dSnicm struct window_pane *wp, void *modedata, struct mode_tree_item *parent) 279a42faf7dSnicm { 280a42faf7dSnicm struct window_tree_modedata *data = modedata; 281a42faf7dSnicm struct window_tree_itemdata *item; 282a42faf7dSnicm char *name, *text; 283a42faf7dSnicm u_int idx; 284a42faf7dSnicm 285a42faf7dSnicm window_pane_index(wp, &idx); 286a42faf7dSnicm 287a42faf7dSnicm item = window_tree_add_item(data); 288a42faf7dSnicm item->type = WINDOW_TREE_PANE; 289a42faf7dSnicm item->session = s->id; 290a42faf7dSnicm item->winlink = wl->idx; 291a42faf7dSnicm item->pane = wp->id; 292a42faf7dSnicm 293bf38e336Snicm text = format_single(NULL, data->format, NULL, s, wl, wp); 294a42faf7dSnicm xasprintf(&name, "%u", idx); 295a42faf7dSnicm 296a42faf7dSnicm mode_tree_add(data->data, parent, item, (uint64_t)wp, name, text, -1); 297a42faf7dSnicm free(text); 298a42faf7dSnicm free(name); 299a42faf7dSnicm } 300a42faf7dSnicm 301a42faf7dSnicm static int 302f5b43402Snicm window_tree_filter_pane(struct session *s, struct winlink *wl, 303f5b43402Snicm struct window_pane *wp, const char *filter) 304f5b43402Snicm { 305f5b43402Snicm char *cp; 306f5b43402Snicm int result; 307f5b43402Snicm 308f5b43402Snicm if (filter == NULL) 309f5b43402Snicm return (1); 310f5b43402Snicm 311f5b43402Snicm cp = format_single(NULL, filter, NULL, s, wl, wp); 312f5b43402Snicm result = format_true(cp); 313f5b43402Snicm free(cp); 314f5b43402Snicm 315f5b43402Snicm return (result); 316f5b43402Snicm } 317f5b43402Snicm 318f5b43402Snicm static int 3193f6f9f7dSnicm window_tree_build_window(struct session *s, struct winlink *wl, 3203f6f9f7dSnicm void *modedata, struct mode_tree_sort_criteria *sort_crit, 3213f6f9f7dSnicm struct mode_tree_item *parent, const char *filter) 322a42faf7dSnicm { 323a42faf7dSnicm struct window_tree_modedata *data = modedata; 324a42faf7dSnicm struct window_tree_itemdata *item; 325a42faf7dSnicm struct mode_tree_item *mti; 326f5b43402Snicm char *name, *text; 327a42faf7dSnicm struct window_pane *wp, **l; 328a42faf7dSnicm u_int n, i; 329a42faf7dSnicm int expanded; 330a42faf7dSnicm 331a42faf7dSnicm item = window_tree_add_item(data); 332a42faf7dSnicm item->type = WINDOW_TREE_WINDOW; 333a42faf7dSnicm item->session = s->id; 334a42faf7dSnicm item->winlink = wl->idx; 335a42faf7dSnicm item->pane = -1; 336a42faf7dSnicm 337bf38e336Snicm text = format_single(NULL, data->format, NULL, s, wl, NULL); 338a42faf7dSnicm xasprintf(&name, "%u", wl->idx); 339a42faf7dSnicm 340a42faf7dSnicm if (data->type == WINDOW_TREE_SESSION || 341a42faf7dSnicm data->type == WINDOW_TREE_WINDOW) 342a42faf7dSnicm expanded = 0; 343a42faf7dSnicm else 344a42faf7dSnicm expanded = 1; 345a42faf7dSnicm mti = mode_tree_add(data->data, parent, item, (uint64_t)wl, name, text, 346a42faf7dSnicm expanded); 347a42faf7dSnicm free(text); 348a42faf7dSnicm free(name); 349a42faf7dSnicm 350a8b654cbSnicm if ((wp = TAILQ_FIRST(&wl->window->panes)) == NULL) 351a8b654cbSnicm goto empty; 352f5b43402Snicm if (TAILQ_NEXT(wp, entry) == NULL) { 353f5b43402Snicm if (!window_tree_filter_pane(s, wl, wp, filter)) 354f5b43402Snicm goto empty; 3552920087aSnicm return (1); 356f5b43402Snicm } 3572920087aSnicm 358a42faf7dSnicm l = NULL; 359a42faf7dSnicm n = 0; 3602920087aSnicm 361a42faf7dSnicm TAILQ_FOREACH(wp, &wl->window->panes, entry) { 362f5b43402Snicm if (!window_tree_filter_pane(s, wl, wp, filter)) 363a42faf7dSnicm continue; 364a42faf7dSnicm l = xreallocarray(l, n + 1, sizeof *l); 365a42faf7dSnicm l[n++] = wp; 366a42faf7dSnicm } 367f5b43402Snicm if (n == 0) 368f5b43402Snicm goto empty; 369a42faf7dSnicm 3703f6f9f7dSnicm window_tree_sort = sort_crit; 3713f6f9f7dSnicm qsort(l, n, sizeof *l, window_tree_cmp_pane); 372a42faf7dSnicm 373a42faf7dSnicm for (i = 0; i < n; i++) 374a42faf7dSnicm window_tree_build_pane(s, wl, l[i], modedata, mti); 375a42faf7dSnicm free(l); 376a42faf7dSnicm return (1); 377f5b43402Snicm 378f5b43402Snicm empty: 379f5b43402Snicm window_tree_free_item(item); 380f5b43402Snicm data->item_size--; 381f5b43402Snicm mode_tree_remove(data->data, mti); 382f5b43402Snicm return (0); 383a42faf7dSnicm } 384a42faf7dSnicm 385a42faf7dSnicm static void 386a42faf7dSnicm window_tree_build_session(struct session *s, void *modedata, 3873f6f9f7dSnicm struct mode_tree_sort_criteria *sort_crit, const char *filter) 388a42faf7dSnicm { 389a42faf7dSnicm struct window_tree_modedata *data = modedata; 390a42faf7dSnicm struct window_tree_itemdata *item; 391a42faf7dSnicm struct mode_tree_item *mti; 392a42faf7dSnicm char *text; 393a42faf7dSnicm struct winlink *wl, **l; 394a42faf7dSnicm u_int n, i, empty; 395a42faf7dSnicm int expanded; 396a42faf7dSnicm 397a42faf7dSnicm item = window_tree_add_item(data); 398a42faf7dSnicm item->type = WINDOW_TREE_SESSION; 399a42faf7dSnicm item->session = s->id; 400a42faf7dSnicm item->winlink = -1; 401a42faf7dSnicm item->pane = -1; 402a42faf7dSnicm 403bf38e336Snicm text = format_single(NULL, data->format, NULL, s, NULL, NULL); 404a42faf7dSnicm 405a42faf7dSnicm if (data->type == WINDOW_TREE_SESSION) 406a42faf7dSnicm expanded = 0; 407a42faf7dSnicm else 408a42faf7dSnicm expanded = 1; 409a42faf7dSnicm mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text, 410a42faf7dSnicm expanded); 411a42faf7dSnicm free(text); 412a42faf7dSnicm 413a42faf7dSnicm l = NULL; 414a42faf7dSnicm n = 0; 415a42faf7dSnicm RB_FOREACH(wl, winlinks, &s->windows) { 416a42faf7dSnicm l = xreallocarray(l, n + 1, sizeof *l); 417a42faf7dSnicm l[n++] = wl; 418a42faf7dSnicm } 4193f6f9f7dSnicm window_tree_sort = sort_crit; 4203f6f9f7dSnicm qsort(l, n, sizeof *l, window_tree_cmp_window); 421a42faf7dSnicm 422a42faf7dSnicm empty = 0; 423a42faf7dSnicm for (i = 0; i < n; i++) { 4243f6f9f7dSnicm if (!window_tree_build_window(s, l[i], modedata, sort_crit, mti, 425024c311aSnicm filter)) 426a42faf7dSnicm empty++; 427a42faf7dSnicm } 428a42faf7dSnicm if (empty == n) { 429a42faf7dSnicm window_tree_free_item(item); 430a42faf7dSnicm data->item_size--; 431a42faf7dSnicm mode_tree_remove(data->data, mti); 432a42faf7dSnicm } 433a42faf7dSnicm free(l); 434a42faf7dSnicm } 435a42faf7dSnicm 436a42faf7dSnicm static void 4373f6f9f7dSnicm window_tree_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, 4383f6f9f7dSnicm uint64_t *tag, const char *filter) 439a42faf7dSnicm { 440a42faf7dSnicm struct window_tree_modedata *data = modedata; 441a42faf7dSnicm struct session *s, **l; 442f443747aSnicm struct session_group *sg, *current; 443a42faf7dSnicm u_int n, i; 444a42faf7dSnicm 445f443747aSnicm current = session_group_contains(data->fs.s); 446f443747aSnicm 447a42faf7dSnicm for (i = 0; i < data->item_size; i++) 448a42faf7dSnicm window_tree_free_item(data->item_list[i]); 449a42faf7dSnicm free(data->item_list); 450a42faf7dSnicm data->item_list = NULL; 451a42faf7dSnicm data->item_size = 0; 452a42faf7dSnicm 453a42faf7dSnicm l = NULL; 454a42faf7dSnicm n = 0; 455a42faf7dSnicm RB_FOREACH(s, sessions, &sessions) { 456988c4c87Snicm if (data->squash_groups && 457f443747aSnicm (sg = session_group_contains(s)) != NULL) { 458f443747aSnicm if ((sg == current && s != data->fs.s) || 459f443747aSnicm (sg != current && s != TAILQ_FIRST(&sg->sessions))) 460988c4c87Snicm continue; 461f443747aSnicm } 462a42faf7dSnicm l = xreallocarray(l, n + 1, sizeof *l); 463a42faf7dSnicm l[n++] = s; 464a42faf7dSnicm } 4653f6f9f7dSnicm window_tree_sort = sort_crit; 4663f6f9f7dSnicm qsort(l, n, sizeof *l, window_tree_cmp_session); 467a42faf7dSnicm 468a42faf7dSnicm for (i = 0; i < n; i++) 4693f6f9f7dSnicm window_tree_build_session(l[i], modedata, sort_crit, filter); 470a42faf7dSnicm free(l); 471a42faf7dSnicm 472a42faf7dSnicm switch (data->type) { 473a42faf7dSnicm case WINDOW_TREE_NONE: 474a42faf7dSnicm break; 475a42faf7dSnicm case WINDOW_TREE_SESSION: 476a42faf7dSnicm *tag = (uint64_t)data->fs.s; 477a42faf7dSnicm break; 478a42faf7dSnicm case WINDOW_TREE_WINDOW: 479a42faf7dSnicm *tag = (uint64_t)data->fs.wl; 480a42faf7dSnicm break; 481a42faf7dSnicm case WINDOW_TREE_PANE: 4829e3444daSnicm if (window_count_panes(data->fs.wl->window) == 1) 4839e3444daSnicm *tag = (uint64_t)data->fs.wl; 4849e3444daSnicm else 485a42faf7dSnicm *tag = (uint64_t)data->fs.wp; 486a42faf7dSnicm break; 487a42faf7dSnicm } 488a42faf7dSnicm } 489a42faf7dSnicm 4900088ef99Snicm static void 4910088ef99Snicm window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py, 4920088ef99Snicm u_int sx, u_int sy, const struct grid_cell *gc, const char *label) 4930088ef99Snicm { 4940088ef99Snicm size_t len; 4950088ef99Snicm u_int ox, oy; 4960088ef99Snicm 4970088ef99Snicm len = strlen(label); 4980088ef99Snicm if (sx == 0 || sy == 1 || len > sx) 4990088ef99Snicm return; 5000088ef99Snicm ox = (sx - len + 1) / 2; 5010088ef99Snicm oy = (sy + 1) / 2; 5020088ef99Snicm 5030088ef99Snicm if (ox > 1 && ox + len < sx - 1 && sy >= 3) { 5045c6c7001Snicm screen_write_cursormove(ctx, px + ox - 1, py + oy - 1, 0); 5050088ef99Snicm screen_write_box(ctx, len + 2, 3); 5060088ef99Snicm } 5075c6c7001Snicm screen_write_cursormove(ctx, px + ox, py + oy, 0); 5080088ef99Snicm screen_write_puts(ctx, gc, "%s", label); 5090088ef99Snicm } 5100088ef99Snicm 511367b65a4Snicm static void 51205d44586Snicm window_tree_draw_session(struct window_tree_modedata *data, struct session *s, 51305d44586Snicm struct screen_write_ctx *ctx, u_int sx, u_int sy) 514367b65a4Snicm { 515367b65a4Snicm struct options *oo = s->options; 516367b65a4Snicm struct winlink *wl; 517367b65a4Snicm struct window *w; 5182b7e51f7Snicm u_int cx = ctx->s->cx, cy = ctx->s->cy; 51924a42803Snicm u_int loop, total, visible, each, width, offset; 52024a42803Snicm u_int current, start, end, remaining, i; 521367b65a4Snicm struct grid_cell gc; 52224a42803Snicm int colour, active_colour, left, right; 523367b65a4Snicm char *label; 524367b65a4Snicm 52524a42803Snicm total = winlink_count(&s->windows); 526367b65a4Snicm 527367b65a4Snicm memcpy(&gc, &grid_default_cell, sizeof gc); 528367b65a4Snicm colour = options_get_number(oo, "display-panes-colour"); 529367b65a4Snicm active_colour = options_get_number(oo, "display-panes-active-colour"); 530367b65a4Snicm 53124a42803Snicm if (sx / total < 24) { 53224a42803Snicm visible = sx / 24; 53324a42803Snicm if (visible == 0) 53424a42803Snicm visible = 1; 53524a42803Snicm } else 53624a42803Snicm visible = total; 537367b65a4Snicm 53824a42803Snicm current = 0; 53924a42803Snicm RB_FOREACH(wl, winlinks, &s->windows) { 54024a42803Snicm if (wl == s->curw) 54124a42803Snicm break; 54224a42803Snicm current++; 54324a42803Snicm } 544367b65a4Snicm 54524a42803Snicm if (current < visible) { 54624a42803Snicm start = 0; 54724a42803Snicm end = visible; 54824a42803Snicm } else if (current >= total - visible) { 54924a42803Snicm start = total - visible; 55024a42803Snicm end = total; 55124a42803Snicm } else { 55224a42803Snicm start = current - (visible / 2); 55324a42803Snicm end = start + visible; 55424a42803Snicm } 55524a42803Snicm 55605d44586Snicm if (data->offset < -(int)start) 55705d44586Snicm data->offset = -(int)start; 55805d44586Snicm if (data->offset > (int)(total - end)) 55905d44586Snicm data->offset = (int)(total - end); 56005d44586Snicm start += data->offset; 56105d44586Snicm end += data->offset; 56205d44586Snicm 56324a42803Snicm left = (start != 0); 56424a42803Snicm right = (end != total); 56524a42803Snicm if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) 56624a42803Snicm left = right = 0; 56724a42803Snicm if (left && right) { 56824a42803Snicm each = (sx - 6) / visible; 56924a42803Snicm remaining = (sx - 6) - (visible * each); 57024a42803Snicm } else if (left || right) { 57124a42803Snicm each = (sx - 3) / visible; 57224a42803Snicm remaining = (sx - 3) - (visible * each); 57324a42803Snicm } else { 57424a42803Snicm each = sx / visible; 57524a42803Snicm remaining = sx - (visible * each); 57624a42803Snicm } 577c224fd4bSnicm if (each == 0) 578367b65a4Snicm return; 579367b65a4Snicm 58024a42803Snicm if (left) { 58163c9949dSnicm data->left = cx + 2; 5825c6c7001Snicm screen_write_cursormove(ctx, cx + 2, cy, 0); 58324a42803Snicm screen_write_vline(ctx, sy, 0, 0); 5845c6c7001Snicm screen_write_cursormove(ctx, cx, cy + sy / 2, 0); 58524a42803Snicm screen_write_puts(ctx, &grid_default_cell, "<"); 58663c9949dSnicm } else 58763c9949dSnicm data->left = -1; 58824a42803Snicm if (right) { 58963c9949dSnicm data->right = cx + sx - 3; 5905c6c7001Snicm screen_write_cursormove(ctx, cx + sx - 3, cy, 0); 59124a42803Snicm screen_write_vline(ctx, sy, 0, 0); 5925c6c7001Snicm screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0); 59324a42803Snicm screen_write_puts(ctx, &grid_default_cell, ">"); 59463c9949dSnicm } else 59563c9949dSnicm data->right = -1; 59663c9949dSnicm 59763c9949dSnicm data->start = start; 59863c9949dSnicm data->end = end; 59963c9949dSnicm data->each = each; 60024a42803Snicm 60124a42803Snicm i = loop = 0; 60224a42803Snicm RB_FOREACH(wl, winlinks, &s->windows) { 60324a42803Snicm if (loop == end) 60424a42803Snicm break; 60524a42803Snicm if (loop < start) { 60624a42803Snicm loop++; 60724a42803Snicm continue; 60824a42803Snicm } 60924a42803Snicm w = wl->window; 61024a42803Snicm 611367b65a4Snicm if (wl == s->curw) 612367b65a4Snicm gc.fg = active_colour; 613367b65a4Snicm else 614367b65a4Snicm gc.fg = colour; 61524a42803Snicm 61624a42803Snicm if (left) 61724a42803Snicm offset = 3 + (i * each); 61824a42803Snicm else 61924a42803Snicm offset = (i * each); 62024a42803Snicm if (loop == end - 1) 62103471407Snicm width = each + remaining; 622367b65a4Snicm else 623367b65a4Snicm width = each - 1; 624367b65a4Snicm 6255c6c7001Snicm screen_write_cursormove(ctx, cx + offset, cy, 0); 626367b65a4Snicm screen_write_preview(ctx, &w->active->base, width, sy); 627367b65a4Snicm 628367b65a4Snicm xasprintf(&label, " %u:%s ", wl->idx, w->name); 629367b65a4Snicm if (strlen(label) > width) 630367b65a4Snicm xasprintf(&label, " %u ", wl->idx); 6312b7e51f7Snicm window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc, 6322b7e51f7Snicm label); 633367b65a4Snicm free(label); 634367b65a4Snicm 63524a42803Snicm if (loop != end - 1) { 6365c6c7001Snicm screen_write_cursormove(ctx, cx + offset + width, cy, 0); 637367b65a4Snicm screen_write_vline(ctx, sy, 0, 0); 638367b65a4Snicm } 63924a42803Snicm loop++; 64024a42803Snicm 64124a42803Snicm i++; 642367b65a4Snicm } 643367b65a4Snicm } 644367b65a4Snicm 645367b65a4Snicm static void 64605d44586Snicm window_tree_draw_window(struct window_tree_modedata *data, struct session *s, 64705d44586Snicm struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy) 648367b65a4Snicm { 649367b65a4Snicm struct options *oo = s->options; 650367b65a4Snicm struct window_pane *wp; 6512b7e51f7Snicm u_int cx = ctx->s->cx, cy = ctx->s->cy; 65224a42803Snicm u_int loop, total, visible, each, width, offset; 65324a42803Snicm u_int current, start, end, remaining, i; 654367b65a4Snicm struct grid_cell gc; 6558807a41eSnicm int colour, active_colour, left, right, pane_idx; 656367b65a4Snicm char *label; 657367b65a4Snicm 65824a42803Snicm total = window_count_panes(w); 659367b65a4Snicm 660367b65a4Snicm memcpy(&gc, &grid_default_cell, sizeof gc); 661367b65a4Snicm colour = options_get_number(oo, "display-panes-colour"); 662367b65a4Snicm active_colour = options_get_number(oo, "display-panes-active-colour"); 663367b65a4Snicm 66424a42803Snicm if (sx / total < 24) { 66524a42803Snicm visible = sx / 24; 66624a42803Snicm if (visible == 0) 66724a42803Snicm visible = 1; 66824a42803Snicm } else 66924a42803Snicm visible = total; 670367b65a4Snicm 67124a42803Snicm current = 0; 67224a42803Snicm TAILQ_FOREACH(wp, &w->panes, entry) { 67324a42803Snicm if (wp == w->active) 67424a42803Snicm break; 67524a42803Snicm current++; 67624a42803Snicm } 677367b65a4Snicm 67824a42803Snicm if (current < visible) { 67924a42803Snicm start = 0; 68024a42803Snicm end = visible; 68124a42803Snicm } else if (current >= total - visible) { 68224a42803Snicm start = total - visible; 68324a42803Snicm end = total; 68424a42803Snicm } else { 68524a42803Snicm start = current - (visible / 2); 68624a42803Snicm end = start + visible; 68724a42803Snicm } 68824a42803Snicm 68905d44586Snicm if (data->offset < -(int)start) 69005d44586Snicm data->offset = -(int)start; 69105d44586Snicm if (data->offset > (int)(total - end)) 69205d44586Snicm data->offset = (int)(total - end); 69305d44586Snicm start += data->offset; 69405d44586Snicm end += data->offset; 69505d44586Snicm 69624a42803Snicm left = (start != 0); 69724a42803Snicm right = (end != total); 69824a42803Snicm if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) 69924a42803Snicm left = right = 0; 70024a42803Snicm if (left && right) { 70124a42803Snicm each = (sx - 6) / visible; 70224a42803Snicm remaining = (sx - 6) - (visible * each); 70324a42803Snicm } else if (left || right) { 70424a42803Snicm each = (sx - 3) / visible; 70524a42803Snicm remaining = (sx - 3) - (visible * each); 70624a42803Snicm } else { 70724a42803Snicm each = sx / visible; 70824a42803Snicm remaining = sx - (visible * each); 70924a42803Snicm } 710c224fd4bSnicm if (each == 0) 711367b65a4Snicm return; 712367b65a4Snicm 71324a42803Snicm if (left) { 71463c9949dSnicm data->left = cx + 2; 7155c6c7001Snicm screen_write_cursormove(ctx, cx + 2, cy, 0); 71624a42803Snicm screen_write_vline(ctx, sy, 0, 0); 7175c6c7001Snicm screen_write_cursormove(ctx, cx, cy + sy / 2, 0); 71824a42803Snicm screen_write_puts(ctx, &grid_default_cell, "<"); 71963c9949dSnicm } else 72063c9949dSnicm data->left = -1; 72124a42803Snicm if (right) { 72263c9949dSnicm data->right = cx + sx - 3; 7235c6c7001Snicm screen_write_cursormove(ctx, cx + sx - 3, cy, 0); 72424a42803Snicm screen_write_vline(ctx, sy, 0, 0); 7255c6c7001Snicm screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0); 72624a42803Snicm screen_write_puts(ctx, &grid_default_cell, ">"); 72763c9949dSnicm } else 72863c9949dSnicm data->right = -1; 72963c9949dSnicm 73063c9949dSnicm data->start = start; 73163c9949dSnicm data->end = end; 73263c9949dSnicm data->each = each; 73324a42803Snicm 73424a42803Snicm i = loop = 0; 73524a42803Snicm TAILQ_FOREACH(wp, &w->panes, entry) { 73624a42803Snicm if (loop == end) 73724a42803Snicm break; 73824a42803Snicm if (loop < start) { 73924a42803Snicm loop++; 74024a42803Snicm continue; 74124a42803Snicm } 74224a42803Snicm 743367b65a4Snicm if (wp == w->active) 744367b65a4Snicm gc.fg = active_colour; 745367b65a4Snicm else 746367b65a4Snicm gc.fg = colour; 74724a42803Snicm 74824a42803Snicm if (left) 74924a42803Snicm offset = 3 + (i * each); 75024a42803Snicm else 75124a42803Snicm offset = (i * each); 75224a42803Snicm if (loop == end - 1) 75303471407Snicm width = each + remaining; 754367b65a4Snicm else 755367b65a4Snicm width = each - 1; 756367b65a4Snicm 7575c6c7001Snicm screen_write_cursormove(ctx, cx + offset, cy, 0); 758367b65a4Snicm screen_write_preview(ctx, &wp->base, width, sy); 759367b65a4Snicm 7608807a41eSnicm if (window_pane_index(wp, &pane_idx) != 0) 7618807a41eSnicm pane_idx = loop; 7628807a41eSnicm xasprintf(&label, " %u ", pane_idx); 7632b7e51f7Snicm window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc, 7642b7e51f7Snicm label); 765367b65a4Snicm free(label); 766367b65a4Snicm 76724a42803Snicm if (loop != end - 1) { 7685c6c7001Snicm screen_write_cursormove(ctx, cx + offset + width, cy, 0); 769367b65a4Snicm screen_write_vline(ctx, sy, 0, 0); 770367b65a4Snicm } 77124a42803Snicm loop++; 77224a42803Snicm 77324a42803Snicm i++; 774367b65a4Snicm } 775367b65a4Snicm } 776367b65a4Snicm 7772b7e51f7Snicm static void 7782b7e51f7Snicm window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, 7792b7e51f7Snicm u_int sx, u_int sy) 780a42faf7dSnicm { 781a42faf7dSnicm struct window_tree_itemdata *item = itemdata; 782a42faf7dSnicm struct session *sp; 783a42faf7dSnicm struct winlink *wlp; 784a42faf7dSnicm struct window_pane *wp; 785a42faf7dSnicm 786a42faf7dSnicm window_tree_pull_item(item, &sp, &wlp, &wp); 787a42faf7dSnicm if (wp == NULL) 7882b7e51f7Snicm return; 789a42faf7dSnicm 790367b65a4Snicm switch (item->type) { 791367b65a4Snicm case WINDOW_TREE_NONE: 7922b7e51f7Snicm break; 793367b65a4Snicm case WINDOW_TREE_SESSION: 7942b7e51f7Snicm window_tree_draw_session(modedata, sp, ctx, sx, sy); 795367b65a4Snicm break; 796367b65a4Snicm case WINDOW_TREE_WINDOW: 7972b7e51f7Snicm window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy); 798367b65a4Snicm break; 799367b65a4Snicm case WINDOW_TREE_PANE: 8002b7e51f7Snicm screen_write_preview(ctx, &wp->base, sx, sy); 801367b65a4Snicm break; 802367b65a4Snicm } 803a42faf7dSnicm } 804a42faf7dSnicm 805943a08b1Snicm static int 806943a08b1Snicm window_tree_search(__unused void *modedata, void *itemdata, const char *ss) 807943a08b1Snicm { 808943a08b1Snicm struct window_tree_itemdata *item = itemdata; 809943a08b1Snicm struct session *s; 810943a08b1Snicm struct winlink *wl; 811943a08b1Snicm struct window_pane *wp; 8120a4d943dSnicm char *cmd; 8130a4d943dSnicm int retval; 814943a08b1Snicm 815943a08b1Snicm window_tree_pull_item(item, &s, &wl, &wp); 816943a08b1Snicm 817943a08b1Snicm switch (item->type) { 818943a08b1Snicm case WINDOW_TREE_NONE: 819943a08b1Snicm return (0); 820943a08b1Snicm case WINDOW_TREE_SESSION: 821943a08b1Snicm if (s == NULL) 822943a08b1Snicm return (0); 823943a08b1Snicm return (strstr(s->name, ss) != NULL); 824943a08b1Snicm case WINDOW_TREE_WINDOW: 825943a08b1Snicm if (s == NULL || wl == NULL) 826943a08b1Snicm return (0); 827943a08b1Snicm return (strstr(wl->window->name, ss) != NULL); 828943a08b1Snicm case WINDOW_TREE_PANE: 829943a08b1Snicm if (s == NULL || wl == NULL || wp == NULL) 830943a08b1Snicm break; 831943a08b1Snicm cmd = get_proc_name(wp->fd, wp->tty); 832943a08b1Snicm if (cmd == NULL || *cmd == '\0') 833943a08b1Snicm return (0); 8340a4d943dSnicm retval = (strstr(cmd, ss) != NULL); 8350a4d943dSnicm free(cmd); 8360a4d943dSnicm return retval; 837943a08b1Snicm } 838943a08b1Snicm return (0); 839943a08b1Snicm } 840943a08b1Snicm 841f43bc87cSnicm static void 842f43bc87cSnicm window_tree_menu(void *modedata, struct client *c, key_code key) 843f43bc87cSnicm { 844f43bc87cSnicm struct window_tree_modedata *data = modedata; 845f43bc87cSnicm struct window_pane *wp = data->wp; 846f43bc87cSnicm struct window_mode_entry *wme; 847f43bc87cSnicm 848f43bc87cSnicm wme = TAILQ_FIRST(&wp->modes); 849f43bc87cSnicm if (wme == NULL || wme->data != modedata) 850f43bc87cSnicm return; 851f43bc87cSnicm window_tree_key(wme, c, NULL, NULL, key, NULL); 852f43bc87cSnicm } 853f43bc87cSnicm 854a42faf7dSnicm static struct screen * 85530a94f45Snicm window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, 856a42faf7dSnicm struct args *args) 857a42faf7dSnicm { 85830a94f45Snicm struct window_pane *wp = wme->wp; 859a42faf7dSnicm struct window_tree_modedata *data; 860a42faf7dSnicm struct screen *s; 861a42faf7dSnicm 86230a94f45Snicm wme->data = data = xcalloc(1, sizeof *data); 863f43bc87cSnicm data->wp = wp; 864f43bc87cSnicm data->references = 1; 865a42faf7dSnicm 866a42faf7dSnicm if (args_has(args, 's')) 867a42faf7dSnicm data->type = WINDOW_TREE_SESSION; 868a42faf7dSnicm else if (args_has(args, 'w')) 869a42faf7dSnicm data->type = WINDOW_TREE_WINDOW; 870a42faf7dSnicm else 871a42faf7dSnicm data->type = WINDOW_TREE_PANE; 872a42faf7dSnicm memcpy(&data->fs, fs, sizeof data->fs); 873a42faf7dSnicm 874bf38e336Snicm if (args == NULL || !args_has(args, 'F')) 875bf38e336Snicm data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT); 876bf38e336Snicm else 877bf38e336Snicm data->format = xstrdup(args_get(args, 'F')); 878a42faf7dSnicm if (args == NULL || args->argc == 0) 879a42faf7dSnicm data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND); 880a42faf7dSnicm else 881a42faf7dSnicm data->command = xstrdup(args->argv[0]); 882988c4c87Snicm data->squash_groups = !args_has(args, 'G'); 883a42faf7dSnicm 884b38aa712Snicm data->data = mode_tree_start(wp, args, window_tree_build, 885f43bc87cSnicm window_tree_draw, window_tree_search, window_tree_menu, data, 8861335341aSnicm window_tree_menu_items, window_tree_sort_list, 887a42faf7dSnicm nitems(window_tree_sort_list), &s); 8884f5e4c93Snicm mode_tree_zoom(data->data, args); 889a42faf7dSnicm 890a42faf7dSnicm mode_tree_build(data->data); 891a42faf7dSnicm mode_tree_draw(data->data); 892a42faf7dSnicm 893a42faf7dSnicm data->type = WINDOW_TREE_NONE; 894a42faf7dSnicm 895a42faf7dSnicm return (s); 896a42faf7dSnicm } 897a42faf7dSnicm 898a42faf7dSnicm static void 899a42faf7dSnicm window_tree_destroy(struct window_tree_modedata *data) 900a42faf7dSnicm { 901a42faf7dSnicm u_int i; 902a42faf7dSnicm 903a42faf7dSnicm if (--data->references != 0) 904a42faf7dSnicm return; 905a42faf7dSnicm 906a42faf7dSnicm for (i = 0; i < data->item_size; i++) 907a42faf7dSnicm window_tree_free_item(data->item_list[i]); 908a42faf7dSnicm free(data->item_list); 909a42faf7dSnicm 910bf38e336Snicm free(data->format); 911a42faf7dSnicm free(data->command); 912bf38e336Snicm 913a42faf7dSnicm free(data); 914a42faf7dSnicm } 915a42faf7dSnicm 916a42faf7dSnicm static void 91730a94f45Snicm window_tree_free(struct window_mode_entry *wme) 918a42faf7dSnicm { 91930a94f45Snicm struct window_tree_modedata *data = wme->data; 920a42faf7dSnicm 921a42faf7dSnicm if (data == NULL) 922a42faf7dSnicm return; 923a42faf7dSnicm 924a42faf7dSnicm data->dead = 1; 925d0ca3a30Snicm mode_tree_free(data->data); 926a42faf7dSnicm window_tree_destroy(data); 927a42faf7dSnicm } 928a42faf7dSnicm 929a42faf7dSnicm static void 93030a94f45Snicm window_tree_resize(struct window_mode_entry *wme, u_int sx, u_int sy) 931a42faf7dSnicm { 93230a94f45Snicm struct window_tree_modedata *data = wme->data; 933a42faf7dSnicm 934a42faf7dSnicm mode_tree_resize(data->data, sx, sy); 935a42faf7dSnicm } 936a42faf7dSnicm 937a42faf7dSnicm static char * 938a42faf7dSnicm window_tree_get_target(struct window_tree_itemdata *item, 939a42faf7dSnicm struct cmd_find_state *fs) 940a42faf7dSnicm { 941a42faf7dSnicm struct session *s; 942a42faf7dSnicm struct winlink *wl; 943a42faf7dSnicm struct window_pane *wp; 944a42faf7dSnicm char *target; 945a42faf7dSnicm 946a42faf7dSnicm window_tree_pull_item(item, &s, &wl, &wp); 947a42faf7dSnicm 948a42faf7dSnicm target = NULL; 949a42faf7dSnicm switch (item->type) { 950a42faf7dSnicm case WINDOW_TREE_NONE: 951a42faf7dSnicm break; 952a42faf7dSnicm case WINDOW_TREE_SESSION: 953a42faf7dSnicm if (s == NULL) 954a42faf7dSnicm break; 955a42faf7dSnicm xasprintf(&target, "=%s:", s->name); 956a42faf7dSnicm break; 957a42faf7dSnicm case WINDOW_TREE_WINDOW: 958a42faf7dSnicm if (s == NULL || wl == NULL) 959a42faf7dSnicm break; 960a42faf7dSnicm xasprintf(&target, "=%s:%u.", s->name, wl->idx); 961a42faf7dSnicm break; 962a42faf7dSnicm case WINDOW_TREE_PANE: 963a42faf7dSnicm if (s == NULL || wl == NULL || wp == NULL) 964a42faf7dSnicm break; 965a42faf7dSnicm xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id); 966a42faf7dSnicm break; 967a42faf7dSnicm } 968a42faf7dSnicm if (target == NULL) 969a42faf7dSnicm cmd_find_clear_state(fs, 0); 970a42faf7dSnicm else 9710772530eSnicm cmd_find_from_winlink_pane(fs, wl, wp, 0); 972a42faf7dSnicm return (target); 973a42faf7dSnicm } 974a42faf7dSnicm 975a42faf7dSnicm static void 976d7af2c28Snicm window_tree_command_each(void *modedata, void *itemdata, struct client *c, 977d7af2c28Snicm __unused key_code key) 978a42faf7dSnicm { 979a42faf7dSnicm struct window_tree_modedata *data = modedata; 980a42faf7dSnicm struct window_tree_itemdata *item = itemdata; 981a42faf7dSnicm char *name; 982a42faf7dSnicm struct cmd_find_state fs; 983a42faf7dSnicm 984a42faf7dSnicm name = window_tree_get_target(item, &fs); 985a42faf7dSnicm if (name != NULL) 986d7af2c28Snicm mode_tree_run_command(c, &fs, data->entered, name); 987a42faf7dSnicm free(name); 988a42faf7dSnicm } 989a42faf7dSnicm 990a42faf7dSnicm static enum cmd_retval 991a42faf7dSnicm window_tree_command_done(__unused struct cmdq_item *item, void *modedata) 992a42faf7dSnicm { 993a42faf7dSnicm struct window_tree_modedata *data = modedata; 994a42faf7dSnicm 995a42faf7dSnicm if (!data->dead) { 996a42faf7dSnicm mode_tree_build(data->data); 997a42faf7dSnicm mode_tree_draw(data->data); 998a42faf7dSnicm data->wp->flags |= PANE_REDRAW; 999a42faf7dSnicm } 1000a42faf7dSnicm window_tree_destroy(data); 1001a42faf7dSnicm return (CMD_RETURN_NORMAL); 1002a42faf7dSnicm } 1003a42faf7dSnicm 1004a42faf7dSnicm static int 1005a42faf7dSnicm window_tree_command_callback(struct client *c, void *modedata, const char *s, 1006a42faf7dSnicm __unused int done) 1007a42faf7dSnicm { 1008a42faf7dSnicm struct window_tree_modedata *data = modedata; 1009a42faf7dSnicm 1010d0ca3a30Snicm if (s == NULL || *s == '\0' || data->dead) 1011a42faf7dSnicm return (0); 1012a42faf7dSnicm 1013a42faf7dSnicm data->entered = s; 1014d7af2c28Snicm mode_tree_each_tagged(data->data, window_tree_command_each, c, 1015d7af2c28Snicm KEYC_NONE, 1); 1016a42faf7dSnicm data->entered = NULL; 1017a42faf7dSnicm 1018a42faf7dSnicm data->references++; 1019a42faf7dSnicm cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1020a42faf7dSnicm 1021a42faf7dSnicm return (0); 1022a42faf7dSnicm } 1023a42faf7dSnicm 1024a42faf7dSnicm static void 1025a42faf7dSnicm window_tree_command_free(void *modedata) 1026a42faf7dSnicm { 1027a42faf7dSnicm struct window_tree_modedata *data = modedata; 1028a42faf7dSnicm 1029a42faf7dSnicm window_tree_destroy(data); 1030a42faf7dSnicm } 1031a42faf7dSnicm 1032d0ca3a30Snicm static void 1033d0ca3a30Snicm window_tree_kill_each(__unused void *modedata, void *itemdata, 1034d0ca3a30Snicm __unused struct client *c, __unused key_code key) 1035d0ca3a30Snicm { 1036d0ca3a30Snicm struct window_tree_itemdata *item = itemdata; 1037d0ca3a30Snicm struct session *s; 1038d0ca3a30Snicm struct winlink *wl; 1039d0ca3a30Snicm struct window_pane *wp; 1040d0ca3a30Snicm 1041d0ca3a30Snicm window_tree_pull_item(item, &s, &wl, &wp); 1042d0ca3a30Snicm 1043d0ca3a30Snicm switch (item->type) { 1044d0ca3a30Snicm case WINDOW_TREE_NONE: 1045d0ca3a30Snicm break; 1046d0ca3a30Snicm case WINDOW_TREE_SESSION: 1047d0ca3a30Snicm if (s != NULL) { 1048d0ca3a30Snicm server_destroy_session(s); 1049c26c4f79Snicm session_destroy(s, 1, __func__); 1050d0ca3a30Snicm } 1051d0ca3a30Snicm break; 1052d0ca3a30Snicm case WINDOW_TREE_WINDOW: 1053d0ca3a30Snicm if (wl != NULL) 1054d0ca3a30Snicm server_kill_window(wl->window); 1055d0ca3a30Snicm break; 1056d0ca3a30Snicm case WINDOW_TREE_PANE: 1057d0ca3a30Snicm if (wp != NULL) 1058d0ca3a30Snicm server_kill_pane(wp); 1059d0ca3a30Snicm break; 1060d0ca3a30Snicm } 1061d0ca3a30Snicm } 1062d0ca3a30Snicm 1063d0ca3a30Snicm static int 1064d0ca3a30Snicm window_tree_kill_current_callback(struct client *c, void *modedata, 1065d0ca3a30Snicm const char *s, __unused int done) 1066d0ca3a30Snicm { 1067d0ca3a30Snicm struct window_tree_modedata *data = modedata; 1068d0ca3a30Snicm struct mode_tree_data *mtd = data->data; 1069d0ca3a30Snicm 1070d0ca3a30Snicm if (s == NULL || *s == '\0' || data->dead) 1071d0ca3a30Snicm return (0); 1072d0ca3a30Snicm if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') 1073d0ca3a30Snicm return (0); 1074d0ca3a30Snicm 1075d0ca3a30Snicm window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE); 1076d0ca3a30Snicm 1077d0ca3a30Snicm data->references++; 1078d0ca3a30Snicm cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1079d0ca3a30Snicm 1080d0ca3a30Snicm return (0); 1081d0ca3a30Snicm } 1082d0ca3a30Snicm 1083d0ca3a30Snicm static int 1084d0ca3a30Snicm window_tree_kill_tagged_callback(struct client *c, void *modedata, 1085d0ca3a30Snicm const char *s, __unused int done) 1086d0ca3a30Snicm { 1087d0ca3a30Snicm struct window_tree_modedata *data = modedata; 1088d0ca3a30Snicm struct mode_tree_data *mtd = data->data; 1089d0ca3a30Snicm 1090d0ca3a30Snicm if (s == NULL || *s == '\0' || data->dead) 1091d0ca3a30Snicm return (0); 1092d0ca3a30Snicm if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') 1093d0ca3a30Snicm return (0); 1094d0ca3a30Snicm 1095d0ca3a30Snicm mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1); 1096d0ca3a30Snicm 1097d0ca3a30Snicm data->references++; 1098d0ca3a30Snicm cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1099d0ca3a30Snicm 1100d0ca3a30Snicm return (0); 1101d0ca3a30Snicm } 1102d0ca3a30Snicm 110363c9949dSnicm static key_code 110463c9949dSnicm window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x, 110563c9949dSnicm struct window_tree_itemdata *item) 110663c9949dSnicm { 110763c9949dSnicm struct session *s; 110863c9949dSnicm struct winlink *wl; 110963c9949dSnicm struct window_pane *wp; 111063c9949dSnicm u_int loop; 111163c9949dSnicm 111263c9949dSnicm if (key != KEYC_MOUSEDOWN1_PANE) 111363c9949dSnicm return (KEYC_NONE); 111463c9949dSnicm 111563c9949dSnicm if (data->left != -1 && x <= (u_int)data->left) 111663c9949dSnicm return ('<'); 111763c9949dSnicm if (data->right != -1 && x >= (u_int)data->right) 111863c9949dSnicm return ('>'); 111963c9949dSnicm 112063c9949dSnicm if (data->left != -1) 112163c9949dSnicm x -= data->left; 112263c9949dSnicm else if (x != 0) 112363c9949dSnicm x--; 112463c9949dSnicm if (x == 0 || data->end == 0) 112563c9949dSnicm x = 0; 112663c9949dSnicm else { 112763c9949dSnicm x = x / data->each; 112863c9949dSnicm if (data->start + x >= data->end) 112963c9949dSnicm x = data->end - 1; 113063c9949dSnicm } 113163c9949dSnicm 113263c9949dSnicm window_tree_pull_item(item, &s, &wl, &wp); 113363c9949dSnicm if (item->type == WINDOW_TREE_SESSION) { 113463c9949dSnicm if (s == NULL) 113563c9949dSnicm return (KEYC_NONE); 113663c9949dSnicm mode_tree_expand_current(data->data); 113763c9949dSnicm loop = 0; 113863c9949dSnicm RB_FOREACH(wl, winlinks, &s->windows) { 113963c9949dSnicm if (loop == data->start + x) 114063c9949dSnicm break; 114163c9949dSnicm loop++; 114263c9949dSnicm } 114363c9949dSnicm if (wl != NULL) 114463c9949dSnicm mode_tree_set_current(data->data, (uint64_t)wl); 114563c9949dSnicm return ('\r'); 114663c9949dSnicm } 114763c9949dSnicm if (item->type == WINDOW_TREE_WINDOW) { 114863c9949dSnicm if (wl == NULL) 114963c9949dSnicm return (KEYC_NONE); 115063c9949dSnicm mode_tree_expand_current(data->data); 115163c9949dSnicm loop = 0; 115263c9949dSnicm TAILQ_FOREACH(wp, &wl->window->panes, entry) { 115363c9949dSnicm if (loop == data->start + x) 115463c9949dSnicm break; 115563c9949dSnicm loop++; 115663c9949dSnicm } 115763c9949dSnicm if (wp != NULL) 115863c9949dSnicm mode_tree_set_current(data->data, (uint64_t)wp); 115963c9949dSnicm return ('\r'); 116063c9949dSnicm } 116163c9949dSnicm return (KEYC_NONE); 116263c9949dSnicm } 116363c9949dSnicm 1164a42faf7dSnicm static void 116530a94f45Snicm window_tree_key(struct window_mode_entry *wme, struct client *c, 1166bf52409eSnicm __unused struct session *s, __unused struct winlink *wl, key_code key, 1167bf52409eSnicm struct mouse_event *m) 1168a42faf7dSnicm { 116930a94f45Snicm struct window_pane *wp = wme->wp; 117030a94f45Snicm struct window_tree_modedata *data = wme->data; 117163c9949dSnicm struct window_tree_itemdata *item, *new_item; 1172d0ca3a30Snicm char *name, *prompt = NULL; 1173a42faf7dSnicm struct cmd_find_state fs; 1174a42faf7dSnicm int finished; 1175d0ca3a30Snicm u_int tagged, x, y, idx; 1176d0ca3a30Snicm struct session *ns; 1177d0ca3a30Snicm struct winlink *nwl; 1178d0ca3a30Snicm struct window_pane *nwp; 1179a42faf7dSnicm 118005d44586Snicm item = mode_tree_get_current(data->data); 118163c9949dSnicm finished = mode_tree_key(data->data, c, &key, m, &x, &y); 118263c9949dSnicm if (item != (new_item = mode_tree_get_current(data->data))) { 118363c9949dSnicm item = new_item; 118405d44586Snicm data->offset = 0; 118563c9949dSnicm } 1186f43bc87cSnicm if (KEYC_IS_MOUSE(key) && m != NULL) 118763c9949dSnicm key = window_tree_mouse(data, key, x, item); 1188a42faf7dSnicm switch (key) { 118905d44586Snicm case '<': 119005d44586Snicm data->offset--; 119105d44586Snicm break; 119205d44586Snicm case '>': 119305d44586Snicm data->offset++; 119405d44586Snicm break; 1195d0ca3a30Snicm case 'x': 1196d0ca3a30Snicm window_tree_pull_item(item, &ns, &nwl, &nwp); 1197d0ca3a30Snicm switch (item->type) { 1198d0ca3a30Snicm case WINDOW_TREE_NONE: 1199d0ca3a30Snicm break; 1200d0ca3a30Snicm case WINDOW_TREE_SESSION: 1201d0ca3a30Snicm if (ns == NULL) 1202d0ca3a30Snicm break; 1203d0ca3a30Snicm xasprintf(&prompt, "Kill session %s? ", ns->name); 1204d0ca3a30Snicm break; 1205d0ca3a30Snicm case WINDOW_TREE_WINDOW: 1206d0ca3a30Snicm if (nwl == NULL) 1207d0ca3a30Snicm break; 1208d0ca3a30Snicm xasprintf(&prompt, "Kill window %u? ", nwl->idx); 1209d0ca3a30Snicm break; 1210d0ca3a30Snicm case WINDOW_TREE_PANE: 1211d0ca3a30Snicm if (nwp == NULL || window_pane_index(nwp, &idx) != 0) 1212d0ca3a30Snicm break; 1213d0ca3a30Snicm xasprintf(&prompt, "Kill pane %u? ", idx); 1214d0ca3a30Snicm break; 1215d0ca3a30Snicm } 1216d0ca3a30Snicm if (prompt == NULL) 1217d0ca3a30Snicm break; 1218d0ca3a30Snicm data->references++; 1219d0ca3a30Snicm status_prompt_set(c, prompt, "", 1220d0ca3a30Snicm window_tree_kill_current_callback, window_tree_command_free, 1221d0ca3a30Snicm data, PROMPT_SINGLE|PROMPT_NOFORMAT); 1222d0ca3a30Snicm free(prompt); 1223d0ca3a30Snicm break; 1224d0ca3a30Snicm case 'X': 1225d0ca3a30Snicm tagged = mode_tree_count_tagged(data->data); 1226d0ca3a30Snicm if (tagged == 0) 1227d0ca3a30Snicm break; 1228d0ca3a30Snicm xasprintf(&prompt, "Kill %u tagged? ", tagged); 1229d0ca3a30Snicm data->references++; 1230d0ca3a30Snicm status_prompt_set(c, prompt, "", 1231d0ca3a30Snicm window_tree_kill_tagged_callback, window_tree_command_free, 1232d0ca3a30Snicm data, PROMPT_SINGLE|PROMPT_NOFORMAT); 1233d0ca3a30Snicm free(prompt); 1234d0ca3a30Snicm break; 1235a42faf7dSnicm case ':': 1236a42faf7dSnicm tagged = mode_tree_count_tagged(data->data); 1237a42faf7dSnicm if (tagged != 0) 1238a42faf7dSnicm xasprintf(&prompt, "(%u tagged) ", tagged); 1239a42faf7dSnicm else 1240a42faf7dSnicm xasprintf(&prompt, "(current) "); 1241a42faf7dSnicm data->references++; 1242a42faf7dSnicm status_prompt_set(c, prompt, "", window_tree_command_callback, 1243a42faf7dSnicm window_tree_command_free, data, PROMPT_NOFORMAT); 1244a42faf7dSnicm free(prompt); 1245a42faf7dSnicm break; 1246a42faf7dSnicm case '\r': 1247a42faf7dSnicm name = window_tree_get_target(item, &fs); 1248a42faf7dSnicm if (name != NULL) 1249d7af2c28Snicm mode_tree_run_command(c, NULL, data->command, name); 1250d7af2c28Snicm finished = 1; 1251a42faf7dSnicm free(name); 1252d7af2c28Snicm break; 1253a42faf7dSnicm } 1254a42faf7dSnicm if (finished) 1255a42faf7dSnicm window_pane_reset_mode(wp); 1256a42faf7dSnicm else { 1257a42faf7dSnicm mode_tree_draw(data->data); 1258a42faf7dSnicm wp->flags |= PANE_REDRAW; 1259a42faf7dSnicm } 1260a42faf7dSnicm } 1261