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