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