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