control-notify.c revision 1.19
1/* $OpenBSD: control-notify.c,v 1.19 2017/02/03 11:57:27 nicm Exp $ */
2
3/*
4 * Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
5 * Copyright (c) 2012 George Nachman <tmux@georgester.com>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
16 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
17 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <sys/types.h>
21
22#include <stdlib.h>
23
24#include "tmux.h"
25
26#define CONTROL_SHOULD_NOTIFY_CLIENT(c) \
27	((c) != NULL && ((c)->flags & CLIENT_CONTROL))
28
29void
30control_notify_input(struct client *c, struct window_pane *wp,
31    struct evbuffer *input)
32{
33	u_char		*buf;
34	size_t		 len;
35	struct evbuffer *message;
36	u_int		 i;
37
38	if (c->session == NULL)
39	    return;
40
41	buf = EVBUFFER_DATA(input);
42	len = EVBUFFER_LENGTH(input);
43
44	/*
45	 * Only write input if the window pane is linked to a window belonging
46	 * to the client's session.
47	 */
48	if (winlink_find_by_window(&c->session->windows, wp->window) != NULL) {
49		message = evbuffer_new();
50		evbuffer_add_printf(message, "%%output %%%u ", wp->id);
51		for (i = 0; i < len; i++) {
52			if (buf[i] < ' ' || buf[i] == '\\')
53			    evbuffer_add_printf(message, "\\%03o", buf[i]);
54			else
55			    evbuffer_add_printf(message, "%c", buf[i]);
56		}
57		control_write_buffer(c, message);
58		evbuffer_free(message);
59	}
60}
61
62void
63control_notify_window_layout_changed(struct window *w)
64{
65	struct client		*c;
66	struct session		*s;
67	struct format_tree	*ft;
68	struct winlink		*wl;
69	const char		*template;
70	char			*expanded;
71
72	template = "%layout-change #{window_id} #{window_layout} "
73	    "#{window_visible_layout} #{window_flags}";
74
75	TAILQ_FOREACH(c, &clients, entry) {
76		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
77			continue;
78		s = c->session;
79
80		if (winlink_find_by_window_id(&s->windows, w->id) == NULL)
81			continue;
82
83		/*
84		 * When the last pane in a window is closed it won't have a
85		 * layout root and we don't need to inform the client about the
86		 * layout change because the whole window will go away soon.
87		 */
88		if (w->layout_root == NULL)
89			continue;
90
91		ft = format_create(NULL, FORMAT_NONE, 0);
92		wl = winlink_find_by_window(&s->windows, w);
93		if (wl != NULL) {
94			format_defaults(ft, c, NULL, wl, NULL);
95			expanded = format_expand(ft, template);
96			control_write(c, "%s", expanded);
97			free(expanded);
98		}
99		format_free(ft);
100	}
101}
102
103void
104control_notify_window_unlinked(__unused struct session *s, struct window *w)
105{
106	struct client	*c;
107	struct session	*cs;
108
109	TAILQ_FOREACH(c, &clients, entry) {
110		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
111			continue;
112		cs = c->session;
113
114		if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
115			control_write(c, "%%window-close @%u", w->id);
116		else
117			control_write(c, "%%unlinked-window-close @%u", w->id);
118	}
119}
120
121void
122control_notify_window_linked(__unused struct session *s, struct window *w)
123{
124	struct client	*c;
125	struct session	*cs;
126
127	TAILQ_FOREACH(c, &clients, entry) {
128		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
129			continue;
130		cs = c->session;
131
132		if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
133			control_write(c, "%%window-add @%u", w->id);
134		else
135			control_write(c, "%%unlinked-window-add @%u", w->id);
136	}
137}
138
139void
140control_notify_window_renamed(struct window *w)
141{
142	struct client	*c;
143	struct session	*cs;
144
145	TAILQ_FOREACH(c, &clients, entry) {
146		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
147			continue;
148		cs = c->session;
149
150		if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) {
151			control_write(c, "%%window-renamed @%u %s", w->id,
152			    w->name);
153		} else {
154			control_write(c, "%%unlinked-window-renamed @%u %s",
155			    w->id, w->name);
156		}
157	}
158}
159
160void
161control_notify_client_session_changed(struct client *c)
162{
163	struct session	*s;
164
165	if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
166		return;
167	s = c->session;
168
169	control_write(c, "%%session-changed $%u %s", s->id, s->name);
170}
171
172void
173control_notify_session_renamed(struct session *s)
174{
175	struct client	*c;
176
177	TAILQ_FOREACH(c, &clients, entry) {
178		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
179			continue;
180
181		control_write(c, "%%session-renamed $%u %s", s->id, s->name);
182	}
183}
184
185void
186control_notify_session_created(__unused struct session *s)
187{
188	struct client	*c;
189
190	TAILQ_FOREACH(c, &clients, entry) {
191		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
192			continue;
193
194		control_write(c, "%%sessions-changed");
195	}
196}
197
198void
199control_notify_session_closed(__unused struct session *s)
200{
201	struct client	*c;
202
203	TAILQ_FOREACH(c, &clients, entry) {
204		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
205			continue;
206
207		control_write(c, "%%sessions-changed");
208	}
209}
210