1 /* $OpenBSD: alerts.c,v 1.11 2016/05/11 20:56:58 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <event.h> 22 23 #include "tmux.h" 24 25 int alerts_fired; 26 27 void alerts_timer(int, short, void *); 28 int alerts_enabled(struct window *, int); 29 void alerts_callback(int, short, void *); 30 void alerts_reset(struct window *); 31 32 void alerts_run_hook(struct session *, struct winlink *, int); 33 int alerts_check_all(struct session *, struct winlink *); 34 int alerts_check_bell(struct session *, struct winlink *); 35 int alerts_check_activity(struct session *, struct winlink *); 36 int alerts_check_silence(struct session *, struct winlink *); 37 void alerts_ring_bell(struct session *); 38 39 void 40 alerts_timer(__unused int fd, __unused short events, void *arg) 41 { 42 struct window *w = arg; 43 44 log_debug("@%u alerts timer expired", w->id); 45 alerts_reset(w); 46 alerts_queue(w, WINDOW_SILENCE); 47 } 48 49 void 50 alerts_callback(__unused int fd, __unused short events, __unused void *arg) 51 { 52 struct window *w; 53 struct session *s; 54 struct winlink *wl; 55 int flags, alerts; 56 57 RB_FOREACH(w, windows, &windows) { 58 RB_FOREACH(s, sessions, &sessions) { 59 RB_FOREACH(wl, winlinks, &s->windows) { 60 if (wl->window != w) 61 continue; 62 flags = w->flags; 63 64 alerts = alerts_check_all(s, wl); 65 66 log_debug("%s:%d @%u alerts check, alerts %#x, " 67 "flags %#x", s->name, wl->idx, w->id, 68 alerts, flags); 69 } 70 } 71 } 72 alerts_fired = 0; 73 } 74 75 void 76 alerts_run_hook(struct session *s, struct winlink *wl, int flags) 77 { 78 struct cmd_find_state fs; 79 80 if (cmd_find_from_winlink(&fs, s, wl) != 0) 81 return; 82 83 if (flags & WINDOW_BELL) 84 hooks_run(s->hooks, NULL, &fs, "alert-bell"); 85 if (flags & WINDOW_SILENCE) 86 hooks_run(s->hooks, NULL, &fs, "alert-silence"); 87 if (flags & WINDOW_ACTIVITY) 88 hooks_run(s->hooks, NULL, &fs, "alert-activity"); 89 } 90 91 int 92 alerts_check_all(struct session *s, struct winlink *wl) 93 { 94 int alerts; 95 96 alerts = alerts_check_bell(s, wl); 97 alerts |= alerts_check_activity(s, wl); 98 alerts |= alerts_check_silence(s, wl); 99 if (alerts != 0) { 100 alerts_run_hook(s, wl, alerts); 101 server_status_session(s); 102 } 103 104 return (alerts); 105 } 106 107 void 108 alerts_check_session(struct session *s) 109 { 110 struct winlink *wl; 111 112 RB_FOREACH(wl, winlinks, &s->windows) 113 alerts_check_all(s, wl); 114 } 115 116 int 117 alerts_enabled(struct window *w, int flags) 118 { 119 if (flags & WINDOW_BELL) 120 return (1); 121 if (flags & WINDOW_ACTIVITY) { 122 if (options_get_number(w->options, "monitor-activity")) 123 return (1); 124 } 125 if (flags & WINDOW_SILENCE) { 126 if (options_get_number(w->options, "monitor-silence") != 0) 127 return (1); 128 } 129 return (0); 130 } 131 132 void 133 alerts_reset_all(void) 134 { 135 struct window *w; 136 137 RB_FOREACH(w, windows, &windows) 138 alerts_reset(w); 139 } 140 141 void 142 alerts_reset(struct window *w) 143 { 144 struct timeval tv; 145 146 w->flags &= ~WINDOW_SILENCE; 147 event_del(&w->alerts_timer); 148 149 timerclear(&tv); 150 tv.tv_sec = options_get_number(w->options, "monitor-silence"); 151 152 log_debug("@%u alerts timer reset %u", w->id, (u_int)tv.tv_sec); 153 if (tv.tv_sec != 0) 154 event_add(&w->alerts_timer, &tv); 155 } 156 157 void 158 alerts_queue(struct window *w, int flags) 159 { 160 if (w->flags & WINDOW_ACTIVITY) 161 alerts_reset(w); 162 163 if (!event_initialized(&w->alerts_timer)) 164 evtimer_set(&w->alerts_timer, alerts_timer, w); 165 166 if ((w->flags & flags) != flags) { 167 w->flags |= flags; 168 log_debug("@%u alerts flags added %#x", w->id, flags); 169 } 170 171 if (!alerts_fired && alerts_enabled(w, flags)) { 172 log_debug("alerts check queued (by @%u)", w->id); 173 event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL); 174 alerts_fired = 1; 175 } 176 } 177 178 int 179 alerts_check_bell(struct session *s, struct winlink *wl) 180 { 181 struct client *c; 182 struct window *w = wl->window; 183 int action, visual; 184 185 if (!(w->flags & WINDOW_BELL)) 186 return (0); 187 if (s->curw != wl) { 188 wl->flags |= WINLINK_BELL; 189 w->flags &= ~WINDOW_BELL; 190 } 191 if (s->curw->window == w) 192 w->flags &= ~WINDOW_BELL; 193 194 action = options_get_number(s->options, "bell-action"); 195 if (action == BELL_NONE) 196 return (0); 197 198 visual = options_get_number(s->options, "visual-bell"); 199 TAILQ_FOREACH(c, &clients, entry) { 200 if (c->session != s || c->flags & CLIENT_CONTROL) 201 continue; 202 if (!visual) { 203 if ((action == BELL_CURRENT && 204 c->session->curw->window == w) || 205 (action == BELL_OTHER && 206 c->session->curw->window != w) || 207 action == BELL_ANY) 208 tty_putcode(&c->tty, TTYC_BEL); 209 continue; 210 } 211 if (action == BELL_CURRENT && c->session->curw->window == w) 212 status_message_set(c, "Bell in current window"); 213 else if (action == BELL_ANY || (action == BELL_OTHER && 214 c->session->curw->window != w)) 215 status_message_set(c, "Bell in window %d", wl->idx); 216 } 217 218 return (WINDOW_BELL); 219 } 220 221 int 222 alerts_check_activity(struct session *s, struct winlink *wl) 223 { 224 struct client *c; 225 struct window *w = wl->window; 226 227 if (s->curw->window == w) 228 w->flags &= ~WINDOW_ACTIVITY; 229 230 if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_ACTIVITY) 231 return (0); 232 if (s->curw == wl) 233 return (0); 234 235 if (!options_get_number(w->options, "monitor-activity")) 236 return (0); 237 238 if (options_get_number(s->options, "bell-on-alert")) 239 alerts_ring_bell(s); 240 wl->flags |= WINLINK_ACTIVITY; 241 242 if (options_get_number(s->options, "visual-activity")) { 243 TAILQ_FOREACH(c, &clients, entry) { 244 if (c->session != s) 245 continue; 246 status_message_set(c, "Activity in window %d", wl->idx); 247 } 248 } 249 250 return (WINDOW_ACTIVITY); 251 } 252 253 int 254 alerts_check_silence(struct session *s, struct winlink *wl) 255 { 256 struct client *c; 257 struct window *w = wl->window; 258 259 if (s->curw->window == w) 260 w->flags &= ~WINDOW_SILENCE; 261 262 if (!(w->flags & WINDOW_SILENCE) || wl->flags & WINLINK_SILENCE) 263 return (0); 264 if (s->curw == wl) 265 return (0); 266 267 if (options_get_number(w->options, "monitor-silence") == 0) 268 return (0); 269 270 if (options_get_number(s->options, "bell-on-alert")) 271 alerts_ring_bell(s); 272 wl->flags |= WINLINK_SILENCE; 273 274 if (options_get_number(s->options, "visual-silence")) { 275 TAILQ_FOREACH(c, &clients, entry) { 276 if (c->session != s) 277 continue; 278 status_message_set(c, "Silence in window %d", wl->idx); 279 } 280 } 281 282 return (WINDOW_SILENCE); 283 } 284 285 void 286 alerts_ring_bell(struct session *s) 287 { 288 struct client *c; 289 290 TAILQ_FOREACH(c, &clients, entry) { 291 if (c->session == s && !(c->flags & CLIENT_CONTROL)) 292 tty_putcode(&c->tty, TTYC_BEL); 293 } 294 } 295