GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/alerts.c Lines: 0 119 0.0 %
Date: 2017-11-07 Branches: 0 96 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: alerts.c,v 1.28 2017/09/22 09:04:46 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
#include <stdlib.h>
23
24
#include "tmux.h"
25
26
static int	alerts_fired;
27
28
static void	alerts_timer(int, short, void *);
29
static int	alerts_enabled(struct window *, int);
30
static void	alerts_callback(int, short, void *);
31
static void	alerts_reset(struct window *);
32
33
static int	alerts_action_applies(struct winlink *, const char *);
34
static int	alerts_check_all(struct window *);
35
static int	alerts_check_bell(struct window *);
36
static int	alerts_check_activity(struct window *);
37
static int	alerts_check_silence(struct window *);
38
static void	alerts_set_message(struct winlink *, const char *,
39
		    const char *);
40
41
static TAILQ_HEAD(, window) alerts_list = TAILQ_HEAD_INITIALIZER(alerts_list);
42
43
static void
44
alerts_timer(__unused int fd, __unused short events, void *arg)
45
{
46
	struct window	*w = arg;
47
48
	log_debug("@%u alerts timer expired", w->id);
49
	alerts_queue(w, WINDOW_SILENCE);
50
}
51
52
static void
53
alerts_callback(__unused int fd, __unused short events, __unused void *arg)
54
{
55
	struct window	*w, *w1;
56
	int		 alerts;
57
58
	TAILQ_FOREACH_SAFE(w, &alerts_list, alerts_entry, w1) {
59
		alerts = alerts_check_all(w);
60
		log_debug("@%u alerts check, alerts %#x", w->id, alerts);
61
62
		w->alerts_queued = 0;
63
		TAILQ_REMOVE(&alerts_list, w, alerts_entry);
64
65
		w->flags &= ~WINDOW_ALERTFLAGS;
66
		window_remove_ref(w, __func__);
67
	}
68
	alerts_fired = 0;
69
}
70
71
static int
72
alerts_action_applies(struct winlink *wl, const char *name)
73
{
74
	int	action;
75
76
	/*
77
	 * {bell,activity,silence}-action determines when to alert: none means
78
	 * nothing happens, current means only do something for the current
79
	 * window and other means only for windows other than the current.
80
	 */
81
82
	action = options_get_number(wl->session->options, name);
83
	if (action == ALERT_ANY)
84
		return (1);
85
	if (action == ALERT_CURRENT)
86
		return (wl == wl->session->curw);
87
	if (action == ALERT_OTHER)
88
		return (wl != wl->session->curw);
89
	return (0);
90
}
91
92
static int
93
alerts_check_all(struct window *w)
94
{
95
	int	alerts;
96
97
	alerts	= alerts_check_bell(w);
98
	alerts |= alerts_check_activity(w);
99
	alerts |= alerts_check_silence(w);
100
	return (alerts);
101
}
102
103
void
104
alerts_check_session(struct session *s)
105
{
106
	struct winlink	*wl;
107
108
	RB_FOREACH(wl, winlinks, &s->windows)
109
		alerts_check_all(wl->window);
110
}
111
112
static int
113
alerts_enabled(struct window *w, int flags)
114
{
115
	if (flags & WINDOW_BELL) {
116
		if (options_get_number(w->options, "monitor-bell"))
117
			return (1);
118
	}
119
	if (flags & WINDOW_ACTIVITY) {
120
		if (options_get_number(w->options, "monitor-activity"))
121
			return (1);
122
	}
123
	if (flags & WINDOW_SILENCE) {
124
		if (options_get_number(w->options, "monitor-silence") != 0)
125
			return (1);
126
	}
127
	return (0);
128
}
129
130
void
131
alerts_reset_all(void)
132
{
133
	struct window	*w;
134
135
	RB_FOREACH(w, windows, &windows)
136
		alerts_reset(w);
137
}
138
139
static void
140
alerts_reset(struct window *w)
141
{
142
	struct timeval	tv;
143
144
	if (!event_initialized(&w->alerts_timer))
145
		evtimer_set(&w->alerts_timer, alerts_timer, w);
146
147
	w->flags &= ~WINDOW_SILENCE;
148
	event_del(&w->alerts_timer);
149
150
	timerclear(&tv);
151
	tv.tv_sec = options_get_number(w->options, "monitor-silence");
152
153
	log_debug("@%u alerts timer reset %u", w->id, (u_int)tv.tv_sec);
154
	if (tv.tv_sec != 0)
155
		event_add(&w->alerts_timer, &tv);
156
}
157
158
void
159
alerts_queue(struct window *w, int flags)
160
{
161
	alerts_reset(w);
162
163
	if ((w->flags & flags) != flags) {
164
		w->flags |= flags;
165
		log_debug("@%u alerts flags added %#x", w->id, flags);
166
	}
167
168
	if (alerts_enabled(w, flags)) {
169
		if (!w->alerts_queued) {
170
			w->alerts_queued = 1;
171
			TAILQ_INSERT_TAIL(&alerts_list, w, alerts_entry);
172
			window_add_ref(w, __func__);
173
		}
174
175
		if (!alerts_fired) {
176
			log_debug("alerts check queued (by @%u)", w->id);
177
			event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL);
178
			alerts_fired = 1;
179
		}
180
	}
