1 /* copyright 2012 - 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
2
3 #include "dunst.h"
4
5 #include <assert.h>
6 #include <glib.h>
7 #include <glib-unix.h>
8 #include <signal.h>
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12
13 #include "dbus.h"
14 #include "draw.h"
15 #include "log.h"
16 #include "menu.h"
17 #include "notification.h"
18 #include "option_parser.h"
19 #include "queues.h"
20 #include "settings.h"
21 #include "utils.h"
22 #include "output.h"
23
24 GMainLoop *mainloop = NULL;
25
26 static struct dunst_status status;
27
28 /* see dunst.h */
dunst_status(const enum dunst_status_field field,bool value)29 void dunst_status(const enum dunst_status_field field,
30 bool value)
31 {
32 switch (field) {
33 case S_FULLSCREEN:
34 status.fullscreen = value;
35 break;
36 case S_IDLE:
37 status.idle = value;
38 break;
39 case S_RUNNING:
40 status.running = value;
41 break;
42 default:
43 LOG_E("Invalid %s enum value in %s:%d", "dunst_status", __FILE__, __LINE__);
44 break;
45 }
46 }
47
48 /* see dunst.h */
dunst_status_get(void)49 struct dunst_status dunst_status_get(void)
50 {
51 return status;
52 }
53
54 /* misc functions */
55 static gboolean run(void *data);
56
wake_up(void)57 void wake_up(void)
58 {
59 LOG_D("Waking up");
60 run(NULL);
61 }
62
run(void * data)63 static gboolean run(void *data)
64 {
65 static gint64 next_timeout = 0;
66
67 LOG_D("RUN");
68
69 dunst_status(S_FULLSCREEN, output->have_fullscreen_window());
70 dunst_status(S_IDLE, output->is_idle());
71
72 queues_update(status);
73
74 bool active = queues_length_displayed() > 0;
75
76 if (active) {
77 // Call draw before showing the window to avoid flickering
78 draw();
79 output->win_show(win);
80 } else {
81 output->win_hide(win);
82 }
83
84 if (active) {
85 gint64 now = time_monotonic_now();
86 gint64 sleep = queues_get_next_datachange(now);
87 gint64 timeout_at = now + sleep;
88
89 LOG_D("Sleeping for %li ms", sleep/1000);
90
91 if (sleep >= 0) {
92 if (next_timeout < now || timeout_at < next_timeout) {
93 g_timeout_add(sleep/1000, run, NULL);
94 next_timeout = timeout_at;
95 }
96 }
97 }
98
99 /* If the execution got triggered by g_timeout_add,
100 * we have to remove the timeout (which is actually a
101 * recurring interval), as we have set a new one
102 * by ourselves.
103 */
104 return G_SOURCE_REMOVE;
105 }
106
pause_signal(gpointer data)107 gboolean pause_signal(gpointer data)
108 {
109 dunst_status(S_RUNNING, false);
110 wake_up();
111
112 return G_SOURCE_CONTINUE;
113 }
114
unpause_signal(gpointer data)115 gboolean unpause_signal(gpointer data)
116 {
117 dunst_status(S_RUNNING, true);
118 wake_up();
119
120 return G_SOURCE_CONTINUE;
121 }
122
quit_signal(gpointer data)123 gboolean quit_signal(gpointer data)
124 {
125 g_main_loop_quit(mainloop);
126
127 return G_SOURCE_CONTINUE;
128 }
129
teardown(void)130 static void teardown(void)
131 {
132 regex_teardown();
133
134 queues_teardown();
135
136 draw_deinit();
137 }
138
dunst_main(int argc,char * argv[])139 int dunst_main(int argc, char *argv[])
140 {
141
142 dunst_status(S_RUNNING, true);
143 dunst_status(S_IDLE, false);
144
145 queues_init();
146
147 cmdline_load(argc, argv);
148
149 dunst_log_init(false);
150
151 if (cmdline_get_bool("-v/-version", false, "Print version")
152 || cmdline_get_bool("--version", false, "Print version")) {
153 print_version();
154 }
155
156 char *verbosity = cmdline_get_string("-verbosity", NULL, "Minimum level for message");
157 log_set_level_from_string(verbosity);
158 g_free(verbosity);
159
160 char *cmdline_config_path;
161 cmdline_config_path =
162 cmdline_get_string("-conf/-config", NULL,
163 "Path to configuration file");
164 load_settings(cmdline_config_path);
165
166 if (cmdline_get_bool("-h/-help", false, "Print help")
167 || cmdline_get_bool("--help", false, "Print help")) {
168 usage(EXIT_SUCCESS);
169 }
170
171 int dbus_owner_id = dbus_init();
172
173 mainloop = g_main_loop_new(NULL, FALSE);
174
175 draw_setup();
176
177 guint pause_src = g_unix_signal_add(SIGUSR1, pause_signal, NULL);
178 guint unpause_src = g_unix_signal_add(SIGUSR2, unpause_signal, NULL);
179
180 /* register SIGINT/SIGTERM handler for
181 * graceful termination */
182 guint term_src = g_unix_signal_add(SIGTERM, quit_signal, NULL);
183 guint int_src = g_unix_signal_add(SIGINT, quit_signal, NULL);
184
185 if (settings.startup_notification) {
186 struct notification *n = notification_create();
187 n->id = 0;
188 n->appname = g_strdup("dunst");
189 n->summary = g_strdup("startup");
190 n->body = g_strdup("dunst is up and running");
191 n->progress = -1;
192 n->timeout = S2US(10);
193 n->markup = MARKUP_NO;
194 n->urgency = URG_LOW;
195 notification_init(n);
196 queues_notification_insert(n);
197 // we do not call wakeup now, wake_up does not work here yet
198 }
199
200 run(NULL);
201 g_main_loop_run(mainloop);
202 g_clear_pointer(&mainloop, g_main_loop_unref);
203
204 /* remove signal handler watches */
205 g_source_remove(pause_src);
206 g_source_remove(unpause_src);
207 g_source_remove(term_src);
208 g_source_remove(int_src);
209
210 dbus_teardown(dbus_owner_id);
211
212 teardown();
213
214 return 0;
215 }
216
usage(int exit_status)217 void usage(int exit_status)
218 {
219 puts("usage:\n");
220 const char *us = cmdline_create_usage();
221 puts(us);
222 exit(exit_status);
223 }
224
print_version(void)225 void print_version(void)
226 {
227 printf
228 ("Dunst - A customizable and lightweight notification-daemon %s\n",
229 VERSION);
230 exit(EXIT_SUCCESS);
231 }
232
233 /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
234