alerts.c revision 1.7
1/* $OpenBSD: alerts.c,v 1.7 2015/11/20 16:33:46 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	if (flags & WINDOW_BELL)
81		return (1);
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	return (0);
91}
92
93void
94alerts_reset_all(void)
95{
96	struct window	*w;
97
98	RB_FOREACH(w, windows, &windows)
99		alerts_reset(w);
100}
101
102void
103alerts_reset(struct window *w)
104{
105	struct timeval	tv;
106
107	w->flags &= ~WINDOW_SILENCE;
108	event_del(&w->alerts_timer);
109
110	timerclear(&tv);
111	tv.tv_sec = options_get_number(w->options, "monitor-silence");
112
113	log_debug("@%u alerts timer reset %u", w->id, (u_int)tv.tv_sec);
114	if (tv.tv_sec != 0)
115		event_add(&w->alerts_timer, &tv);
116}
117
118void
119alerts_queue(struct window *w, int flags)
120{
121	if (w->flags & WINDOW_ACTIVITY)
122		alerts_reset(w);
123
124	if (!event_initialized(&w->alerts_timer))
125		evtimer_set(&w->alerts_timer, alerts_timer, w);
126
127	if (!alerts_fired) {
128		w->flags |= flags;
129		log_debug("@%u alerts flags added %#x", w->id, flags);
130
131		if (alerts_enabled(w, flags)) {
132			log_debug("alerts check queued (by @%u)", w->id);
133			event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL);
134			alerts_fired = 1;
135		}
136	}
137}
138
139int
140alerts_check_bell(struct session *s, struct winlink *wl)
141{
142	struct client	*c;
143	struct window	*w = wl->window;
144	int		 action, visual;
145
146	if (!(w->flags & WINDOW_BELL) || wl->flags & WINLINK_BELL)
147		return (0);
148	if (s->curw != wl || s->flags & SESSION_UNATTACHED)
149		wl->flags |= WINLINK_BELL;
150	if (s->flags & SESSION_UNATTACHED)
151		return (0);
152	if (s->curw->window == w)
153		w->flags &= ~WINDOW_BELL;
154
155	action = options_get_number(s->options, "bell-action");
156	if (action == BELL_NONE)
157		return (0);
158
159	visual = options_get_number(s->options, "visual-bell");
160	TAILQ_FOREACH(c, &clients, entry) {
161		if (c->session != s || c->flags & CLIENT_CONTROL)
162			continue;
163		if (!visual) {
164			if ((action == BELL_CURRENT &&
165			    c->session->curw->window == w) ||
166			    (action == BELL_OTHER &&
167			    c->session->curw->window != w) ||
168			    action == BELL_ANY)
169				tty_putcode(&c->tty, TTYC_BEL);
170			continue;
171		}
172		if (action == BELL_CURRENT && c->session->curw->window == w)
173			status_message_set(c, "Bell in current window");
174		else if (action == BELL_ANY || (action == BELL_OTHER &&
175		    c->session->curw->window != w))
176			status_message_set(c, "Bell in window %d", wl->idx);
177	}
178
179	return (WINDOW_BELL);
180}
181
182int
183alerts_check_activity(struct session *s, struct winlink *wl)
184{
185	struct client	*c;
186	struct window	*w = wl->window;
187
188	if (s->curw->window == w)
189		w->flags &= ~WINDOW_ACTIVITY;
190
191	if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_ACTIVITY)
192		return (0);
193	if (s->curw == wl && !(s->flags & SESSION_UNATTACHED))
194		return (0);
195
196	if (!options_get_number(w->options, "monitor-activity"))
197		return (0);
198
199	if (options_get_number(s->options, "bell-on-alert"))
200		alerts_ring_bell(s);
201	wl->flags |= WINLINK_ACTIVITY;
202
203	if (options_get_number(s->options, "visual-activity")) {
204		TAILQ_FOREACH(c, &clients, entry) {
205			if (c->session != s)
206				continue;
207			status_message_set(c, "Activity in window %d", wl->idx);
208		}
209	}
210
211	return (WINDOW_ACTIVITY);
212}
213
214int
215alerts_check_silence(struct session *s, struct winlink *wl)
216{
217	struct client	*c;
218	struct window	*w = wl->window;
219
220	if (s->curw->window == w)
221		w->flags &= ~WINDOW_SILENCE;
222
223	if (!(w->flags & WINDOW_SILENCE) || wl->flags & WINLINK_SILENCE)
224		return (0);
225	if (s->curw == wl && !(s->flags & SESSION_UNATTACHED))
226		return (0);
227
228	if (options_get_number(w->options, "monitor-silence") == 0)
229		return (0);
230
231	if (options_get_number(s->options, "bell-on-alert"))
232		alerts_ring_bell(s);
233	wl->flags |= WINLINK_SILENCE;
234
235	if (options_get_number(s->options, "visual-silence")) {
236		TAILQ_FOREACH(c, &clients, entry) {
237			if (c->session != s)
238				continue;
239			status_message_set(c, "Silence in window %d", wl->idx);
240		}
241	}
242
243	return (WINDOW_SILENCE);
244}
245
246void
247alerts_ring_bell(struct session *s)
248{
249	struct client	*c;
250
251	TAILQ_FOREACH(c, &clients, entry) {
252		if (c->session == s && !(c->flags & CLIENT_CONTROL))
253			tty_putcode(&c->tty, TTYC_BEL);
254	}
255}
256