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