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
control_notify_input(struct client * c,struct window_pane * wp,struct evbuffer * input)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 		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 
62 void
control_notify_window_layout_changed(struct window * w)63 control_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, 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 
103 void
control_notify_window_unlinked(__unused struct session * s,struct window * w)104 control_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 
121 void
control_notify_window_linked(__unused struct session * s,struct window * w)122 control_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 
139 void
control_notify_window_renamed(struct window * w)140 control_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 
160 void
control_notify_attached_session_changed(struct client * c)161 control_notify_attached_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 
172 void
control_notify_session_renamed(struct session * s)173 control_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 
185 void
control_notify_session_created(__unused struct session * s)186 control_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 
198 void
control_notify_session_close(__unused struct session * s)199 control_notify_session_close(__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