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