4 * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
28 static struct screen
*window_client_init(struct window_mode_entry
*,
29 struct cmd_find_state
*, struct args
*);
30 static void window_client_free(struct window_mode_entry
*);
31 static void window_client_resize(struct window_mode_entry
*, u_int
,
33 static void window_client_update(struct window_mode_entry
*);
34 static void window_client_key(struct window_mode_entry
*,
35 struct client
*, struct session
*,
36 struct winlink
*, key_code
, struct mouse_event
*);
38 #define WINDOW_CLIENT_DEFAULT_COMMAND "detach-client -t '%%'"
40 #define WINDOW_CLIENT_DEFAULT_FORMAT \
41 "#{t/p:client_activity}: session #{session_name}"
43 #define WINDOW_CLIENT_DEFAULT_KEY_FORMAT \
44 "#{?#{e|<:#{line},10}," \
47 "#{?#{e|<:#{line},36}," \
48 "M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
54 static const struct menu_item window_client_menu_items
[] = {
55 { "Detach", 'd', NULL
},
56 { "Detach Tagged", 'D', NULL
},
57 { "", KEYC_NONE
, NULL
},
59 { "Tag All", '\024', NULL
},
60 { "Tag None", 'T', NULL
},
61 { "", KEYC_NONE
, NULL
},
62 { "Cancel", 'q', NULL
},
64 { NULL
, KEYC_NONE
, NULL
}
67 const struct window_mode window_client_mode
= {
68 .name
= "client-mode",
69 .default_format
= WINDOW_CLIENT_DEFAULT_FORMAT
,
71 .init
= window_client_init
,
72 .free
= window_client_free
,
73 .resize
= window_client_resize
,
74 .update
= window_client_update
,
75 .key
= window_client_key
,
78 enum window_client_sort_type
{
79 WINDOW_CLIENT_BY_NAME
,
80 WINDOW_CLIENT_BY_SIZE
,
81 WINDOW_CLIENT_BY_CREATION_TIME
,
82 WINDOW_CLIENT_BY_ACTIVITY_TIME
,
84 static const char *window_client_sort_list
[] = {
90 static struct mode_tree_sort_criteria
*window_client_sort
;
92 struct window_client_itemdata
{
96 struct window_client_modedata
{
97 struct window_pane
*wp
;
99 struct mode_tree_data
*data
;
104 struct window_client_itemdata
**item_list
;
108 static struct window_client_itemdata
*
109 window_client_add_item(struct window_client_modedata
*data
)
111 struct window_client_itemdata
*item
;
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
);
120 window_client_free_item(struct window_client_itemdata
*item
)
122 server_client_unref(item
->c
);
127 window_client_cmp(const void *a0
, const void *b0
)
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
;
137 switch (window_client_sort
->field
) {
138 case WINDOW_CLIENT_BY_SIZE
:
139 result
= ca
->tty
.sx
- cb
->tty
.sx
;
141 result
= ca
->tty
.sy
- cb
->tty
.sy
;
143 case WINDOW_CLIENT_BY_CREATION_TIME
:
144 if (timercmp(&ca
->creation_time
, &cb
->creation_time
, >))
146 else if (timercmp(&ca
->creation_time
, &cb
->creation_time
, <))
149 case WINDOW_CLIENT_BY_ACTIVITY_TIME
:
150 if (timercmp(&ca
->activity_time
, &cb
->activity_time
, >))
152 else if (timercmp(&ca
->activity_time
, &cb
->activity_time
, <))
157 /* Use WINDOW_CLIENT_BY_NAME as default order and tie breaker. */
159 result
= strcmp(ca
->name
, cb
->name
);
161 if (window_client_sort
->reversed
)
167 window_client_build(void *modedata
, struct mode_tree_sort_criteria
*sort_crit
,
168 __unused
uint64_t *tag
, const char *filter
)
170 struct window_client_modedata
*data
= modedata
;
171 struct window_client_itemdata
*item
;
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
;
182 TAILQ_FOREACH(c
, &clients
, entry
) {
183 if (c
->session
== NULL
|| (c
->flags
& CLIENT_UNATTACHEDFLAGS
))
186 item
= window_client_add_item(data
);
192 window_client_sort
= sort_crit
;
193 qsort(data
->item_list
, data
->item_size
, sizeof *data
->item_list
,
196 for (i
= 0; i
< data
->item_size
; i
++) {
197 item
= data
->item_list
[i
];
200 if (filter
!= NULL
) {
201 cp
= format_single(NULL
, filter
, c
, NULL
, NULL
, NULL
);
202 if (!format_true(cp
)) {
209 text
= format_single(NULL
, data
->format
, c
, NULL
, NULL
, NULL
);
210 mode_tree_add(data
->data
, NULL
, item
, (uint64_t)c
, c
->name
,
217 window_client_draw(__unused
void *modedata
, void *itemdata
,
218 struct screen_write_ctx
*ctx
, u_int sx
, u_int sy
)
220 struct window_client_itemdata
*item
= itemdata
;
221 struct client
*c
= item
->c
;
222 struct screen
*s
= ctx
->s
;
223 struct window_pane
*wp
;
224 u_int cx
= s
->cx
, cy
= s
->cy
, lines
, at
;
226 if (c
->session
== NULL
|| (c
->flags
& CLIENT_UNATTACHEDFLAGS
))
228 wp
= c
->session
->curw
->window
->active
;
230 lines
= status_line_size(c
);
233 if (status_at_line(c
) == 0)
238 screen_write_cursormove(ctx
, cx
, cy
+ at
, 0);
239 screen_write_preview(ctx
, &wp
->base
, sx
, sy
- 2 - lines
);
242 screen_write_cursormove(ctx
, cx
, cy
+ 2, 0);
244 screen_write_cursormove(ctx
, cx
, cy
+ sy
- 1 - lines
, 0);
245 screen_write_hline(ctx
, sx
, 0, 0);
248 screen_write_cursormove(ctx
, cx
, cy
, 0);
250 screen_write_cursormove(ctx
, cx
, cy
+ sy
- lines
, 0);
251 screen_write_fast_copy(ctx
, &c
->status
.screen
, 0, 0, sx
, lines
);
255 window_client_menu(void *modedata
, struct client
*c
, key_code key
)
257 struct window_client_modedata
*data
= modedata
;
258 struct window_pane
*wp
= data
->wp
;
259 struct window_mode_entry
*wme
;
261 wme
= TAILQ_FIRST(&wp
->modes
);
262 if (wme
== NULL
|| wme
->data
!= modedata
)
264 window_client_key(wme
, c
, NULL
, NULL
, key
, NULL
);
268 window_client_get_key(void *modedata
, void *itemdata
, u_int line
)
270 struct window_client_modedata
*data
= modedata
;
271 struct window_client_itemdata
*item
= itemdata
;
272 struct format_tree
*ft
;
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
);
280 expanded
= format_expand(ft
, data
->key_format
);
281 key
= key_string_lookup_string(expanded
);
287 static struct screen
*
288 window_client_init(struct window_mode_entry
*wme
,
289 __unused
struct cmd_find_state
*fs
, struct args
*args
)
291 struct window_pane
*wp
= wme
->wp
;
292 struct window_client_modedata
*data
;
295 wme
->data
= data
= xcalloc(1, sizeof *data
);
298 if (args
== NULL
|| !args_has(args
, 'F'))
299 data
->format
= xstrdup(WINDOW_CLIENT_DEFAULT_FORMAT
);
301 data
->format
= xstrdup(args_get(args
, 'F'));
302 if (args
== NULL
|| !args_has(args
, 'K'))
303 data
->key_format
= xstrdup(WINDOW_CLIENT_DEFAULT_KEY_FORMAT
);
305 data
->key_format
= xstrdup(args_get(args
, 'K'));
306 if (args
== NULL
|| args_count(args
) == 0)
307 data
->command
= xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND
);
309 data
->command
= xstrdup(args_string(args
, 0));
311 data
->data
= mode_tree_start(wp
, args
, window_client_build
,
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
);
315 mode_tree_zoom(data
->data
, args
);
317 mode_tree_build(data
->data
);
318 mode_tree_draw(data
->data
);
324 window_client_free(struct window_mode_entry
*wme
)
326 struct window_client_modedata
*data
= wme
->data
;
332 mode_tree_free(data
->data
);
334 for (i
= 0; i
< data
->item_size
; i
++)
335 window_client_free_item(data
->item_list
[i
]);
336 free(data
->item_list
);
339 free(data
->key_format
);
346 window_client_resize(struct window_mode_entry
*wme
, u_int sx
, u_int sy
)
348 struct window_client_modedata
*data
= wme
->data
;
350 mode_tree_resize(data
->data
, sx
, sy
);
354 window_client_update(struct window_mode_entry
*wme
)
356 struct window_client_modedata
*data
= wme
->data
;
358 mode_tree_build(data
->data
);
359 mode_tree_draw(data
->data
);
360 data
->wp
->flags
|= PANE_REDRAW
;
364 window_client_do_detach(void *modedata
, void *itemdata
,
365 __unused
struct client
*c
, key_code key
)
367 struct window_client_modedata
*data
= modedata
;
368 struct window_client_itemdata
*item
= itemdata
;
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
);
381 window_client_key(struct window_mode_entry
*wme
, struct client
*c
,
382 __unused
struct session
*s
, __unused
struct winlink
*wl
, key_code key
,
383 struct mouse_event
*m
)
385 struct window_pane
*wp
= wme
->wp
;
386 struct window_client_modedata
*data
= wme
->data
;
387 struct mode_tree_data
*mtd
= data
->data
;
388 struct window_client_itemdata
*item
;
391 finished
= mode_tree_key(mtd
, c
, &key
, m
, NULL
, NULL
);
396 item
= mode_tree_get_current(mtd
);
397 window_client_do_detach(data
, item
, c
, key
);
398 mode_tree_build(mtd
);
403 mode_tree_each_tagged(mtd
, window_client_do_detach
, c
, key
, 0);
404 mode_tree_build(mtd
);
407 item
= mode_tree_get_current(mtd
);
408 mode_tree_run_command(c
, NULL
, data
->command
, item
->c
->ttyname
);
412 if (finished
|| server_client_how_many() == 0)
413 window_pane_reset_mode(wp
);
416 wp
->flags
|= PANE_REDRAW
;