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>
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 *,
40 static TAILQ_HEAD(, window
) alerts_list
= TAILQ_HEAD_INITIALIZER(alerts_list
);
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
);
52 alerts_callback(__unused
int fd
, __unused
short events
, __unused
void *arg
)
54 struct window
*w
, *w1
;
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
);
62 TAILQ_REMOVE(&alerts_list
, w
, alerts_entry
);
64 w
->flags
&= ~WINDOW_ALERTFLAGS
;
65 window_remove_ref(w
, __func__
);
71 alerts_action_applies(struct winlink
*wl
, const char *name
)
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
)
84 if (action
== ALERT_CURRENT
)
85 return (wl
== wl
->session
->curw
);
86 if (action
== ALERT_OTHER
)
87 return (wl
!= wl
->session
->curw
);
92 alerts_check_all(struct window
*w
)
96 alerts
= alerts_check_bell(w
);
97 alerts
|= alerts_check_activity(w
);
98 alerts
|= alerts_check_silence(w
);
103 alerts_check_session(struct session
*s
)
107 RB_FOREACH(wl
, winlinks
, &s
->windows
)
108 alerts_check_all(wl
->window
);
112 alerts_enabled(struct window
*w
, int flags
)
114 if (flags
& WINDOW_BELL
) {
115 if (options_get_number(w
->options
, "monitor-bell"))
118 if (flags
& WINDOW_ACTIVITY
) {
119 if (options_get_number(w
->options
, "monitor-activity"))
122 if (flags
& WINDOW_SILENCE
) {
123 if (options_get_number(w
->options
, "monitor-silence") != 0)
130 alerts_reset_all(void)
134 RB_FOREACH(w
, windows
, &windows
)
139 alerts_reset(struct window
*w
)
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
);
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
);
154 event_add(&w
->alerts_timer
, &tv
);
158 alerts_queue(struct window
*w
, int flags
)
162 if ((w
->flags
& 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__
);
175 log_debug("alerts check queued (by @%u)", w
->id
);
176 event_once(-1, EV_TIMEOUT
, alerts_callback
, NULL
, NULL
);
183 alerts_check_bell(struct window
*w
)
188 if (~w
->flags
& WINDOW_BELL
)
190 if (!options_get_number(w
->options
, "monitor-bell"))
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).
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"))
208 notify_winlink("alert-bell", wl
);
210 if (s
->flags
& SESSION_ALERTED
)
212 s
->flags
|= SESSION_ALERTED
;
214 alerts_set_message(wl
, "Bell", "visual-bell");
217 return (WINDOW_BELL
);
221 alerts_check_activity(struct window
*w
)
226 if (~w
->flags
& WINDOW_ACTIVITY
)
228 if (!options_get_number(w
->options
, "monitor-activity"))
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
)
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"))
244 notify_winlink("alert-activity", wl
);
246 if (s
->flags
& SESSION_ALERTED
)
248 s
->flags
|= SESSION_ALERTED
;
250 alerts_set_message(wl
, "Activity", "visual-activity");
253 return (WINDOW_ACTIVITY
);
257 alerts_check_silence(struct window
*w
)
262 if (~w
->flags
& WINDOW_SILENCE
)
264 if (options_get_number(w
->options
, "monitor-silence") == 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
)
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"))
280 notify_winlink("alert-silence", wl
);
282 if (s
->flags
& SESSION_ALERTED
)
284 s
->flags
|= SESSION_ALERTED
;
286 alerts_set_message(wl
, "Silence", "visual-silence");
289 return (WINDOW_SILENCE
);
293 alerts_set_message(struct winlink
*wl
, const char *type
, const char *option
)
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
)
313 if (visual
== VISUAL_OFF
|| visual
== VISUAL_BOTH
)
314 tty_putcode(&c
->tty
, TTYC_BEL
);
315 if (visual
== VISUAL_OFF
)
317 if (c
->session
->curw
== wl
) {
318 status_message_set(c
, -1, 1, 0, "%s in current window",
321 status_message_set(c
, -1, 1, 0, "%s in window %d", type
,