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