1/* $OpenBSD$ */
2
3/*
4 * Copyright (c) 2007 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
21#include <stdlib.h>
22#include <string.h>
23
24#include "tmux.h"
25
26/*
27 * Refresh client.
28 */
29
30static enum cmd_retval	cmd_refresh_client_exec(struct cmd *,
31			    struct cmdq_item *);
32
33const struct cmd_entry cmd_refresh_client_entry = {
34	.name = "refresh-client",
35	.alias = "refresh",
36
37	.args = { "A:B:cC:Df:F:l::LRSt:U", 0, 1, NULL },
38	.usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] "
39		 "[-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]",
40
41	.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
42	.exec = cmd_refresh_client_exec
43};
44
45static void
46cmd_refresh_client_update_subscription(struct client *tc, const char *value)
47{
48	char			*copy, *split, *name, *what;
49	enum control_sub_type	 subtype;
50	int			 subid = -1;
51
52	copy = name = xstrdup(value);
53	if ((split = strchr(copy, ':')) == NULL) {
54		control_remove_sub(tc, copy);
55		goto out;
56	}
57	*split++ = '\0';
58
59	what = split;
60	if ((split = strchr(what, ':')) == NULL)
61		goto out;
62	*split++ = '\0';
63
64	if (strcmp(what, "%*") == 0)
65		subtype = CONTROL_SUB_ALL_PANES;
66	else if (sscanf(what, "%%%d", &subid) == 1 && subid >= 0)
67		subtype = CONTROL_SUB_PANE;
68	else if (strcmp(what, "@*") == 0)
69		subtype = CONTROL_SUB_ALL_WINDOWS;
70	else if (sscanf(what, "@%d", &subid) == 1 && subid >= 0)
71		subtype = CONTROL_SUB_WINDOW;
72	else
73		subtype = CONTROL_SUB_SESSION;
74	control_add_sub(tc, name, subtype, subid, split);
75
76out:
77	free(copy);
78}
79
80static enum cmd_retval
81cmd_refresh_client_control_client_size(struct cmd *self, struct cmdq_item *item)
82{
83	struct args		*args = cmd_get_args(self);
84	struct client		*tc = cmdq_get_target_client(item);
85	const char		*size = args_get(args, 'C');
86	u_int			 w, x, y;
87	struct client_window	*cw;
88
89	if (sscanf(size, "@%u:%ux%u", &w, &x, &y) == 3) {
90		if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM ||
91		    y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) {
92			cmdq_error(item, "size too small or too big");
93			return (CMD_RETURN_ERROR);
94		}
95		log_debug("%s: client %s window @%u: size %ux%u", __func__,
96		    tc->name, w, x, y);
97		cw = server_client_add_client_window(tc, w);
98		cw->sx = x;
99		cw->sy = y;
100		tc->flags |= CLIENT_WINDOWSIZECHANGED;
101		recalculate_sizes_now(1);
102		return (CMD_RETURN_NORMAL);
103	}
104	if (sscanf(size, "@%u:", &w) == 1) {
105		cw = server_client_get_client_window(tc, w);
106		if (cw != NULL) {
107			log_debug("%s: client %s window @%u: no size", __func__,
108			    tc->name, w);
109			cw->sx = 0;
110			cw->sy = 0;
111			recalculate_sizes_now(1);
112		}
113		return (CMD_RETURN_NORMAL);
114	}
115
116	if (sscanf(size, "%u,%u", &x, &y) != 2 &&
117	    sscanf(size, "%ux%u", &x, &y) != 2) {
118		cmdq_error(item, "bad size argument");
119		return (CMD_RETURN_ERROR);
120	}
121	if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM ||
122	    y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) {
123		cmdq_error(item, "size too small or too big");
124		return (CMD_RETURN_ERROR);
125	}
126	tty_set_size(&tc->tty, x, y, 0, 0);
127	tc->flags |= CLIENT_SIZECHANGED;
128	recalculate_sizes_now(1);
129	return (CMD_RETURN_NORMAL);
130}
131
132static void
133cmd_refresh_client_update_offset(struct client *tc, const char *value)
134{
135	struct window_pane	*wp;
136	char			*copy, *split;
137	u_int			 pane;
138
139	if (*value != '%')
140		return;
141	copy = xstrdup(value);
142	if ((split = strchr(copy, ':')) == NULL)
143		goto out;
144	*split++ = '\0';
145
146	if (sscanf(copy, "%%%u", &pane) != 1)
147		goto out;
148	wp = window_pane_find_by_id(pane);
149	if (wp == NULL)
150		goto out;
151
152	if (strcmp(split, "on") == 0)
153		control_set_pane_on(tc, wp);
154	else if (strcmp(split, "off") == 0)
155		control_set_pane_off(tc, wp);
156	else if (strcmp(split, "continue") == 0)
157		control_continue_pane(tc, wp);
158	else if (strcmp(split, "pause") == 0)
159		control_pause_pane(tc, wp);
160
161out:
162	free(copy);
163}
164
165static enum cmd_retval
166cmd_refresh_client_clipboard(struct cmd *self, struct cmdq_item *item)
167{
168	struct args		*args = cmd_get_args(self);
169	struct client		*tc = cmdq_get_target_client(item);
170	const char		*p;
171	u_int			 i;
172	struct cmd_find_state	 fs;
173
174	p = args_get(args, 'l');
175	if (p == NULL) {
176		if (tc->flags & CLIENT_CLIPBOARDBUFFER)
177			return (CMD_RETURN_NORMAL);
178		tc->flags |= CLIENT_CLIPBOARDBUFFER;
179	} else {
180		if (cmd_find_target(&fs, item, p, CMD_FIND_PANE, 0) != 0)
181			return (CMD_RETURN_ERROR);
182		for (i = 0; i < tc->clipboard_npanes; i++) {
183			if (tc->clipboard_panes[i] == fs.wp->id)
184				break;
185		}
186		if (i != tc->clipboard_npanes)
187			return (CMD_RETURN_NORMAL);
188		tc->clipboard_panes = xreallocarray(tc->clipboard_panes,
189		    tc->clipboard_npanes + 1, sizeof *tc->clipboard_panes);
190		tc->clipboard_panes[tc->clipboard_npanes++] = fs.wp->id;
191	}
192	tty_clipboard_query(&tc->tty);
193	return (CMD_RETURN_NORMAL);
194}
195
196static enum cmd_retval
197cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
198{
199	struct args		*args = cmd_get_args(self);
200	struct client		*tc = cmdq_get_target_client(item);
201	struct tty		*tty = &tc->tty;
202	struct window		*w;
203	const char		*errstr;
204	u_int			 adjust;
205	struct args_value	*av;
206
207	if (args_has(args, 'c') ||
208	    args_has(args, 'L') ||
209	    args_has(args, 'R') ||
210	    args_has(args, 'U') ||
211	    args_has(args, 'D'))
212	{
213		if (args_count(args) == 0)
214			adjust = 1;
215		else {
216			adjust = strtonum(args_string(args, 0), 1, INT_MAX,
217			    &errstr);
218			if (errstr != NULL) {
219				cmdq_error(item, "adjustment %s", errstr);
220				return (CMD_RETURN_ERROR);
221			}
222		}
223
224		if (args_has(args, 'c'))
225			tc->pan_window = NULL;
226		else {
227			w = tc->session->curw->window;
228			if (tc->pan_window != w) {
229				tc->pan_window = w;
230				tc->pan_ox = tty->oox;
231				tc->pan_oy = tty->ooy;
232			}
233			if (args_has(args, 'L')) {
234				if (tc->pan_ox > adjust)
235					tc->pan_ox -= adjust;
236				else
237					tc->pan_ox = 0;
238			} else if (args_has(args, 'R')) {
239				tc->pan_ox += adjust;
240				if (tc->pan_ox > w->sx - tty->osx)
241					tc->pan_ox = w->sx - tty->osx;
242			} else if (args_has(args, 'U')) {
243				if (tc->pan_oy > adjust)
244					tc->pan_oy -= adjust;
245				else
246					tc->pan_oy = 0;
247			} else if (args_has(args, 'D')) {
248				tc->pan_oy += adjust;
249				if (tc->pan_oy > w->sy - tty->osy)
250					tc->pan_oy = w->sy - tty->osy;
251			}
252		}
253		tty_update_client_offset(tc);
254		server_redraw_client(tc);
255		return (CMD_RETURN_NORMAL);
256	}
257
258	if (args_has(args, 'l'))
259		return (cmd_refresh_client_clipboard(self, item));
260
261	if (args_has(args, 'F')) /* -F is an alias for -f */
262		server_client_set_flags(tc, args_get(args, 'F'));
263	if (args_has(args, 'f'))
264		server_client_set_flags(tc, args_get(args, 'f'));
265
266	if (args_has(args, 'A')) {
267		if (~tc->flags & CLIENT_CONTROL)
268			goto not_control_client;
269		av = args_first_value(args, 'A');
270		while (av != NULL) {
271			cmd_refresh_client_update_offset(tc, av->string);
272			av = args_next_value(av);
273		}
274		return (CMD_RETURN_NORMAL);
275	}
276	if (args_has(args, 'B')) {
277		if (~tc->flags & CLIENT_CONTROL)
278			goto not_control_client;
279		av = args_first_value(args, 'B');
280		while (av != NULL) {
281			cmd_refresh_client_update_subscription(tc, av->string);
282			av = args_next_value(av);
283		}
284		return (CMD_RETURN_NORMAL);
285	}
286	if (args_has(args, 'C')) {
287		if (~tc->flags & CLIENT_CONTROL)
288			goto not_control_client;
289		return (cmd_refresh_client_control_client_size(self, item));
290	}
291
292	if (args_has(args, 'S')) {
293		tc->flags |= CLIENT_STATUSFORCE;
294		server_status_client(tc);
295	} else {
296		tc->flags |= CLIENT_STATUSFORCE;
297		server_redraw_client(tc);
298	}
299	return (CMD_RETURN_NORMAL);
300
301not_control_client:
302	cmdq_error(item, "not a control client");
303	return (CMD_RETURN_ERROR);
304}
305