xref: /openbsd/usr.bin/tmux/control-notify.c (revision 8529ddd3)
1 /* $OpenBSD: control-notify.c,v 1.11 2015/04/24 23:17:11 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2012 Nicholas Marriott <nicm@users.sourceforge.net>
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 "tmux.h"
23 
24 #define CONTROL_SHOULD_NOTIFY_CLIENT(c) \
25 	((c) != NULL && ((c)->flags & CLIENT_CONTROL))
26 
27 void
28 control_notify_input(struct client *c, struct window_pane *wp,
29     struct evbuffer *input)
30 {
31 	u_char		*buf;
32 	size_t		 len;
33 	struct evbuffer *message;
34 	u_int		 i;
35 
36 	if (c->session == NULL)
37 	    return;
38 
39 	buf = EVBUFFER_DATA(input);
40 	len = EVBUFFER_LENGTH(input);
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 		evbuffer_add_printf(message, "%%output %%%u ", wp->id);
49 		for (i = 0; i < len; i++) {
50 			if (buf[i] < ' ' || buf[i] == '\\')
51 			    evbuffer_add_printf(message, "\\%03o", buf[i]);
52 			else
53 			    evbuffer_add_printf(message, "%c", buf[i]);
54 		}
55 		control_write_buffer(c, message);
56 		evbuffer_free(message);
57 	}
58 }
59 
60 void
61 control_notify_window_layout_changed(struct window *w)
62 {
63 	struct client		*c;
64 	struct session		*s;
65 	struct format_tree	*ft;
66 	struct winlink		*wl;
67 	const char		*template;
68 
69 	TAILQ_FOREACH(c, &clients, entry) {
70 		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
71 			continue;
72 		s = c->session;
73 
74 		if (winlink_find_by_window_id(&s->windows, w->id) == NULL)
75 			continue;
76 
77 		/*
78 		 * When the last pane in a window is closed it won't have a
79 		 * layout root and we don't need to inform the client about the
80 		 * layout change because the whole window will go away soon.
81 		 */
82 		if (w->layout_root == NULL)
83 			continue;
84 		template = "%layout-change #{window_id} #{window_layout}";
85 
86 		ft = format_create();
87 		wl = winlink_find_by_window(&s->windows, w);
88 		if (wl != NULL) {
89 			format_defaults(ft, c, NULL, wl, NULL);
90 			control_write(c, "%s", format_expand(ft, template));
91 		}
92 		format_free(ft);
93 	}
94 }
95 
96 void
97 control_notify_window_unlinked(unused struct session *s, struct window *w)
98 {
99 	struct client	*c;
100 	struct session	*cs;
101 
102 	TAILQ_FOREACH(c, &clients, entry) {
103 		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
104 			continue;
105 		cs = c->session;
106 
107 		if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
108 			control_write(c, "%%window-close @%u", w->id);
109 		else
110 			control_write(c, "%%unlinked-window-close @%u", w->id);
111 	}
112 }
113 
114 void
115 control_notify_window_linked(unused struct session *s, struct window *w)
116 {
117 	struct client	*c;
118 	struct session	*cs;
119 
120 	TAILQ_FOREACH(c, &clients, entry) {
121 		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
122 			continue;
123 		cs = c->session;
124 
125 		if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
126 			control_write(c, "%%window-add @%u", w->id);
127 		else
128 			control_write(c, "%%unlinked-window-add @%u", w->id);
129 	}
130 }
131 
132 void
133 control_notify_window_renamed(struct window *w)
134 {
135 	struct client	*c;
136 	struct session	*cs;
137 
138 	TAILQ_FOREACH(c, &clients, entry) {
139 		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
140 			continue;
141 		cs = c->session;
142 
143 		if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) {
144 			control_write(c, "%%window-renamed @%u %s", w->id,
145 			    w->name);
146 		} else {
147 			control_write(c, "%%unlinked-window-renamed @%u %s",
148 			    w->id, w->name);
149 		}
150 	}
151 }
152 
153 void
154 control_notify_attached_session_changed(struct client *c)
155 {
156 	struct session	*s;
157 
158 	if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
159 		return;
160 	s = c->session;
161 
162 	control_write(c, "%%session-changed $%u %s", s->id, s->name);
163 }
164 
165 void
166 control_notify_session_renamed(struct session *s)
167 {
168 	struct client	*c;
169 
170 	TAILQ_FOREACH(c, &clients, entry) {
171 		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
172 			continue;
173 
174 		control_write(c, "%%session-renamed $%u %s", s->id, s->name);
175 	}
176 }
177 
178 void
179 control_notify_session_created(unused struct session *s)
180 {
181 	struct client	*c;
182 
183 	TAILQ_FOREACH(c, &clients, entry) {
184 		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
185 			continue;
186 
187 		control_write(c, "%%sessions-changed");
188 	}
189 }
190 
191 void
192 control_notify_session_close(unused struct session *s)
193 {
194 	struct client	*c;
195 
196 	TAILQ_FOREACH(c, &clients, entry) {
197 		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
198 			continue;
199 
200 		control_write(c, "%%sessions-changed");
201 	}
202 }
203