1 /* $OpenBSD: control-notify.c,v 1.31 2022/10/28 13:00:02 nicm Exp $ */
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