Annotation of src/usr.bin/tmux/window-client.c, Revision 1.2
1.2 ! nicm 1: /* $OpenBSD: window-client.c,v 1.1 2017/05/30 21:44:59 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:
28: static struct screen *window_client_init(struct window_pane *,
29: struct cmd_find_state *, struct args *);
30: static void window_client_free(struct window_pane *);
31: static void window_client_resize(struct window_pane *, u_int,
32: u_int);
33: static void window_client_key(struct window_pane *,
34: struct client *, struct session *, key_code,
35: struct mouse_event *);
36:
37: #define WINDOW_CLIENT_DEFAULT_COMMAND "detach-client -t '%%'"
38:
39: const struct window_mode window_client_mode = {
40: .name = "client-mode",
41:
42: .init = window_client_init,
43: .free = window_client_free,
44: .resize = window_client_resize,
45: .key = window_client_key,
46: };
47:
48: enum window_client_sort_type {
49: WINDOW_CLIENT_BY_NAME,
50: WINDOW_CLIENT_BY_CREATION_TIME,
51: WINDOW_CLIENT_BY_ACTIVITY_TIME,
52: };
53: static const char *window_client_sort_list[] = {
54: "name",
55: "creation time",
56: "activity time"
57: };
58:
59: struct window_client_itemdata {
60: struct client *c;
61: };
62:
63: struct window_client_modedata {
64: struct mode_tree_data *data;
65: char *command;
66:
67: struct window_client_itemdata **item_list;
68: u_int item_size;
69: };
70:
71: static struct window_client_itemdata *
72: window_client_add_item(struct window_client_modedata *data)
73: {
74: struct window_client_itemdata *item;
75:
76: data->item_list = xreallocarray(data->item_list, data->item_size + 1,
77: sizeof *data->item_list);
78: item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
79: return (item);
80: }
81:
82: static void
83: window_client_free_item(struct window_client_itemdata *item)
84: {
85: server_client_unref(item->c);
86: free(item);
87: }
88:
89: static int
90: window_client_cmp_name(const void *a0, const void *b0)
91: {
92: const struct window_client_itemdata *const *a = a0;
93: const struct window_client_itemdata *const *b = b0;
94:
95: return (strcmp((*a)->c->name, (*b)->c->name));
96: }
97:
98: static int
99: window_client_cmp_creation_time(const void *a0, const void *b0)
100: {
101: const struct window_client_itemdata *const *a = a0;
102: const struct window_client_itemdata *const *b = b0;
103:
104: if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, >))
105: return (-1);
106: if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, <))
107: return (1);
108: return (0);
109: }
110:
111: static int
112: window_client_cmp_activity_time(const void *a0, const void *b0)
113: {
114: const struct window_client_itemdata *const *a = a0;
115: const struct window_client_itemdata *const *b = b0;
116:
117: if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, >))
118: return (-1);
119: if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, <))
120: return (1);
121: return (0);
122: }
123:
124: static void
125: window_client_build(void *modedata, u_int sort_type, __unused uint64_t *tag)
126: {
127: struct window_client_modedata *data = modedata;
128: struct window_client_itemdata *item;
129: u_int i;
130: struct client *c;
131: char *tim;
132: char *text;
133:
134: for (i = 0; i < data->item_size; i++)
135: window_client_free_item(data->item_list[i]);
136: free(data->item_list);
137: data->item_list = NULL;
138: data->item_size = 0;
139:
140: TAILQ_FOREACH(c, &clients, entry) {
141: if (c->session == NULL || (c->flags & (CLIENT_DETACHING)))
142: continue;
143:
144: item = window_client_add_item(data);
145: item->c = c;
146:
147: c->references++;
148: }
149:
150: switch (sort_type) {
151: case WINDOW_CLIENT_BY_NAME:
152: qsort(data->item_list, data->item_size, sizeof *data->item_list,
153: window_client_cmp_name);
154: break;
155: case WINDOW_CLIENT_BY_CREATION_TIME:
156: qsort(data->item_list, data->item_size, sizeof *data->item_list,
157: window_client_cmp_creation_time);
158: break;
159: case WINDOW_CLIENT_BY_ACTIVITY_TIME:
160: qsort(data->item_list, data->item_size, sizeof *data->item_list,
161: window_client_cmp_activity_time);
162: break;
163: }
164:
165: for (i = 0; i < data->item_size; i++) {
166: item = data->item_list[i];
167: c = item->c;
168:
169: tim = ctime(&c->activity_time.tv_sec);
170: *strchr(tim, '\n') = '\0';
171:
172: xasprintf(&text, "session %s (%s)", c->session->name, tim);
173: mode_tree_add(data->data, NULL, item, (uint64_t)c, c->name,
174: text, -1);
175: free(text);
176: }
177: }
178:
179: static struct screen *
180: window_client_draw(__unused void *modedata, void *itemdata, u_int sx, u_int sy)
181: {
182: struct window_client_itemdata *item = itemdata;
183: struct client *c = item->c;
184: struct window_pane *wp;
185: static struct screen s;
186: struct screen_write_ctx ctx;
187:
188: if (c->session == NULL || (c->flags & (CLIENT_DEAD|CLIENT_DETACHING)))
189: return (NULL);
190: wp = c->session->curw->window->active;
191:
192: screen_init(&s, sx, sy, 0);
193:
194: screen_write_start(&ctx, NULL, &s);
195: screen_write_clearscreen(&ctx, 8);
196:
197: screen_write_preview(&ctx, &wp->base, sx, sy - 3);
198:
199: screen_write_cursormove(&ctx, 0, sy - 2);
200: screen_write_line(&ctx, sx, 0, 0);
201:
202: screen_write_cursormove(&ctx, 0, sy - 1);
203: if (c->old_status != NULL)
204: screen_write_copy(&ctx, c->old_status, 0, 0, sx, 1, NULL, NULL);
205: else
206: screen_write_copy(&ctx, &c->status, 0, 0, sx, 1, NULL, NULL);
207:
208: screen_write_stop(&ctx);
209: return (&s);
210: }
211:
212: static struct screen *
213: window_client_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
214: struct args *args)
215: {
216: struct window_client_modedata *data;
217: struct screen *s;
218:
219: wp->modedata = data = xcalloc(1, sizeof *data);
220:
221: if (args == NULL || args->argc == 0)
222: data->command = xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND);
223: else
224: data->command = xstrdup(args->argv[0]);
225:
226: data->data = mode_tree_start(wp, window_client_build,
227: window_client_draw, data, window_client_sort_list,
228: nitems(window_client_sort_list), &s);
229:
230: mode_tree_build(data->data);
231: mode_tree_draw(data->data);
232:
233: return (s);
234: }
235:
236: static void
237: window_client_free(struct window_pane *wp)
238: {
239: struct window_client_modedata *data = wp->modedata;
240: u_int i;
241:
242: if (data == NULL)
243: return;
244:
245: mode_tree_free(data->data);
246:
247: for (i = 0; i < data->item_size; i++)
248: window_client_free_item(data->item_list[i]);
249: free(data->item_list);
250:
251: free(data->command);
252: free(data);
253: }
254:
255: static void
256: window_client_resize(struct window_pane *wp, u_int sx, u_int sy)
257: {
258: struct window_client_modedata *data = wp->modedata;
259:
260: mode_tree_resize(data->data, sx, sy);
261: }
262:
263: static void
264: window_client_do_detach(void* modedata, void *itemdata, key_code key)
265: {
266: struct window_client_modedata *data = modedata;
267: struct window_client_itemdata *item = itemdata;
268:
269: if (item == mode_tree_get_current(data->data))
270: mode_tree_down(data->data, 0);
271: if (key == 'd' || key == 'D')
272: server_client_detach(item->c, MSG_DETACH);
273: else if (key == 'x' || key == 'X')
274: server_client_detach(item->c, MSG_DETACHKILL);
275: else if (key == 'z' || key == 'Z')
276: server_client_suspend(item->c);
277: }
278:
279: static void
280: window_client_key(struct window_pane *wp, struct client *c,
281: __unused struct session *s, key_code key, struct mouse_event *m)
282: {
283: struct window_client_modedata *data = wp->modedata;
284: struct window_client_itemdata *item;
285: char *command, *name;
286: int finished;
287:
288: /*
289: * t = toggle tag
290: * T = tag none
291: * C-t = tag all
292: * q = exit
293: * O = change sort order
294: *
295: * d = detach client
296: * D = detach tagged clients
297: * x = detach and kill client
298: * X = detach and kill tagged clients
299: * z = suspend client
300: * Z = suspend tagged clients
301: * Enter = detach client
302: */
303:
304: finished = mode_tree_key(data->data, &key, m);
305: switch (key) {
306: case 'd':
307: case 'x':
308: case 'z':
309: item = mode_tree_get_current(data->data);
310: window_client_do_detach(data, item, key);
311: mode_tree_build(data->data);
312: break;
313: case 'D':
314: case 'X':
315: case 'Z':
316: mode_tree_each_tagged(data->data, window_client_do_detach, key,
317: 0);
318: mode_tree_build(data->data);
319: break;
320: case '\r':
321: item = mode_tree_get_current(data->data);
322: command = xstrdup(data->command);
323: name = xstrdup(item->c->ttyname);
324: window_pane_reset_mode(wp);
325: mode_tree_run_command(c, NULL, command, name);
326: free(name);
327: free(command);
328: return;
329: }
330: if (finished || server_client_how_many() == 0)
331: window_pane_reset_mode(wp);
332: else {
333: mode_tree_draw(data->data);
334: wp->flags |= PANE_REDRAW;
335: }
336: }