181
}
182
183
static int
184
alerts_check_bell(struct window *w)
185
{
186
	struct winlink	*wl;
187
	struct session	*s;
188
189
	if (~w->flags & WINDOW_BELL)
190
		return (0);
191
	if (!options_get_number(w->options, "monitor-bell"))
192
		return (0);
193
194
	TAILQ_FOREACH(wl, &w->winlinks, wentry)
195
		wl->session->flags &= ~SESSION_ALERTED;
196
197
	TAILQ_FOREACH(wl, &w->winlinks, wentry) {
198
		/*
199
		 * Bells are allowed even if there is an existing bell (so do
200
		 * not check WINLINK_BELL).
201
		 */
202
		s = wl->session;
203
		if (s->curw != wl)
204
			wl->flags |= WINLINK_BELL;
205
		if (!alerts_action_applies(wl, "bell-action"))
206
			continue;
207
		notify_winlink("alert-bell", wl);
208
209
		if (s->flags & SESSION_ALERTED)
210
			continue;
211
		s->flags |= SESSION_ALERTED;
212
213
		alerts_set_message(wl, "Bell", "visual-bell");
214
	}
215
216
	return (WINDOW_BELL);
217
}
218
219
static int
220
alerts_check_activity(struct window *w)
221
{
222
	struct winlink	*wl;
223
	struct session	*s;
224
225
	if (~w->flags & WINDOW_ACTIVITY)
226
		return (0);
227
	if (!options_get_number(w->options, "monitor-activity"))
228
		return (0);
229
230
	TAILQ_FOREACH(wl, &w->winlinks, wentry)
231
		wl->session->flags &= ~SESSION_ALERTED;
232
233
	TAILQ_FOREACH(wl, &w->winlinks, wentry) {
234
		if (wl->flags & WINLINK_ACTIVITY)
235
			continue;
236
		s = wl->session;
237
		if (s->curw != wl)
238
			wl->flags |= WINLINK_ACTIVITY;
239
		if (!alerts_action_applies(wl, "activity-action"))
240
			continue;
241
		notify_winlink("alert-activity", wl);
242
243
		if (s->flags & SESSION_ALERTED)
244
			continue;
245
		s->flags |= SESSION_ALERTED;
246
247
		alerts_set_message(wl, "Activity", "visual-activity");
248
	}
249
250
	return (WINDOW_ACTIVITY);
251
}
252
253
static int
254
alerts_check_silence(struct window *w)
255
{
256
	struct winlink	*wl;
257
	struct session	*s;
258
259
	if (~w->flags & WINDOW_SILENCE)
260
		return (0);
261
	if (options_get_number(w->options, "monitor-silence") == 0)
262
		return (0);
263
264
	TAILQ_FOREACH(wl, &w->winlinks, wentry)
265
		wl->session->flags &= ~SESSION_ALERTED;
266
267
	TAILQ_FOREACH(wl, &w->winlinks, wentry) {
268
		if (wl->flags & WINLINK_SILENCE)
269
			continue;
270
		s = wl->session;
271
		if (s->curw != wl)
272
			wl->flags |= WINLINK_SILENCE;
273
		if (!alerts_action_applies(wl, "silence-action"))
274
			continue;
275
		notify_winlink("alert-silence", wl);
276
277
		if (s->flags & SESSION_ALERTED)
278
			continue;
279
		s->flags |= SESSION_ALERTED;
280
281
		alerts_set_message(wl, "Silence", "visual-silence");
282
	}
283
284
	return (WINDOW_SILENCE);
285
}
286
287
static void
288
alerts_set_message(struct winlink *wl, const char *type, const char *option)
289
{
290
	struct client	*c;
291
	int		 visual;
292
293
	/*
294
	 * We have found an alert (bell, activity or silence), so we need to
295
	 * pass it on to the user. For each client attached to this session,
296
	 * decide whether a bell, message or both is needed.
297
	 *
298
	 * If visual-{bell,activity,silence} is on, then a message is
299
	 * substituted for a bell; if it is off, a bell is sent as normal; both
300
	 * mean both a bell and message is sent.
301
	 */
302
303
	visual = options_get_number(wl->session->options, option);
304
	TAILQ_FOREACH(c, &clients, entry) {
305
		if (c->session != wl->session || c->flags & CLIENT_CONTROL)
306
			continue;
307
308
		if (visual == VISUAL_OFF || visual == VISUAL_BOTH)
309
			tty_putcode(&c->tty, TTYC_BEL);
310
		if (visual == VISUAL_OFF)
311
			continue;
312
		if (c->session->curw == wl)
313
			status_message_set(c, "%s in current window", type);
314
		else
315
			status_message_set(c, "%s in window %d", type, wl->idx);
316
	}
317
}