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>
26 static int alerts_fired
;
28 static void alerts_timer(int, short, void *);
29 static int alerts_enabled(struct window
*, int);
30 static void alerts_callback(int, short, void *);
31 static void alerts_reset(struct window
*);
33 static int alerts_action_applies(struct winlink
*, const char *);
34 static int alerts_check_all(struct window
*);
35 static int alerts_check_bell(struct window
*);
36 static int alerts_check_activity(struct window
*);
37 static int alerts_check_silence(struct window
*);
38 static void alerts_set_message(struct winlink
*, const char *,
41 static TAILQ_HEAD(, window
) alerts_list
= TAILQ_HEAD_INITIALIZER(alerts_list
);
44 alerts_timer(__unused
int fd
, __unused
short events
, void *arg
)
46 struct window
*w
= arg
;
48 log_debug("@%u alerts timer expired", w
->id
);
49 alerts_queue(w
, WINDOW_SILENCE
);
53 alerts_callback(__unused
int fd
, __unused
short events
, __unused
void *arg
)
55 struct window
*w
, *w1
;
58 TAILQ_FOREACH_SAFE(w
, &alerts_list
, alerts_entry
, w1
) {
59 alerts
= alerts_check_all(w
);
60 log_debug("@%u alerts check, alerts %#x", w
->id
, alerts
);
63 TAILQ_REMOVE(&alerts_list
, w
, alerts_entry
);
65 w
->flags
&= ~WINDOW_ALERTFLAGS
;
66 window_remove_ref(w
, __func__
);
72 alerts_action_applies(struct winlink
*wl
, const char *name
)
77 * {bell,activity,silence}-action determines when to alert: none means
78 * nothing happens, current means only do something for the current
79 * window and other means only for windows other than the current.
82 action
= options_get_number(wl
->session
->options
, name
);
83 if (action
== ALERT_ANY
)
85 if (action
== ALERT_CURRENT
)
86 return (wl
== wl
->session
->curw
);
87 if (action
== ALERT_OTHER
)
88 return (wl
!= wl
->session
->curw
);
93 alerts_check_all(struct window
*w
)
97 alerts
= alerts_check_bell(w
);
98 alerts
|= alerts_check_activity(w
);
99 alerts
|= alerts_check_silence(w
);
104 alerts_check_session(struct session
*s
)
108 RB_FOREACH(wl
, winlinks
, &s
->windows
)
109 alerts_check_all(wl
->window
);
113 alerts_enabled(struct window
*w
, int flags
)
115 if (flags
& WINDOW_BELL
) {
116 if (options_get_number(w
->options
, "monitor-bell"))
119 if (flags
& WINDOW_ACTIVITY
) {
120 if (options_get_number(w
->options
, "monitor-activity"))
123 if (flags
& WINDOW_SILENCE
) {
124 if (options_get_number(w
->options
, "monitor-silence") != 0)
131 alerts_reset_all(void)
135 RB_FOREACH(w
, windows
, &windows
)
140 alerts_reset(struct window
*w
)
144 if (!event_initialized(&w
->alerts_timer
))
145 evtimer_set(&w
->alerts_timer
, alerts_timer
, w
);
147 w
->flags
&= ~WINDOW_SILENCE
;
148 event_del(&w
->alerts_timer
);
151 tv
.tv_sec
= options_get_number(w
->options
, "monitor-silence");
153 log_debug("@%u alerts timer reset %u", w
->id
, (u_int
)tv
.tv_sec
);
155 event_add(&w
->alerts_timer
, &tv
);
159 alerts_queue(struct window
*w
, int flags
)
163 if ((w
->flags
& flags
) != flags
) {
165 log_debug("@%u alerts flags added %#x", w
->id
, flags
);
168 if (alerts_enabled(w
, flags
)) {
169 if (!w
->alerts_queued
) {
170 w
->alerts_queued
= 1;
171 TAILQ_INSERT_TAIL(&alerts_list
, w
, alerts_entry
);
172 window_add_ref(w
, __func__
);
176 log_debug("alerts check queued (by @%u)", w
->id
);
177 event_once(-1, EV_TIMEOUT
, alerts_callback
, NULL
, NULL
);
184 alerts_check_bell(struct window
*w
)
189 if (~w
->flags
& WINDOW_BELL
)
191 if (!options_get_number(w
->options
, "monitor-bell"))
194 TAILQ_FOREACH(wl
, &w
->winlinks
, wentry
)
195 wl
->session
->flags
&= ~SESSION_ALERTED
;
197 TAILQ_FOREACH(wl
, &w
->winlinks
, wentry
) {
199 * Bells are allowed even if there is an existing bell (so do
200 * not check WINLINK_BELL).
203 if (s
->curw
!= wl
|| s
->attached
== 0) {
204 wl
->flags
|= WINLINK_BELL
;
205 server_status_session(s
);
207 if (!alerts_action_applies(wl
, "bell-action"))
209 notify_winlink("alert-bell", wl
);
211 if (s
->flags
& SESSION_ALERTED
)
213 s
->flags
|= SESSION_ALERTED
;
215 alerts_set_message(wl
, "Bell", "visual-bell");
218 return (WINDOW_BELL
);
222 alerts_check_activity(struct window
*w
)
227 if (~w
->flags
& WINDOW_ACTIVITY
)
229 if (!options_get_number(w
->options
, "monitor-activity"))
232 TAILQ_FOREACH(wl
, &w
->winlinks
, wentry
)
233 wl
->session
->flags
&= ~SESSION_ALERTED
;
235 TAILQ_FOREACH(wl
, &w
->winlinks
, wentry
) {
236 if (wl
->flags
& WINLINK_ACTIVITY
)
239 if (s
->curw
!= wl
|| s
->attached
== 0) {
240 wl
->flags
|= WINLINK_ACTIVITY
;
241 server_status_session(s
);
243 if (!alerts_action_applies(wl
, "activity-action"))
245 notify_winlink("alert-activity", wl
);
247 if (s
->flags
& SESSION_ALERTED
)
249 s
->flags
|= SESSION_ALERTED
;
251 alerts_set_message(wl
, "Activity", "visual-activity");
254 return (WINDOW_ACTIVITY
);
258 alerts_check_silence(struct window
*w
)
263 if (~w
->flags
& WINDOW_SILENCE
)
265 if (options_get_number(w
->options
, "monitor-silence") == 0)
268 TAILQ_FOREACH(wl
, &w
->winlinks
, wentry
)
269 wl
->session
->flags
&= ~SESSION_ALERTED
;
271 TAILQ_FOREACH(wl
, &w
->winlinks
, wentry
) {
272 if (wl
->flags
& WINLINK_SILENCE
)
275 if (s
->curw
!= wl
|| s
->attached
== 0) {
276 wl
->flags
|= WINLINK_SILENCE
;
277 server_status_session(s
);
279 if (!alerts_action_applies(wl
, "silence-action"))
281 notify_winlink("alert-silence", wl
);
283 if (s
->flags
& SESSION_ALERTED
)
285 s
->flags
|= SESSION_ALERTED
;
287 alerts_set_message(wl
, "Silence", "visual-silence");
290 return (WINDOW_SILENCE
);
294 alerts_set_message(struct winlink
*wl
, const char *type
, const char *option
)
300 * We have found an alert (bell, activity or silence), so we need to
301 * pass it on to the user. For each client attached to this session,
302 * decide whether a bell, message or both is needed.
304 * If visual-{bell,activity,silence} is on, then a message is
305 * substituted for a bell; if it is off, a bell is sent as normal; both
306 * mean both a bell and message is sent.
309 visual
= options_get_number(wl
->session
->options
, option
);
310 TAILQ_FOREACH(c
, &clients
, entry
) {
311 if (c
->session
!= wl
->session
|| c
->flags
& CLIENT_CONTROL
)
314 if (visual
== VISUAL_OFF
|| visual
== VISUAL_BOTH
)
315 tty_putcode(&c
->tty
, TTYC_BEL
);
316 if (visual
== VISUAL_OFF
)
318 if (c
->session
->curw
== wl
) {
319 status_message_set(c
, -1, 1, 0, "%s in current window",
322 status_message_set(c
, -1, 1, 0, "%s in window %d", type
,