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_pane_mode_changed(int pane)30 control_notify_pane_mode_changed(int pane)
31 {
32           struct client       *c;
33 
34           TAILQ_FOREACH(c, &clients, entry) {
35                     if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
36                               continue;
37 
38                     control_write(c, "%%pane-mode-changed %%%u", pane);
39           }
40 }
41 
42 void
control_notify_window_layout_changed(struct window * w)43 control_notify_window_layout_changed(struct window *w)
44 {
45           struct client       *c;
46           struct session      *s;
47           struct winlink      *wl;
48           const char          *template;
49           char                *cp;
50 
51           template = "%layout-change #{window_id} #{window_layout} "
52               "#{window_visible_layout} #{window_raw_flags}";
53 
54           TAILQ_FOREACH(c, &clients, entry) {
55                     if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
56                               continue;
57                     s = c->session;
58 
59                     if (winlink_find_by_window_id(&s->windows, w->id) == NULL)
60                               continue;
61 
62                     /*
63                      * When the last pane in a window is closed it won't have a
64                      * layout root and we don't need to inform the client about the
65                      * layout change because the whole window will go away soon.
66                      */
67                     if (w->layout_root == NULL)
68                               continue;
69 
70                     wl = winlink_find_by_window(&s->windows, w);
71                     if (wl != NULL) {
72                               cp = format_single(NULL, template, c, NULL, wl, NULL);
73                               control_write(c, "%s", cp);
74                               free(cp);
75                     }
76           }
77 }
78 
79 void
control_notify_window_pane_changed(struct window * w)80 control_notify_window_pane_changed(struct window *w)
81 {
82           struct client       *c;
83 
84           TAILQ_FOREACH(c, &clients, entry) {
85                     if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
86                               continue;
87 
88                     control_write(c, "%%window-pane-changed @%u %%%u", w->id,
89                         w->active->id);
90           }
91 }
92 
93 void
control_notify_window_unlinked(__unused struct session * s,struct window * w)94 control_notify_window_unlinked(__unused struct session *s, struct window *w)
95 {
96           struct client       *c;
97           struct session      *cs;
98 
99           TAILQ_FOREACH(c, &clients, entry) {
100                     if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
101                               continue;
102                     cs = c->session;
103 
104                     if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
105                               control_write(c, "%%window-close @%u", w->id);
106                     else
107                               control_write(c, "%%unlinked-window-close @%u", w->id);
108           }
109 }
110 
111 void
control_notify_window_linked(__unused struct session * s,struct window * w)112 control_notify_window_linked(__unused struct session *s, struct window *w)
113 {
114           struct client       *c;
115           struct session      *cs;
116 
117           TAILQ_FOREACH(c, &clients, entry) {
118                     if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
119                               continue;
120                     cs = c->session;
121 
122                     if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
123                               control_write(c, "%%window-add @%u", w->id);
124                     else
125                               control_write(c, "%%unlinked-window-add @%u", w->id);
126           }
127 }
128 
129 void
control_notify_window_renamed(struct window * w)130 control_notify_window_renamed(struct window *w)
131 {
132           struct client       *c;
133           struct session      *cs;
134 
135           TAILQ_FOREACH(c, &clients, entry) {
136                     if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
137                               continue;
138                     cs = c->session;
139 
140                     if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) {
141                               control_write(c, "%%window-renamed @%u %s", w->id,
142                                   w->name);
143                     } else {
144                               control_write(c, "%%unlinked-window-renamed @%u %s",
145                                   w->id, w->name);
146                     }
147           }
148 }
149 
150 void
control_notify_client_session_changed(struct client * cc)151 control_notify_client_session_changed(struct client *cc)
152 {
153           struct client       *c;
154           struct session      *s;
155 
156           if (cc->session == NULL)
157                     return;
158           s = cc->session;
159 
160           TAILQ_FOREACH(c, &clients, entry) {
161                     if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
162                               continue;
163 
164                     if (cc == c) {
165                               control_write(c, "%%session-changed $%u %s", s->id,
166                                   s->name);
167                     } else {
168                               control_write(c, "%%client-session-changed %s $%u %s",
169                                   cc->name, s->id, s->name);
170                     }
171           }
172 }
173 
174 void
control_notify_client_detached(struct client * cc)175 control_notify_client_detached(struct client *cc)
176 {
177           struct client       *c;
178 
179           TAILQ_FOREACH(c, &clients, entry) {
180                     if (CONTROL_SHOULD_NOTIFY_CLIENT(c))
181                               control_write(c, "%%client-detached %s", cc->name);
182           }
183 }
184 
185 void
control_notify_session_renamed(struct session * s)186 control_notify_session_renamed(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, "%%session-renamed $%u %s", s->id, s->name);
195           }
196 }
197 
198 void
control_notify_session_created(__unused struct session * s)199 control_notify_session_created(__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 
211 void
control_notify_session_closed(__unused struct session * s)212 control_notify_session_closed(__unused struct session *s)
213 {
214           struct client       *c;
215 
216           TAILQ_FOREACH(c, &clients, entry) {
217                     if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
218                               continue;
219 
220                     control_write(c, "%%sessions-changed");
221           }
222 }
223 
224 void
control_notify_session_window_changed(struct session * s)225 control_notify_session_window_changed(struct session *s)
226 {
227           struct client       *c;
228 
229           TAILQ_FOREACH(c, &clients, entry) {
230                     if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
231                               continue;
232 
233                     control_write(c, "%%session-window-changed $%u @%u", s->id,
234                         s->curw->window->id);
235           }
236 }
237 
238 void
control_notify_paste_buffer_changed(const char * name)239 control_notify_paste_buffer_changed(const char *name)
240 {
241           struct client       *c;
242 
243           TAILQ_FOREACH(c, &clients, entry) {
244                     if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
245                               continue;
246 
247                     control_write(c, "%%paste-buffer-changed %s", name);
248           }
249 }
250 
251 void
control_notify_paste_buffer_deleted(const char * name)252 control_notify_paste_buffer_deleted(const char *name)
253 {
254           struct client       *c;
255 
256           TAILQ_FOREACH(c, &clients, entry) {
257                     if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
258                               continue;
259 
260                     control_write(c, "%%paste-buffer-deleted %s", name);
261           }
262 }
263