alerts.c revision 1.3
1/* $OpenBSD: alerts.c,v 1.3 2015/09/21 09:34:52 nicm Exp $ */ 2 3/* 4 * Copyright (c) 2015 Nicholas Marriott <nicm@users.sourceforge.net> 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 25int alerts_fired; 26 27void alerts_timer(int, short, void *); 28int alerts_enabled(struct window *, int); 29void alerts_callback(int, short, void *); 30void alerts_reset(struct window *); 31 32int alerts_check_bell(struct session *, struct winlink *); 33int alerts_check_activity(struct session *, struct winlink *); 34int alerts_check_silence(struct session *, struct winlink *); 35void alerts_ring_bell(struct session *); 36 37void 38alerts_timer(unused int fd, unused short events, void *arg) 39{ 40 struct window *w = arg; 41 42 log_debug("@%u alerts timer expired", w->id); 43 alerts_reset(w); 44 alerts_queue(w, WINDOW_SILENCE); 45} 46 47void 48alerts_callback(unused int fd, unused short events, unused void *arg) 49{ 50 struct window *w; 51 struct session *s; 52 struct winlink *wl; 53 int flags, alerts; 54 55 RB_FOREACH(w, windows, &windows) { 56 RB_FOREACH(s, sessions, &sessions) { 57 RB_FOREACH(wl, winlinks, &s->windows) { 58 if (wl->window != w) 59 continue; 60 flags = w->flags; 61 62 alerts = alerts_check_bell(s, wl); 63 alerts |= alerts_check_activity(s, wl); 64 alerts |= alerts_check_silence(s, wl); 65 if (alerts != 0) 66 server_status_session(s); 67 68 log_debug("%s:%d @%u alerts check, alerts %#x, " 69 "flags %#x", s->name, wl->idx, w->id, 70 alerts, flags); 71 } 72 } 73 } 74 alerts_fired = 0; 75} 76 77int 78alerts_enabled(struct window *w, int flags) 79{ 80 struct session *s; 81 82 if (flags & WINDOW_ACTIVITY) { 83 if (options_get_number(&w->options, "monitor-activity")) 84 return (1); 85 } 86 if (flags & WINDOW_SILENCE) { 87 if (options_get_number(&w->options, "monitor-silence") != 0) 88 return (1); 89 } 90 if (~flags & WINDOW_BELL) 91 return (0); 92 RB_FOREACH(s, sessions, &sessions) { 93 if (!session_has(s, w)) 94 continue; 95 if (options_get_number(&s->options, "bell-action") != BELL_NONE) 96 return (1); 97 } 98 return (0); 99} 100 101void 102alerts_reset_all(void) 103{ 104 struct window *w; 105 106 RB_FOREACH(w, windows, &windows) 107 alerts_reset(w); 108} 109 110void 111alerts_reset(struct window *w) 112{ 113 struct timeval tv; 114 115 w->flags &= ~WINDOW_SILENCE; 116 event_del(&w->alerts_timer); 117 118 timerclear(&tv); 119 tv.tv_sec = options_get_number(&w->options, "monitor-silence"); 120 121 log_debug("@%u alerts timer reset %u", w->id, (u_int)tv.tv_sec); 122 if (tv.tv_sec != 0) 123 event_add(&w->alerts_timer, &tv); 124} 125 126void 127alerts_queue(struct window *w, int flags) 128{ 129 if (w->flags & WINDOW_ACTIVITY) 130 alerts_reset(w); 131 132 if (!event_initialized(&w->alerts_timer)) 133 evtimer_set(&w->alerts_timer, alerts_timer, w); 134 135 if (w->flags & flags) 136 return; 137 w->flags |= flags; 138 log_debug("@%u alerts flags added %#x", w->id, flags); 139 140 if (!alerts_fired && alerts_enabled(w, flags)) { 141 log_debug("alerts check queued (by @%u)", w->id); 142 event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL); 143 alerts_fired = 1; 144 } 145} 146 147int 148alerts_check_bell(struct session *s, struct winlink *wl) 149{ 150 struct client *c; 151 struct window *w = wl->window; 152 int action, visual; 153 154 if (!(w->flags & WINDOW_BELL) || wl->flags & WINLINK_BELL) 155 return (0); 156 if (s->curw != wl || s->flags & SESSION_UNATTACHED) 157 wl->flags |= WINLINK_BELL; 158 if (s->flags & SESSION_UNATTACHED) 159 return (0); 160 if (s->curw->window == w) 161 w->flags &= ~WINDOW_BELL; 162 163 action = options_get_number(&s->options, "bell-action"); 164 if (action == BELL_NONE) 165 return (0); 166 167 visual = options_get_number(&s->options, "visual-bell"); 168 TAILQ_FOREACH(c, &clients, entry) { 169 if (c->session != s || c->flags & CLIENT_CONTROL) 170 continue; 171 if (!visual) { 172 if ((action == BELL_CURRENT && 173 c->session->curw->window == w) || 174 (action == BELL_OTHER && 175 c->session->curw->window != w) || 176 action == BELL_ANY) 177 tty_putcode(&c->tty, TTYC_BEL); 178 continue; 179 } 180 if (action == BELL_CURRENT && c->session->curw->window == w) 181 status_message_set(c, "Bell in current window"); 182 else if (action == BELL_ANY || (action == BELL_OTHER && 183 c->session->curw->window != w)) 184 status_message_set(c, "Bell in window %d", wl->idx); 185 } 186 187 return (WINDOW_BELL); 188} 189 190int 191alerts_check_activity(struct session *s, struct winlink *wl) 192{ 193 struct client *c; 194 struct window *w = wl->window; 195 196 if (s->curw->window == w) 197 w->flags &= ~WINDOW_ACTIVITY; 198 199 if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_ACTIVITY) 200 return (0); 201 if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) 202 return (0); 203 204 if (!options_get_number(&w->options, "monitor-activity")) 205 return (0); 206 207 if (options_get_number(&s->options, "bell-on-alert")) 208 alerts_ring_bell(s); 209 wl->flags |= WINLINK_ACTIVITY; 210 211 if (options_get_number(&s->options, "visual-activity")) { 212 TAILQ_FOREACH(c, &clients, entry) { 213 if (c->session != s) 214 continue; 215 status_message_set(c, "Activity in window %d", wl->idx); 216 } 217 } 218 219 return (WINDOW_ACTIVITY); 220} 221 222int 223alerts_check_silence(struct session *s, struct winlink *wl) 224{ 225 struct client *c; 226 struct window *w = wl->window; 227 228 if (s->curw->window == w) 229 w->flags &= ~WINDOW_SILENCE; 230 231 if (!(w->flags & WINDOW_SILENCE) || wl->flags & WINLINK_SILENCE) 232 return (0); 233 if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) 234 return (0); 235 236 if (options_get_number(&w->options, "monitor-silence") == 0) 237 return (0); 238 239 if (options_get_number(&s->options, "bell-on-alert")) 240 alerts_ring_bell(s); 241 wl->flags |= WINLINK_SILENCE; 242 243 if (options_get_number(&s->options, "visual-silence")) { 244 TAILQ_FOREACH(c, &clients, entry) { 245 if (c->session != s) 246 continue; 247 status_message_set(c, "Silence in window %d", wl->idx); 248 } 249 } 250 251 return (WINDOW_SILENCE); 252} 253 254void 255alerts_ring_bell(struct session *s) 256{ 257 struct client *c; 258 259 TAILQ_FOREACH(c, &clients, entry) { 260 if (c->session == s && !(c->flags & CLIENT_CONTROL)) 261 tty_putcode(&c->tty, TTYC_BEL); 262 } 263} 264