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