Dunst
Lightweight notification daemon
Loading...
Searching...
No Matches
dunst.c
Go to the documentation of this file.
1/* SPDX-License-Identifier: BSD-3-Clause */
8
9#include <assert.h>
10#include <glib.h>
11#include <glib-unix.h>
12#include <signal.h>
13#include <stdbool.h>
14#include <stdio.h>
15#include <stdlib.h>
16
17#include "dunst.h"
18#include "dbus.h"
19#include "draw.h"
20#include "log.h"
21#include "menu.h"
22#include "rules.h"
23#include "notification.h"
24#include "option_parser.h"
25#include "queues.h"
26#include "settings.h"
27#include "utils.h"
28
29GMainLoop *mainloop = NULL;
30
31static struct dunst_status status;
32static bool setup_done = false;
33char **config_paths = NULL;
34
35void dunst_status(const enum dunst_status_field field,
36 bool value)
37{
38 switch (field) {
39 case S_FULLSCREEN:
40 status.fullscreen = value;
41 break;
42 case S_IDLE:
43 status.idle = value;
44 break;
45 case S_MOUSE_OVER:
46 status.mouse_over = value;
47 break;
48 default:
49 LOG_E("Invalid %s enum value in %s:%d for bool type", "dunst_status", __FILE__, __LINE__);
50 break;
51 }
52}
53
54void dunst_status_int(const enum dunst_status_field field,
55 int value)
56{
57 switch (field) {
58 case S_PAUSE_LEVEL:
59 status.pause_level = value;
60 break;
61 default:
62 LOG_E("Invalid %s enum value in %s:%d for int type", "dunst_status", __FILE__, __LINE__);
63 break;
64 }
65}
66
67struct dunst_status dunst_status_get(void)
68{
69 return status;
70}
71
72/* misc functions */
73static gboolean run(void *data);
74
83 DUNST_TIMER,
84 DUNST_WAKEUP,
85};
86
87const char* dunst_run_reason_str(enum dunst_run_reason reason) {
88 switch(reason) {
89 case DUNST_TIMER:
90 return "DUNST_TIMER";
91 case DUNST_WAKEUP:
92 return "DUNST_WAKEUP";
93 default:
94 return "BAD VALUE";
95 }
96}
97
98void wake_up(void)
99{
100 // If wake_up is being called before the output has been setup we should
101 // return.
102 if (!setup_done) {
103 LOG_D("Ignoring wake up");
104 return;
105 }
106
107 LOG_D("Waking up");
108 run(GINT_TO_POINTER(DUNST_WAKEUP));
109}
110
111static gboolean run(void *data)
112{
113 /* Timer gestion
114 * =============
115 *
116 * - At any time (except transiently during the execution of `run`), at
117 * most one glib timeout source (or "timer" here) exists. If `run`
118 * was invoked by a timer, it will be deleted upon return, as the
119 * function always returns G_SOURCE_REMOVE.
120 * - Furthermore, if next_timeout_id is not null, a timer with this
121 * glib source id exists and is running. As a consequence,
122 * - if reason is DUNST_TIMER, it is the timer that triggered the
123 * current call to run;
124 * - if reason is DUNST_WAKEUP, this timer was scheduled some time in
125 * the future (or in the past, but not yet executed my the main loop,
126 * which is equivalent for our purpose).
127 *
128 * Thus, in each call to run,
129 * - if reason is DUNST_WAKEUP and next_timeout_id != 0, we delete this
130 * timer -- we now have more recent information on which we can
131 * decide of a (maybe) better timeout.
132 * - in any case, we reset next_timeout_id to 0.
133 * - if there is any event to be run in the future, we set a new timer
134 * to this time, and update next_timeout_id accordingly.
135 */
136
137 static guint next_timeout_id = 0;
138 enum dunst_run_reason reason = GPOINTER_TO_INT(data);
139
140 LOG_D("RUN, reason %i: %s", reason, dunst_run_reason_str(reason));
141 gint64 now = time_monotonic_now();
142
143 dunst_status(S_FULLSCREEN, output->have_fullscreen_window());
144 dunst_status(S_IDLE, output->is_idle());
145
146 queues_update(status, now);
147
148 if(reason == DUNST_WAKEUP && next_timeout_id != 0) {
149 // Delete the upcoming timer
150 g_source_remove(next_timeout_id);
151 }
152 next_timeout_id = 0;
153
155 output->win_hide(win);
156 return G_SOURCE_REMOVE;
157 }
158
159 // Call draw before showing the window to avoid flickering
160 draw();
161 output->win_show(win);
162
163 gint64 timeout_at = queues_get_next_datachange(now);
164 if (timeout_at != -1) {
165 // Previous computations may have taken time, update `now`
166 // This might mean that `timeout_at` is now before `now`, so
167 // we have to make sure that `sleep` is still positive.
168 now = time_monotonic_now();
169 gint64 sleep = timeout_at - now;
170 sleep = MAX(sleep, 1000); // Sleep at least 1ms
171
172 LOG_D("Sleeping for %"G_GINT64_FORMAT" ms", sleep/1000);
173
174 next_timeout_id = g_timeout_add(sleep/1000, run, NULL);
175 }
176
177 /* If the execution got triggered by g_timeout_add,
178 * we have to remove the timeout (which is actually a
179 * recurring interval), as we have set a new one
180 * by ourselves.
181 */
182 return G_SOURCE_REMOVE;
183}
184
185gboolean pause_signal(gpointer data)
186{
187 (void)data;
188
189 dunst_status_int(S_PAUSE_LEVEL, MAX_PAUSE_LEVEL);
190 wake_up();
191
192 signal_paused_propertieschanged();
193
194 return G_SOURCE_CONTINUE;
195}
196
197gboolean unpause_signal(gpointer data)
198{
199 (void)data;
200
201 dunst_status_int(S_PAUSE_LEVEL, 0);
202 wake_up();
203
204 signal_paused_propertieschanged();
205
206 return G_SOURCE_CONTINUE;
207}
208
209gboolean quit_signal(gpointer data)
210{
211 (void)data;
212 g_main_loop_quit(mainloop);
213
214 return G_SOURCE_CONTINUE;
215}
216
217static void teardown(void)
218{
219 regex_teardown();
220
222
223 draw_deinit();
224
225 g_strfreev(config_paths);
226
227 g_slist_free_full(rules, (GDestroyNotify)rule_free);
228}
229
230void reload(char **const configs)
231{
232 guint length = g_strv_length(configs);
233 LOG_M("Reloading settings (with the %s files)", length != 0 ? "new" : "old");
234
235 pause_signal(NULL);
236
237 setup_done = false;
238 draw_deinit();
239
240 g_slist_free_full(rules, (GDestroyNotify)rule_free);
241 rules = NULL;
242
243 settings_free(&settings);
244 load_settings(length != 0 ? configs : config_paths);
245
246 draw_setup();
247 setup_done = true;
248
250
251 unpause_signal(NULL);
252}
253
254int dunst_main(int argc, char *argv[])
255{
256 dunst_status_int(S_PAUSE_LEVEL, 0);
257 dunst_status(S_IDLE, false);
258
259 queues_init();
260
261 cmdline_load(argc, argv);
262
263 dunst_log_init(DUNST_LOG_AUTO);
264
265 if (cmdline_get_bool("-v/-version/--version", false, "Print version")) {
266 print_version();
267 }
268
269 char *verbosity = cmdline_get_string("-verbosity", NULL, "Minimum level for message");
270 log_set_level_from_string(verbosity);
271 g_free(verbosity);
272
273 cmdline_usage_append("-conf/-config", "string", "Path to configuration file");
274
275 int start = 1, count = 1;
276 while (cmdline_get_string_offset("-conf/-config", NULL, start, &start))
277 count++;
278
279 // Leaves an extra space for the NULL
280 config_paths = g_malloc0(sizeof(char *) * count);
281 start = 1, count = 0;
282 char *path = NULL;
283
284 do {
285 path = cmdline_get_string_offset("-conf/-config", NULL, start, &start);
286 config_paths[count++] = path;
287 } while (path != NULL);
288
289 print_notifications = cmdline_get_bool("-print/--print", false, "Print notifications to stdout");
290
291 bool startup_notification = cmdline_get_bool("-startup_notification/--startup_notification",
292 false, "Display a notification on startup.");
293
294 /* Help should always be the last to set up as calls to cmdline_get_* (as a side effect) add entries to the usage list. */
295 if (cmdline_get_bool("-h/-help/--help", false, "Print help")) {
296 usage(EXIT_SUCCESS);
297 }
298
299 load_settings(config_paths);
300 int dbus_owner_id = dbus_init();
301
302 mainloop = g_main_loop_new(NULL, FALSE);
303
304 draw_setup();
305
306 guint pause_src = g_unix_signal_add(SIGUSR1, pause_signal, NULL);
307 guint unpause_src = g_unix_signal_add(SIGUSR2, unpause_signal, NULL);
308
309 /* register SIGINT/SIGTERM handler for
310 * graceful termination */
311 guint term_src = g_unix_signal_add(SIGTERM, quit_signal, NULL);
312 guint int_src = g_unix_signal_add(SIGINT, quit_signal, NULL);
313
314
315 if (startup_notification) {
316 struct notification *n = notification_create();
317 n->id = 0;
318 n->appname = g_strdup("dunst");
319 n->summary = g_strdup("startup");
320 n->body = g_strdup("dunst is up and running");
321 n->progress = -1;
322 n->timeout = S2US(10);
323 n->markup = MARKUP_NO;
324 n->urgency = URG_LOW;
327 // we do not call wakeup now, wake_up does not work here yet
328 }
329
330 setup_done = true;
331 run(GINT_TO_POINTER(DUNST_TIMER)); // The first run() is a scheduled one
332
333
334 // Set default_pause_level only after showing the startup notification
335 dunst_status_int(S_PAUSE_LEVEL, settings.default_pause_level);
336
337 g_main_loop_run(mainloop);
338 g_clear_pointer(&mainloop, g_main_loop_unref);
339
340 /* remove signal handler watches */
341 g_source_remove(pause_src);
342 g_source_remove(unpause_src);
343 g_source_remove(term_src);
344 g_source_remove(int_src);
345
346 dbus_teardown(dbus_owner_id);
347
348 teardown();
349
350 settings_free(&settings);
351
352 return 0;
353}
354
355void usage(int exit_status)
356{
357 puts("usage:\n");
358 const char *us = cmdline_create_usage();
359 puts(us);
360 exit(exit_status);
361}
362
363void print_version(void)
364{
365 printf("Dunst - A customizable and lightweight notification-daemon %s\n", VERSION);
366#ifdef _CCDATE
367 printf("Compiled on %s with the following options:\n", STR_TO(_CCDATE));
368#endif
369
370 printf("X11 support: %s\n", X11_SUPPORT ? "enabled" : "disabled");
371 printf("Wayland support: %s\n", WAYLAND_SUPPORT ? "enabled" : "disabled");
372 printf("SYSCONFDIR set to: %s\n", SYSCONFDIR);
373
374#ifdef _CFLAGS
375 printf("Compiler flags: %s\n", STR_TO(_CFLAGS));
376#endif
377#ifdef _LDFLAGS
378 printf("Linker flags: %s\n", STR_TO(_LDFLAGS));
379#endif
380 exit(EXIT_SUCCESS);
381}
DBus support and implementation of the Desktop Notifications Specification.
reason
The reasons according to the notification spec.
Definition dbus.h:18
Layout and render notifications.
dunst_run_reason
The reason for which the run function was invoked.
Definition dunst.c:82
void dunst_status(const enum dunst_status_field field, bool value)
Modify the current status of dunst.
Definition dunst.c:35
Main event loop logic.
#define MAX_PAUSE_LEVEL
A structure to describe dunst's global window status.
Definition dunst.h:21
void log_set_level_from_string(const char *level)
Set the current loglevel to level
Definition log.c:32
void dunst_log_init(enum log_mask mask)
Initialise log handling.
Definition log.c:95
Logging subsystem and helpers.
#define LOG_E
Prefix message with "[<source path>:<function name>:<line number>] ".
Definition log.h:42
Context menu for actions and helpers.
void notification_init(struct notification *n)
Sanitize values of notification, apply all matching rules and generate derived fields.
struct notification * notification_create(void)
Create notification struct and initialise all fields with either.
Notification type definitions.
@ URG_LOW
Low urgency.
Parser for settings and cmdline arguments.
int queues_notification_insert(struct notification *n)
Insert a fully initialized notification into queues.
Definition queues.c:169
void queues_init(void)
Initialise necessary queues.
Definition queues.c:33
void queues_teardown(void)
Remove all notifications from all list and free the notifications.
Definition queues.c:706
gint64 queues_get_next_datachange(gint64 time)
Calculate the distance to the next event, when an element in the queues changes.
Definition queues.c:620
void queues_reapply_all_rules(void)
Reapply all rules to the queue (used when reloading configs)
Definition queues.c:679
unsigned int queues_length_displayed(void)
Returns the current amount of notifications, which are shown in the UI.
Definition queues.c:61
void queues_update(struct dunst_status status, gint64 time)
Move inserted notifications from waiting queue to displayed queue and show them.
Definition queues.c:501
Queues for history, waiting and displayed notifications.
Rules managment and helpers.
#define SYSCONFDIR
Since this gets defined by $DEFAULT_CPPFLAGS at compile time doxygen seems to miss the correct value.
Definition settings.c:28
Type definitions for settings.
int progress
percentage (-1: undefined)
gint64 timeout
time to display (in milliseconds)
gint64 time_monotonic_now(void)
Get the current monotonic time.
Definition utils.c:315
String, time and other various helpers.
#define S2US(s)
Convert seconds into microseconds.
Definition utils.h:46
#define STR_TO(...)
Stringify the given expression or macro.
Definition utils.h:38