control-notify.c revision 1.23
1/* $OpenBSD: control-notify.c,v 1.23 2019/05/07 10:25:15 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    const u_char *buf, size_t len)
32{
33	struct evbuffer *message;
34	u_int		 i;
35
36	if (c->session == NULL)
37	    return;
38
39	/*
40	 * Only write input if the window pane is linked to a window belonging
41	 * to the client's session.
42	 */
43	if (winlink_find_by_window(&c->session->windows, wp->window) != NULL) {
44		message = evbuffer_new();
45		if (message == NULL)
46			fatalx("out of memory");
47		evbuffer_add_printf(message, "%%output %%%u ", wp->id);
48		for (i = 0; i < len; i++) {
49			if (buf[i] < ' ' || buf[i] == '\\')
50			    evbuffer_add_printf(message, "\\%03o", buf[i]);
51			else
52			    evbuffer_add_printf(message, "%c", buf[i]);
53		}
54		control_write_buffer(c, message);
55		evbuffer_free(message);
56	}
57}
58
59void
60control_notify_pane_mode_changed(int pane)
61{
62	struct client	*c;
63
64	TAILQ_FOREACH(c, &clients, entry) {
65		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
66			continue;
67
68		control_write(c, "%%pane-mode-changed %%%u", pane);
69	}
70}
71
72void
73control_notify_window_layout_changed(struct window *w)
74{
75	struct client	*c;
76	struct session	*s;
77	struct winlink	*wl;
78	const char	*template;
79	char		*cp;
80
81	template = "%layout-change #{window_id} #{window_layout} "
82	    "#{window_visible_layout} #{window_flags}";
83
84	TAILQ_FOREACH(c, &clients, entry) {
85		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
86			continue;
87		s = c->session;
88
89		if (winlink_find_by_window_id(&s->windows, w->id) == NULL)
90			continue;
91
92		/*
93		 * When the last pane in a window is closed it won't have a
94		 * layout root and we don't need to inform the client about the
95		 * layout change because the whole window will go away soon.
96		 */
97		if (w->layout_root == NULL)
98			continue;
99
100		wl = winlink_find_by_window(&s->windows, w);
101		if (wl != NULL) {
102			cp = format_single(NULL, template, c, NULL, wl, NULL);
103			control_write(c, "%s", cp);
104			free(cp);
105		}
106	}
107}
108
109void
110control_notify_window_pane_changed(struct window *w)
111{
112	struct client	*c;
113
114	TAILQ_FOREACH(c, &clients, entry) {
115		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
116			continue;
117
118		control_write(c, "%%window-pane-changed @%u %%%u", w->id,
119		    w->active->id);
120	}
121}
122
123void
124control_notify_window_unlinked(__unused struct session *s, struct window *w)
125{
126	struct client	*c;
127	struct session	*cs;
128
129	TAILQ_FOREACH(c, &clients, entry) {
130		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
131			continue;
132		cs = c->session;
133
134		if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
135			control_write(c, "%%window-close @%u", w->id);
136		else
137			control_write(c, "%%unlinked-window-close @%u", w->id);
138	}
139}
140
141void
142control_notify_window_linked(__unused struct session *s, struct window *w)
143{
144	struct client	*c;
145	struct session	*cs;
146
147	TAILQ_FOREACH(c, &clients, entry) {
148		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
149			continue;
150		cs = c->session;
151
152		if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
153			control_write(c, "%%window-add @%u", w->id);
154		else
155			control_write(c, "%%unlinked-window-add @%u", w->id);
156	}
157}
158
159void
160control_notify_window_renamed(struct window *w)
161{
162	struct client	*c;
163	struct session	*cs;
164
165	TAILQ_FOREACH(c, &clients, entry) {
166		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
167			continue;
168		cs = c->session;
169
170		if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) {
171			control_write(c, "%%window-renamed @%u %s", w->id,
172			    w->name);
173		} else {
174			control_write(c, "%%unlinked-window-renamed @%u %s",
175			    w->id, w->name);
176		}
177	}
178}
179
180void
181control_notify_client_session_changed(struct client *cc)
182{
183	struct client	*c;
184	struct session	*s;
185
186	if (cc->session == NULL)
187		return;
188	s = cc->session;
189
190	TAILQ_FOREACH(c, &clients, entry) {
191		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
192			continue;
193
194		if (cc == c) {
195			control_write(c, "%%session-changed $%u %s", s->id,
196			    s->name);
197		} else {
198			control_write(c, "%%client-session-changed %s $%u %s",
199			    cc->name, s->id, s->name);
200		}
201	}
202}
203
204void
205control_notify_session_renamed(struct session *s)
206{
207	struct client	*c;
208
209	TAILQ_FOREACH(c, &clients, entry) {
210		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
211			continue;
212
213		control_write(c, "%%session-renamed $%u %s", s->id, s->name);
214	}
215}
216
217void
218control_notify_session_created(__unused struct session *s)
219{
220	struct client	*c;
221
222	TAILQ_FOREACH(c, &clients, entry) {
223		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
224			continue;
225
226		control_write(c, "%%sessions-changed");
227	}
228}
229
230void
231control_notify_session_closed(__unused struct session *s)
232{
233	struct client	*c;
234
235	TAILQ_FOREACH(c, &clients, entry) {
236		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
237			continue;
238
239		control_write(c, "%%sessions-changed");
240	}
241}
242
243void
244control_notify_session_window_changed(struct session *s)
245{
246	struct client	*c;
247
248	TAILQ_FOREACH(c, &clients, entry) {
249		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
250			continue;
251
252		control_write(c, "%%session-window-changed $%u @%u", s->id,
253		    s->curw->window->id);
254	}
255}
256