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 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 evbuffer_add(message, "", 1); 58 control_write(c, "%s", EVBUFFER_DATA(message)); 59 evbuffer_free(message); 60 } 61 } 62 63 void 64 control_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 76 void 77 control_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 113 void 114 control_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 127 void 128 control_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 145 void 146 control_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 163 void 164 control_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 184 void 185 control_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 208 void 209 control_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 221 void 222 control_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 234 void 235 control_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 247 void 248 control_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