1 1.1 christos /* $OpenBSD$ */ 2 1.1 christos 3 1.1 christos /* 4 1.1 christos * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott (at) gmail.com> 5 1.1 christos * 6 1.1 christos * Permission to use, copy, modify, and distribute this software for any 7 1.1 christos * purpose with or without fee is hereby granted, provided that the above 8 1.1 christos * copyright notice and this permission notice appear in all copies. 9 1.1 christos * 10 1.1 christos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 1.1 christos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 1.1 christos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 1.1 christos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 1.1 christos * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 1.1 christos * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 1.1 christos * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 1.1 christos */ 18 1.1 christos 19 1.1 christos #include <sys/types.h> 20 1.1 christos 21 1.3 christos #include <ctype.h> 22 1.1 christos #include <stdlib.h> 23 1.1 christos #include <string.h> 24 1.1 christos 25 1.1 christos #include "tmux.h" 26 1.1 christos 27 1.5 christos static struct screen *window_tree_init(struct window_mode_entry *, 28 1.1 christos struct cmd_find_state *, struct args *); 29 1.5 christos static void window_tree_free(struct window_mode_entry *); 30 1.5 christos static void window_tree_resize(struct window_mode_entry *, u_int, 31 1.5 christos u_int); 32 1.8 christos static void window_tree_update(struct window_mode_entry *); 33 1.5 christos static void window_tree_key(struct window_mode_entry *, 34 1.5 christos struct client *, struct session *, 35 1.5 christos struct winlink *, key_code, struct mouse_event *); 36 1.1 christos 37 1.7 christos #define WINDOW_TREE_DEFAULT_COMMAND "switch-client -Zt '%%'" 38 1.1 christos 39 1.1 christos #define WINDOW_TREE_DEFAULT_FORMAT \ 40 1.1 christos "#{?pane_format," \ 41 1.8 christos "#{?pane_marked,#[reverse],}" \ 42 1.8 christos "#{pane_current_command}#{?pane_active,*,}#{?pane_marked,M,}" \ 43 1.8 christos "#{?#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}},: \"#{pane_title}\",}" \ 44 1.12 wiz ",window_format," \ 45 1.12 wiz "#{?window_marked_flag,#[reverse],}" \ 46 1.12 wiz "#{window_name}#{window_flags}" \ 47 1.12 wiz "#{?#{&&:#{==:#{window_panes},1},#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}}},: \"#{pane_title}\",}" \ 48 1.1 christos "," \ 49 1.12 wiz "#{session_windows} windows" \ 50 1.12 wiz "#{?session_grouped, " \ 51 1.12 wiz "(group #{session_group}: " \ 52 1.12 wiz "#{session_group_list})," \ 53 1.1 christos "}" \ 54 1.12 wiz "#{?session_attached, (attached),}" \ 55 1.1 christos "}" 56 1.1 christos 57 1.8 christos #define WINDOW_TREE_DEFAULT_KEY_FORMAT \ 58 1.8 christos "#{?#{e|<:#{line},10}," \ 59 1.8 christos "#{line}" \ 60 1.12 wiz ",#{e|<:#{line},36}," \ 61 1.12 wiz "M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \ 62 1.8 christos "}" 63 1.8 christos 64 1.6 christos static const struct menu_item window_tree_menu_items[] = { 65 1.8 christos { "Select", '\r', NULL }, 66 1.8 christos { "Expand", KEYC_RIGHT, NULL }, 67 1.8 christos { "Mark", 'm', NULL }, 68 1.6 christos { "", KEYC_NONE, NULL }, 69 1.6 christos { "Tag", 't', NULL }, 70 1.6 christos { "Tag All", '\024', NULL }, 71 1.6 christos { "Tag None", 'T', NULL }, 72 1.6 christos { "", KEYC_NONE, NULL }, 73 1.6 christos { "Kill", 'x', NULL }, 74 1.6 christos { "Kill Tagged", 'X', NULL }, 75 1.6 christos { "", KEYC_NONE, NULL }, 76 1.6 christos { "Cancel", 'q', NULL }, 77 1.6 christos 78 1.6 christos { NULL, KEYC_NONE, NULL } 79 1.6 christos }; 80 1.6 christos 81 1.1 christos const struct window_mode window_tree_mode = { 82 1.1 christos .name = "tree-mode", 83 1.5 christos .default_format = WINDOW_TREE_DEFAULT_FORMAT, 84 1.1 christos 85 1.1 christos .init = window_tree_init, 86 1.1 christos .free = window_tree_free, 87 1.1 christos .resize = window_tree_resize, 88 1.8 christos .update = window_tree_update, 89 1.1 christos .key = window_tree_key, 90 1.1 christos }; 91 1.1 christos 92 1.1 christos enum window_tree_sort_type { 93 1.1 christos WINDOW_TREE_BY_INDEX, 94 1.1 christos WINDOW_TREE_BY_NAME, 95 1.1 christos WINDOW_TREE_BY_TIME, 96 1.1 christos }; 97 1.1 christos static const char *window_tree_sort_list[] = { 98 1.1 christos "index", 99 1.1 christos "name", 100 1.1 christos "time" 101 1.1 christos }; 102 1.7 christos static struct mode_tree_sort_criteria *window_tree_sort; 103 1.1 christos 104 1.1 christos enum window_tree_type { 105 1.1 christos WINDOW_TREE_NONE, 106 1.1 christos WINDOW_TREE_SESSION, 107 1.1 christos WINDOW_TREE_WINDOW, 108 1.1 christos WINDOW_TREE_PANE, 109 1.1 christos }; 110 1.1 christos 111 1.1 christos struct window_tree_itemdata { 112 1.1 christos enum window_tree_type type; 113 1.1 christos int session; 114 1.1 christos int winlink; 115 1.1 christos int pane; 116 1.1 christos }; 117 1.1 christos 118 1.1 christos struct window_tree_modedata { 119 1.1 christos struct window_pane *wp; 120 1.1 christos int dead; 121 1.1 christos int references; 122 1.1 christos 123 1.1 christos struct mode_tree_data *data; 124 1.1 christos char *format; 125 1.8 christos char *key_format; 126 1.1 christos char *command; 127 1.3 christos int squash_groups; 128 1.12 wiz int prompt_flags; 129 1.1 christos 130 1.1 christos struct window_tree_itemdata **item_list; 131 1.1 christos u_int item_size; 132 1.1 christos 133 1.1 christos const char *entered; 134 1.1 christos 135 1.1 christos struct cmd_find_state fs; 136 1.1 christos enum window_tree_type type; 137 1.1 christos 138 1.1 christos int offset; 139 1.3 christos 140 1.3 christos int left; 141 1.3 christos int right; 142 1.3 christos u_int start; 143 1.3 christos u_int end; 144 1.3 christos u_int each; 145 1.1 christos }; 146 1.1 christos 147 1.1 christos static void 148 1.1 christos window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp, 149 1.1 christos struct winlink **wlp, struct window_pane **wp) 150 1.1 christos { 151 1.1 christos *wp = NULL; 152 1.1 christos *wlp = NULL; 153 1.1 christos *sp = session_find_by_id(item->session); 154 1.1 christos if (*sp == NULL) 155 1.1 christos return; 156 1.1 christos if (item->type == WINDOW_TREE_SESSION) { 157 1.1 christos *wlp = (*sp)->curw; 158 1.1 christos *wp = (*wlp)->window->active; 159 1.1 christos return; 160 1.1 christos } 161 1.1 christos 162 1.1 christos *wlp = winlink_find_by_index(&(*sp)->windows, item->winlink); 163 1.1 christos if (*wlp == NULL) { 164 1.1 christos *sp = NULL; 165 1.1 christos return; 166 1.1 christos } 167 1.1 christos if (item->type == WINDOW_TREE_WINDOW) { 168 1.1 christos *wp = (*wlp)->window->active; 169 1.1 christos return; 170 1.1 christos } 171 1.1 christos 172 1.1 christos *wp = window_pane_find_by_id(item->pane); 173 1.1 christos if (!window_has_pane((*wlp)->window, *wp)) 174 1.1 christos *wp = NULL; 175 1.1 christos if (*wp == NULL) { 176 1.1 christos *sp = NULL; 177 1.1 christos *wlp = NULL; 178 1.1 christos return; 179 1.1 christos } 180 1.1 christos } 181 1.1 christos 182 1.1 christos static struct window_tree_itemdata * 183 1.1 christos window_tree_add_item(struct window_tree_modedata *data) 184 1.1 christos { 185 1.1 christos struct window_tree_itemdata *item; 186 1.1 christos 187 1.1 christos data->item_list = xreallocarray(data->item_list, data->item_size + 1, 188 1.1 christos sizeof *data->item_list); 189 1.1 christos item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item); 190 1.1 christos return (item); 191 1.1 christos } 192 1.1 christos 193 1.1 christos static void 194 1.1 christos window_tree_free_item(struct window_tree_itemdata *item) 195 1.1 christos { 196 1.1 christos free(item); 197 1.1 christos } 198 1.1 christos 199 1.1 christos static int 200 1.7 christos window_tree_cmp_session(const void *a0, const void *b0) 201 1.1 christos { 202 1.7 christos const struct session *const *a = a0; 203 1.7 christos const struct session *const *b = b0; 204 1.7 christos const struct session *sa = *a; 205 1.7 christos const struct session *sb = *b; 206 1.7 christos int result = 0; 207 1.1 christos 208 1.7 christos switch (window_tree_sort->field) { 209 1.7 christos case WINDOW_TREE_BY_INDEX: 210 1.7 christos result = sa->id - sb->id; 211 1.7 christos break; 212 1.7 christos case WINDOW_TREE_BY_TIME: 213 1.7 christos if (timercmp(&sa->activity_time, &sb->activity_time, >)) { 214 1.7 christos result = -1; 215 1.7 christos break; 216 1.7 christos } 217 1.7 christos if (timercmp(&sa->activity_time, &sb->activity_time, <)) { 218 1.7 christos result = 1; 219 1.7 christos break; 220 1.7 christos } 221 1.7 christos /* FALLTHROUGH */ 222 1.7 christos case WINDOW_TREE_BY_NAME: 223 1.7 christos result = strcmp(sa->name, sb->name); 224 1.7 christos break; 225 1.7 christos } 226 1.1 christos 227 1.7 christos if (window_tree_sort->reversed) 228 1.7 christos result = -result; 229 1.7 christos return (result); 230 1.1 christos } 231 1.1 christos 232 1.1 christos static int 233 1.7 christos window_tree_cmp_window(const void *a0, const void *b0) 234 1.1 christos { 235 1.7 christos const struct winlink *const *a = a0; 236 1.7 christos const struct winlink *const *b = b0; 237 1.7 christos const struct winlink *wla = *a; 238 1.7 christos const struct winlink *wlb = *b; 239 1.7 christos struct window *wa = wla->window; 240 1.7 christos struct window *wb = wlb->window; 241 1.7 christos int result = 0; 242 1.1 christos 243 1.7 christos switch (window_tree_sort->field) { 244 1.7 christos case WINDOW_TREE_BY_INDEX: 245 1.7 christos result = wla->idx - wlb->idx; 246 1.7 christos break; 247 1.7 christos case WINDOW_TREE_BY_TIME: 248 1.7 christos if (timercmp(&wa->activity_time, &wb->activity_time, >)) { 249 1.7 christos result = -1; 250 1.7 christos break; 251 1.7 christos } 252 1.7 christos if (timercmp(&wa->activity_time, &wb->activity_time, <)) { 253 1.7 christos result = 1; 254 1.7 christos break; 255 1.7 christos } 256 1.7 christos /* FALLTHROUGH */ 257 1.7 christos case WINDOW_TREE_BY_NAME: 258 1.7 christos result = strcmp(wa->name, wb->name); 259 1.7 christos break; 260 1.7 christos } 261 1.1 christos 262 1.7 christos if (window_tree_sort->reversed) 263 1.7 christos result = -result; 264 1.7 christos return (result); 265 1.1 christos } 266 1.1 christos 267 1.1 christos static int 268 1.7 christos window_tree_cmp_pane(const void *a0, const void *b0) 269 1.1 christos { 270 1.11 wiz struct window_pane **a = (struct window_pane **)__UNCONST(a0); 271 1.11 wiz struct window_pane **b = (struct window_pane **)__UNCONST(b0); 272 1.11 wiz int result; 273 1.11 wiz u_int ai, bi; 274 1.1 christos 275 1.7 christos if (window_tree_sort->field == WINDOW_TREE_BY_TIME) 276 1.7 christos result = (*a)->active_point - (*b)->active_point; 277 1.7 christos else { 278 1.7 christos /* 279 1.7 christos * Panes don't have names, so use number order for any other 280 1.7 christos * sort field. 281 1.7 christos */ 282 1.11 wiz window_pane_index(*a, &ai); 283 1.11 wiz window_pane_index(*b, &bi); 284 1.11 wiz result = ai - bi; 285 1.7 christos } 286 1.7 christos if (window_tree_sort->reversed) 287 1.7 christos result = -result; 288 1.7 christos return (result); 289 1.1 christos } 290 1.1 christos 291 1.1 christos static void 292 1.1 christos window_tree_build_pane(struct session *s, struct winlink *wl, 293 1.1 christos struct window_pane *wp, void *modedata, struct mode_tree_item *parent) 294 1.1 christos { 295 1.1 christos struct window_tree_modedata *data = modedata; 296 1.1 christos struct window_tree_itemdata *item; 297 1.12 wiz struct mode_tree_item *mti; 298 1.1 christos char *name, *text; 299 1.1 christos u_int idx; 300 1.12 wiz struct format_tree *ft; 301 1.1 christos 302 1.1 christos window_pane_index(wp, &idx); 303 1.1 christos 304 1.1 christos item = window_tree_add_item(data); 305 1.1 christos item->type = WINDOW_TREE_PANE; 306 1.1 christos item->session = s->id; 307 1.1 christos item->winlink = wl->idx; 308 1.1 christos item->pane = wp->id; 309 1.1 christos 310 1.12 wiz ft = format_create(NULL, NULL, FORMAT_PANE|wp->id, 0); 311 1.12 wiz format_defaults(ft, NULL, s, wl, wp); 312 1.12 wiz text = format_expand(ft, data->format); 313 1.1 christos xasprintf(&name, "%u", idx); 314 1.12 wiz format_free(ft); 315 1.1 christos 316 1.12 wiz mti = mode_tree_add(data->data, parent, item, (uintptr_t)wp, name, text, 317 1.12 wiz -1); 318 1.1 christos free(text); 319 1.1 christos free(name); 320 1.12 wiz mode_tree_align(mti, 1); 321 1.1 christos } 322 1.1 christos 323 1.1 christos static int 324 1.1 christos window_tree_filter_pane(struct session *s, struct winlink *wl, 325 1.1 christos struct window_pane *wp, const char *filter) 326 1.1 christos { 327 1.1 christos char *cp; 328 1.1 christos int result; 329 1.1 christos 330 1.1 christos if (filter == NULL) 331 1.1 christos return (1); 332 1.1 christos 333 1.1 christos cp = format_single(NULL, filter, NULL, s, wl, wp); 334 1.1 christos result = format_true(cp); 335 1.1 christos free(cp); 336 1.1 christos 337 1.1 christos return (result); 338 1.1 christos } 339 1.1 christos 340 1.1 christos static int 341 1.7 christos window_tree_build_window(struct session *s, struct winlink *wl, 342 1.7 christos void *modedata, struct mode_tree_sort_criteria *sort_crit, 343 1.7 christos struct mode_tree_item *parent, const char *filter) 344 1.1 christos { 345 1.1 christos struct window_tree_modedata *data = modedata; 346 1.1 christos struct window_tree_itemdata *item; 347 1.1 christos struct mode_tree_item *mti; 348 1.1 christos char *name, *text; 349 1.1 christos struct window_pane *wp, **l; 350 1.1 christos u_int n, i; 351 1.1 christos int expanded; 352 1.12 wiz struct format_tree *ft; 353 1.1 christos 354 1.1 christos item = window_tree_add_item(data); 355 1.1 christos item->type = WINDOW_TREE_WINDOW; 356 1.1 christos item->session = s->id; 357 1.1 christos item->winlink = wl->idx; 358 1.1 christos item->pane = -1; 359 1.1 christos 360 1.12 wiz ft = format_create(NULL, NULL, FORMAT_PANE|wl->window->active->id, 0); 361 1.12 wiz format_defaults(ft, NULL, s, wl, NULL); 362 1.12 wiz text = format_expand(ft, data->format); 363 1.1 christos xasprintf(&name, "%u", wl->idx); 364 1.12 wiz format_free(ft); 365 1.1 christos 366 1.1 christos if (data->type == WINDOW_TREE_SESSION || 367 1.1 christos data->type == WINDOW_TREE_WINDOW) 368 1.1 christos expanded = 0; 369 1.1 christos else 370 1.1 christos expanded = 1; 371 1.2 kre mti = mode_tree_add(data->data, parent, item, (uintptr_t)wl, name, text, 372 1.1 christos expanded); 373 1.1 christos free(text); 374 1.1 christos free(name); 375 1.12 wiz mode_tree_align(mti, 1); 376 1.1 christos 377 1.3 christos if ((wp = TAILQ_FIRST(&wl->window->panes)) == NULL) 378 1.3 christos goto empty; 379 1.1 christos if (TAILQ_NEXT(wp, entry) == NULL) { 380 1.1 christos if (!window_tree_filter_pane(s, wl, wp, filter)) 381 1.1 christos goto empty; 382 1.1 christos } 383 1.1 christos 384 1.1 christos l = NULL; 385 1.1 christos n = 0; 386 1.1 christos 387 1.1 christos TAILQ_FOREACH(wp, &wl->window->panes, entry) { 388 1.1 christos if (!window_tree_filter_pane(s, wl, wp, filter)) 389 1.1 christos continue; 390 1.1 christos l = xreallocarray(l, n + 1, sizeof *l); 391 1.1 christos l[n++] = wp; 392 1.1 christos } 393 1.1 christos if (n == 0) 394 1.1 christos goto empty; 395 1.1 christos 396 1.7 christos window_tree_sort = sort_crit; 397 1.7 christos qsort(l, n, sizeof *l, window_tree_cmp_pane); 398 1.1 christos 399 1.1 christos for (i = 0; i < n; i++) 400 1.1 christos window_tree_build_pane(s, wl, l[i], modedata, mti); 401 1.1 christos free(l); 402 1.1 christos return (1); 403 1.1 christos 404 1.1 christos empty: 405 1.1 christos window_tree_free_item(item); 406 1.1 christos data->item_size--; 407 1.1 christos mode_tree_remove(data->data, mti); 408 1.1 christos return (0); 409 1.1 christos } 410 1.1 christos 411 1.1 christos static void 412 1.7 christos window_tree_build_session(struct session *s, void *modedata, 413 1.7 christos struct mode_tree_sort_criteria *sort_crit, const char *filter) 414 1.1 christos { 415 1.1 christos struct window_tree_modedata *data = modedata; 416 1.1 christos struct window_tree_itemdata *item; 417 1.1 christos struct mode_tree_item *mti; 418 1.1 christos char *text; 419 1.12 wiz struct winlink *wl = s->curw, **l; 420 1.1 christos u_int n, i, empty; 421 1.1 christos int expanded; 422 1.12 wiz struct format_tree *ft; 423 1.1 christos 424 1.1 christos item = window_tree_add_item(data); 425 1.1 christos item->type = WINDOW_TREE_SESSION; 426 1.1 christos item->session = s->id; 427 1.1 christos item->winlink = -1; 428 1.1 christos item->pane = -1; 429 1.1 christos 430 1.12 wiz ft = format_create(NULL, NULL, FORMAT_PANE|wl->window->active->id, 0); 431 1.12 wiz format_defaults(ft, NULL, s, NULL, NULL); 432 1.12 wiz text = format_expand(ft, data->format); 433 1.12 wiz format_free(ft); 434 1.1 christos 435 1.1 christos if (data->type == WINDOW_TREE_SESSION) 436 1.1 christos expanded = 0; 437 1.1 christos else 438 1.1 christos expanded = 1; 439 1.2 kre mti = mode_tree_add(data->data, NULL, item, (uintptr_t)s, s->name, text, 440 1.1 christos expanded); 441 1.1 christos free(text); 442 1.1 christos 443 1.1 christos l = NULL; 444 1.1 christos n = 0; 445 1.1 christos RB_FOREACH(wl, winlinks, &s->windows) { 446 1.1 christos l = xreallocarray(l, n + 1, sizeof *l); 447 1.1 christos l[n++] = wl; 448 1.1 christos } 449 1.7 christos window_tree_sort = sort_crit; 450 1.7 christos qsort(l, n, sizeof *l, window_tree_cmp_window); 451 1.1 christos 452 1.1 christos empty = 0; 453 1.1 christos for (i = 0; i < n; i++) { 454 1.7 christos if (!window_tree_build_window(s, l[i], modedata, sort_crit, mti, 455 1.1 christos filter)) 456 1.1 christos empty++; 457 1.1 christos } 458 1.1 christos if (empty == n) { 459 1.1 christos window_tree_free_item(item); 460 1.1 christos data->item_size--; 461 1.1 christos mode_tree_remove(data->data, mti); 462 1.1 christos } 463 1.1 christos free(l); 464 1.1 christos } 465 1.1 christos 466 1.1 christos static void 467 1.7 christos window_tree_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, 468 1.7 christos uint64_t *tag, const char *filter) 469 1.1 christos { 470 1.1 christos struct window_tree_modedata *data = modedata; 471 1.1 christos struct session *s, **l; 472 1.3 christos struct session_group *sg, *current; 473 1.1 christos u_int n, i; 474 1.1 christos 475 1.3 christos current = session_group_contains(data->fs.s); 476 1.3 christos 477 1.1 christos for (i = 0; i < data->item_size; i++) 478 1.1 christos window_tree_free_item(data->item_list[i]); 479 1.1 christos free(data->item_list); 480 1.1 christos data->item_list = NULL; 481 1.1 christos data->item_size = 0; 482 1.1 christos 483 1.1 christos l = NULL; 484 1.1 christos n = 0; 485 1.1 christos RB_FOREACH(s, sessions, &sessions) { 486 1.3 christos if (data->squash_groups && 487 1.3 christos (sg = session_group_contains(s)) != NULL) { 488 1.3 christos if ((sg == current && s != data->fs.s) || 489 1.3 christos (sg != current && s != TAILQ_FIRST(&sg->sessions))) 490 1.3 christos continue; 491 1.3 christos } 492 1.1 christos l = xreallocarray(l, n + 1, sizeof *l); 493 1.1 christos l[n++] = s; 494 1.1 christos } 495 1.7 christos window_tree_sort = sort_crit; 496 1.7 christos qsort(l, n, sizeof *l, window_tree_cmp_session); 497 1.1 christos 498 1.1 christos for (i = 0; i < n; i++) 499 1.7 christos window_tree_build_session(l[i], modedata, sort_crit, filter); 500 1.1 christos free(l); 501 1.1 christos 502 1.1 christos switch (data->type) { 503 1.1 christos case WINDOW_TREE_NONE: 504 1.1 christos break; 505 1.1 christos case WINDOW_TREE_SESSION: 506 1.2 kre *tag = (uintptr_t)data->fs.s; 507 1.1 christos break; 508 1.1 christos case WINDOW_TREE_WINDOW: 509 1.2 kre *tag = (uintptr_t)data->fs.wl; 510 1.1 christos break; 511 1.1 christos case WINDOW_TREE_PANE: 512 1.3 christos if (window_count_panes(data->fs.wl->window) == 1) 513 1.4 christos *tag = (uintptr_t)data->fs.wl; 514 1.3 christos else 515 1.4 christos *tag = (uintptr_t)data->fs.wp; 516 1.1 christos break; 517 1.1 christos } 518 1.1 christos } 519 1.1 christos 520 1.3 christos static void 521 1.3 christos window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py, 522 1.3 christos u_int sx, u_int sy, const struct grid_cell *gc, const char *label) 523 1.3 christos { 524 1.3 christos size_t len; 525 1.3 christos u_int ox, oy; 526 1.3 christos 527 1.3 christos len = strlen(label); 528 1.3 christos if (sx == 0 || sy == 1 || len > sx) 529 1.3 christos return; 530 1.3 christos ox = (sx - len + 1) / 2; 531 1.3 christos oy = (sy + 1) / 2; 532 1.3 christos 533 1.3 christos if (ox > 1 && ox + len < sx - 1 && sy >= 3) { 534 1.5 christos screen_write_cursormove(ctx, px + ox - 1, py + oy - 1, 0); 535 1.10 wiz screen_write_box(ctx, len + 2, 3, BOX_LINES_DEFAULT, NULL, 536 1.10 wiz NULL); 537 1.3 christos } 538 1.5 christos screen_write_cursormove(ctx, px + ox, py + oy, 0); 539 1.3 christos screen_write_puts(ctx, gc, "%s", label); 540 1.3 christos } 541 1.3 christos 542 1.1 christos static void 543 1.1 christos window_tree_draw_session(struct window_tree_modedata *data, struct session *s, 544 1.1 christos struct screen_write_ctx *ctx, u_int sx, u_int sy) 545 1.1 christos { 546 1.1 christos struct options *oo = s->options; 547 1.1 christos struct winlink *wl; 548 1.1 christos struct window *w; 549 1.3 christos u_int cx = ctx->s->cx, cy = ctx->s->cy; 550 1.1 christos u_int loop, total, visible, each, width, offset; 551 1.1 christos u_int current, start, end, remaining, i; 552 1.1 christos struct grid_cell gc; 553 1.1 christos int colour, active_colour, left, right; 554 1.1 christos char *label; 555 1.1 christos 556 1.1 christos total = winlink_count(&s->windows); 557 1.1 christos 558 1.1 christos memcpy(&gc, &grid_default_cell, sizeof gc); 559 1.1 christos colour = options_get_number(oo, "display-panes-colour"); 560 1.1 christos active_colour = options_get_number(oo, "display-panes-active-colour"); 561 1.1 christos 562 1.1 christos if (sx / total < 24) { 563 1.1 christos visible = sx / 24; 564 1.1 christos if (visible == 0) 565 1.1 christos visible = 1; 566 1.1 christos } else 567 1.1 christos visible = total; 568 1.1 christos 569 1.1 christos current = 0; 570 1.1 christos RB_FOREACH(wl, winlinks, &s->windows) { 571 1.1 christos if (wl == s->curw) 572 1.1 christos break; 573 1.1 christos current++; 574 1.1 christos } 575 1.1 christos 576 1.1 christos if (current < visible) { 577 1.1 christos start = 0; 578 1.1 christos end = visible; 579 1.1 christos } else if (current >= total - visible) { 580 1.1 christos start = total - visible; 581 1.1 christos end = total; 582 1.1 christos } else { 583 1.1 christos start = current - (visible / 2); 584 1.1 christos end = start + visible; 585 1.1 christos } 586 1.1 christos 587 1.1 christos if (data->offset < -(int)start) 588 1.1 christos data->offset = -(int)start; 589 1.1 christos if (data->offset > (int)(total - end)) 590 1.1 christos data->offset = (int)(total - end); 591 1.1 christos start += data->offset; 592 1.1 christos end += data->offset; 593 1.1 christos 594 1.1 christos left = (start != 0); 595 1.1 christos right = (end != total); 596 1.1 christos if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) 597 1.1 christos left = right = 0; 598 1.1 christos if (left && right) { 599 1.1 christos each = (sx - 6) / visible; 600 1.1 christos remaining = (sx - 6) - (visible * each); 601 1.1 christos } else if (left || right) { 602 1.1 christos each = (sx - 3) / visible; 603 1.1 christos remaining = (sx - 3) - (visible * each); 604 1.1 christos } else { 605 1.1 christos each = sx / visible; 606 1.1 christos remaining = sx - (visible * each); 607 1.1 christos } 608 1.1 christos if (each == 0) 609 1.1 christos return; 610 1.1 christos 611 1.1 christos if (left) { 612 1.3 christos data->left = cx + 2; 613 1.5 christos screen_write_cursormove(ctx, cx + 2, cy, 0); 614 1.1 christos screen_write_vline(ctx, sy, 0, 0); 615 1.5 christos screen_write_cursormove(ctx, cx, cy + sy / 2, 0); 616 1.1 christos screen_write_puts(ctx, &grid_default_cell, "<"); 617 1.3 christos } else 618 1.3 christos data->left = -1; 619 1.1 christos if (right) { 620 1.3 christos data->right = cx + sx - 3; 621 1.5 christos screen_write_cursormove(ctx, cx + sx - 3, cy, 0); 622 1.1 christos screen_write_vline(ctx, sy, 0, 0); 623 1.5 christos screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0); 624 1.1 christos screen_write_puts(ctx, &grid_default_cell, ">"); 625 1.3 christos } else 626 1.3 christos data->right = -1; 627 1.3 christos 628 1.3 christos data->start = start; 629 1.3 christos data->end = end; 630 1.3 christos data->each = each; 631 1.1 christos 632 1.1 christos i = loop = 0; 633 1.1 christos RB_FOREACH(wl, winlinks, &s->windows) { 634 1.1 christos if (loop == end) 635 1.1 christos break; 636 1.1 christos if (loop < start) { 637 1.1 christos loop++; 638 1.1 christos continue; 639 1.1 christos } 640 1.1 christos w = wl->window; 641 1.1 christos 642 1.1 christos if (wl == s->curw) 643 1.1 christos gc.fg = active_colour; 644 1.1 christos else 645 1.1 christos gc.fg = colour; 646 1.1 christos 647 1.1 christos if (left) 648 1.1 christos offset = 3 + (i * each); 649 1.1 christos else 650 1.1 christos offset = (i * each); 651 1.1 christos if (loop == end - 1) 652 1.1 christos width = each + remaining; 653 1.1 christos else 654 1.1 christos width = each - 1; 655 1.1 christos 656 1.5 christos screen_write_cursormove(ctx, cx + offset, cy, 0); 657 1.1 christos screen_write_preview(ctx, &w->active->base, width, sy); 658 1.1 christos 659 1.1 christos xasprintf(&label, " %u:%s ", wl->idx, w->name); 660 1.12 wiz if (strlen(label) > width) { 661 1.12 wiz free(label); 662 1.1 christos xasprintf(&label, " %u ", wl->idx); 663 1.12 wiz } 664 1.3 christos window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc, 665 1.3 christos label); 666 1.1 christos free(label); 667 1.1 christos 668 1.1 christos if (loop != end - 1) { 669 1.5 christos screen_write_cursormove(ctx, cx + offset + width, cy, 0); 670 1.1 christos screen_write_vline(ctx, sy, 0, 0); 671 1.1 christos } 672 1.1 christos loop++; 673 1.1 christos 674 1.1 christos i++; 675 1.1 christos } 676 1.1 christos } 677 1.1 christos 678 1.1 christos static void 679 1.1 christos window_tree_draw_window(struct window_tree_modedata *data, struct session *s, 680 1.1 christos struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy) 681 1.1 christos { 682 1.1 christos struct options *oo = s->options; 683 1.1 christos struct window_pane *wp; 684 1.3 christos u_int cx = ctx->s->cx, cy = ctx->s->cy; 685 1.1 christos u_int loop, total, visible, each, width, offset; 686 1.11 wiz u_int current, start, end, remaining, i, pane_idx; 687 1.1 christos struct grid_cell gc; 688 1.1 christos int colour, active_colour, left, right; 689 1.1 christos char *label; 690 1.1 christos 691 1.1 christos total = window_count_panes(w); 692 1.1 christos 693 1.1 christos memcpy(&gc, &grid_default_cell, sizeof gc); 694 1.1 christos colour = options_get_number(oo, "display-panes-colour"); 695 1.1 christos active_colour = options_get_number(oo, "display-panes-active-colour"); 696 1.1 christos 697 1.1 christos if (sx / total < 24) { 698 1.1 christos visible = sx / 24; 699 1.1 christos if (visible == 0) 700 1.1 christos visible = 1; 701 1.1 christos } else 702 1.1 christos visible = total; 703 1.1 christos 704 1.1 christos current = 0; 705 1.1 christos TAILQ_FOREACH(wp, &w->panes, entry) { 706 1.1 christos if (wp == w->active) 707 1.1 christos break; 708 1.1 christos current++; 709 1.1 christos } 710 1.1 christos 711 1.1 christos if (current < visible) { 712 1.1 christos start = 0; 713 1.1 christos end = visible; 714 1.1 christos } else if (current >= total - visible) { 715 1.1 christos start = total - visible; 716 1.1 christos end = total; 717 1.1 christos } else { 718 1.1 christos start = current - (visible / 2); 719 1.1 christos end = start + visible; 720 1.1 christos } 721 1.1 christos 722 1.1 christos if (data->offset < -(int)start) 723 1.1 christos data->offset = -(int)start; 724 1.1 christos if (data->offset > (int)(total - end)) 725 1.1 christos data->offset = (int)(total - end); 726 1.1 christos start += data->offset; 727 1.1 christos end += data->offset; 728 1.1 christos 729 1.1 christos left = (start != 0); 730 1.1 christos right = (end != total); 731 1.1 christos if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) 732 1.1 christos left = right = 0; 733 1.1 christos if (left && right) { 734 1.1 christos each = (sx - 6) / visible; 735 1.1 christos remaining = (sx - 6) - (visible * each); 736 1.1 christos } else if (left || right) { 737 1.1 christos each = (sx - 3) / visible; 738 1.1 christos remaining = (sx - 3) - (visible * each); 739 1.1 christos } else { 740 1.1 christos each = sx / visible; 741 1.1 christos remaining = sx - (visible * each); 742 1.1 christos } 743 1.1 christos if (each == 0) 744 1.1 christos return; 745 1.1 christos 746 1.1 christos if (left) { 747 1.3 christos data->left = cx + 2; 748 1.5 christos screen_write_cursormove(ctx, cx + 2, cy, 0); 749 1.1 christos screen_write_vline(ctx, sy, 0, 0); 750 1.5 christos screen_write_cursormove(ctx, cx, cy + sy / 2, 0); 751 1.1 christos screen_write_puts(ctx, &grid_default_cell, "<"); 752 1.3 christos } else 753 1.3 christos data->left = -1; 754 1.1 christos if (right) { 755 1.3 christos data->right = cx + sx - 3; 756 1.5 christos screen_write_cursormove(ctx, cx + sx - 3, cy, 0); 757 1.1 christos screen_write_vline(ctx, sy, 0, 0); 758 1.5 christos screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0); 759 1.1 christos screen_write_puts(ctx, &grid_default_cell, ">"); 760 1.3 christos } else 761 1.3 christos data->right = -1; 762 1.3 christos 763 1.3 christos data->start = start; 764 1.3 christos data->end = end; 765 1.3 christos data->each = each; 766 1.1 christos 767 1.1 christos i = loop = 0; 768 1.1 christos TAILQ_FOREACH(wp, &w->panes, entry) { 769 1.1 christos if (loop == end) 770 1.1 christos break; 771 1.1 christos if (loop < start) { 772 1.1 christos loop++; 773 1.1 christos continue; 774 1.1 christos } 775 1.1 christos 776 1.1 christos if (wp == w->active) 777 1.1 christos gc.fg = active_colour; 778 1.1 christos else 779 1.1 christos gc.fg = colour; 780 1.1 christos 781 1.1 christos if (left) 782 1.1 christos offset = 3 + (i * each); 783 1.1 christos else 784 1.1 christos offset = (i * each); 785 1.1 christos if (loop == end - 1) 786 1.1 christos width = each + remaining; 787 1.1 christos else 788 1.1 christos width = each - 1; 789 1.1 christos 790 1.5 christos screen_write_cursormove(ctx, cx + offset, cy, 0); 791 1.1 christos screen_write_preview(ctx, &wp->base, width, sy); 792 1.1 christos 793 1.3 christos if (window_pane_index(wp, &pane_idx) != 0) 794 1.3 christos pane_idx = loop; 795 1.3 christos xasprintf(&label, " %u ", pane_idx); 796 1.3 christos window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc, 797 1.3 christos label); 798 1.1 christos free(label); 799 1.1 christos 800 1.1 christos if (loop != end - 1) { 801 1.5 christos screen_write_cursormove(ctx, cx + offset + width, cy, 0); 802 1.1 christos screen_write_vline(ctx, sy, 0, 0); 803 1.1 christos } 804 1.1 christos loop++; 805 1.1 christos 806 1.1 christos i++; 807 1.1 christos } 808 1.1 christos } 809 1.1 christos 810 1.3 christos static void 811 1.3 christos window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, 812 1.3 christos u_int sx, u_int sy) 813 1.1 christos { 814 1.1 christos struct window_tree_itemdata *item = itemdata; 815 1.1 christos struct session *sp; 816 1.1 christos struct winlink *wlp; 817 1.1 christos struct window_pane *wp; 818 1.1 christos 819 1.1 christos window_tree_pull_item(item, &sp, &wlp, &wp); 820 1.1 christos if (wp == NULL) 821 1.3 christos return; 822 1.1 christos 823 1.1 christos switch (item->type) { 824 1.1 christos case WINDOW_TREE_NONE: 825 1.3 christos break; 826 1.1 christos case WINDOW_TREE_SESSION: 827 1.3 christos window_tree_draw_session(modedata, sp, ctx, sx, sy); 828 1.1 christos break; 829 1.1 christos case WINDOW_TREE_WINDOW: 830 1.3 christos window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy); 831 1.1 christos break; 832 1.1 christos case WINDOW_TREE_PANE: 833 1.3 christos screen_write_preview(ctx, &wp->base, sx, sy); 834 1.1 christos break; 835 1.1 christos } 836 1.1 christos } 837 1.1 christos 838 1.1 christos static int 839 1.12 wiz window_tree_search(__unused void *modedata, void *itemdata, const char *ss, 840 1.12 wiz int icase) 841 1.1 christos { 842 1.1 christos struct window_tree_itemdata *item = itemdata; 843 1.1 christos struct session *s; 844 1.1 christos struct winlink *wl; 845 1.1 christos struct window_pane *wp; 846 1.6 christos char *cmd; 847 1.6 christos int retval; 848 1.1 christos 849 1.1 christos window_tree_pull_item(item, &s, &wl, &wp); 850 1.1 christos 851 1.1 christos switch (item->type) { 852 1.1 christos case WINDOW_TREE_NONE: 853 1.1 christos return (0); 854 1.1 christos case WINDOW_TREE_SESSION: 855 1.1 christos if (s == NULL) 856 1.1 christos return (0); 857 1.12 wiz if (icase) 858 1.12 wiz return (strcasestr(s->name, ss) != NULL); 859 1.12 wiz return (strstr(s->name, ss) != NULL); 860 1.1 christos case WINDOW_TREE_WINDOW: 861 1.1 christos if (s == NULL || wl == NULL) 862 1.1 christos return (0); 863 1.12 wiz if (icase) 864 1.12 wiz return (strcasestr(wl->window->name, ss) != NULL); 865 1.1 christos return (strstr(wl->window->name, ss) != NULL); 866 1.1 christos case WINDOW_TREE_PANE: 867 1.1 christos if (s == NULL || wl == NULL || wp == NULL) 868 1.1 christos break; 869 1.1 christos cmd = osdep_get_name(wp->fd, wp->tty); 870 1.12 wiz if (cmd == NULL || *cmd == '\0') { 871 1.12 wiz free(cmd); 872 1.1 christos return (0); 873 1.12 wiz } 874 1.12 wiz if (icase) 875 1.12 wiz retval = (strcasestr(cmd, ss) != NULL); 876 1.12 wiz else 877 1.12 wiz retval = (strstr(cmd, ss) != NULL); 878 1.6 christos free(cmd); 879 1.8 christos return (retval); 880 1.1 christos } 881 1.1 christos return (0); 882 1.1 christos } 883 1.1 christos 884 1.6 christos static void 885 1.6 christos window_tree_menu(void *modedata, struct client *c, key_code key) 886 1.6 christos { 887 1.6 christos struct window_tree_modedata *data = modedata; 888 1.6 christos struct window_pane *wp = data->wp; 889 1.6 christos struct window_mode_entry *wme; 890 1.6 christos 891 1.6 christos wme = TAILQ_FIRST(&wp->modes); 892 1.6 christos if (wme == NULL || wme->data != modedata) 893 1.6 christos return; 894 1.6 christos window_tree_key(wme, c, NULL, NULL, key, NULL); 895 1.6 christos } 896 1.6 christos 897 1.8 christos static key_code 898 1.8 christos window_tree_get_key(void *modedata, void *itemdata, u_int line) 899 1.8 christos { 900 1.8 christos struct window_tree_modedata *data = modedata; 901 1.8 christos struct window_tree_itemdata *item = itemdata; 902 1.8 christos struct format_tree *ft; 903 1.8 christos struct session *s; 904 1.8 christos struct winlink *wl; 905 1.8 christos struct window_pane *wp; 906 1.8 christos char *expanded; 907 1.8 christos key_code key; 908 1.8 christos 909 1.8 christos ft = format_create(NULL, NULL, FORMAT_NONE, 0); 910 1.8 christos window_tree_pull_item(item, &s, &wl, &wp); 911 1.8 christos if (item->type == WINDOW_TREE_SESSION) 912 1.8 christos format_defaults(ft, NULL, s, NULL, NULL); 913 1.8 christos else if (item->type == WINDOW_TREE_WINDOW) 914 1.8 christos format_defaults(ft, NULL, s, wl, NULL); 915 1.8 christos else 916 1.8 christos format_defaults(ft, NULL, s, wl, wp); 917 1.8 christos format_add(ft, "line", "%u", line); 918 1.8 christos 919 1.8 christos expanded = format_expand(ft, data->key_format); 920 1.8 christos key = key_string_lookup_string(expanded); 921 1.8 christos free(expanded); 922 1.8 christos format_free(ft); 923 1.10 wiz return (key); 924 1.8 christos } 925 1.8 christos 926 1.12 wiz static int 927 1.12 wiz window_tree_swap(void *cur_itemdata, void *other_itemdata) 928 1.12 wiz { 929 1.12 wiz struct window_tree_itemdata *cur = cur_itemdata; 930 1.12 wiz struct window_tree_itemdata *other = other_itemdata; 931 1.12 wiz struct session *cur_session, *other_session; 932 1.12 wiz struct winlink *cur_winlink, *other_winlink; 933 1.12 wiz struct window *cur_window, *other_window; 934 1.12 wiz struct window_pane *cur_pane, *other_pane; 935 1.12 wiz 936 1.12 wiz if (cur->type != other->type) 937 1.12 wiz return (0); 938 1.12 wiz if (cur->type != WINDOW_TREE_WINDOW) 939 1.12 wiz return (0); 940 1.12 wiz 941 1.12 wiz window_tree_pull_item(cur, &cur_session, &cur_winlink, &cur_pane); 942 1.12 wiz window_tree_pull_item(other, &other_session, &other_winlink, 943 1.12 wiz &other_pane); 944 1.12 wiz 945 1.12 wiz if (cur_session != other_session) 946 1.12 wiz return (0); 947 1.12 wiz 948 1.12 wiz if (window_tree_sort->field != WINDOW_TREE_BY_INDEX && 949 1.12 wiz window_tree_cmp_window(&cur_winlink, &other_winlink) != 0) { 950 1.12 wiz /* 951 1.12 wiz * Swapping indexes would not swap positions in the tree, so 952 1.12 wiz * prevent swapping to avoid confusing the user. 953 1.12 wiz */ 954 1.12 wiz return (0); 955 1.12 wiz } 956 1.12 wiz 957 1.12 wiz other_window = other_winlink->window; 958 1.12 wiz TAILQ_REMOVE(&other_window->winlinks, other_winlink, wentry); 959 1.12 wiz cur_window = cur_winlink->window; 960 1.12 wiz TAILQ_REMOVE(&cur_window->winlinks, cur_winlink, wentry); 961 1.12 wiz 962 1.12 wiz other_winlink->window = cur_window; 963 1.12 wiz TAILQ_INSERT_TAIL(&cur_window->winlinks, other_winlink, wentry); 964 1.12 wiz cur_winlink->window = other_window; 965 1.12 wiz TAILQ_INSERT_TAIL(&other_window->winlinks, cur_winlink, wentry); 966 1.12 wiz 967 1.12 wiz if (cur_session->curw == cur_winlink) 968 1.12 wiz session_set_current(cur_session, other_winlink); 969 1.12 wiz else if (cur_session->curw == other_winlink) 970 1.12 wiz session_set_current(cur_session, cur_winlink); 971 1.12 wiz session_group_synchronize_from(cur_session); 972 1.12 wiz server_redraw_session_group(cur_session); 973 1.12 wiz recalculate_sizes(); 974 1.12 wiz 975 1.12 wiz return (1); 976 1.12 wiz } 977 1.12 wiz 978 1.1 christos static struct screen * 979 1.5 christos window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, 980 1.1 christos struct args *args) 981 1.1 christos { 982 1.5 christos struct window_pane *wp = wme->wp; 983 1.1 christos struct window_tree_modedata *data; 984 1.1 christos struct screen *s; 985 1.1 christos 986 1.5 christos wme->data = data = xcalloc(1, sizeof *data); 987 1.6 christos data->wp = wp; 988 1.6 christos data->references = 1; 989 1.1 christos 990 1.1 christos if (args_has(args, 's')) 991 1.1 christos data->type = WINDOW_TREE_SESSION; 992 1.1 christos else if (args_has(args, 'w')) 993 1.1 christos data->type = WINDOW_TREE_WINDOW; 994 1.1 christos else 995 1.1 christos data->type = WINDOW_TREE_PANE; 996 1.1 christos memcpy(&data->fs, fs, sizeof data->fs); 997 1.1 christos 998 1.1 christos if (args == NULL || !args_has(args, 'F')) 999 1.1 christos data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT); 1000 1.1 christos else 1001 1.1 christos data->format = xstrdup(args_get(args, 'F')); 1002 1.8 christos if (args == NULL || !args_has(args, 'K')) 1003 1.8 christos data->key_format = xstrdup(WINDOW_TREE_DEFAULT_KEY_FORMAT); 1004 1.8 christos else 1005 1.8 christos data->key_format = xstrdup(args_get(args, 'K')); 1006 1.10 wiz if (args == NULL || args_count(args) == 0) 1007 1.1 christos data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND); 1008 1.1 christos else 1009 1.10 wiz data->command = xstrdup(args_string(args, 0)); 1010 1.3 christos data->squash_groups = !args_has(args, 'G'); 1011 1.12 wiz if (args_has(args, 'y')) 1012 1.12 wiz data->prompt_flags = PROMPT_ACCEPT; 1013 1.1 christos 1014 1.1 christos data->data = mode_tree_start(wp, args, window_tree_build, 1015 1.8 christos window_tree_draw, window_tree_search, window_tree_menu, NULL, 1016 1.12 wiz window_tree_get_key, window_tree_swap, data, window_tree_menu_items, 1017 1.8 christos window_tree_sort_list, nitems(window_tree_sort_list), &s); 1018 1.3 christos mode_tree_zoom(data->data, args); 1019 1.1 christos 1020 1.1 christos mode_tree_build(data->data); 1021 1.1 christos mode_tree_draw(data->data); 1022 1.1 christos 1023 1.1 christos data->type = WINDOW_TREE_NONE; 1024 1.1 christos 1025 1.1 christos return (s); 1026 1.1 christos } 1027 1.1 christos 1028 1.1 christos static void 1029 1.1 christos window_tree_destroy(struct window_tree_modedata *data) 1030 1.1 christos { 1031 1.1 christos u_int i; 1032 1.1 christos 1033 1.1 christos if (--data->references != 0) 1034 1.1 christos return; 1035 1.1 christos 1036 1.1 christos for (i = 0; i < data->item_size; i++) 1037 1.1 christos window_tree_free_item(data->item_list[i]); 1038 1.1 christos free(data->item_list); 1039 1.1 christos 1040 1.1 christos free(data->format); 1041 1.8 christos free(data->key_format); 1042 1.1 christos free(data->command); 1043 1.1 christos 1044 1.1 christos free(data); 1045 1.1 christos } 1046 1.1 christos 1047 1.1 christos static void 1048 1.5 christos window_tree_free(struct window_mode_entry *wme) 1049 1.1 christos { 1050 1.5 christos struct window_tree_modedata *data = wme->data; 1051 1.1 christos 1052 1.1 christos if (data == NULL) 1053 1.1 christos return; 1054 1.1 christos 1055 1.1 christos data->dead = 1; 1056 1.3 christos mode_tree_free(data->data); 1057 1.1 christos window_tree_destroy(data); 1058 1.1 christos } 1059 1.1 christos 1060 1.1 christos static void 1061 1.5 christos window_tree_resize(struct window_mode_entry *wme, u_int sx, u_int sy) 1062 1.1 christos { 1063 1.5 christos struct window_tree_modedata *data = wme->data; 1064 1.1 christos 1065 1.1 christos mode_tree_resize(data->data, sx, sy); 1066 1.1 christos } 1067 1.1 christos 1068 1.8 christos static void 1069 1.8 christos window_tree_update(struct window_mode_entry *wme) 1070 1.8 christos { 1071 1.8 christos struct window_tree_modedata *data = wme->data; 1072 1.8 christos 1073 1.8 christos mode_tree_build(data->data); 1074 1.8 christos mode_tree_draw(data->data); 1075 1.8 christos data->wp->flags |= PANE_REDRAW; 1076 1.8 christos } 1077 1.8 christos 1078 1.1 christos static char * 1079 1.1 christos window_tree_get_target(struct window_tree_itemdata *item, 1080 1.1 christos struct cmd_find_state *fs) 1081 1.1 christos { 1082 1.1 christos struct session *s; 1083 1.1 christos struct winlink *wl; 1084 1.1 christos struct window_pane *wp; 1085 1.1 christos char *target; 1086 1.1 christos 1087 1.1 christos window_tree_pull_item(item, &s, &wl, &wp); 1088 1.1 christos 1089 1.1 christos target = NULL; 1090 1.1 christos switch (item->type) { 1091 1.1 christos case WINDOW_TREE_NONE: 1092 1.1 christos break; 1093 1.1 christos case WINDOW_TREE_SESSION: 1094 1.1 christos if (s == NULL) 1095 1.1 christos break; 1096 1.1 christos xasprintf(&target, "=%s:", s->name); 1097 1.1 christos break; 1098 1.1 christos case WINDOW_TREE_WINDOW: 1099 1.1 christos if (s == NULL || wl == NULL) 1100 1.1 christos break; 1101 1.1 christos xasprintf(&target, "=%s:%u.", s->name, wl->idx); 1102 1.1 christos break; 1103 1.1 christos case WINDOW_TREE_PANE: 1104 1.1 christos if (s == NULL || wl == NULL || wp == NULL) 1105 1.1 christos break; 1106 1.1 christos xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id); 1107 1.1 christos break; 1108 1.1 christos } 1109 1.1 christos if (target == NULL) 1110 1.1 christos cmd_find_clear_state(fs, 0); 1111 1.1 christos else 1112 1.1 christos cmd_find_from_winlink_pane(fs, wl, wp, 0); 1113 1.1 christos return (target); 1114 1.1 christos } 1115 1.1 christos 1116 1.1 christos static void 1117 1.7 christos window_tree_command_each(void *modedata, void *itemdata, struct client *c, 1118 1.3 christos __unused key_code key) 1119 1.1 christos { 1120 1.1 christos struct window_tree_modedata *data = modedata; 1121 1.1 christos struct window_tree_itemdata *item = itemdata; 1122 1.1 christos char *name; 1123 1.1 christos struct cmd_find_state fs; 1124 1.1 christos 1125 1.1 christos name = window_tree_get_target(item, &fs); 1126 1.1 christos if (name != NULL) 1127 1.3 christos mode_tree_run_command(c, &fs, data->entered, name); 1128 1.1 christos free(name); 1129 1.1 christos } 1130 1.1 christos 1131 1.1 christos static enum cmd_retval 1132 1.1 christos window_tree_command_done(__unused struct cmdq_item *item, void *modedata) 1133 1.1 christos { 1134 1.1 christos struct window_tree_modedata *data = modedata; 1135 1.1 christos 1136 1.1 christos if (!data->dead) { 1137 1.1 christos mode_tree_build(data->data); 1138 1.1 christos mode_tree_draw(data->data); 1139 1.1 christos data->wp->flags |= PANE_REDRAW; 1140 1.1 christos } 1141 1.1 christos window_tree_destroy(data); 1142 1.1 christos return (CMD_RETURN_NORMAL); 1143 1.1 christos } 1144 1.1 christos 1145 1.1 christos static int 1146 1.1 christos window_tree_command_callback(struct client *c, void *modedata, const char *s, 1147 1.1 christos __unused int done) 1148 1.1 christos { 1149 1.1 christos struct window_tree_modedata *data = modedata; 1150 1.1 christos 1151 1.3 christos if (s == NULL || *s == '\0' || data->dead) 1152 1.1 christos return (0); 1153 1.1 christos 1154 1.1 christos data->entered = s; 1155 1.3 christos mode_tree_each_tagged(data->data, window_tree_command_each, c, 1156 1.3 christos KEYC_NONE, 1); 1157 1.1 christos data->entered = NULL; 1158 1.1 christos 1159 1.1 christos data->references++; 1160 1.1 christos cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1161 1.1 christos 1162 1.1 christos return (0); 1163 1.1 christos } 1164 1.1 christos 1165 1.1 christos static void 1166 1.1 christos window_tree_command_free(void *modedata) 1167 1.1 christos { 1168 1.1 christos struct window_tree_modedata *data = modedata; 1169 1.1 christos 1170 1.1 christos window_tree_destroy(data); 1171 1.1 christos } 1172 1.1 christos 1173 1.1 christos static void 1174 1.7 christos window_tree_kill_each(__unused void *modedata, void *itemdata, 1175 1.3 christos __unused struct client *c, __unused key_code key) 1176 1.3 christos { 1177 1.3 christos struct window_tree_itemdata *item = itemdata; 1178 1.3 christos struct session *s; 1179 1.3 christos struct winlink *wl; 1180 1.3 christos struct window_pane *wp; 1181 1.3 christos 1182 1.3 christos window_tree_pull_item(item, &s, &wl, &wp); 1183 1.3 christos 1184 1.3 christos switch (item->type) { 1185 1.3 christos case WINDOW_TREE_NONE: 1186 1.3 christos break; 1187 1.3 christos case WINDOW_TREE_SESSION: 1188 1.3 christos if (s != NULL) { 1189 1.3 christos server_destroy_session(s); 1190 1.6 christos session_destroy(s, 1, __func__); 1191 1.3 christos } 1192 1.3 christos break; 1193 1.3 christos case WINDOW_TREE_WINDOW: 1194 1.3 christos if (wl != NULL) 1195 1.8 christos server_kill_window(wl->window, 0); 1196 1.3 christos break; 1197 1.3 christos case WINDOW_TREE_PANE: 1198 1.3 christos if (wp != NULL) 1199 1.3 christos server_kill_pane(wp); 1200 1.3 christos break; 1201 1.3 christos } 1202 1.3 christos } 1203 1.3 christos 1204 1.3 christos static int 1205 1.3 christos window_tree_kill_current_callback(struct client *c, void *modedata, 1206 1.3 christos const char *s, __unused int done) 1207 1.3 christos { 1208 1.3 christos struct window_tree_modedata *data = modedata; 1209 1.3 christos struct mode_tree_data *mtd = data->data; 1210 1.3 christos 1211 1.3 christos if (s == NULL || *s == '\0' || data->dead) 1212 1.3 christos return (0); 1213 1.3 christos if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') 1214 1.3 christos return (0); 1215 1.3 christos 1216 1.3 christos window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE); 1217 1.8 christos server_renumber_all(); 1218 1.3 christos 1219 1.3 christos data->references++; 1220 1.3 christos cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1221 1.3 christos 1222 1.3 christos return (0); 1223 1.3 christos } 1224 1.3 christos 1225 1.3 christos static int 1226 1.3 christos window_tree_kill_tagged_callback(struct client *c, void *modedata, 1227 1.3 christos const char *s, __unused int done) 1228 1.3 christos { 1229 1.3 christos struct window_tree_modedata *data = modedata; 1230 1.3 christos struct mode_tree_data *mtd = data->data; 1231 1.3 christos 1232 1.3 christos if (s == NULL || *s == '\0' || data->dead) 1233 1.3 christos return (0); 1234 1.3 christos if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') 1235 1.3 christos return (0); 1236 1.3 christos 1237 1.3 christos mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1); 1238 1.8 christos server_renumber_all(); 1239 1.3 christos 1240 1.3 christos data->references++; 1241 1.3 christos cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); 1242 1.3 christos 1243 1.3 christos return (0); 1244 1.3 christos } 1245 1.3 christos 1246 1.3 christos static key_code 1247 1.3 christos window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x, 1248 1.3 christos struct window_tree_itemdata *item) 1249 1.3 christos { 1250 1.3 christos struct session *s; 1251 1.3 christos struct winlink *wl; 1252 1.3 christos struct window_pane *wp; 1253 1.3 christos u_int loop; 1254 1.3 christos 1255 1.3 christos if (key != KEYC_MOUSEDOWN1_PANE) 1256 1.3 christos return (KEYC_NONE); 1257 1.3 christos 1258 1.3 christos if (data->left != -1 && x <= (u_int)data->left) 1259 1.3 christos return ('<'); 1260 1.3 christos if (data->right != -1 && x >= (u_int)data->right) 1261 1.3 christos return ('>'); 1262 1.3 christos 1263 1.3 christos if (data->left != -1) 1264 1.3 christos x -= data->left; 1265 1.3 christos else if (x != 0) 1266 1.3 christos x--; 1267 1.3 christos if (x == 0 || data->end == 0) 1268 1.3 christos x = 0; 1269 1.3 christos else { 1270 1.3 christos x = x / data->each; 1271 1.3 christos if (data->start + x >= data->end) 1272 1.3 christos x = data->end - 1; 1273 1.3 christos } 1274 1.3 christos 1275 1.3 christos window_tree_pull_item(item, &s, &wl, &wp); 1276 1.3 christos if (item->type == WINDOW_TREE_SESSION) { 1277 1.3 christos if (s == NULL) 1278 1.3 christos return (KEYC_NONE); 1279 1.3 christos mode_tree_expand_current(data->data); 1280 1.3 christos loop = 0; 1281 1.3 christos RB_FOREACH(wl, winlinks, &s->windows) { 1282 1.3 christos if (loop == data->start + x) 1283 1.3 christos break; 1284 1.3 christos loop++; 1285 1.3 christos } 1286 1.3 christos if (wl != NULL) 1287 1.4 christos mode_tree_set_current(data->data, (uintptr_t)wl); 1288 1.3 christos return ('\r'); 1289 1.3 christos } 1290 1.3 christos if (item->type == WINDOW_TREE_WINDOW) { 1291 1.3 christos if (wl == NULL) 1292 1.3 christos return (KEYC_NONE); 1293 1.3 christos mode_tree_expand_current(data->data); 1294 1.3 christos loop = 0; 1295 1.3 christos TAILQ_FOREACH(wp, &wl->window->panes, entry) { 1296 1.3 christos if (loop == data->start + x) 1297 1.3 christos break; 1298 1.3 christos loop++; 1299 1.3 christos } 1300 1.3 christos if (wp != NULL) 1301 1.4 christos mode_tree_set_current(data->data, (uintptr_t)wp); 1302 1.3 christos return ('\r'); 1303 1.3 christos } 1304 1.3 christos return (KEYC_NONE); 1305 1.3 christos } 1306 1.3 christos 1307 1.3 christos static void 1308 1.5 christos window_tree_key(struct window_mode_entry *wme, struct client *c, 1309 1.5 christos __unused struct session *s, __unused struct winlink *wl, key_code key, 1310 1.5 christos struct mouse_event *m) 1311 1.1 christos { 1312 1.5 christos struct window_pane *wp = wme->wp; 1313 1.5 christos struct window_tree_modedata *data = wme->data; 1314 1.3 christos struct window_tree_itemdata *item, *new_item; 1315 1.3 christos char *name, *prompt = NULL; 1316 1.8 christos struct cmd_find_state fs, *fsp = &data->fs; 1317 1.1 christos int finished; 1318 1.3 christos u_int tagged, x, y, idx; 1319 1.3 christos struct session *ns; 1320 1.3 christos struct winlink *nwl; 1321 1.3 christos struct window_pane *nwp; 1322 1.1 christos 1323 1.1 christos item = mode_tree_get_current(data->data); 1324 1.3 christos finished = mode_tree_key(data->data, c, &key, m, &x, &y); 1325 1.11 wiz 1326 1.11 wiz again: 1327 1.3 christos if (item != (new_item = mode_tree_get_current(data->data))) { 1328 1.3 christos item = new_item; 1329 1.1 christos data->offset = 0; 1330 1.3 christos } 1331 1.11 wiz if (KEYC_IS_MOUSE(key) && m != NULL) { 1332 1.3 christos key = window_tree_mouse(data, key, x, item); 1333 1.11 wiz goto again; 1334 1.11 wiz } 1335 1.11 wiz 1336 1.1 christos switch (key) { 1337 1.1 christos case '<': 1338 1.1 christos data->offset--; 1339 1.1 christos break; 1340 1.1 christos case '>': 1341 1.1 christos data->offset++; 1342 1.1 christos break; 1343 1.8 christos case 'H': 1344 1.9 christos mode_tree_expand(data->data, (uintptr_t)fsp->s); 1345 1.9 christos mode_tree_expand(data->data, (uintptr_t)fsp->wl); 1346 1.9 christos if (!mode_tree_set_current(data->data, (uintptr_t)wme->wp)) 1347 1.9 christos mode_tree_set_current(data->data, (uintptr_t)fsp->wl); 1348 1.8 christos break; 1349 1.8 christos case 'm': 1350 1.8 christos window_tree_pull_item(item, &ns, &nwl, &nwp); 1351 1.8 christos server_set_marked(ns, nwl, nwp); 1352 1.8 christos mode_tree_build(data->data); 1353 1.8 christos break; 1354 1.8 christos case 'M': 1355 1.8 christos server_clear_marked(); 1356 1.8 christos mode_tree_build(data->data); 1357 1.8 christos break; 1358 1.3 christos case 'x': 1359 1.3 christos window_tree_pull_item(item, &ns, &nwl, &nwp); 1360 1.3 christos switch (item->type) { 1361 1.3 christos case WINDOW_TREE_NONE: 1362 1.3 christos break; 1363 1.3 christos case WINDOW_TREE_SESSION: 1364 1.3 christos if (ns == NULL) 1365 1.3 christos break; 1366 1.3 christos xasprintf(&prompt, "Kill session %s? ", ns->name); 1367 1.3 christos break; 1368 1.3 christos case WINDOW_TREE_WINDOW: 1369 1.3 christos if (nwl == NULL) 1370 1.3 christos break; 1371 1.3 christos xasprintf(&prompt, "Kill window %u? ", nwl->idx); 1372 1.3 christos break; 1373 1.3 christos case WINDOW_TREE_PANE: 1374 1.3 christos if (nwp == NULL || window_pane_index(nwp, &idx) != 0) 1375 1.3 christos break; 1376 1.3 christos xasprintf(&prompt, "Kill pane %u? ", idx); 1377 1.3 christos break; 1378 1.3 christos } 1379 1.3 christos if (prompt == NULL) 1380 1.3 christos break; 1381 1.3 christos data->references++; 1382 1.8 christos status_prompt_set(c, NULL, prompt, "", 1383 1.3 christos window_tree_kill_current_callback, window_tree_command_free, 1384 1.12 wiz data, PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags, 1385 1.12 wiz PROMPT_TYPE_COMMAND); 1386 1.3 christos free(prompt); 1387 1.3 christos break; 1388 1.3 christos case 'X': 1389 1.3 christos tagged = mode_tree_count_tagged(data->data); 1390 1.3 christos if (tagged == 0) 1391 1.3 christos break; 1392 1.3 christos xasprintf(&prompt, "Kill %u tagged? ", tagged); 1393 1.3 christos data->references++; 1394 1.8 christos status_prompt_set(c, NULL, prompt, "", 1395 1.3 christos window_tree_kill_tagged_callback, window_tree_command_free, 1396 1.12 wiz data, PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags, 1397 1.12 wiz PROMPT_TYPE_COMMAND); 1398 1.3 christos free(prompt); 1399 1.3 christos break; 1400 1.1 christos case ':': 1401 1.1 christos tagged = mode_tree_count_tagged(data->data); 1402 1.1 christos if (tagged != 0) 1403 1.1 christos xasprintf(&prompt, "(%u tagged) ", tagged); 1404 1.1 christos else 1405 1.1 christos xasprintf(&prompt, "(current) "); 1406 1.1 christos data->references++; 1407 1.8 christos status_prompt_set(c, NULL, prompt, "", 1408 1.8 christos window_tree_command_callback, window_tree_command_free, 1409 1.10 wiz data, PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); 1410 1.1 christos free(prompt); 1411 1.1 christos break; 1412 1.1 christos case '\r': 1413 1.1 christos name = window_tree_get_target(item, &fs); 1414 1.1 christos if (name != NULL) 1415 1.3 christos mode_tree_run_command(c, NULL, data->command, name); 1416 1.3 christos finished = 1; 1417 1.1 christos free(name); 1418 1.3 christos break; 1419 1.1 christos } 1420 1.1 christos if (finished) 1421 1.1 christos window_pane_reset_mode(wp); 1422 1.1 christos else { 1423 1.1 christos mode_tree_draw(data->data); 1424 1.1 christos wp->flags |= PANE_REDRAW; 1425 1.1 christos } 1426 1.1 christos } 1427