1 /* $OpenBSD$ */
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
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 void alerts_run_hook(struct session *, struct winlink *, int);
33 int alerts_check_all(struct session *, struct winlink *);
34 int alerts_check_bell(struct session *, struct winlink *);
35 int alerts_check_activity(struct session *, struct winlink *);
36 int alerts_check_silence(struct session *, struct winlink *);
37 void alerts_ring_bell(struct session *);
38
39 void
alerts_timer(__unused int fd,__unused short events,void * arg)40 alerts_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
49 void
alerts_callback(__unused int fd,__unused short events,__unused void * arg)50 alerts_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
75 void
alerts_run_hook(struct session * s,struct winlink * wl,int flags)76 alerts_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
91 int
alerts_check_all(struct session * s,struct winlink * wl)92 alerts_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
107 void
alerts_check_session(struct session * s)108 alerts_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
116 int
alerts_enabled(struct window * w,int flags)117 alerts_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
132 void
alerts_reset_all(void)133 alerts_reset_all(void)
134 {
135 struct window *w;
136
137 RB_FOREACH(w, windows, &windows)
138 alerts_reset(w);
139 }
140
141 void
alerts_reset(struct window * w)142 alerts_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
157 void
alerts_queue(struct window * w,int flags)158 alerts_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
178 int
alerts_check_bell(struct session * s,struct winlink * wl)179 alerts_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
221 int
alerts_check_activity(struct session * s,struct winlink * wl)222 alerts_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
253 int
alerts_check_silence(struct session * s,struct winlink * wl)254 alerts_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
285 void
alerts_ring_bell(struct session * s)286 alerts_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