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