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