1*e7e79d0aSnicm /* $OpenBSD: alerts.c,v 1.33 2021/04/12 09:36:12 nicm Exp $ */
281fe4598Snicm
381fe4598Snicm /*
498ca8272Snicm * Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
581fe4598Snicm *
681fe4598Snicm * Permission to use, copy, modify, and distribute this software for any
781fe4598Snicm * purpose with or without fee is hereby granted, provided that the above
881fe4598Snicm * copyright notice and this permission notice appear in all copies.
981fe4598Snicm *
1081fe4598Snicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1181fe4598Snicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1281fe4598Snicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1381fe4598Snicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1481fe4598Snicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
1581fe4598Snicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
1681fe4598Snicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1781fe4598Snicm */
1881fe4598Snicm
1981fe4598Snicm #include <sys/types.h>
2081fe4598Snicm
2181fe4598Snicm #include <event.h>
22de5a0fddSnicm #include <stdlib.h>
2381fe4598Snicm
2481fe4598Snicm #include "tmux.h"
2581fe4598Snicm
26413e5e52Snicm static int alerts_fired;
2781fe4598Snicm
28413e5e52Snicm static void alerts_timer(int, short, void *);
29413e5e52Snicm static int alerts_enabled(struct window *, int);
30413e5e52Snicm static void alerts_callback(int, short, void *);
31413e5e52Snicm static void alerts_reset(struct window *);
3281fe4598Snicm
337d08f26cSnicm static int alerts_action_applies(struct winlink *, const char *);
34de5a0fddSnicm static int alerts_check_all(struct window *);
35de5a0fddSnicm static int alerts_check_bell(struct window *);
36de5a0fddSnicm static int alerts_check_activity(struct window *);
37de5a0fddSnicm static int alerts_check_silence(struct window *);
387d08f26cSnicm static void alerts_set_message(struct winlink *, const char *,
397d08f26cSnicm const char *);
4081fe4598Snicm
41de5a0fddSnicm static TAILQ_HEAD(, window) alerts_list = TAILQ_HEAD_INITIALIZER(alerts_list);
42de5a0fddSnicm
43413e5e52Snicm static void
alerts_timer(__unused int fd,__unused short events,void * arg)44d0e2e7f1Snicm alerts_timer(__unused int fd, __unused short events, void *arg)
4581fe4598Snicm {
4681fe4598Snicm struct window *w = arg;
4781fe4598Snicm
4881fe4598Snicm log_debug("@%u alerts timer expired", w->id);
4981fe4598Snicm alerts_queue(w, WINDOW_SILENCE);
5081fe4598Snicm }
5181fe4598Snicm
52413e5e52Snicm static void
alerts_callback(__unused int fd,__unused short events,__unused void * arg)53d0e2e7f1Snicm alerts_callback(__unused int fd, __unused short events, __unused void *arg)
5481fe4598Snicm {
55de5a0fddSnicm struct window *w, *w1;
56de5a0fddSnicm int alerts;
5781fe4598Snicm
58de5a0fddSnicm TAILQ_FOREACH_SAFE(w, &alerts_list, alerts_entry, w1) {
59de5a0fddSnicm alerts = alerts_check_all(w);
60de5a0fddSnicm log_debug("@%u alerts check, alerts %#x", w->id, alerts);
6181fe4598Snicm
62de5a0fddSnicm w->alerts_queued = 0;
63de5a0fddSnicm TAILQ_REMOVE(&alerts_list, w, alerts_entry);
6423a32ceeSnicm
6523a32ceeSnicm w->flags &= ~WINDOW_ALERTFLAGS;
6654279ec3Snicm window_remove_ref(w, __func__);
6781fe4598Snicm }
6881fe4598Snicm alerts_fired = 0;
6981fe4598Snicm }
7081fe4598Snicm
71413e5e52Snicm static int
alerts_action_applies(struct winlink * wl,const char * name)727d08f26cSnicm alerts_action_applies(struct winlink *wl, const char *name)
737d08f26cSnicm {
747d08f26cSnicm int action;
757d08f26cSnicm
767d08f26cSnicm /*
777d08f26cSnicm * {bell,activity,silence}-action determines when to alert: none means
787d08f26cSnicm * nothing happens, current means only do something for the current
797d08f26cSnicm * window and other means only for windows other than the current.
807d08f26cSnicm */
817d08f26cSnicm
827d08f26cSnicm action = options_get_number(wl->session->options, name);
837d08f26cSnicm if (action == ALERT_ANY)
847d08f26cSnicm return (1);
857d08f26cSnicm if (action == ALERT_CURRENT)
867d08f26cSnicm return (wl == wl->session->curw);
877d08f26cSnicm if (action == ALERT_OTHER)
887d08f26cSnicm return (wl != wl->session->curw);
897d08f26cSnicm return (0);
907d08f26cSnicm }
917d08f26cSnicm
927d08f26cSnicm static int
alerts_check_all(struct window * w)93de5a0fddSnicm alerts_check_all(struct window *w)
94705f6e28Snicm {
95705f6e28Snicm int alerts;
96705f6e28Snicm
97de5a0fddSnicm alerts = alerts_check_bell(w);
98de5a0fddSnicm alerts |= alerts_check_activity(w);
99de5a0fddSnicm alerts |= alerts_check_silence(w);
100705f6e28Snicm return (alerts);
101705f6e28Snicm }
102705f6e28Snicm
103705f6e28Snicm void
alerts_check_session(struct session * s)104705f6e28Snicm alerts_check_session(struct session *s)
105705f6e28Snicm {
106705f6e28Snicm struct winlink *wl;
107705f6e28Snicm
108705f6e28Snicm RB_FOREACH(wl, winlinks, &s->windows)
109de5a0fddSnicm alerts_check_all(wl->window);
110705f6e28Snicm }
111705f6e28Snicm
112413e5e52Snicm static int
alerts_enabled(struct window * w,int flags)11381fe4598Snicm alerts_enabled(struct window *w, int flags)
11481fe4598Snicm {
1159a48cd7dSnicm if (flags & WINDOW_BELL) {
1169a48cd7dSnicm if (options_get_number(w->options, "monitor-bell"))
11783079eb9Snicm return (1);
1189a48cd7dSnicm }
11981fe4598Snicm if (flags & WINDOW_ACTIVITY) {
120d89252e5Snicm if (options_get_number(w->options, "monitor-activity"))
12181fe4598Snicm return (1);
12281fe4598Snicm }
12381fe4598Snicm if (flags & WINDOW_SILENCE) {
124d89252e5Snicm if (options_get_number(w->options, "monitor-silence") != 0)
12581fe4598Snicm return (1);
12681fe4598Snicm }
12781fe4598Snicm return (0);
12881fe4598Snicm }
12981fe4598Snicm
13081fe4598Snicm void
alerts_reset_all(void)13181fe4598Snicm alerts_reset_all(void)
13281fe4598Snicm {
13381fe4598Snicm struct window *w;
13481fe4598Snicm
13581fe4598Snicm RB_FOREACH(w, windows, &windows)
13681fe4598Snicm alerts_reset(w);
13781fe4598Snicm }
13881fe4598Snicm
139413e5e52Snicm static void
alerts_reset(struct window * w)14081fe4598Snicm alerts_reset(struct window *w)
14181fe4598Snicm {
14281fe4598Snicm struct timeval tv;
14381fe4598Snicm
14451ae4248Snicm if (!event_initialized(&w->alerts_timer))
14551ae4248Snicm evtimer_set(&w->alerts_timer, alerts_timer, w);
14651ae4248Snicm
14781fe4598Snicm w->flags &= ~WINDOW_SILENCE;
14881fe4598Snicm event_del(&w->alerts_timer);
14981fe4598Snicm
15081fe4598Snicm timerclear(&tv);
151d89252e5Snicm tv.tv_sec = options_get_number(w->options, "monitor-silence");
15281fe4598Snicm
15381fe4598Snicm log_debug("@%u alerts timer reset %u", w->id, (u_int)tv.tv_sec);
15481fe4598Snicm if (tv.tv_sec != 0)
15581fe4598Snicm event_add(&w->alerts_timer, &tv);
15681fe4598Snicm }
15781fe4598Snicm
15881fe4598Snicm void
alerts_queue(struct window * w,int flags)15981fe4598Snicm alerts_queue(struct window *w, int flags)
16081fe4598Snicm {
161a08c4a20Snicm alerts_reset(w);
162a08c4a20Snicm
1630c25f9dcSnicm if ((w->flags & flags) != flags) {
16481fe4598Snicm w->flags |= flags;
16581fe4598Snicm log_debug("@%u alerts flags added %#x", w->id, flags);
1660c25f9dcSnicm }
16781fe4598Snicm
168b3c6572bSnicm if (alerts_enabled(w, flags)) {
169de5a0fddSnicm if (!w->alerts_queued) {
170de5a0fddSnicm w->alerts_queued = 1;
171de5a0fddSnicm TAILQ_INSERT_TAIL(&alerts_list, w, alerts_entry);
17254279ec3Snicm window_add_ref(w, __func__);
173de5a0fddSnicm }
174de5a0fddSnicm
175b3c6572bSnicm if (!alerts_fired) {
17681fe4598Snicm log_debug("alerts check queued (by @%u)", w->id);
17781fe4598Snicm event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL);
17881fe4598Snicm alerts_fired = 1;
17981fe4598Snicm }
18081fe4598Snicm }
181b3c6572bSnicm }
18281fe4598Snicm
183413e5e52Snicm static int
alerts_check_bell(struct window * w)184de5a0fddSnicm alerts_check_bell(struct window *w)
18581fe4598Snicm {
186de5a0fddSnicm struct winlink *wl;
187de5a0fddSnicm struct session *s;
18881fe4598Snicm
189de5a0fddSnicm if (~w->flags & WINDOW_BELL)
19081fe4598Snicm return (0);
1919a48cd7dSnicm if (!options_get_number(w->options, "monitor-bell"))
1929a48cd7dSnicm return (0);
193de5a0fddSnicm
194de5a0fddSnicm TAILQ_FOREACH(wl, &w->winlinks, wentry)
195de5a0fddSnicm wl->session->flags &= ~SESSION_ALERTED;
196de5a0fddSnicm
197de5a0fddSnicm TAILQ_FOREACH(wl, &w->winlinks, wentry) {
198ac0a37a1Snicm /*
199ac0a37a1Snicm * Bells are allowed even if there is an existing bell (so do
200ac0a37a1Snicm * not check WINLINK_BELL).
201ac0a37a1Snicm */
202de5a0fddSnicm s = wl->session;
2036434f49bSnicm if (s->curw != wl || s->attached == 0) {
20481fe4598Snicm wl->flags |= WINLINK_BELL;
205a48fd00bSnicm server_status_session(s);
206a48fd00bSnicm }
2077d08f26cSnicm if (!alerts_action_applies(wl, "bell-action"))
2087d08f26cSnicm continue;
2096aa2e59aSnicm notify_winlink("alert-bell", wl);
210de5a0fddSnicm
211de5a0fddSnicm if (s->flags & SESSION_ALERTED)
212de5a0fddSnicm continue;
213de5a0fddSnicm s->flags |= SESSION_ALERTED;
21481fe4598Snicm
2157d08f26cSnicm alerts_set_message(wl, "Bell", "visual-bell");
21681fe4598Snicm }
21781fe4598Snicm
21881fe4598Snicm return (WINDOW_BELL);
21981fe4598Snicm }
22081fe4598Snicm
221413e5e52Snicm static int
alerts_check_activity(struct window * w)222de5a0fddSnicm alerts_check_activity(struct window *w)
22381fe4598Snicm {
224de5a0fddSnicm struct winlink *wl;
225de5a0fddSnicm struct session *s;
22681fe4598Snicm
227de5a0fddSnicm if (~w->flags & WINDOW_ACTIVITY)
22881fe4598Snicm return (0);
229d89252e5Snicm if (!options_get_number(w->options, "monitor-activity"))
23081fe4598Snicm return (0);
23181fe4598Snicm
232de5a0fddSnicm TAILQ_FOREACH(wl, &w->winlinks, wentry)
233de5a0fddSnicm wl->session->flags &= ~SESSION_ALERTED;
234de5a0fddSnicm
235de5a0fddSnicm TAILQ_FOREACH(wl, &w->winlinks, wentry) {
236de5a0fddSnicm if (wl->flags & WINLINK_ACTIVITY)
237de5a0fddSnicm continue;
238de5a0fddSnicm s = wl->session;
2396434f49bSnicm if (s->curw != wl || s->attached == 0) {
240de5a0fddSnicm wl->flags |= WINLINK_ACTIVITY;
241a48fd00bSnicm server_status_session(s);
242a48fd00bSnicm }
2437d08f26cSnicm if (!alerts_action_applies(wl, "activity-action"))
2447d08f26cSnicm continue;
2456aa2e59aSnicm notify_winlink("alert-activity", wl);
246de5a0fddSnicm
247de5a0fddSnicm if (s->flags & SESSION_ALERTED)
248de5a0fddSnicm continue;
249de5a0fddSnicm s->flags |= SESSION_ALERTED;
250de5a0fddSnicm
2517d08f26cSnicm alerts_set_message(wl, "Activity", "visual-activity");
25281fe4598Snicm }
25381fe4598Snicm
25481fe4598Snicm return (WINDOW_ACTIVITY);
25581fe4598Snicm }
25681fe4598Snicm
257413e5e52Snicm static int
alerts_check_silence(struct window * w)258de5a0fddSnicm alerts_check_silence(struct window *w)
25981fe4598Snicm {
260de5a0fddSnicm struct winlink *wl;
261de5a0fddSnicm struct session *s;
26281fe4598Snicm
263de5a0fddSnicm if (~w->flags & WINDOW_SILENCE)
26481fe4598Snicm return (0);
2651328eebfSnicm if (options_get_number(w->options, "monitor-silence") == 0)
26681fe4598Snicm return (0);
26781fe4598Snicm
268de5a0fddSnicm TAILQ_FOREACH(wl, &w->winlinks, wentry)
269de5a0fddSnicm wl->session->flags &= ~SESSION_ALERTED;
270de5a0fddSnicm
271de5a0fddSnicm TAILQ_FOREACH(wl, &w->winlinks, wentry) {
272de5a0fddSnicm if (wl->flags & WINLINK_SILENCE)
273de5a0fddSnicm continue;
274de5a0fddSnicm s = wl->session;
2756434f49bSnicm if (s->curw != wl || s->attached == 0) {
276de5a0fddSnicm wl->flags |= WINLINK_SILENCE;
277a48fd00bSnicm server_status_session(s);
278a48fd00bSnicm }
2797d08f26cSnicm if (!alerts_action_applies(wl, "silence-action"))
2807d08f26cSnicm continue;
2816aa2e59aSnicm notify_winlink("alert-silence", wl);
282de5a0fddSnicm
283de5a0fddSnicm if (s->flags & SESSION_ALERTED)
284de5a0fddSnicm continue;
285de5a0fddSnicm s->flags |= SESSION_ALERTED;
28681fe4598Snicm
2877d08f26cSnicm alerts_set_message(wl, "Silence", "visual-silence");
28881fe4598Snicm }
28981fe4598Snicm
29081fe4598Snicm return (WINDOW_SILENCE);
29181fe4598Snicm }
29281fe4598Snicm
293413e5e52Snicm static void
alerts_set_message(struct winlink * wl,const char * type,const char * option)2947d08f26cSnicm alerts_set_message(struct winlink *wl, const char *type, const char *option)
295de5a0fddSnicm {
296de5a0fddSnicm struct client *c;
2977d08f26cSnicm int visual;
298de5a0fddSnicm
2991328eebfSnicm /*
3001fd731f6Snicm * We have found an alert (bell, activity or silence), so we need to
3011fd731f6Snicm * pass it on to the user. For each client attached to this session,
3027d08f26cSnicm * decide whether a bell, message or both is needed.
3031328eebfSnicm *
3041328eebfSnicm * If visual-{bell,activity,silence} is on, then a message is
3051328eebfSnicm * substituted for a bell; if it is off, a bell is sent as normal; both
3067d08f26cSnicm * mean both a bell and message is sent.
3071328eebfSnicm */
308de5a0fddSnicm
3097d08f26cSnicm visual = options_get_number(wl->session->options, option);
310de5a0fddSnicm TAILQ_FOREACH(c, &clients, entry) {
3117d08f26cSnicm if (c->session != wl->session || c->flags & CLIENT_CONTROL)
3121328eebfSnicm continue;
313de5a0fddSnicm
3141328eebfSnicm if (visual == VISUAL_OFF || visual == VISUAL_BOTH)
315e42da932Snicm tty_putcode(&c->tty, TTYC_BEL);
3161328eebfSnicm if (visual == VISUAL_OFF)
3171328eebfSnicm continue;
318*e7e79d0aSnicm if (c->session->curw == wl) {
319*e7e79d0aSnicm status_message_set(c, -1, 1, 0, "%s in current window",
320*e7e79d0aSnicm type);
321*e7e79d0aSnicm } else {
322*e7e79d0aSnicm status_message_set(c, -1, 1, 0, "%s in window %d", type,
3234f4307f9Snicm wl->idx);
3244f4307f9Snicm }
32581fe4598Snicm }
32681fe4598Snicm }
327