xref: /openbsd-src/usr.bin/tmux/control-notify.c (revision 7350f337b9e3eb4461d99580e625c7ef148d107c)
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 
29 void
30 control_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 
59 void
60 control_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 
72 void
73 control_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 
109 void
110 control_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 
123 void
124 control_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 
141 void
142 control_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 
159 void
160 control_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 
180 void
181 control_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 
204 void
205 control_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 
217 void
218 control_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 
230 void
231 control_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 
243 void
244 control_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