xref: /netbsd-src/external/bsd/tmux/dist/alerts.c (revision 9fb66d812c00ebfb445c0b47dea128f32aa6fe96)
15494e770Schristos /* $OpenBSD$ */
25494e770Schristos 
35494e770Schristos /*
4ed4e6cd4Schristos  * Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
55494e770Schristos  *
65494e770Schristos  * Permission to use, copy, modify, and distribute this software for any
75494e770Schristos  * purpose with or without fee is hereby granted, provided that the above
85494e770Schristos  * copyright notice and this permission notice appear in all copies.
95494e770Schristos  *
105494e770Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
115494e770Schristos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
125494e770Schristos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
135494e770Schristos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
145494e770Schristos  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
155494e770Schristos  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
165494e770Schristos  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
175494e770Schristos  */
185494e770Schristos 
195494e770Schristos #include <sys/types.h>
205494e770Schristos 
214e179ddaSchristos #include <stdlib.h>
225494e770Schristos 
235494e770Schristos #include "tmux.h"
245494e770Schristos 
254e179ddaSchristos static int	alerts_fired;
265494e770Schristos 
274e179ddaSchristos static void	alerts_timer(int, short, void *);
284e179ddaSchristos static int	alerts_enabled(struct window *, int);
294e179ddaSchristos static void	alerts_callback(int, short, void *);
304e179ddaSchristos static void	alerts_reset(struct window *);
315494e770Schristos 
32c9ad075bSchristos static int	alerts_action_applies(struct winlink *, const char *);
334e179ddaSchristos static int	alerts_check_all(struct window *);
344e179ddaSchristos static int	alerts_check_bell(struct window *);
354e179ddaSchristos static int	alerts_check_activity(struct window *);
364e179ddaSchristos static int	alerts_check_silence(struct window *);
37c9ad075bSchristos static void	alerts_set_message(struct winlink *, const char *,
38c9ad075bSchristos 		    const char *);
395494e770Schristos 
404e179ddaSchristos static TAILQ_HEAD(, window) alerts_list = TAILQ_HEAD_INITIALIZER(alerts_list);
414e179ddaSchristos 
424e179ddaSchristos static void
alerts_timer(__unused int fd,__unused short events,void * arg)43ed4e6cd4Schristos alerts_timer(__unused int fd, __unused short events, void *arg)
445494e770Schristos {
455494e770Schristos 	struct window	*w = arg;
465494e770Schristos 
475494e770Schristos 	log_debug("@%u alerts timer expired", w->id);
485494e770Schristos 	alerts_queue(w, WINDOW_SILENCE);
495494e770Schristos }
505494e770Schristos 
514e179ddaSchristos static void
alerts_callback(__unused int fd,__unused short events,__unused void * arg)52ed4e6cd4Schristos alerts_callback(__unused int fd, __unused short events, __unused void *arg)
535494e770Schristos {
544e179ddaSchristos 	struct window	*w, *w1;
554e179ddaSchristos 	int		 alerts;
565494e770Schristos 
574e179ddaSchristos 	TAILQ_FOREACH_SAFE(w, &alerts_list, alerts_entry, w1) {
584e179ddaSchristos 		alerts = alerts_check_all(w);
594e179ddaSchristos 		log_debug("@%u alerts check, alerts %#x", w->id, alerts);
605494e770Schristos 
614e179ddaSchristos 		w->alerts_queued = 0;
624e179ddaSchristos 		TAILQ_REMOVE(&alerts_list, w, alerts_entry);
635494e770Schristos 
644e179ddaSchristos 		w->flags &= ~WINDOW_ALERTFLAGS;
65c9ad075bSchristos 		window_remove_ref(w, __func__);
665494e770Schristos 	}
675494e770Schristos 	alerts_fired = 0;
685494e770Schristos }
695494e770Schristos 
704e179ddaSchristos static int
alerts_action_applies(struct winlink * wl,const char * name)71c9ad075bSchristos alerts_action_applies(struct winlink *wl, const char *name)
72c9ad075bSchristos {
73c9ad075bSchristos 	int	action;
74c9ad075bSchristos 
75c9ad075bSchristos 	/*
76c9ad075bSchristos 	 * {bell,activity,silence}-action determines when to alert: none means
77c9ad075bSchristos 	 * nothing happens, current means only do something for the current
78c9ad075bSchristos 	 * window and other means only for windows other than the current.
79c9ad075bSchristos 	 */
80c9ad075bSchristos 
81c9ad075bSchristos 	action = options_get_number(wl->session->options, name);
82c9ad075bSchristos 	if (action == ALERT_ANY)
83c9ad075bSchristos 		return (1);
84c9ad075bSchristos 	if (action == ALERT_CURRENT)
85c9ad075bSchristos 		return (wl == wl->session->curw);
86c9ad075bSchristos 	if (action == ALERT_OTHER)
87c9ad075bSchristos 		return (wl != wl->session->curw);
88c9ad075bSchristos 	return (0);
89c9ad075bSchristos }
90c9ad075bSchristos 
91c9ad075bSchristos static int
alerts_check_all(struct window * w)924e179ddaSchristos alerts_check_all(struct window *w)
93ed4e6cd4Schristos {
94ed4e6cd4Schristos 	int	alerts;
95ed4e6cd4Schristos 
964e179ddaSchristos 	alerts	= alerts_check_bell(w);
974e179ddaSchristos 	alerts |= alerts_check_activity(w);
984e179ddaSchristos 	alerts |= alerts_check_silence(w);
99ed4e6cd4Schristos 	return (alerts);
100ed4e6cd4Schristos }
101ed4e6cd4Schristos 
102ed4e6cd4Schristos void
alerts_check_session(struct session * s)103ed4e6cd4Schristos alerts_check_session(struct session *s)
104ed4e6cd4Schristos {
105ed4e6cd4Schristos 	struct winlink	*wl;
106ed4e6cd4Schristos 
107ed4e6cd4Schristos 	RB_FOREACH(wl, winlinks, &s->windows)
1084e179ddaSchristos 		alerts_check_all(wl->window);
109ed4e6cd4Schristos }
110ed4e6cd4Schristos 
1114e179ddaSchristos static int
alerts_enabled(struct window * w,int flags)1125494e770Schristos alerts_enabled(struct window *w, int flags)
1135494e770Schristos {
114c9ad075bSchristos 	if (flags & WINDOW_BELL) {
115c9ad075bSchristos 		if (options_get_number(w->options, "monitor-bell"))
116ed4e6cd4Schristos 			return (1);
117c9ad075bSchristos 	}
1185494e770Schristos 	if (flags & WINDOW_ACTIVITY) {
119ed4e6cd4Schristos 		if (options_get_number(w->options, "monitor-activity"))
1205494e770Schristos 			return (1);
1215494e770Schristos 	}
1225494e770Schristos 	if (flags & WINDOW_SILENCE) {
123ed4e6cd4Schristos 		if (options_get_number(w->options, "monitor-silence") != 0)
1245494e770Schristos 			return (1);
1255494e770Schristos 	}
1265494e770Schristos 	return (0);
1275494e770Schristos }
1285494e770Schristos 
1295494e770Schristos void
alerts_reset_all(void)1305494e770Schristos alerts_reset_all(void)
1315494e770Schristos {
1325494e770Schristos 	struct window	*w;
1335494e770Schristos 
1345494e770Schristos 	RB_FOREACH(w, windows, &windows)
1355494e770Schristos 		alerts_reset(w);
1365494e770Schristos }
1375494e770Schristos 
1384e179ddaSchristos static void
alerts_reset(struct window * w)1395494e770Schristos alerts_reset(struct window *w)
1405494e770Schristos {
1415494e770Schristos 	struct timeval	tv;
1425494e770Schristos 
143c9ad075bSchristos 	if (!event_initialized(&w->alerts_timer))
144c9ad075bSchristos 		evtimer_set(&w->alerts_timer, alerts_timer, w);
145c9ad075bSchristos 
1465494e770Schristos 	w->flags &= ~WINDOW_SILENCE;
1475494e770Schristos 	event_del(&w->alerts_timer);
1485494e770Schristos 
1495494e770Schristos 	timerclear(&tv);
150ed4e6cd4Schristos 	tv.tv_sec = options_get_number(w->options, "monitor-silence");
1515494e770Schristos 
1525494e770Schristos 	log_debug("@%u alerts timer reset %u", w->id, (u_int)tv.tv_sec);
1535494e770Schristos 	if (tv.tv_sec != 0)
1545494e770Schristos 		event_add(&w->alerts_timer, &tv);
1555494e770Schristos }
1565494e770Schristos 
1575494e770Schristos void
alerts_queue(struct window * w,int flags)1585494e770Schristos alerts_queue(struct window *w, int flags)
1595494e770Schristos {
1605494e770Schristos 	alerts_reset(w);
1615494e770Schristos 
1624e179ddaSchristos 	if ((w->flags & flags) != flags) {
1635494e770Schristos 		w->flags |= flags;
1645494e770Schristos 		log_debug("@%u alerts flags added %#x", w->id, flags);
1654e179ddaSchristos 	}
1665494e770Schristos 
167c9ad075bSchristos 	if (alerts_enabled(w, flags)) {
1684e179ddaSchristos 		if (!w->alerts_queued) {
1694e179ddaSchristos 			w->alerts_queued = 1;
1704e179ddaSchristos 			TAILQ_INSERT_TAIL(&alerts_list, w, alerts_entry);
171c9ad075bSchristos 			window_add_ref(w, __func__);
1724e179ddaSchristos 		}
1734e179ddaSchristos 
174c9ad075bSchristos 		if (!alerts_fired) {
1755494e770Schristos 			log_debug("alerts check queued (by @%u)", w->id);
1765494e770Schristos 			event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL);
1775494e770Schristos 			alerts_fired = 1;
1785494e770Schristos 		}
1795494e770Schristos 	}
180c9ad075bSchristos }
1815494e770Schristos 
1824e179ddaSchristos static int
alerts_check_bell(struct window * w)1834e179ddaSchristos alerts_check_bell(struct window *w)
1845494e770Schristos {
1854e179ddaSchristos 	struct winlink	*wl;
1864e179ddaSchristos 	struct session	*s;
1875494e770Schristos 
1884e179ddaSchristos 	if (~w->flags & WINDOW_BELL)
1895494e770Schristos 		return (0);
190c9ad075bSchristos 	if (!options_get_number(w->options, "monitor-bell"))
191c9ad075bSchristos 		return (0);
1924e179ddaSchristos 
1934e179ddaSchristos 	TAILQ_FOREACH(wl, &w->winlinks, wentry)
1944e179ddaSchristos 		wl->session->flags &= ~SESSION_ALERTED;
1954e179ddaSchristos 
1964e179ddaSchristos 	TAILQ_FOREACH(wl, &w->winlinks, wentry) {
197c9ad075bSchristos 		/*
198c9ad075bSchristos 		 * Bells are allowed even if there is an existing bell (so do
199c9ad075bSchristos 		 * not check WINLINK_BELL).
200c9ad075bSchristos 		 */
2014e179ddaSchristos 		s = wl->session;
202*9fb66d81Schristos 		if (s->curw != wl || s->attached == 0) {
2035494e770Schristos 			wl->flags |= WINLINK_BELL;
2048f3b9483Schristos 			server_status_session(s);
2058f3b9483Schristos 		}
206c9ad075bSchristos 		if (!alerts_action_applies(wl, "bell-action"))
207c9ad075bSchristos 			continue;
208c9ad075bSchristos 		notify_winlink("alert-bell", wl);
2094e179ddaSchristos 
2104e179ddaSchristos 		if (s->flags & SESSION_ALERTED)
2114e179ddaSchristos 			continue;
2124e179ddaSchristos 		s->flags |= SESSION_ALERTED;
2135494e770Schristos 
214c9ad075bSchristos 		alerts_set_message(wl, "Bell", "visual-bell");
2155494e770Schristos 	}
2165494e770Schristos 
2175494e770Schristos 	return (WINDOW_BELL);
2185494e770Schristos }
2195494e770Schristos 
2204e179ddaSchristos static int
alerts_check_activity(struct window * w)2214e179ddaSchristos alerts_check_activity(struct window *w)
2225494e770Schristos {
2234e179ddaSchristos 	struct winlink	*wl;
2244e179ddaSchristos 	struct session	*s;
2255494e770Schristos 
2264e179ddaSchristos 	if (~w->flags & WINDOW_ACTIVITY)
2275494e770Schristos 		return (0);
228ed4e6cd4Schristos 	if (!options_get_number(w->options, "monitor-activity"))
2295494e770Schristos 		return (0);
2305494e770Schristos 
2314e179ddaSchristos 	TAILQ_FOREACH(wl, &w->winlinks, wentry)
2324e179ddaSchristos 		wl->session->flags &= ~SESSION_ALERTED;
2334e179ddaSchristos 
2344e179ddaSchristos 	TAILQ_FOREACH(wl, &w->winlinks, wentry) {
2354e179ddaSchristos 		if (wl->flags & WINLINK_ACTIVITY)
2364e179ddaSchristos 			continue;
2374e179ddaSchristos 		s = wl->session;
238*9fb66d81Schristos 		if (s->curw != wl || s->attached == 0) {
2394e179ddaSchristos 			wl->flags |= WINLINK_ACTIVITY;
2408f3b9483Schristos 			server_status_session(s);
2418f3b9483Schristos 		}
242c9ad075bSchristos 		if (!alerts_action_applies(wl, "activity-action"))
243c9ad075bSchristos 			continue;
244c9ad075bSchristos 		notify_winlink("alert-activity", wl);
2454e179ddaSchristos 
2464e179ddaSchristos 		if (s->flags & SESSION_ALERTED)
2474e179ddaSchristos 			continue;
2484e179ddaSchristos 		s->flags |= SESSION_ALERTED;
2494e179ddaSchristos 
250c9ad075bSchristos 		alerts_set_message(wl, "Activity", "visual-activity");
2515494e770Schristos 	}
2525494e770Schristos 
2535494e770Schristos 	return (WINDOW_ACTIVITY);
2545494e770Schristos }
2555494e770Schristos 
2564e179ddaSchristos static int
alerts_check_silence(struct window * w)2574e179ddaSchristos alerts_check_silence(struct window *w)
2585494e770Schristos {
2594e179ddaSchristos 	struct winlink	*wl;
2604e179ddaSchristos 	struct session	*s;
2615494e770Schristos 
2624e179ddaSchristos 	if (~w->flags & WINDOW_SILENCE)
2635494e770Schristos 		return (0);
264c9ad075bSchristos 	if (options_get_number(w->options, "monitor-silence") == 0)
2654e179ddaSchristos 		return (0);
2664e179ddaSchristos 
2674e179ddaSchristos 	TAILQ_FOREACH(wl, &w->winlinks, wentry)
2684e179ddaSchristos 		wl->session->flags &= ~SESSION_ALERTED;
2694e179ddaSchristos 
2704e179ddaSchristos 	TAILQ_FOREACH(wl, &w->winlinks, wentry) {
2714e179ddaSchristos 		if (wl->flags & WINLINK_SILENCE)
2724e179ddaSchristos 			continue;
2734e179ddaSchristos 		s = wl->session;
274*9fb66d81Schristos 		if (s->curw != wl || s->attached == 0) {
2754e179ddaSchristos 			wl->flags |= WINLINK_SILENCE;
2768f3b9483Schristos 			server_status_session(s);
2778f3b9483Schristos 		}
278c9ad075bSchristos 		if (!alerts_action_applies(wl, "silence-action"))
279c9ad075bSchristos 			continue;
280c9ad075bSchristos 		notify_winlink("alert-silence", wl);
2815494e770Schristos 
2824e179ddaSchristos 		if (s->flags & SESSION_ALERTED)
2834e179ddaSchristos 			continue;
2844e179ddaSchristos 		s->flags |= SESSION_ALERTED;
2855494e770Schristos 
286c9ad075bSchristos 		alerts_set_message(wl, "Silence", "visual-silence");
2875494e770Schristos 	}
2885494e770Schristos 
2895494e770Schristos 	return (WINDOW_SILENCE);
2905494e770Schristos }
2915494e770Schristos 
2924e179ddaSchristos static void
alerts_set_message(struct winlink * wl,const char * type,const char * option)293c9ad075bSchristos alerts_set_message(struct winlink *wl, const char *type, const char *option)
2944e179ddaSchristos {
2954e179ddaSchristos 	struct client	*c;
296c9ad075bSchristos 	int		 visual;
2974e179ddaSchristos 
298c9ad075bSchristos 	/*
299c9ad075bSchristos 	 * We have found an alert (bell, activity or silence), so we need to
300c9ad075bSchristos 	 * pass it on to the user. For each client attached to this session,
301c9ad075bSchristos 	 * decide whether a bell, message or both is needed.
302c9ad075bSchristos 	 *
303c9ad075bSchristos 	 * If visual-{bell,activity,silence} is on, then a message is
304c9ad075bSchristos 	 * substituted for a bell; if it is off, a bell is sent as normal; both
305c9ad075bSchristos 	 * mean both a bell and message is sent.
306c9ad075bSchristos 	 */
3074e179ddaSchristos 
308c9ad075bSchristos 	visual = options_get_number(wl->session->options, option);
3094e179ddaSchristos 	TAILQ_FOREACH(c, &clients, entry) {
310c9ad075bSchristos 		if (c->session != wl->session || c->flags & CLIENT_CONTROL)
311c9ad075bSchristos 			continue;
3124e179ddaSchristos 
313c9ad075bSchristos 		if (visual == VISUAL_OFF || visual == VISUAL_BOTH)
3145494e770Schristos 			tty_putcode(&c->tty, TTYC_BEL);
315c9ad075bSchristos 		if (visual == VISUAL_OFF)
316c9ad075bSchristos 			continue;
317*9fb66d81Schristos 		if (c->session->curw == wl) {
318*9fb66d81Schristos 			status_message_set(c, -1, 1, 0, "%s in current window",
319*9fb66d81Schristos 			    type);
320*9fb66d81Schristos 		} else {
321*9fb66d81Schristos 			status_message_set(c, -1, 1, 0, "%s in window %d", type,
322*9fb66d81Schristos 			    wl->idx);
323*9fb66d81Schristos 		}
3245494e770Schristos 	}
3255494e770Schristos }
326