xref: /netbsd-src/external/bsd/tmux/dist/alerts.c (revision d909946ca08dceb44d7d0f22ec9488679695d976)
1 /* $OpenBSD$ */
2 
3 /*
4  * Copyright (c) 2015 Nicholas Marriott <nicm@users.sourceforge.net>
5  *
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.
9  *
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.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <event.h>
22 
23 #include "tmux.h"
24 
25 int	alerts_fired;
26 
27 void	alerts_timer(int, short, void *);
28 int	alerts_enabled(struct window *, int);
29 void	alerts_callback(int, short, void *);
30 void	alerts_reset(struct window *);
31 
32 int	alerts_check_bell(struct session *, struct winlink *);
33 int	alerts_check_activity(struct session *, struct winlink *);
34 int	alerts_check_silence(struct session *, struct winlink *);
35 void	alerts_ring_bell(struct session *);
36 
37 void
38 alerts_timer(unused int fd, unused short events, void *arg)
39 {
40 	struct window	*w = arg;
41 
42 	log_debug("@%u alerts timer expired", w->id);
43 	alerts_reset(w);
44 	alerts_queue(w, WINDOW_SILENCE);
45 }
46 
47 void
48 alerts_callback(unused int fd, unused short events, unused void *arg)
49 {
50 	struct window	*w;
51 	struct session	*s;
52 	struct winlink	*wl;
53 	int		 flags, alerts;
54 
55 	RB_FOREACH(w, windows, &windows) {
56 		RB_FOREACH(s, sessions, &sessions) {
57 			RB_FOREACH(wl, winlinks, &s->windows) {
58 				if (wl->window != w)
59 					continue;
60 				flags = w->flags;
61 
62 				alerts  = alerts_check_bell(s, wl);
63 				alerts |= alerts_check_activity(s, wl);
64 				alerts |= alerts_check_silence(s, wl);
65 				if (alerts != 0)
66 					server_status_session(s);
67 
68 				log_debug("%s:%d @%u alerts check, alerts %#x, "
69 				    "flags %#x", s->name, wl->idx, w->id,
70 				    alerts, flags);
71 			}
72 		}
73 	}
74 	alerts_fired = 0;
75 }
76 
77 int
78 alerts_enabled(struct window *w, int flags)
79 {
80 	struct session	*s;
81 
82 	if (flags & WINDOW_ACTIVITY) {
83 		if (options_get_number(&w->options, "monitor-activity"))
84 			return (1);
85 	}
86 	if (flags & WINDOW_SILENCE) {
87 		if (options_get_number(&w->options, "monitor-silence") != 0)
88 			return (1);
89 	}
90 	if (~flags & WINDOW_BELL)
91 		return (0);
92 	RB_FOREACH(s, sessions, &sessions) {
93 		if (!session_has(s, w))
94 			continue;
95 		if (options_get_number(&s->options, "bell-action") != BELL_NONE)
96 			return (1);
97 	}
98 	return (0);
99 }
100 
101 void
102 alerts_reset_all(void)
103 {
104 	struct window	*w;
105 
106 	RB_FOREACH(w, windows, &windows)
107 		alerts_reset(w);
108 }
109 
110 void
111 alerts_reset(struct window *w)
112 {
113 	struct timeval	tv;
114 
115 	w->flags &= ~WINDOW_SILENCE;
116 	event_del(&w->alerts_timer);
117 
118 	timerclear(&tv);
119 	tv.tv_sec = options_get_number(&w->options, "monitor-silence");
120 
121 	log_debug("@%u alerts timer reset %u", w->id, (u_int)tv.tv_sec);
122 	if (tv.tv_sec != 0)
123 		event_add(&w->alerts_timer, &tv);
124 }
125 
126 void
127 alerts_queue(struct window *w, int flags)
128 {
129 	if (w->flags & WINDOW_ACTIVITY)
130 		alerts_reset(w);
131 
132 	if (!event_initialized(&w->alerts_timer))
133 		evtimer_set(&w->alerts_timer, alerts_timer, w);
134 
135 	if (w->flags & flags)
136 		return;
137 	w->flags |= flags;
138 	log_debug("@%u alerts flags added %#x", w->id, flags);
139 
140 	if (!alerts_fired && alerts_enabled(w, flags)) {
141 		log_debug("alerts check queued (by @%u)", w->id);
142 		event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL);
143 		alerts_fired = 1;
144 	}
145 }
146 
147 int
148 alerts_check_bell(struct session *s, struct winlink *wl)
149 {
150 	struct client	*c;
151 	struct window	*w = wl->window;
152 	int		 action, visual;
153 
154 	if (!(w->flags & WINDOW_BELL) || wl->flags & WINLINK_BELL)
155 		return (0);
156 	if (s->curw != wl || s->flags & SESSION_UNATTACHED)
157 		wl->flags |= WINLINK_BELL;
158 	if (s->flags & SESSION_UNATTACHED)
159 		return (0);
160 	if (s->curw->window == w)
161 		w->flags &= ~WINDOW_BELL;
162 
163 	action = options_get_number(&s->options, "bell-action");
164 	if (action == BELL_NONE)
165 		return (0);
166 
167 	visual = options_get_number(&s->options, "visual-bell");
168 	TAILQ_FOREACH(c, &clients, entry) {
169 		if (c->session != s || c->flags & CLIENT_CONTROL)
170 			continue;
171 		if (!visual) {
172 			if ((action == BELL_CURRENT &&
173 			    c->session->curw->window == w) ||
174 			    (action == BELL_OTHER &&
175 			    c->session->curw->window != w) ||
176 			    action == BELL_ANY)
177 				tty_putcode(&c->tty, TTYC_BEL);
178 			continue;
179 		}
180 		if (action == BELL_CURRENT && c->session->curw->window == w)
181 			status_message_set(c, "Bell in current window");
182 		else if (action == BELL_ANY || (action == BELL_OTHER &&
183 		    c->session->curw->window != w))
184 			status_message_set(c, "Bell in window %d", wl->idx);
185 	}
186 
187 	return (WINDOW_BELL);
188 }
189 
190 int
191 alerts_check_activity(struct session *s, struct winlink *wl)
192 {
193 	struct client	*c;
194 	struct window	*w = wl->window;
195 
196 	if (s->curw->window == w)
197 		w->flags &= ~WINDOW_ACTIVITY;
198 
199 	if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_ACTIVITY)
200 		return (0);
201 	if (s->curw == wl && !(s->flags & SESSION_UNATTACHED))
202 		return (0);
203 
204 	if (!options_get_number(&w->options, "monitor-activity"))
205 		return (0);
206 
207 	if (options_get_number(&s->options, "bell-on-alert"))
208 		alerts_ring_bell(s);
209 	wl->flags |= WINLINK_ACTIVITY;
210 
211 	if (options_get_number(&s->options, "visual-activity")) {
212 		TAILQ_FOREACH(c, &clients, entry) {
213 			if (c->session != s)
214 				continue;
215 			status_message_set(c, "Activity in window %d", wl->idx);
216 		}
217 	}
218 
219 	return (WINDOW_ACTIVITY);
220 }
221 
222 int
223 alerts_check_silence(struct session *s, struct winlink *wl)
224 {
225 	struct client	*c;
226 	struct window	*w = wl->window;
227 
228 	if (s->curw->window == w)
229 		w->flags &= ~WINDOW_SILENCE;
230 
231 	if (!(w->flags & WINDOW_SILENCE) || wl->flags & WINLINK_SILENCE)
232 		return (0);
233 	if (s->curw == wl && !(s->flags & SESSION_UNATTACHED))
234 		return (0);
235 
236 	if (options_get_number(&w->options, "monitor-silence") == 0)
237 		return (0);
238 
239 	if (options_get_number(&s->options, "bell-on-alert"))
240 		alerts_ring_bell(s);
241 	wl->flags |= WINLINK_SILENCE;
242 
243 	if (options_get_number(&s->options, "visual-silence")) {
244 		TAILQ_FOREACH(c, &clients, entry) {
245 			if (c->session != s)
246 				continue;
247 			status_message_set(c, "Silence in window %d", wl->idx);
248 		}
249 	}
250 
251 	return (WINDOW_SILENCE);
252 }
253 
254 void
255 alerts_ring_bell(struct session *s)
256 {
257 	struct client	*c;
258 
259 	TAILQ_FOREACH(c, &clients, entry) {
260 		if (c->session == s && !(c->flags & CLIENT_CONTROL))
261 			tty_putcode(&c->tty, TTYC_BEL);
262 	}
263 }
264