Annotation of src/usr.bin/tmux/alerts.c, Revision 1.33
1.33 ! nicm 1: /* $OpenBSD: alerts.c,v 1.32 2020/08/19 07:15:42 nicm Exp $ */
1.1 nicm 2:
3: /*
1.10 nicm 4: * Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1 nicm 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>
1.14 nicm 22: #include <stdlib.h>
1.1 nicm 23:
24: #include "tmux.h"
25:
1.12 nicm 26: static int alerts_fired;
1.1 nicm 27:
1.12 nicm 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:
1.25 nicm 33: static int alerts_action_applies(struct winlink *, const char *);
1.14 nicm 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 *);
1.25 nicm 38: static void alerts_set_message(struct winlink *, const char *,
39: const char *);
1.1 nicm 40:
1.14 nicm 41: static TAILQ_HEAD(, window) alerts_list = TAILQ_HEAD_INITIALIZER(alerts_list);
42:
1.12 nicm 43: static void
1.5 nicm 44: alerts_timer(__unused int fd, __unused short events, void *arg)
1.1 nicm 45: {
46: struct window *w = arg;
47:
48: log_debug("@%u alerts timer expired", w->id);
49: alerts_queue(w, WINDOW_SILENCE);
50: }
51:
1.12 nicm 52: static void
1.5 nicm 53: alerts_callback(__unused int fd, __unused short events, __unused void *arg)
1.1 nicm 54: {
1.14 nicm 55: struct window *w, *w1;
56: int alerts;
1.1 nicm 57:
1.14 nicm 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);
1.16 nicm 64:
65: w->flags &= ~WINDOW_ALERTFLAGS;
1.19 nicm 66: window_remove_ref(w, __func__);
1.1 nicm 67: }
68: alerts_fired = 0;
69: }
70:
1.12 nicm 71: static int
1.25 nicm 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
1.14 nicm 93: alerts_check_all(struct window *w)
1.8 nicm 94: {
95: int alerts;
96:
1.25 nicm 97: alerts = alerts_check_bell(w);
1.14 nicm 98: alerts |= alerts_check_activity(w);
99: alerts |= alerts_check_silence(w);
1.8 nicm 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)
1.14 nicm 109: alerts_check_all(wl->window);
1.8 nicm 110: }
111:
1.12 nicm 112: static int
1.1 nicm 113: alerts_enabled(struct window *w, int flags)
114: {
1.24 nicm 115: if (flags & WINDOW_BELL) {
116: if (options_get_number(w->options, "monitor-bell"))
117: return (1);
118: }
1.1 nicm 119: if (flags & WINDOW_ACTIVITY) {
1.4 nicm 120: if (options_get_number(w->options, "monitor-activity"))
1.1 nicm 121: return (1);
122: }
123: if (flags & WINDOW_SILENCE) {
1.4 nicm 124: if (options_get_number(w->options, "monitor-silence") != 0)
1.1 nicm 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:
1.12 nicm 139: static void
1.1 nicm 140: alerts_reset(struct window *w)
141: {
142: struct timeval tv;
143:
1.28 nicm 144: if (!event_initialized(&w->alerts_timer))
145: evtimer_set(&w->alerts_timer, alerts_timer, w);
146:
1.1 nicm 147: w->flags &= ~WINDOW_SILENCE;
1.28 nicm 148: event_del(&w->alerts_timer);
1.1 nicm 149:
150: timerclear(&tv);
1.4 nicm 151: tv.tv_sec = options_get_number(w->options, "monitor-silence");
1.1 nicm 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: {
1.22 nicm 161: alerts_reset(w);
1.1 nicm 162:
1.11 nicm 163: if ((w->flags & flags) != flags) {
1.6 nicm 164: w->flags |= flags;
165: log_debug("@%u alerts flags added %#x", w->id, flags);
1.11 nicm 166: }
1.6 nicm 167:
1.18 nicm 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);
1.19 nicm 172: window_add_ref(w, __func__);
1.18 nicm 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: }
1.1 nicm 180: }
181: }
182:
1.12 nicm 183: static int
1.14 nicm 184: alerts_check_bell(struct window *w)
1.1 nicm 185: {
1.14 nicm 186: struct winlink *wl;
187: struct session *s;
1.1 nicm 188:
1.14 nicm 189: if (~w->flags & WINDOW_BELL)
1.24 nicm 190: return (0);
191: if (!options_get_number(w->options, "monitor-bell"))
1.1 nicm 192: return (0);
193:
1.14 nicm 194: TAILQ_FOREACH(wl, &w->winlinks, wentry)
195: wl->session->flags &= ~SESSION_ALERTED;
1.1 nicm 196:
1.14 nicm 197: TAILQ_FOREACH(wl, &w->winlinks, wentry) {
1.26 nicm 198: /*
199: * Bells are allowed even if there is an existing bell (so do
200: * not check WINLINK_BELL).
201: */
1.14 nicm 202: s = wl->session;
1.32 nicm 203: if (s->curw != wl || s->attached == 0) {
1.14 nicm 204: wl->flags |= WINLINK_BELL;
1.29 nicm 205: server_status_session(s);
206: }
1.25 nicm 207: if (!alerts_action_applies(wl, "bell-action"))
208: continue;
209: notify_winlink("alert-bell", wl);
1.14 nicm 210:
211: if (s->flags & SESSION_ALERTED)
1.1 nicm 212: continue;
1.14 nicm 213: s->flags |= SESSION_ALERTED;
214:
1.25 nicm 215: alerts_set_message(wl, "Bell", "visual-bell");
1.1 nicm 216: }
217:
218: return (WINDOW_BELL);
219: }
220:
1.12 nicm 221: static int
1.14 nicm 222: alerts_check_activity(struct window *w)
1.1 nicm 223: {
1.14 nicm 224: struct winlink *wl;
225: struct session *s;
1.1 nicm 226:
1.14 nicm 227: if (~w->flags & WINDOW_ACTIVITY)
1.1 nicm 228: return (0);
1.4 nicm 229: if (!options_get_number(w->options, "monitor-activity"))
1.1 nicm 230: return (0);
231:
1.14 nicm 232: TAILQ_FOREACH(wl, &w->winlinks, wentry)
233: wl->session->flags &= ~SESSION_ALERTED;
1.1 nicm 234:
1.14 nicm 235: TAILQ_FOREACH(wl, &w->winlinks, wentry) {
236: if (wl->flags & WINLINK_ACTIVITY)
237: continue;
238: s = wl->session;
1.32 nicm 239: if (s->curw != wl || s->attached == 0) {
1.21 nicm 240: wl->flags |= WINLINK_ACTIVITY;
1.29 nicm 241: server_status_session(s);
242: }
1.25 nicm 243: if (!alerts_action_applies(wl, "activity-action"))
244: continue;
245: notify_winlink("alert-activity", wl);
1.14 nicm 246:
247: if (s->flags & SESSION_ALERTED)
248: continue;
249: s->flags |= SESSION_ALERTED;
250:
1.25 nicm 251: alerts_set_message(wl, "Activity", "visual-activity");
1.1 nicm 252: }
253:
254: return (WINDOW_ACTIVITY);
255: }
256:
1.12 nicm 257: static int
1.14 nicm 258: alerts_check_silence(struct window *w)
1.1 nicm 259: {
1.14 nicm 260: struct winlink *wl;
261: struct session *s;
1.1 nicm 262:
1.14 nicm 263: if (~w->flags & WINDOW_SILENCE)
1.1 nicm 264: return (0);
1.21 nicm 265: if (options_get_number(w->options, "monitor-silence") == 0)
1.1 nicm 266: return (0);
267:
1.14 nicm 268: TAILQ_FOREACH(wl, &w->winlinks, wentry)
269: wl->session->flags &= ~SESSION_ALERTED;
270:
271: TAILQ_FOREACH(wl, &w->winlinks, wentry) {
272: if (wl->flags & WINLINK_SILENCE)
273: continue;
274: s = wl->session;
1.32 nicm 275: if (s->curw != wl || s->attached == 0) {
1.21 nicm 276: wl->flags |= WINLINK_SILENCE;
1.29 nicm 277: server_status_session(s);
278: }
1.25 nicm 279: if (!alerts_action_applies(wl, "silence-action"))
280: continue;
281: notify_winlink("alert-silence", wl);
1.14 nicm 282:
283: if (s->flags & SESSION_ALERTED)
284: continue;
285: s->flags |= SESSION_ALERTED;
1.1 nicm 286:
1.25 nicm 287: alerts_set_message(wl, "Silence", "visual-silence");
1.1 nicm 288: }
289:
290: return (WINDOW_SILENCE);
1.14 nicm 291: }
292:
293: static void
1.25 nicm 294: alerts_set_message(struct winlink *wl, const char *type, const char *option)
1.14 nicm 295: {
296: struct client *c;
1.25 nicm 297: int visual;
1.14 nicm 298:
1.21 nicm 299: /*
1.23 nicm 300: * We have found an alert (bell, activity or silence), so we need to
301: * pass it on to the user. For each client attached to this session,
1.25 nicm 302: * decide whether a bell, message or both is needed.
1.21 nicm 303: *
304: * If visual-{bell,activity,silence} is on, then a message is
305: * substituted for a bell; if it is off, a bell is sent as normal; both
1.25 nicm 306: * mean both a bell and message is sent.
1.21 nicm 307: */
1.14 nicm 308:
1.25 nicm 309: visual = options_get_number(wl->session->options, option);
1.14 nicm 310: TAILQ_FOREACH(c, &clients, entry) {
1.25 nicm 311: if (c->session != wl->session || c->flags & CLIENT_CONTROL)
1.21 nicm 312: continue;
1.1 nicm 313:
1.21 nicm 314: if (visual == VISUAL_OFF || visual == VISUAL_BOTH)
1.2 nicm 315: tty_putcode(&c->tty, TTYC_BEL);
1.21 nicm 316: if (visual == VISUAL_OFF)
317: continue;
1.33 ! nicm 318: if (c->session->curw == wl) {
! 319: status_message_set(c, -1, 1, 0, "%s in current window",
! 320: type);
! 321: } else {
! 322: status_message_set(c, -1, 1, 0, "%s in window %d", type,
1.30 nicm 323: wl->idx);
324: }
1.1 nicm 325: }
326: }