alerts.c revision 1.3
1/* $OpenBSD: alerts.c,v 1.3 2015/09/21 09:34:52 nicm Exp $ */
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
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
32int	alerts_check_bell(struct session *, struct winlink *);
33int	alerts_check_activity(struct session *, struct winlink *);
34int	alerts_check_silence(struct session *, struct winlink *);
35void	alerts_ring_bell(struct session *);
36
37void
38alerts_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
47void
48alerts_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
77int
78alerts_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
101void
102alerts_reset_all(void)
103{
104	struct window	*w;
105
106	RB_FOREACH(w, windows, &windows)
107		alerts_reset(w);
108}
109
110void
111alerts_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
126void
127alerts_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
147int
148alerts_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
190int
191alerts_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
222int
223alerts_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
254void
255alerts_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