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