Annotation of src/usr.bin/tmux/window-client.c, Revision 1.27
1.27 ! nicm 1: /* $OpenBSD: window-client.c,v 1.26 2020/01/28 08:06:11 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: #include <sys/time.h>
21:
22: #include <stdlib.h>
23: #include <string.h>
1.2 nicm 24: #include <time.h>
1.1 nicm 25:
26: #include "tmux.h"
27:
1.16 nicm 28: static struct screen *window_client_init(struct window_mode_entry *,
1.1 nicm 29: struct cmd_find_state *, struct args *);
1.16 nicm 30: static void window_client_free(struct window_mode_entry *);
31: static void window_client_resize(struct window_mode_entry *, u_int,
1.1 nicm 32: u_int);
1.16 nicm 33: static void window_client_key(struct window_mode_entry *,
1.15 nicm 34: struct client *, struct session *,
35: struct winlink *, key_code, struct mouse_event *);
1.1 nicm 36:
37: #define WINDOW_CLIENT_DEFAULT_COMMAND "detach-client -t '%%'"
38:
1.8 nicm 39: #define WINDOW_CLIENT_DEFAULT_FORMAT \
1.27 ! nicm 40: "#{t/p:client_activity}: session #{session_name}"
1.8 nicm 41:
1.23 nicm 42: static const struct menu_item window_client_menu_items[] = {
43: { "Detach", 'd', NULL },
44: { "Detach Tagged", 'D', NULL },
45: { "", KEYC_NONE, NULL },
46: { "Tag", 't', NULL },
47: { "Tag All", '\024', NULL },
48: { "Tag None", 'T', NULL },
49: { "", KEYC_NONE, NULL },
50: { "Cancel", 'q', NULL },
51:
52: { NULL, KEYC_NONE, NULL }
53: };
1.22 nicm 54:
1.1 nicm 55: const struct window_mode window_client_mode = {
56: .name = "client-mode",
1.20 nicm 57: .default_format = WINDOW_CLIENT_DEFAULT_FORMAT,
1.1 nicm 58:
59: .init = window_client_init,
60: .free = window_client_free,
61: .resize = window_client_resize,
62: .key = window_client_key,
63: };
64:
65: enum window_client_sort_type {
66: WINDOW_CLIENT_BY_NAME,
1.4 nicm 67: WINDOW_CLIENT_BY_SIZE,
1.1 nicm 68: WINDOW_CLIENT_BY_CREATION_TIME,
69: WINDOW_CLIENT_BY_ACTIVITY_TIME,
70: };
71: static const char *window_client_sort_list[] = {
72: "name",
1.4 nicm 73: "size",
1.5 nicm 74: "creation",
75: "activity"
1.1 nicm 76: };
1.24 nicm 77: static struct mode_tree_sort_criteria *window_client_sort;
1.1 nicm 78:
79: struct window_client_itemdata {
80: struct client *c;
81: };
82:
83: struct window_client_modedata {
1.22 nicm 84: struct window_pane *wp;
85:
1.1 nicm 86: struct mode_tree_data *data;
1.8 nicm 87: char *format;
1.1 nicm 88: char *command;
89:
90: struct window_client_itemdata **item_list;
91: u_int item_size;
92: };
93:
94: static struct window_client_itemdata *
95: window_client_add_item(struct window_client_modedata *data)
96: {
97: struct window_client_itemdata *item;
98:
99: data->item_list = xreallocarray(data->item_list, data->item_size + 1,
100: sizeof *data->item_list);
101: item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
102: return (item);
103: }
104:
105: static void
106: window_client_free_item(struct window_client_itemdata *item)
107: {
108: server_client_unref(item->c);
109: free(item);
110: }
111:
112: static int
1.24 nicm 113: window_client_cmp(const void *a0, const void *b0)
1.1 nicm 114: {
1.24 nicm 115: const struct window_client_itemdata *const *a = a0;
116: const struct window_client_itemdata *const *b = b0;
117: const struct window_client_itemdata *itema = *a;
118: const struct window_client_itemdata *itemb = *b;
119: struct client *ca = itema->c;
120: struct client *cb = itemb->c;
121: int result = 0;
1.1 nicm 122:
1.24 nicm 123: switch (window_client_sort->field) {
124: case WINDOW_CLIENT_BY_SIZE:
125: result = ca->tty.sx - cb->tty.sx;
126: if (result == 0)
127: result = ca->tty.sy - cb->tty.sy;
128: break;
129: case WINDOW_CLIENT_BY_CREATION_TIME:
130: if (timercmp(&ca->creation_time, &cb->creation_time, >))
131: result = -1;
132: else if (timercmp(&ca->creation_time, &cb->creation_time, <))
133: result = 1;
134: break;
135: case WINDOW_CLIENT_BY_ACTIVITY_TIME:
136: if (timercmp(&ca->activity_time, &cb->activity_time, >))
137: result = -1;
138: else if (timercmp(&ca->activity_time, &cb->activity_time, <))
139: result = 1;
140: break;
141: }
1.1 nicm 142:
1.24 nicm 143: /* Use WINDOW_CLIENT_BY_NAME as default order and tie breaker. */
144: if (result == 0)
145: result = strcmp(ca->name, cb->name);
146:
147: if (window_client_sort->reversed)
148: result = -result;
149: return (result);
1.1 nicm 150: }
151:
152: static void
1.24 nicm 153: window_client_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
154: __unused uint64_t *tag, const char *filter)
1.1 nicm 155: {
156: struct window_client_modedata *data = modedata;
157: struct window_client_itemdata *item;
158: u_int i;
159: struct client *c;
1.8 nicm 160: char *text, *cp;
1.1 nicm 161:
162: for (i = 0; i < data->item_size; i++)
163: window_client_free_item(data->item_list[i]);
164: free(data->item_list);
165: data->item_list = NULL;
166: data->item_size = 0;
167:
168: TAILQ_FOREACH(c, &clients, entry) {
169: if (c->session == NULL || (c->flags & (CLIENT_DETACHING)))
170: continue;
171:
172: item = window_client_add_item(data);
173: item->c = c;
174:
175: c->references++;
176: }
177:
1.24 nicm 178: window_client_sort = sort_crit;
179: qsort(data->item_list, data->item_size, sizeof *data->item_list,
180: window_client_cmp);
1.1 nicm 181:
182: for (i = 0; i < data->item_size; i++) {
183: item = data->item_list[i];
184: c = item->c;
1.6 nicm 185:
186: if (filter != NULL) {
187: cp = format_single(NULL, filter, c, NULL, NULL, NULL);
188: if (!format_true(cp)) {
189: free(cp);
190: continue;
191: }
192: free(cp);
193: }
1.1 nicm 194:
1.8 nicm 195: text = format_single(NULL, data->format, c, NULL, NULL, NULL);
1.1 nicm 196: mode_tree_add(data->data, NULL, item, (uint64_t)c, c->name,
197: text, -1);
198: free(text);
199: }
200: }
201:
1.11 nicm 202: static void
203: window_client_draw(__unused void *modedata, void *itemdata,
204: struct screen_write_ctx *ctx, u_int sx, u_int sy)
1.1 nicm 205: {
206: struct window_client_itemdata *item = itemdata;
207: struct client *c = item->c;
1.21 nicm 208: struct screen *s = ctx->s;
1.1 nicm 209: struct window_pane *wp;
1.21 nicm 210: u_int cx = s->cx, cy = s->cy, lines, at;
1.1 nicm 211:
1.26 nicm 212: if (c->session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
1.11 nicm 213: return;
1.1 nicm 214: wp = c->session->curw->window->active;
215:
1.21 nicm 216: lines = status_line_size(c);
217: if (lines >= sy)
218: lines = 0;
219: if (status_at_line(c) == 0)
220: at = lines;
221: else
222: at = 0;
223:
224: screen_write_cursormove(ctx, cx, cy + at, 0);
225: screen_write_preview(ctx, &wp->base, sx, sy - 2 - lines);
226:
227: if (at != 0)
228: screen_write_cursormove(ctx, cx, cy + 2, 0);
229: else
230: screen_write_cursormove(ctx, cx, cy + sy - 1 - lines, 0);
1.11 nicm 231: screen_write_hline(ctx, sx, 0, 0);
1.1 nicm 232:
1.21 nicm 233: if (at != 0)
234: screen_write_cursormove(ctx, cx, cy, 0);
235: else
236: screen_write_cursormove(ctx, cx, cy + sy - lines, 0);
237: screen_write_fast_copy(ctx, &c->status.screen, 0, 0, sx, lines);
1.1 nicm 238: }
239:
1.22 nicm 240: static void
241: window_client_menu(void *modedata, struct client *c, key_code key)
242: {
243: struct window_client_modedata *data = modedata;
244: struct window_pane *wp = data->wp;
245: struct window_mode_entry *wme;
246:
247: wme = TAILQ_FIRST(&wp->modes);
248: if (wme == NULL || wme->data != modedata)
249: return;
250: window_client_key(wme, c, NULL, NULL, key, NULL);
251: }
252:
1.1 nicm 253: static struct screen *
1.16 nicm 254: window_client_init(struct window_mode_entry *wme,
255: __unused struct cmd_find_state *fs, struct args *args)
1.1 nicm 256: {
1.16 nicm 257: struct window_pane *wp = wme->wp;
1.1 nicm 258: struct window_client_modedata *data;
259: struct screen *s;
260:
1.16 nicm 261: wme->data = data = xcalloc(1, sizeof *data);
1.22 nicm 262: data->wp = wp;
1.1 nicm 263:
1.8 nicm 264: if (args == NULL || !args_has(args, 'F'))
265: data->format = xstrdup(WINDOW_CLIENT_DEFAULT_FORMAT);
266: else
267: data->format = xstrdup(args_get(args, 'F'));
1.1 nicm 268: if (args == NULL || args->argc == 0)
269: data->command = xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND);
270: else
271: data->command = xstrdup(args->argv[0]);
272:
1.5 nicm 273: data->data = mode_tree_start(wp, args, window_client_build,
1.22 nicm 274: window_client_draw, NULL, window_client_menu, data,
1.23 nicm 275: window_client_menu_items, window_client_sort_list,
1.1 nicm 276: nitems(window_client_sort_list), &s);
1.14 nicm 277: mode_tree_zoom(data->data, args);
1.1 nicm 278:
279: mode_tree_build(data->data);
280: mode_tree_draw(data->data);
281:
282: return (s);
283: }
284:
285: static void
1.16 nicm 286: window_client_free(struct window_mode_entry *wme)
1.1 nicm 287: {
1.16 nicm 288: struct window_client_modedata *data = wme->data;
1.1 nicm 289: u_int i;
290:
291: if (data == NULL)
292: return;
293:
294: mode_tree_free(data->data);
295:
296: for (i = 0; i < data->item_size; i++)
297: window_client_free_item(data->item_list[i]);
298: free(data->item_list);
299:
1.8 nicm 300: free(data->format);
1.1 nicm 301: free(data->command);
1.8 nicm 302:
1.1 nicm 303: free(data);
304: }
305:
306: static void
1.16 nicm 307: window_client_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
1.1 nicm 308: {
1.16 nicm 309: struct window_client_modedata *data = wme->data;
1.1 nicm 310:
311: mode_tree_resize(data->data, sx, sy);
312: }
313:
314: static void
1.25 nicm 315: window_client_do_detach(void *modedata, void *itemdata,
1.9 nicm 316: __unused struct client *c, key_code key)
1.1 nicm 317: {
318: struct window_client_modedata *data = modedata;
319: struct window_client_itemdata *item = itemdata;
320:
321: if (item == mode_tree_get_current(data->data))
322: mode_tree_down(data->data, 0);
323: if (key == 'd' || key == 'D')
324: server_client_detach(item->c, MSG_DETACH);
325: else if (key == 'x' || key == 'X')
326: server_client_detach(item->c, MSG_DETACHKILL);
327: else if (key == 'z' || key == 'Z')
328: server_client_suspend(item->c);
329: }
330:
331: static void
1.16 nicm 332: window_client_key(struct window_mode_entry *wme, struct client *c,
1.15 nicm 333: __unused struct session *s, __unused struct winlink *wl, key_code key,
334: struct mouse_event *m)
1.1 nicm 335: {
1.16 nicm 336: struct window_pane *wp = wme->wp;
337: struct window_client_modedata *data = wme->data;
1.9 nicm 338: struct mode_tree_data *mtd = data->data;
1.1 nicm 339: struct window_client_itemdata *item;
340: int finished;
341:
1.12 nicm 342: finished = mode_tree_key(mtd, c, &key, m, NULL, NULL);
1.1 nicm 343: switch (key) {
344: case 'd':
345: case 'x':
346: case 'z':
1.9 nicm 347: item = mode_tree_get_current(mtd);
348: window_client_do_detach(data, item, c, key);
349: mode_tree_build(mtd);
1.1 nicm 350: break;
351: case 'D':
352: case 'X':
353: case 'Z':
1.9 nicm 354: mode_tree_each_tagged(mtd, window_client_do_detach, c, key, 0);
355: mode_tree_build(mtd);
1.1 nicm 356: break;
357: case '\r':
1.9 nicm 358: item = mode_tree_get_current(mtd);
359: mode_tree_run_command(c, NULL, data->command, item->c->ttyname);
360: finished = 1;
361: break;
1.1 nicm 362: }
363: if (finished || server_client_how_many() == 0)
364: window_pane_reset_mode(wp);
365: else {
1.9 nicm 366: mode_tree_draw(mtd);
1.1 nicm 367: wp->flags |= PANE_REDRAW;
368: }
369: }