1/* $OpenBSD$ */
2
3/*
4 * Copyright (c) 2012 Nicholas Marriott <[email protected]>
5 * Copyright (c) 2012 George Nachman <[email protected]>
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
29void
30control_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
63void
64control_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
76void
77control_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
113void
114control_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
127void
128control_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
145void
146control_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
163void
164control_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
184void
185control_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
208void
209control_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
221void
222control_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
234void
235control_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
247void
248control_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