Merge branch 'obsd-master'
[tmux.git] / alerts.c
blobd3c5df0501ff4822c112640c49c5df5e8b42caed
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
21 #include <stdlib.h>
23 #include "tmux.h"
25 static int alerts_fired;
27 static void alerts_timer(int, short, void *);
28 static int alerts_enabled(struct window *, int);
29 static void alerts_callback(int, short, void *);
30 static void alerts_reset(struct window *);
32 static int alerts_action_applies(struct winlink *, const char *);
33 static int alerts_check_all(struct window *);
34 static int alerts_check_bell(struct window *);
35 static int alerts_check_activity(struct window *);
36 static int alerts_check_silence(struct window *);
37 static void alerts_set_message(struct winlink *, const char *,
38 const char *);
40 static TAILQ_HEAD(, window) alerts_list = TAILQ_HEAD_INITIALIZER(alerts_list);
42 static void
43 alerts_timer(__unused int fd, __unused short events, void *arg)
45 struct window *w = arg;
47 log_debug("@%u alerts timer expired", w->id);
48 alerts_queue(w, WINDOW_SILENCE);
51 static void
52 alerts_callback(__unused int fd, __unused short events, __unused void *arg)
54 struct window *w, *w1;
55 int alerts;
57 TAILQ_FOREACH_SAFE(w, &alerts_list, alerts_entry, w1) {
58 alerts = alerts_check_all(w);
59 log_debug("@%u alerts check, alerts %#x", w->id, alerts);
61 w->alerts_queued = 0;
62 TAILQ_REMOVE(&alerts_list, w, alerts_entry);
64 w->flags &= ~WINDOW_ALERTFLAGS;
65 window_remove_ref(w, __func__);
67 alerts_fired = 0;
70 static int
71 alerts_action_applies(struct winlink *wl, const char *name)
73 int action;
76 * {bell,activity,silence}-action determines when to alert: none means
77 * nothing happens, current means only do something for the current
78 * window and other means only for windows other than the current.
81 action = options_get_number(wl->session->options, name);
82 if (action == ALERT_ANY)
83 return (1);
84 if (action == ALERT_CURRENT)
85 return (wl == wl->session->curw);
86 if (action == ALERT_OTHER)
87 return (wl != wl->session->curw);
88 return (0);
91 static int
92 alerts_check_all(struct window *w)
94 int alerts;
96 alerts = alerts_check_bell(w);
97 alerts |= alerts_check_activity(w);
98 alerts |= alerts_check_silence(w);
99 return (alerts);
102 void
103 alerts_check_session(struct session *s)
105 struct winlink *wl;
107 RB_FOREACH(wl, winlinks, &s->windows)
108 alerts_check_all(wl->window);
111 static int
112 alerts_enabled(struct window *w, int flags)
114 if (flags & WINDOW_BELL) {
115 if (options_get_number(w->options, "monitor-bell"))
116 return (1);
118 if (flags & WINDOW_ACTIVITY) {
119 if (options_get_number(w->options, "monitor-activity"))
120 return (1);
122 if (flags & WINDOW_SILENCE) {
123 if (options_get_number(w->options, "monitor-silence") != 0)
124 return (1);
126 return (0);
129 void
130 alerts_reset_all(void)
132 struct window *w;
134 RB_FOREACH(w, windows, &windows)
135 alerts_reset(w);
138 static void
139 alerts_reset(struct window *w)
141 struct timeval tv;
143 if (!event_initialized(&w->alerts_timer))
144 evtimer_set(&w->alerts_timer, alerts_timer, w);
146 w->flags &= ~WINDOW_SILENCE;
147 event_del(&w->alerts_timer);
149 timerclear(&tv);
150 tv.tv_sec = options_get_number(w->options, "monitor-silence");
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);
157 void
158 alerts_queue(struct window *w, int flags)
160 alerts_reset(w);
162 if ((w->flags & flags) != flags) {
163 w->flags |= flags;
164 log_debug("@%u alerts flags added %#x", w->id, flags);
167 if (alerts_enabled(w, flags)) {
168 if (!w->alerts_queued) {
169 w->alerts_queued = 1;
170 TAILQ_INSERT_TAIL(&alerts_list, w, alerts_entry);
171 window_add_ref(w, __func__);
174 if (!alerts_fired) {
175 log_debug("alerts check queued (by @%u)", w->id);
176 event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL);
177 alerts_fired = 1;
182 static int
183 alerts_check_bell(struct window *w)
185 struct winlink *wl;
186 struct session *s;
188 if (~w->flags & WINDOW_BELL)
189 return (0);
190 if (!options_get_number(w->options, "monitor-bell"))
191 return (0);
193 TAILQ_FOREACH(wl, &w->winlinks, wentry)
194 wl->session->flags &= ~SESSION_ALERTED;
196 TAILQ_FOREACH(wl, &w->winlinks, wentry) {
198 * Bells are allowed even if there is an existing bell (so do
199 * not check WINLINK_BELL).
201 s = wl->session;
202 if (s->curw != wl || s->attached == 0) {
203 wl->flags |= WINLINK_BELL;
204 server_status_session(s);
206 if (!alerts_action_applies(wl, "bell-action"))
207 continue;
208 notify_winlink("alert-bell", wl);
210 if (s->flags & SESSION_ALERTED)
211 continue;
212 s->flags |= SESSION_ALERTED;
214 alerts_set_message(wl, "Bell", "visual-bell");
217 return (WINDOW_BELL);
220 static int
221 alerts_check_activity(struct window *w)
223 struct winlink *wl;
224 struct session *s;
226 if (~w->flags & WINDOW_ACTIVITY)
227 return (0);
228 if (!options_get_number(w->options, "monitor-activity"))
229 return (0);
231 TAILQ_FOREACH(wl, &w->winlinks, wentry)
232 wl->session->flags &= ~SESSION_ALERTED;
234 TAILQ_FOREACH(wl, &w->winlinks, wentry) {
235 if (wl->flags & WINLINK_ACTIVITY)
236 continue;
237 s = wl->session;
238 if (s->curw != wl || s->attached == 0) {
239 wl->flags |= WINLINK_ACTIVITY;
240 server_status_session(s);
242 if (!alerts_action_applies(wl, "activity-action"))
243 continue;
244 notify_winlink("alert-activity", wl);
246 if (s->flags & SESSION_ALERTED)
247 continue;
248 s->flags |= SESSION_ALERTED;
250 alerts_set_message(wl, "Activity", "visual-activity");
253 return (WINDOW_ACTIVITY);
256 static int
257 alerts_check_silence(struct window *w)
259 struct winlink *wl;
260 struct session *s;
262 if (~w->flags & WINDOW_SILENCE)
263 return (0);
264 if (options_get_number(w->options, "monitor-silence") == 0)
265 return (0);
267 TAILQ_FOREACH(wl, &w->winlinks, wentry)
268 wl->session->flags &= ~SESSION_ALERTED;
270 TAILQ_FOREACH(wl, &w->winlinks, wentry) {
271 if (wl->flags & WINLINK_SILENCE)
272 continue;
273 s = wl->session;
274 if (s->curw != wl || s->attached == 0) {
275 wl->flags |= WINLINK_SILENCE;
276 server_status_session(s);
278 if (!alerts_action_applies(wl, "silence-action"))
279 continue;
280 notify_winlink("alert-silence", wl);
282 if (s->flags & SESSION_ALERTED)
283 continue;
284 s->flags |= SESSION_ALERTED;
286 alerts_set_message(wl, "Silence", "visual-silence");
289 return (WINDOW_SILENCE);
292 static void
293 alerts_set_message(struct winlink *wl, const char *type, const char *option)
295 struct client *c;
296 int visual;
299 * We have found an alert (bell, activity or silence), so we need to
300 * pass it on to the user. For each client attached to this session,
301 * decide whether a bell, message or both is needed.
303 * If visual-{bell,activity,silence} is on, then a message is
304 * substituted for a bell; if it is off, a bell is sent as normal; both
305 * mean both a bell and message is sent.
308 visual = options_get_number(wl->session->options, option);
309 TAILQ_FOREACH(c, &clients, entry) {
310 if (c->session != wl->session || c->flags & CLIENT_CONTROL)
311 continue;
313 if (visual == VISUAL_OFF || visual == VISUAL_BOTH)
314 tty_putcode(&c->tty, TTYC_BEL);
315 if (visual == VISUAL_OFF)
316 continue;
317 if (c->session->curw == wl) {
318 status_message_set(c, -1, 1, 0, "%s in current window",
319 type);
320 } else {
321 status_message_set(c, -1, 1, 0, "%s in window %d", type,
322 wl->idx);