Dunst
Lightweight notification daemon
Loading...
Searching...
No Matches
dbus.c
Go to the documentation of this file.
1/* SPDX-License-Identifier: BSD-3-Clause */
8
9#include <gio/gio.h>
10#include <glib.h>
11#include <stdio.h>
12#include <stdlib.h>
13
14#include "dbus.h"
15#include "draw.h"
16#include "dunst.h"
17#include "log.h"
18#include "menu.h"
19#include "rules.h"
20#include "queues.h"
21#include "settings.h"
22#include "utils.h"
23#include "settings_data.h"
24
25#define FDN_PATH "/org/freedesktop/Notifications"
26#define FDN_IFAC "org.freedesktop.Notifications"
27#define FDN_NAME "org.freedesktop.Notifications"
28
29#define DUNST_PATH "/org/freedesktop/Notifications"
30#define DUNST_IFAC "org.dunstproject.cmd0"
31#define DUNST_NAME "org.freedesktop.Notifications"
32
33#define PROPERTIES_IFAC "org.freedesktop.DBus.Properties"
34
35GDBusConnection *dbus_conn = NULL;
36
37static GDBusNodeInfo *introspection_data = NULL;
38
39static const char *introspection_xml =
40 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
41 "<node name=\""FDN_PATH"\">"
42 " <interface name=\""FDN_IFAC"\">"
43
44 " <method name=\"GetCapabilities\">"
45 " <arg direction=\"out\" name=\"capabilities\" type=\"as\"/>"
46 " </method>"
47
48 " <method name=\"Notify\">"
49 " <arg direction=\"in\" name=\"app_name\" type=\"s\"/>"
50 " <arg direction=\"in\" name=\"replaces_id\" type=\"u\"/>"
51 " <arg direction=\"in\" name=\"app_icon\" type=\"s\"/>"
52 " <arg direction=\"in\" name=\"summary\" type=\"s\"/>"
53 " <arg direction=\"in\" name=\"body\" type=\"s\"/>"
54 " <arg direction=\"in\" name=\"actions\" type=\"as\"/>"
55 " <arg direction=\"in\" name=\"hints\" type=\"a{sv}\"/>"
56 " <arg direction=\"in\" name=\"expire_timeout\" type=\"i\"/>"
57 " <arg direction=\"out\" name=\"id\" type=\"u\"/>"
58 " </method>"
59
60 " <method name=\"CloseNotification\">"
61 " <arg direction=\"in\" name=\"id\" type=\"u\"/>"
62 " </method>"
63
64 " <method name=\"GetServerInformation\">"
65 " <arg direction=\"out\" name=\"name\" type=\"s\"/>"
66 " <arg direction=\"out\" name=\"vendor\" type=\"s\"/>"
67 " <arg direction=\"out\" name=\"version\" type=\"s\"/>"
68 " <arg direction=\"out\" name=\"spec_version\" type=\"s\"/>"
69 " </method>"
70
71 " <signal name=\"NotificationClosed\">"
72 " <arg name=\"id\" type=\"u\"/>"
73 " <arg name=\"reason\" type=\"u\"/>"
74 " </signal>"
75
76 " <signal name=\"ActionInvoked\">"
77 " <arg name=\"id\" type=\"u\"/>"
78 " <arg name=\"action_key\" type=\"s\"/>"
79 " </signal>"
80 " </interface>"
81 " <interface name=\""DUNST_IFAC"\">"
82
83 " <method name=\"ContextMenuCall\"/>"
84 " <method name=\"NotificationAction\">"
85 " <arg name=\"number\" type=\"u\"/>"
86 " </method>"
87 " <method name=\"NotificationClearHistory\"/>"
88 " <method name=\"NotificationCloseLast\"/>"
89 " <method name=\"NotificationCloseAll\"/>"
90 " <method name=\"NotificationListHistory\">"
91 " <arg direction=\"out\" name=\"notifications\" type=\"aa{sv}\"/>"
92 " </method>"
93 " <method name=\"NotificationPopHistory\">"
94 " <arg direction=\"in\" name=\"id\" type=\"u\"/>"
95 " </method>"
96 " <method name=\"NotificationRemoveFromHistory\">"
97 " <arg direction=\"in\" name=\"id\" type=\"u\"/>"
98 " </method>"
99 " <method name=\"NotificationShow\"/>"
100 " <method name=\"RuleEnable\">"
101 " <arg name=\"name\" type=\"s\"/>"
102 " <arg name=\"state\" type=\"i\"/>"
103 " </method>"
104 " <method name=\"RuleList\">"
105 " <arg direction=\"out\" name=\"rules\" type=\"aa{sv}\"/>"
106 " </method>"
107 " <method name=\"ConfigReload\">"
108 " <arg direction=\"in\" name=\"configs\" type=\"as\"/>"
109 " </method>"
110 " <method name=\"Ping\"/>"
111
112 " <property name=\"paused\" type=\"b\" access=\"readwrite\">"
113 " <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"true\"/>"
114 " </property>"
115
116 " <property name=\"pauseLevel\" type=\"u\" access=\"readwrite\">"
117 " <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"true\"/>"
118 " </property>"
119
120 " <property name=\"displayedLength\" type=\"u\" access=\"read\">"
121 " <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"true\"/>"
122 " </property>"
123 " <property name=\"historyLength\" type=\"u\" access=\"read\">"
124 " <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"true\"/>"
125 " </property>"
126
127 " <property name=\"waitingLength\" type=\"u\" access=\"read\">"
128 " <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"true\"/>"
129 " </property>"
130
131
132 " <signal name=\"NotificationHistoryRemoved\">"
133 " <arg name=\"id\" type=\"u\"/>"
134 " </signal>"
135
136 " <signal name=\"NotificationHistoryCleared\">"
137 " <arg name=\"count\" type=\"u\"/>"
138 " </signal>"
139
140 " <signal name=\"ConfigReloaded\">"
141 " <arg name=\"configs\" type=\"as\"/>"
142 " </signal>"
143
144 " </interface>"
145 "</node>";
146
147static const char *stack_tag_hints[] = {
148 "synchronous",
149 "private-synchronous",
150 "x-canonical-private-synchronous",
151 "x-dunst-stack-tag"
152};
153
155 const char *method_name;
156 void (*method) (GDBusConnection *connection,
157 const gchar *sender,
158 GVariant *parameters,
159 GDBusMethodInvocation *invocation);
160};
161
162#define DBUS_METHOD(name) static void dbus_cb_##name( \
163 GDBusConnection *connection, \
164 const gchar *sender, \
165 GVariant *parameters, \
166 GDBusMethodInvocation *invocation)
167
168int cmp_methods(const void *vkey, const void *velem)
169{
170 const char *key = (const char*)vkey;
171 const struct dbus_method *m = (const struct dbus_method*)velem;
172
173 return strcmp(key, m->method_name);
174}
175
176DBUS_METHOD(Notify);
177DBUS_METHOD(CloseNotification);
178DBUS_METHOD(GetCapabilities);
179DBUS_METHOD(GetServerInformation);
180static struct dbus_method methods_fdn[] = {
181 {"CloseNotification", dbus_cb_CloseNotification},
182 {"GetCapabilities", dbus_cb_GetCapabilities},
183 {"GetServerInformation", dbus_cb_GetServerInformation},
184 {"Notify", dbus_cb_Notify},
185};
186
187void dbus_cb_fdn_methods(GDBusConnection *connection,
188 const gchar *sender,
189 const gchar *object_path,
190 const gchar *interface_name,
191 const gchar *method_name,
192 GVariant *parameters,
193 GDBusMethodInvocation *invocation,
194 gpointer user_data)
195{
196 struct dbus_method *m = bsearch(method_name,
197 methods_fdn,
198 G_N_ELEMENTS(methods_fdn),
199 sizeof(struct dbus_method),
200 cmp_methods);
201
202 if (m) {
203 m->method(connection, sender, parameters, invocation);
204 } else {
205 LOG_M("Unknown method name: '%s' (sender: '%s').",
206 method_name,
207 sender);
208 }
209}
210
211DBUS_METHOD(dunst_ContextMenuCall);
212DBUS_METHOD(dunst_NotificationAction);
213DBUS_METHOD(dunst_NotificationClearHistory);
214DBUS_METHOD(dunst_NotificationCloseAll);
215DBUS_METHOD(dunst_NotificationCloseLast);
216DBUS_METHOD(dunst_NotificationListHistory);
217DBUS_METHOD(dunst_NotificationPopHistory);
218DBUS_METHOD(dunst_NotificationRemoveFromHistory);
219DBUS_METHOD(dunst_NotificationShow);
220DBUS_METHOD(dunst_RuleEnable);
221DBUS_METHOD(dunst_RuleList);
222DBUS_METHOD(dunst_ConfigReload);
223DBUS_METHOD(dunst_Ping);
224
225// NOTE: Keep the names sorted alphabetically
226static struct dbus_method methods_dunst[] = {
227 {"ConfigReload", dbus_cb_dunst_ConfigReload},
228 {"ContextMenuCall", dbus_cb_dunst_ContextMenuCall},
229 {"NotificationAction", dbus_cb_dunst_NotificationAction},
230 {"NotificationClearHistory", dbus_cb_dunst_NotificationClearHistory},
231 {"NotificationCloseAll", dbus_cb_dunst_NotificationCloseAll},
232 {"NotificationCloseLast", dbus_cb_dunst_NotificationCloseLast},
233 {"NotificationListHistory", dbus_cb_dunst_NotificationListHistory},
234 {"NotificationPopHistory", dbus_cb_dunst_NotificationPopHistory},
235 {"NotificationRemoveFromHistory", dbus_cb_dunst_NotificationRemoveFromHistory},
236 {"NotificationShow", dbus_cb_dunst_NotificationShow},
237 {"Ping", dbus_cb_dunst_Ping},
238 {"RuleEnable", dbus_cb_dunst_RuleEnable},
239 {"RuleList", dbus_cb_dunst_RuleList},
240};
241
242void dbus_cb_dunst_methods(GDBusConnection *connection,
243 const gchar *sender,
244 const gchar *object_path,
245 const gchar *interface_name,
246 const gchar *method_name,
247 GVariant *parameters,
248 GDBusMethodInvocation *invocation,
249 gpointer user_data)
250{
251 struct dbus_method *m = bsearch(method_name,
252 methods_dunst,
253 G_N_ELEMENTS(methods_dunst),
254 sizeof(struct dbus_method),
255 cmp_methods);
256
257 if (m) {
258 m->method(connection, sender, parameters, invocation);
259 } else {
260 LOG_M("Unknown method name: '%s' (sender: '%s').",
261 method_name, sender);
262 }
263}
264
265static void dbus_cb_dunst_ContextMenuCall(GDBusConnection *connection,
266 const gchar *sender,
267 GVariant *parameters,
268 GDBusMethodInvocation *invocation)
269{
270 LOG_D("CMD: Calling context menu");
271 context_menu();
272
273 g_dbus_method_invocation_return_value(invocation, NULL);
274 g_dbus_connection_flush(connection, NULL, NULL, NULL);
275}
276
277static void dbus_cb_dunst_NotificationAction(GDBusConnection *connection,
278 const gchar *sender,
279 GVariant *parameters,
280 GDBusMethodInvocation *invocation)
281{
282 guint32 notification_nr = 0;
283 g_variant_get(parameters, "(u)", &notification_nr);
284
285 LOG_D("CMD: Calling action for notification %d", notification_nr);
286
287 if (queues_length_waiting() < notification_nr) {
288 g_dbus_method_invocation_return_error(invocation,
289 G_DBUS_ERROR,
290 G_DBUS_ERROR_INVALID_ARGS,
291 "Couldn't activate action for notification in position %d, %d notifications currently open",
292 notification_nr, queues_length_waiting());
293 return;
294 }
295
296 struct notification *n = g_list_nth_data(queues_get_displayed(), notification_nr);
297
298 if (n) {
299 LOG_D("CMD: Calling action for notification %s", n->summary);
301 }
302
303 g_dbus_method_invocation_return_value(invocation, NULL);
304 g_dbus_connection_flush(connection, NULL, NULL, NULL);
305}
306
307static void dbus_cb_dunst_NotificationClearHistory(GDBusConnection *connection,
308 const gchar *sender,
309 GVariant *parameters,
310 GDBusMethodInvocation *invocation)
311{
312 LOG_D("CMD: Clearing the history");
313 guint n = queues_history_clear();
314 wake_up();
315
316 signal_history_cleared(n);
317
318 g_dbus_method_invocation_return_value(invocation, NULL);
319 g_dbus_connection_flush(connection, NULL, NULL, NULL);
320}
321
322static void dbus_cb_dunst_NotificationCloseAll(GDBusConnection *connection,
323 const gchar *sender,
324 GVariant *parameters,
325 GDBusMethodInvocation *invocation)
326{
327 LOG_D("CMD: Pushing all to history");
329 wake_up();
330
331 g_dbus_method_invocation_return_value(invocation, NULL);
332 g_dbus_connection_flush(connection, NULL, NULL, NULL);
333}
334
335static void dbus_cb_dunst_NotificationCloseLast(GDBusConnection *connection,
336 const gchar *sender,
337 GVariant *parameters,
338 GDBusMethodInvocation *invocation)
339{
340 LOG_D("CMD: Closing last notification");
341 const GList *list = queues_get_displayed();
342 if (list && list->data) {
343 struct notification *n = list->data;
345 wake_up();
346 }
347
348 g_dbus_method_invocation_return_value(invocation, NULL);
349 g_dbus_connection_flush(connection, NULL, NULL, NULL);
350}
351
352static void dbus_cb_dunst_NotificationShow(GDBusConnection *connection,
353 const gchar *sender,
354 GVariant *parameters,
355 GDBusMethodInvocation *invocation)
356{
357 LOG_D("CMD: Showing last notification from history");
359 wake_up();
360
361 g_dbus_method_invocation_return_value(invocation, NULL);
362 g_dbus_connection_flush(connection, NULL, NULL, NULL);
363}
364
365static void dbus_cb_dunst_NotificationListHistory(GDBusConnection *connection,
366 const gchar *sender,
367 GVariant *parameters,
368 GDBusMethodInvocation *invocation)
369{
370 LOG_D("CMD: Listing all notifications from history");
371
372 GVariantBuilder builder;
373 g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}"));
374
375 GList *notification_list = queues_get_history();
376
377 // reverse chronological list
378 for(int i = queues_length_history(); i > 0; i--) {
379 struct notification *n;
380 n = g_list_nth_data(notification_list, i-1);
381
382 GVariantBuilder n_builder;
383 g_variant_builder_init(&n_builder, G_VARIANT_TYPE("a{sv}"));
384
385 char *body, *msg, *summary, *appname, *category;
387 char *urls, *stack_tag;
388 const char *urgency;
389
390 body = (n->body == NULL) ? "" : n->body;
391 msg = (n->msg == NULL) ? "" : n->msg;
392 summary = (n->summary == NULL) ? "" : n->summary;
393 appname = (n->appname == NULL) ? "" : n->appname;
394 category = (n->category == NULL) ? "" : n->category;
396 "" : n->default_action_name;
397 icon_path = (n->icon_path == NULL) ? "" : n->icon_path;
398 urgency = notification_urgency_to_string(n->urgency);
399 urls = (n->urls == NULL) ? "" : n->urls;
400 stack_tag = (n->stack_tag == NULL) ? "" : n->stack_tag;
401
402 g_variant_builder_add(&n_builder, "{sv}", "body", g_variant_new_string(body));
403 g_variant_builder_add(&n_builder, "{sv}", "message", g_variant_new_string(msg));
404 g_variant_builder_add(&n_builder, "{sv}", "summary", g_variant_new_string(summary));
405 g_variant_builder_add(&n_builder, "{sv}", "appname", g_variant_new_string(appname));
406 g_variant_builder_add(&n_builder, "{sv}", "category", g_variant_new_string(category));
407 g_variant_builder_add(&n_builder, "{sv}", "default_action_name",
408 g_variant_new_string(default_action_name));
409 g_variant_builder_add(&n_builder, "{sv}", "icon_path", g_variant_new_string(icon_path));
410 g_variant_builder_add(&n_builder, "{sv}", "id", g_variant_new_int32(n->id));
411 g_variant_builder_add(&n_builder, "{sv}", "timestamp", g_variant_new_int64(n->timestamp));
412 g_variant_builder_add(&n_builder, "{sv}", "timeout", g_variant_new_int64(n->timeout));
413 g_variant_builder_add(&n_builder, "{sv}", "progress", g_variant_new_int32(n->progress));
414 g_variant_builder_add(&n_builder, "{sv}", "urgency", g_variant_new_string(urgency));
415 g_variant_builder_add(&n_builder, "{sv}", "stack_tag", g_variant_new_string(stack_tag));
416 g_variant_builder_add(&n_builder, "{sv}", "urls", g_variant_new_string(urls));
417
418 g_variant_builder_add(&builder, "a{sv}", &n_builder);
419 }
420
421 g_dbus_method_invocation_return_value(invocation, g_variant_new("(aa{sv})", &builder));
422 g_dbus_connection_flush(connection, NULL, NULL, NULL);
423}
424
425static void dbus_cb_dunst_NotificationPopHistory(GDBusConnection *connection,
426 const gchar *sender,
427 GVariant *parameters,
428 GDBusMethodInvocation *invocation)
429{
430 LOG_D("CMD: Popping notification from history");
431
432 guint32 id;
433 g_variant_get(parameters, "(u)", &id);
434
436 wake_up();
437
438 g_dbus_method_invocation_return_value(invocation, NULL);
439 g_dbus_connection_flush(connection, NULL, NULL, NULL);
440}
441
442static void dbus_cb_dunst_NotificationRemoveFromHistory(GDBusConnection *connection,
443 const gchar *sender,
444 GVariant *parameters,
445 GDBusMethodInvocation *invocation)
446{
447 LOG_D("CMD: Removing notification from history");
448
449 guint32 id;
450 g_variant_get(parameters, "(u)", &id);
451
453 signal_history_removed(id);
454 wake_up();
455 }
456
457 g_dbus_method_invocation_return_value(invocation, NULL);
458 g_dbus_connection_flush(connection, NULL, NULL, NULL);
459}
460
461static const char *enum_to_string(const struct string_to_enum_def values[], int enum_value)
462{
463 for (size_t i = 0; values[i].string != NULL; i++) {
464 if (values[i].enum_value == enum_value) {
465 return values[i].string;
466 }
467 }
468 return NULL;
469}
470
471static void color_entry(const struct color c, GVariantDict *dict, const char *field_name) {
472 char buf[10];
473 if (color_to_string(c, buf)) {
474 g_variant_dict_insert(dict, field_name, "s", buf);
475 }
476}
477
478static void gradient_entry(const struct gradient *grad, GVariantDict *dict, const char *field_name) {
479 if (GRADIENT_VALID(grad)) {
480 if (grad->length == 1) {
481 color_entry(grad->colors[0], dict, field_name);
482 return;
483 }
484
485 char **strv = g_malloc((grad->length + 1) * sizeof(char *));
486 for (size_t i = 0; i < grad->length; i++) {
487 char buf[10];
488 if (color_to_string(grad->colors[i], buf))
489 strv[i] = g_strdup(buf);
490 }
491 strv[grad->length] = NULL;
492
493 g_variant_dict_insert(dict, field_name, "^as", strv);
494 }
495}
496
497static void dbus_cb_dunst_RuleList(GDBusConnection *connection,
498 const gchar *sender,
499 GVariant *parameters,
500 GDBusMethodInvocation *invocation)
501{
502 LOG_D("CMD: Listing all configured rules");
503
504 GVariantBuilder builder;
505 g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}"));
506
507 for (GSList *iter = rules; iter; iter = iter->next) {
508 struct rule *r = iter->data;
509
510 if (is_special_section(r->name)) {
511 continue;
512 }
513
514 GVariantDict dict;
515 g_variant_dict_init(&dict, NULL);
516 g_variant_dict_insert(&dict, "name", "s", r->name);
517
518 // filters - order according to rule_matches_notification
519 g_variant_dict_insert(&dict, "enabled", "b", BOOL2G(r->enabled));
520 // undocumented filter?
521 if (r->match_dbus_timeout > -1)
522 g_variant_dict_insert(&dict, "match_dbus_timeout", "i", r->match_dbus_timeout);
523 if (r->msg_urgency != URG_NONE)
524 g_variant_dict_insert(&dict,
525 "msg_urgency",
526 "s",
527 enum_to_string(urgency_enum_data, r->msg_urgency));
528 if (r->match_transient > -1)
529 g_variant_dict_insert(&dict, "match_transient", "b", BOOL2G(r->match_transient));
530 if (r->appname)
531 g_variant_dict_insert(&dict, "appname", "s", r->appname);
532 if (r->desktop_entry)
533 g_variant_dict_insert(&dict, "desktop_entry", "s", r->desktop_entry);
534 if (r->summary)
535 g_variant_dict_insert(&dict, "summary", "s", r->summary);
536 if (r->body)
537 g_variant_dict_insert(&dict, "body", "s", r->body);
538 if (r->category)
539 g_variant_dict_insert(&dict, "category", "s", r->category);
540 if (r->stack_tag)
541 g_variant_dict_insert(&dict, "stack_tag", "s", r->stack_tag);
542
543 // settings to apply - order according to rule_apply
544 if (r->timeout != -1)
545 g_variant_dict_insert(&dict, "timeout", "x", r->timeout);
546 if (r->override_dbus_timeout != -1)
547 g_variant_dict_insert(&dict, "override_dbus_timeout", "x", r->override_dbus_timeout);
548 if (r->urgency != URG_NONE)
549 g_variant_dict_insert(&dict, "urgency", "s", enum_to_string(urgency_enum_data, r->urgency));
550 if (r->fullscreen != FS_NULL)
551 g_variant_dict_insert(&dict,
552 "fullscreen",
553 "s",
554 enum_to_string(fullscreen_enum_data, r->fullscreen));
555 if (r->history_ignore != -1)
556 g_variant_dict_insert(&dict, "history_ignore", "b", BOOL2G(r->history_ignore));
557 if (r->set_transient != -1)
558 g_variant_dict_insert(&dict, "set_transient", "b", BOOL2G(r->set_transient));
559 if (r->skip_display != -1)
560 g_variant_dict_insert(&dict, "skip_display", "b", BOOL2G(r->skip_display));
561 if (r->word_wrap != -1)
562 g_variant_dict_insert(&dict, "word_wrap", "b", BOOL2G(r->word_wrap));
563 if (r->ellipsize != -1)
564 g_variant_dict_insert(&dict,
565 "ellipsize",
566 "s",
567 enum_to_string(ellipsize_enum_data, r->ellipsize));
568 if (r->alignment != -1)
569 g_variant_dict_insert(&dict,
570 "alignment",
571 "s",
572 enum_to_string(horizontal_alignment_enum_data, r->alignment));
573 if (r->hide_text != -1)
574 g_variant_dict_insert(&dict, "hide_text", "b", BOOL2G(r->hide_text));
575 if (r->progress_bar_alignment != -1)
576 g_variant_dict_insert(&dict,
577 "progress_bar_alignment",
578 "s",
579 enum_to_string(horizontal_alignment_enum_data,
580 r->progress_bar_alignment));
581 if (r->min_icon_size != -1)
582 g_variant_dict_insert(&dict, "min_icon_size", "i", r->min_icon_size);
583 if (r->max_icon_size != -1)
584 g_variant_dict_insert(&dict, "max_icon_size", "i", r->max_icon_size);
585 if (r->action_name)
586 g_variant_dict_insert(&dict, "action_name", "s", r->action_name);
587 if (r->set_category)
588 g_variant_dict_insert(&dict, "set_category", "s", r->set_category);
589 if (r->markup != MARKUP_NULL)
590 g_variant_dict_insert(&dict, "markup", "s", enum_to_string(markup_mode_enum_data, r->markup));
591 if (r->icon_position != -1)
592 g_variant_dict_insert(&dict,
593 "icon_position",
594 "s",
595 enum_to_string(icon_position_enum_data, r->icon_position));
596 color_entry(r->fg, &dict, "fg");
597 color_entry(r->bg, &dict, "bg");
598 gradient_entry(r->highlight, &dict, "highlight");
599 color_entry(r->fc, &dict, "fc");
600 if (r->format)
601 g_variant_dict_insert(&dict, "format", "s", r->format);
602 if (r->default_icon)
603 g_variant_dict_insert(&dict, "default_icon", "s", r->default_icon);
604 if (r->new_icon)
605 g_variant_dict_insert(&dict, "new_icon", "s", r->new_icon);
606 if (r->script)
607 g_variant_dict_insert(&dict, "script", "s", r->script);
608 if (r->set_stack_tag)
609 g_variant_dict_insert(&dict, "set_stack_tag", "s", r->set_stack_tag);
610 if (r->override_pause_level != -1)
611 g_variant_dict_insert(&dict, "override_pause_level", "i", r->override_pause_level);
612
613 g_variant_builder_add_value(&builder, g_variant_dict_end(&dict));
614 }
615
616 g_dbus_method_invocation_return_value(invocation, g_variant_new("(aa{sv})", &builder));
617 g_dbus_connection_flush(connection, NULL, NULL, NULL);
618}
619
620static void dbus_cb_dunst_RuleEnable(GDBusConnection *connection,
621 const gchar *sender,
622 GVariant *parameters,
623 GDBusMethodInvocation *invocation)
624{
625 // dbus param state: 0 → disable, 1 → enable, 2 → toggle.
626
627 int state = 0;
628 char *name = NULL;
629 g_variant_get(parameters, "(si)", &name, &state);
630
631 LOG_D("CMD: Changing rule \"%s\" enable state to %d", name, state);
632
633 if (state < 0 || state > 2) {
634 g_dbus_method_invocation_return_error(invocation,
635 G_DBUS_ERROR,
636 G_DBUS_ERROR_INVALID_ARGS,
637 "Couldn't understand state %d. It must be 0, 1 or 2",
638 state);
639 return;
640 }
641
642 struct rule *target_rule = get_rule(name);
643 if (target_rule == NULL) {
644 g_dbus_method_invocation_return_error(invocation,
645 G_DBUS_ERROR,
646 G_DBUS_ERROR_INVALID_ARGS,
647 "There is no rule named \"%s\"",
648 name);
649 g_free(name);
650 return;
651 }
652 g_free(name);
653
654 if (state == 0)
655 target_rule->enabled = false;
656 else if (state == 1)
657 target_rule->enabled = true;
658 else if (state == 2)
659 target_rule->enabled = !target_rule->enabled;
660
661 g_dbus_method_invocation_return_value(invocation, NULL);
662 g_dbus_connection_flush(connection, NULL, NULL, NULL);
663}
664
665static void dbus_cb_dunst_ConfigReload(GDBusConnection *connection,
666 const gchar *sender,
667 GVariant *parameters,
668 GDBusMethodInvocation *invocation)
669{
670 gchar **configs = NULL;
671 g_variant_get(parameters, "(^as)", &configs);
672 reload(configs);
673
674 signal_config_reloaded(configs);
675
676 g_dbus_method_invocation_return_value(invocation, NULL);
677 g_dbus_connection_flush(connection, NULL, NULL, NULL);
678}
679
680/* Just a simple Ping command to give the ability to dunstctl to test for the existence of this interface
681 * Any other way requires parsing the XML of the Introspection or other foo. Just calling the Ping on an old dunst version will fail. */
682static void dbus_cb_dunst_Ping(GDBusConnection *connection,
683 const gchar *sender,
684 GVariant *parameters,
685 GDBusMethodInvocation *invocation)
686{
687 g_dbus_method_invocation_return_value(invocation, NULL);
688 g_dbus_connection_flush(connection, NULL, NULL, NULL);
689}
690
691
692static void dbus_cb_GetCapabilities(
693 GDBusConnection *connection,
694 const gchar *sender,
695 GVariant *parameters,
696 GDBusMethodInvocation *invocation)
697{
698 GVariantBuilder *builder;
699 GVariant *value;
700
701 builder = g_variant_builder_new(G_VARIANT_TYPE("as"));
702 g_variant_builder_add(builder, "s", "actions");
703 g_variant_builder_add(builder, "s", "body");
704 g_variant_builder_add(builder, "s", "body-hyperlinks");
705 g_variant_builder_add(builder, "s", "icon-static");
706
707 for (size_t i = 0; i < sizeof(stack_tag_hints)/sizeof(*stack_tag_hints); ++i)
708 g_variant_builder_add(builder, "s", stack_tag_hints[i]);
709
710 // Since markup isn't a global variable anymore, look it up in the
711 // global rule
712 struct rule *global_rule = get_rule("global");
713 if (global_rule && global_rule->markup != MARKUP_NO)
714 g_variant_builder_add(builder, "s", "body-markup");
715
716 value = g_variant_new("(as)", builder);
717 g_clear_pointer(&builder, g_variant_builder_unref);
718 g_dbus_method_invocation_return_value(invocation, value);
719
720 g_dbus_connection_flush(connection, NULL, NULL, NULL);
721}
722
723static struct notification *dbus_message_to_notification(const gchar *sender, GVariant *parameters)
724{
725 /* Assert that the parameters' type is actually correct. Albeit usually DBus
726 * already rejects ill typed parameters, it may not be always the case. */
727 GVariantType *required_type = g_variant_type_new("(susssasa{sv}i)");
728 if (!g_variant_is_of_type(parameters, required_type)) {
729 g_variant_type_free(required_type);
730 return NULL;
731 }
732
733 struct notification *n = notification_create();
734 n->dbus_client = g_strdup(sender);
735 n->dbus_valid = true;
736
737 GVariant *hints;
738 gchar **actions;
739 int timeout;
740
741 GVariantIter i;
742 g_variant_iter_init(&i, parameters);
743
744 g_variant_iter_next(&i, "s", &n->appname);
745 g_variant_iter_next(&i, "u", &n->id);
746 g_variant_iter_next(&i, "s", &n->iconname);
747 g_variant_iter_next(&i, "s", &n->summary);
748 g_variant_iter_next(&i, "s", &n->body);
749 g_variant_iter_next(&i, "^a&s", &actions);
750 g_variant_iter_next(&i, "@a{?*}", &hints);
751 g_variant_iter_next(&i, "i", &timeout);
752
753 gsize num = 0;
754 while (actions[num]) {
755 if (actions[num+1]) {
756 g_hash_table_insert(n->actions,
757 g_strdup(actions[num]),
758 g_strdup(actions[num+1]));
759 num+=2;
760 } else {
761 LOG_W("Odd length in actions array. Ignoring element: %s", actions[num]);
762 break;
763 }
764 }
765
766 GVariant *dict_value;
767 GVariant *icon_value = NULL;
768
769 // First process the items that can be filtered on
770 if ((dict_value = g_variant_lookup_value(hints, "urgency", G_VARIANT_TYPE_BYTE))) {
771 n->urgency = g_variant_get_byte(dict_value);
772 g_variant_unref(dict_value);
773 }
774
775 if ((dict_value = g_variant_lookup_value(hints, "category", G_VARIANT_TYPE_STRING))) {
776 n->category = g_variant_dup_string(dict_value, NULL);
777 g_variant_unref(dict_value);
778 }
779
780 if ((dict_value = g_variant_lookup_value(hints, "desktop-entry", G_VARIANT_TYPE_STRING))) {
781 n->desktop_entry = g_variant_dup_string(dict_value, NULL);
782 g_variant_unref(dict_value);
783 }
784
785 if ((dict_value = g_variant_lookup_value(hints, "value", G_VARIANT_TYPE_INT32))) {
786 n->progress = g_variant_get_int32(dict_value);
787 g_variant_unref(dict_value);
788 } else if ((dict_value = g_variant_lookup_value(hints, "value", G_VARIANT_TYPE_UINT32))) {
789 n->progress = g_variant_get_uint32(dict_value);
790 g_variant_unref(dict_value);
791 }
792 if (n->progress < 0)
793 n->progress = -1;
794
795 /* Check for hints that define the stack_tag
796 *
797 * Only accept to first one we find.
798 */
799 for (size_t i = 0; i < sizeof(stack_tag_hints)/sizeof(*stack_tag_hints); ++i) {
800 if ((dict_value = g_variant_lookup_value(hints, stack_tag_hints[i], G_VARIANT_TYPE_STRING))) {
801 n->stack_tag = g_variant_dup_string(dict_value, NULL);
802 g_variant_unref(dict_value);
803 break;
804 }
805 }
806
807 /* Check for transient hints
808 *
809 * According to the spec, the transient hint should be boolean.
810 * But notify-send does not support hints of type 'boolean'.
811 * So let's check for int and boolean until notify-send is fixed.
812 */
813 if ((dict_value = g_variant_lookup_value(hints, "transient", G_VARIANT_TYPE_BOOLEAN))) {
814 n->transient = g_variant_get_boolean(dict_value);
815 g_variant_unref(dict_value);
816 } else if ((dict_value = g_variant_lookup_value(hints, "transient", G_VARIANT_TYPE_UINT32))) {
817 n->transient = g_variant_get_uint32(dict_value) > 0;
818 g_variant_unref(dict_value);
819 } else if ((dict_value = g_variant_lookup_value(hints, "transient", G_VARIANT_TYPE_INT32))) {
820 n->transient = g_variant_get_int32(dict_value) > 0;
821 g_variant_unref(dict_value);
822 }
823
824 dict_value = g_variant_lookup_value(hints, "image-path", G_VARIANT_TYPE_STRING);
825 if (!dict_value)
826 dict_value = g_variant_lookup_value(hints, "image_path", G_VARIANT_TYPE_STRING);
827
828 if (dict_value) {
829 g_free(n->iconname);
830 n->iconname = g_variant_dup_string(dict_value, NULL);
831 g_variant_unref(dict_value);
832 }
833
834 // Set raw icon data only after initializing the notification, so the
835 // desired icon size is known. This way the buffer can be immediately
836 // rescaled. If at some point you might want to match by if a
837 // notificaton has an image, this has to be reworked.
838 dict_value = g_variant_lookup_value(hints, "image-data", G_VARIANT_TYPE("(iiibiiay)"));
839 if (!dict_value)
840 dict_value = g_variant_lookup_value(hints, "image_data", G_VARIANT_TYPE("(iiibiiay)"));
841 if (!dict_value)
842 dict_value = g_variant_lookup_value(hints, "icon_data", G_VARIANT_TYPE("(iiibiiay)"));
843 if (dict_value) {
844 // Signal that the notification is still waiting for a raw
845 // icon. It cannot be set now, because min_icon_size and
846 // max_icon_size aren't known yet. It cannot be set later,
847 // because it has to be overwritten by the new_icon rule.
848 n->receiving_raw_icon = true;
849 icon_value = dict_value;
850 dict_value = NULL;
851 }
852
853 // Set the dbus timeout
854 if (timeout >= 0)
855 n->dbus_timeout = ((gint64)timeout) * 1000;
856
857 // All attributes that have to be set before initializations are set,
858 // so we can initialize the notification. This applies all rules that
859 // are defined and applies the formatting to the message.
861
862 if (icon_value) {
863 if (n->receiving_raw_icon)
864 notification_icon_replace_data(n, icon_value);
865 g_variant_unref(icon_value);
866 }
867
868 // Modify these values after the notification is initialized and all rules are applied.
869 if ((dict_value = g_variant_lookup_value(hints, "fgcolor", G_VARIANT_TYPE_STRING))) {
870 struct color c;
871 if (string_parse_color(g_variant_get_string(dict_value, NULL), &c)) {
872 notification_keep_original(n);
873 if (!COLOR_VALID(n->original->fg)) n->original->fg = n->colors.fg;
874 n->colors.fg = c;
875 }
876 g_variant_unref(dict_value);
877 }
878
879 if ((dict_value = g_variant_lookup_value(hints, "bgcolor", G_VARIANT_TYPE_STRING))) {
880 struct color c;
881 if (string_parse_color(g_variant_get_string(dict_value, NULL), &c)) {
882 notification_keep_original(n);
883 if (!COLOR_VALID(n->original->bg)) n->original->bg = n->colors.bg;
884 n->colors.bg = c;
885 }
886 g_variant_unref(dict_value);
887 }
888
889 if ((dict_value = g_variant_lookup_value(hints, "frcolor", G_VARIANT_TYPE_STRING))) {
890 struct color c;
891 if (string_parse_color(g_variant_get_string(dict_value, NULL), &c)) {
892 notification_keep_original(n);
893 if (!COLOR_VALID(n->original->fc)) n->original->fc = n->colors.frame;
894 n->colors.frame = c;
895 }
896 g_variant_unref(dict_value);
897 }
898
899 if ((dict_value = g_variant_lookup_value(hints, "hlcolor", G_VARIANT_TYPE_STRING_ARRAY))) {
900 char **cols = (char **)g_variant_get_strv(dict_value, NULL);
901 size_t length = g_strv_length(cols);
902 struct gradient *grad = gradient_alloc(length);
903
904 for (size_t i = 0; i < length; i++) {
905 if (!string_parse_color(cols[i], &grad->colors[i])) {
906 g_free(grad);
907 goto end;
908 }
909 }
910
911 gradient_pattern(grad);
912
913 notification_keep_original(n);
914 if (!GRADIENT_VALID(n->original->highlight)) n->original->highlight = gradient_acquire(n->colors.highlight);
915 gradient_release(n->colors.highlight);
916 n->colors.highlight = gradient_acquire(grad);
917
918end:
919 g_variant_unref(dict_value);
920 } else if ((dict_value = g_variant_lookup_value(hints, "hlcolor", G_VARIANT_TYPE_STRING))) {
921 struct color c;
922 if (string_parse_color(g_variant_get_string(dict_value, NULL), &c)) {
923 struct gradient *grad = gradient_alloc(1);
924 grad->colors[0] = c;
925 gradient_pattern(grad);
926
927 notification_keep_original(n);
928 if (!GRADIENT_VALID(n->original->highlight)) n->original->highlight = gradient_acquire(n->colors.highlight);
929 gradient_release(n->colors.highlight);
930 n->colors.highlight = gradient_acquire(grad);
931 }
932 g_variant_unref(dict_value);
933 }
934
935 g_variant_unref(hints);
936 g_variant_type_free(required_type);
937 g_free(actions); // the strv is only a shallow copy
938
939 return n;
940}
941
942void signal_length_propertieschanged(void)
943{
944 static unsigned int last_displayed = 0;
945 static unsigned int last_history = 0;
946 static unsigned int last_waiting = 0;
947
948 if (!dbus_conn)
949 return;
950
951 GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE_VARDICT);
952 GVariantBuilder *invalidated_builder = g_variant_builder_new(G_VARIANT_TYPE_STRING_ARRAY);
953
954 unsigned int displayed = queues_length_displayed();
955 unsigned int history = queues_length_history();
956 unsigned int waiting = queues_length_waiting();
957 bool properties_changed = false;
958
959 if (last_displayed != displayed) {
960 g_variant_builder_add(builder,
961 "{sv}",
962 "displayedLength", g_variant_new_uint32(displayed));
963 last_displayed = displayed;
964 properties_changed = true;
965 }
966
967 if (last_history != history) {
968 g_variant_builder_add(builder,
969 "{sv}",
970 "historyLength", g_variant_new_uint32(history));
971 last_history = history;
972 properties_changed = true;
973 }
974 if (last_waiting != waiting) {
975 g_variant_builder_add(builder,
976 "{sv}",
977 "waitingLength", g_variant_new_uint32(waiting));
978 last_waiting = waiting;
979 properties_changed = true;
980 }
981
982 if (properties_changed) {
983 GVariant *body = g_variant_new("(sa{sv}as)",
984 DUNST_IFAC,
985 builder,
986 invalidated_builder);
987
988 GError *err = NULL;
989
990 g_dbus_connection_emit_signal(dbus_conn,
991 NULL,
992 FDN_PATH,
993 PROPERTIES_IFAC,
994 "PropertiesChanged",
995 body,
996 &err);
997
998 if (err) {
999 LOG_W("Unable to emit signal: %s", err->message);
1000 g_error_free(err);
1001 }
1002 }
1003
1004 g_clear_pointer(&builder, g_variant_builder_unref);
1005 g_clear_pointer(&invalidated_builder, g_variant_builder_unref);
1006}
1007
1008void signal_paused_propertieschanged(void)
1009{
1010 if(!dbus_conn)
1011 return;
1012
1013 struct dunst_status status = dunst_status_get();
1014
1015 GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE_VARDICT);
1016 GVariantBuilder *invalidated_builder = g_variant_builder_new(G_VARIANT_TYPE_STRING_ARRAY);
1017
1018 g_variant_builder_add(builder,
1019 "{sv}",
1020 "paused", g_variant_new_boolean(status.pause_level != 0));
1021 g_variant_builder_add(builder,
1022 "{sv}",
1023 "pauseLevel", g_variant_new_uint32(status.pause_level));
1024
1025 g_dbus_connection_emit_signal(dbus_conn,
1026 NULL,
1027 FDN_PATH,
1028 PROPERTIES_IFAC,
1029 "PropertiesChanged",
1030 g_variant_new("(sa{sv}as)",
1031 DUNST_IFAC,
1032 builder,
1033 invalidated_builder),
1034 NULL);
1035
1036 g_clear_pointer(&builder, g_variant_builder_unref);
1037 g_clear_pointer(&invalidated_builder, g_variant_builder_unref);
1038
1039}
1040
1041static void dbus_cb_Notify(
1042 GDBusConnection *connection,
1043 const gchar *sender,
1044 GVariant *parameters,
1045 GDBusMethodInvocation *invocation)
1046{
1047 struct notification *n = dbus_message_to_notification(sender, parameters);
1048 if (!n) {
1049 LOG_W("A notification failed to decode.");
1050 g_dbus_method_invocation_return_dbus_error(
1051 invocation,
1052 FDN_IFAC".Error",
1053 "Cannot decode notification!");
1054 return;
1055 }
1056
1057 int id = queues_notification_insert(n);
1058
1059 GVariant *reply = g_variant_new("(u)", id);
1060 g_dbus_method_invocation_return_value(invocation, reply);
1061 g_dbus_connection_flush(connection, NULL, NULL, NULL);
1062
1063 // The message got discarded
1064 if (id == 0) {
1065 signal_notification_closed(n, REASON_USER);
1067 }
1068
1069 wake_up();
1070}
1071
1072static void dbus_cb_CloseNotification(
1073 GDBusConnection *connection,
1074 const gchar *sender,
1075 GVariant *parameters,
1076 GDBusMethodInvocation *invocation)
1077{
1078 guint32 id;
1079 g_variant_get(parameters, "(u)", &id);
1080 if (settings.ignore_dbusclose) {
1081 LOG_D("Ignoring CloseNotification message");
1082 // Stay commpliant by lying to the sender, telling him we closed the notification
1083 if (id > 0) {
1084 struct notification *n = queues_get_by_id(id);
1085 if (n)
1086 signal_notification_closed(n, REASON_SIG);
1087 }
1088 } else {
1090 }
1091 wake_up();
1092 g_dbus_method_invocation_return_value(invocation, NULL);
1093 g_dbus_connection_flush(connection, NULL, NULL, NULL);
1094}
1095
1096static void dbus_cb_GetServerInformation(
1097 GDBusConnection *connection,
1098 const gchar *sender,
1099 GVariant *parameters,
1100 GDBusMethodInvocation *invocation)
1101{
1102 GVariant *answer = g_variant_new("(ssss)", "dunst", "knopwob", VERSION, "1.2");
1103
1104 g_dbus_method_invocation_return_value(invocation, answer);
1105 g_dbus_connection_flush(connection, NULL, NULL, NULL);
1106}
1107
1108void signal_notification_closed(struct notification *n, enum reason reason)
1109{
1110 if (!n->dbus_valid) {
1111 return;
1112 }
1113
1114 if (reason < REASON_MIN || REASON_MAX < reason) {
1115 LOG_W("Closing notification with reason '%d' not supported. "
1116 "Closing it with reason '%d'.", reason, REASON_UNDEF);
1118 }
1119
1120 if (!dbus_conn) {
1121 LOG_E("Unable to close notification: No DBus connection.");
1122 }
1123
1124 GVariant *body = g_variant_new("(uu)", n->id, reason);
1125 GError *err = NULL;
1126
1127 g_dbus_connection_emit_signal(dbus_conn,
1128 n->dbus_client,
1129 FDN_PATH,
1130 FDN_IFAC,
1131 "NotificationClosed",
1132 body,
1133 &err);
1134
1136
1137 n->dbus_valid = false;
1138
1139 if (err) {
1140 LOG_W("Unable to close notification: %s", err->message);
1141 g_error_free(err);
1142 } else {
1143 char* reason_string;
1144 switch (reason) {
1145 case REASON_TIME:
1146 reason_string="time";
1147 break;
1148 case REASON_USER:
1149 reason_string="user";
1150 break;
1151 case REASON_SIG:
1152 reason_string="signal";
1153 break;
1154 case REASON_UNDEF:
1155 reason_string="undefined";
1156 break;
1157 default:
1158 reason_string="unknown";
1159 }
1160
1161 LOG_D("Queues: Closing notification for reason: %s", reason_string);
1162 }
1163}
1164
1165void signal_action_invoked(const struct notification *n, const char *identifier)
1166{
1167 if (!n->dbus_valid) {
1168 LOG_W("Invoking action '%s' not supported. "
1169 "Notification already closed.", identifier);
1170 return;
1171 }
1172
1173 GVariant *body = g_variant_new("(us)", n->id, identifier);
1174 GError *err = NULL;
1175
1176 g_dbus_connection_emit_signal(dbus_conn,
1177 n->dbus_client,
1178 FDN_PATH,
1179 FDN_IFAC,
1180 "ActionInvoked",
1181 body,
1182 &err);
1183
1184 if (err) {
1185 LOG_W("Unable to invoke action: %s", err->message);
1186 g_error_free(err);
1187 }
1188}
1189
1190GVariant *dbus_cb_dunst_Properties_Get(GDBusConnection *connection,
1191 const gchar *sender,
1192 const gchar *object_path,
1193 const gchar *interface_name,
1194 const gchar *property_name,
1195 GError **error,
1196 gpointer user_data)
1197{
1198 struct dunst_status status = dunst_status_get();
1199
1200 if (STR_EQ(property_name, "paused")) {
1201 return g_variant_new_boolean(status.pause_level != 0);
1202 } else if (STR_EQ(property_name, "pauseLevel")) {
1203 return g_variant_new_uint32(status.pause_level);
1204 } else if (STR_EQ(property_name, "displayedLength")) {
1205 unsigned int displayed = queues_length_displayed();
1206 return g_variant_new_uint32(displayed);
1207 } else if (STR_EQ(property_name, "historyLength")) {
1208 unsigned int history = queues_length_history();
1209 return g_variant_new_uint32(history);
1210 } else if (STR_EQ(property_name, "waitingLength")) {
1211 unsigned int waiting = queues_length_waiting();
1212 return g_variant_new_uint32(waiting);
1213 } else {
1214 LOG_W("Unknown property!\n");
1215 *error = g_error_new(G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property");
1216 return NULL;
1217 }
1218}
1219
1220gboolean dbus_cb_dunst_Properties_Set(GDBusConnection *connection,
1221 const gchar *sender,
1222 const gchar *object_path,
1223 const gchar *interface_name,
1224 const gchar *property_name,
1225 GVariant *value,
1226 GError **error,
1227 gpointer user_data)
1228{
1229 int targetPauseLevel = -1;
1230 if (STR_EQ(property_name, "paused")) {
1231 if (g_variant_get_boolean(value)) {
1232 targetPauseLevel = MAX_PAUSE_LEVEL;
1233 } else {
1234 targetPauseLevel = 0;
1235 }
1236 } else if STR_EQ(property_name, "pauseLevel") {
1237 targetPauseLevel = g_variant_get_uint32(value);
1238 if (targetPauseLevel > MAX_PAUSE_LEVEL) {
1239 targetPauseLevel = MAX_PAUSE_LEVEL;
1240 }
1241 }
1242
1243 if (targetPauseLevel >= 0) {
1244 dunst_status_int(S_PAUSE_LEVEL, targetPauseLevel);
1245 wake_up();
1246
1247 signal_paused_propertieschanged();
1248
1249 return true;
1250 }
1251
1252 *error = g_error_new(G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property");
1253
1254 return false;
1255}
1256
1257
1258static const GDBusInterfaceVTable interface_vtable_fdn = {
1259 dbus_cb_fdn_methods,
1260};
1261
1262static const GDBusInterfaceVTable interface_vtable_dunst = {
1263 dbus_cb_dunst_methods,
1264 dbus_cb_dunst_Properties_Get,
1265 dbus_cb_dunst_Properties_Set,
1266};
1267
1268static void dbus_cb_bus_acquired(GDBusConnection *connection,
1269 const gchar *name,
1270 gpointer user_data)
1271{
1272 GError *err = NULL;
1273 if(!g_dbus_connection_register_object(
1274 connection,
1275 FDN_PATH,
1276 introspection_data->interfaces[0],
1277 &interface_vtable_fdn,
1278 NULL,
1279 NULL,
1280 &err)) {
1281 DIE("Unable to register dbus connection interface '%s': %s", introspection_data->interfaces[0]->name, err->message);
1282 }
1283
1284 if(!g_dbus_connection_register_object(
1285 connection,
1286 FDN_PATH,
1287 introspection_data->interfaces[1],
1288 &interface_vtable_dunst,
1289 NULL,
1290 NULL,
1291 &err)) {
1292 DIE("Unable to register dbus connection interface '%s': %s", introspection_data->interfaces[1]->name, err->message);
1293 }
1294}
1295
1296static void dbus_cb_name_acquired(GDBusConnection *connection,
1297 const gchar *name,
1298 gpointer user_data)
1299{
1300 // If we're not able to get org.fd.N bus, we've still got a problem
1301 if (STR_EQ(name, FDN_NAME))
1302 dbus_conn = connection;
1303}
1304
1319static bool dbus_get_fdn_daemon_info(GDBusConnection *connection,
1320 guint *pid,
1321 char **name,
1322 char **vendor)
1323{
1324 ASSERT_OR_RET(pid, false);
1325 ASSERT_OR_RET(connection, false);
1326
1327 char *owner = NULL;
1328 GError *error = NULL;
1329
1330 GDBusProxy *proxy_fdn;
1331 GDBusProxy *proxy_dbus;
1332
1333 proxy_fdn = g_dbus_proxy_new_sync(
1334 connection,
1335 /* do not trigger a start of the notification daemon */
1336 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
1337 NULL, /* info */
1338 FDN_NAME,
1339 FDN_PATH,
1340 FDN_IFAC,
1341 NULL, /* cancelable */
1342 &error);
1343
1344 if (error) {
1345 g_error_free(error);
1346 return false;
1347 }
1348
1349 GVariant *daemoninfo = NULL;
1350 if (name || vendor) {
1351 daemoninfo = g_dbus_proxy_call_sync(
1352 proxy_fdn,
1353 FDN_IFAC ".GetServerInformation",
1354 NULL,
1355 G_DBUS_CALL_FLAGS_NONE,
1356 /* It's not worth to wait for the info
1357 * longer than half a second when dying */
1358 500,
1359 NULL, /* cancelable */
1360 &error);
1361 }
1362
1363 if (error) {
1364 /* Ignore the error, we may still be able to retrieve the PID */
1365 g_clear_pointer(&error, g_error_free);
1366 } else {
1367 g_variant_get(daemoninfo, "(ssss)", name, vendor, NULL, NULL);
1368 }
1369
1370 owner = g_dbus_proxy_get_name_owner(proxy_fdn);
1371
1372 proxy_dbus = g_dbus_proxy_new_sync(
1373 connection,
1374 G_DBUS_PROXY_FLAGS_NONE,
1375 NULL, /* info */
1376 "org.freedesktop.DBus",
1377 "/org/freedesktop/DBus",
1378 "org.freedesktop.DBus",
1379 NULL, /* cancelable */
1380 &error);
1381
1382 if (error) {
1383 g_error_free(error);
1384 return false;
1385 }
1386
1387 GVariant *pidinfo = g_dbus_proxy_call_sync(
1388 proxy_dbus,
1389 "org.freedesktop.DBus.GetConnectionUnixProcessID",
1390 g_variant_new("(s)", owner),
1391 G_DBUS_CALL_FLAGS_NONE,
1392 /* It's not worth to wait for the PID
1393 * longer than half a second when dying */
1394 500,
1395 NULL,
1396 &error);
1397
1398 if (error) {
1399 g_error_free(error);
1400 return false;
1401 }
1402
1403 g_object_unref(proxy_fdn);
1404 g_object_unref(proxy_dbus);
1405 g_free(owner);
1406 if (daemoninfo)
1407 g_variant_unref(daemoninfo);
1408
1409 if (pidinfo) {
1410 g_variant_get(pidinfo, "(u)", pid);
1411 g_variant_unref(pidinfo);
1412 return true;
1413 } else {
1414 return false;
1415 }
1416}
1417
1418
1419static void dbus_cb_name_lost(GDBusConnection *connection,
1420 const gchar *name,
1421 gpointer user_data)
1422{
1423 if (connection) {
1424 char *name;
1425 unsigned int pid;
1426 if (dbus_get_fdn_daemon_info(connection, &pid, &name, NULL)) {
1427 DIE("Cannot acquire '"FDN_NAME"': "
1428 "Name is acquired by '%s' with PID '%d'.", name, pid);
1429 } else {
1430 DIE("Cannot acquire '"FDN_NAME"'.");
1431 }
1432 } else {
1433 const char *env = getenv("DBUS_SESSION_BUS_ADDRESS");
1434 if (STR_EMPTY(env)) {
1435 LOG_W("DBUS_SESSION_BUS_ADDRESS is blank. Is the dbus session configured correctly?");
1436 }
1437
1438 DIE("Cannot connect to DBus.");
1439 }
1440 exit(1);
1441}
1442
1443int dbus_init(void)
1444{
1445 guint owner_id;
1446
1447 introspection_data = g_dbus_node_info_new_for_xml(introspection_xml,
1448 NULL);
1449
1450 owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
1451 FDN_NAME,
1452 G_BUS_NAME_OWNER_FLAGS_NONE,
1453 dbus_cb_bus_acquired,
1454 dbus_cb_name_acquired,
1455 dbus_cb_name_lost,
1456 NULL,
1457 NULL);
1458
1459 return owner_id;
1460}
1461
1462void signal_history_removed(guint id)
1463{
1464 if (!dbus_conn) {
1465 LOG_E("Unable to send signal: No DBus connection.");
1466 }
1467
1468 GVariant *body = g_variant_new("(u)", id);
1469 GError *err = NULL;
1470
1471 g_dbus_connection_emit_signal(dbus_conn,
1472 NULL,
1473 FDN_PATH,
1474 DUNST_IFAC,
1475 "NotificationHistoryRemoved",
1476 body,
1477 &err);
1478
1479 if (err) {
1480 LOG_W("Unable to send NotificationHistoryRemoved signal: %s", err->message);
1481 g_error_free(err);
1482 }
1483}
1484
1485void signal_history_cleared(guint n)
1486{
1487 if (!dbus_conn) {
1488 LOG_E("Unable to send signal: No DBus connection.");
1489 }
1490
1491 GVariant *body = g_variant_new("(u)", n);
1492 GError *err = NULL;
1493
1494 g_dbus_connection_emit_signal(dbus_conn,
1495 NULL,
1496 FDN_PATH,
1497 DUNST_IFAC,
1498 "NotificationHistoryCleared",
1499 body,
1500 &err);
1501
1502 if (err) {
1503 LOG_W("Unable to send NotificationHistoryCleared signal: %s", err->message);
1504 g_error_free(err);
1505 }
1506}
1507
1508void signal_config_reloaded(char **const configs)
1509{
1510 if (!dbus_conn) {
1511 LOG_E("Unable to send signal: No DBus connection.");
1512 }
1513
1514 guint length = g_strv_length(configs);
1515 GVariant *body = g_variant_new("(^as)", length != 0 ? configs : config_paths);
1516 GError *err = NULL;
1517
1518 g_dbus_connection_emit_signal(dbus_conn,
1519 NULL,
1520 FDN_PATH,
1521 DUNST_IFAC,
1522 "ConfigReloaded",
1523 body,
1524 &err);
1525
1526 if (err) {
1527 LOG_W("Unable to send ConfigReloaded signal: %s", err->message);
1528 g_error_free(err);
1529 }
1530}
1531
1532void dbus_teardown(int owner_id)
1533{
1534 g_clear_pointer(&introspection_data, g_dbus_node_info_unref);
1535
1536 g_bus_unown_name(owner_id);
1537 dbus_conn = NULL;
1538}
static bool dbus_get_fdn_daemon_info(GDBusConnection *connection, guint *pid, char **name, char **vendor)
Get the PID of the current process, which acquired FDN DBus Name.
Definition dbus.c:1319
DBus support and implementation of the Desktop Notifications Specification.
reason
The reasons according to the notification spec.
Definition dbus.h:18
@ REASON_UNDEF
Undefined reason not matching the previous ones.
Definition dbus.h:23
@ REASON_SIG
The daemon received a NotificationClose signal.
Definition dbus.h:22
@ REASON_MAX
Maximum value, useful for boundary checking.
Definition dbus.h:24
@ REASON_TIME
The notification timed out.
Definition dbus.h:20
@ REASON_USER
The user closed the notification.
Definition dbus.h:21
@ REASON_MIN
Minimum value, useful for boundary checking.
Definition dbus.h:19
char * color_to_string(struct color c, char buf[10])
Stringify a color struct to a RRGGBBAA string.
Definition draw.c:68
Layout and render notifications.
Main event loop logic.
#define MAX_PAUSE_LEVEL
A structure to describe dunst's global window status.
Definition dunst.h:21
Logging subsystem and helpers.
#define LOG_E
Prefix message with "[<source path>:<function name>:<line number>] ".
Definition log.h:42
void context_menu(void)
Open the context menu that lets the user select urls/actions/etc for all displayed notifications.
Definition menu.c:320
Context menu for actions and helpers.
void notification_do_action(struct notification *n)
If the notification has an action named n->default_action_name or there is only one action and n->def...
void notification_invalidate_actions(struct notification *n)
Remove all client action data from the notification.
void notification_init(struct notification *n)
Sanitize values of notification, apply all matching rules and generate derived fields.
void notification_unref(struct notification *n)
Decrease the reference counter of the notification.
struct notification * notification_create(void)
Create notification struct and initialise all fields with either.
void notification_icon_replace_data(struct notification *n, GVariant *new_icon)
Replace the current notification's icon with the raw icon given in the GVariant.
urgency
Representing the urgencies according to the notification spec.
@ URG_NONE
Urgency not set (invalid)
@ FS_NULL
Invalid value.
int string_parse_color(const char *s, struct color *ret)
Parse a color string.
int queues_notification_insert(struct notification *n)
Insert a fully initialized notification into queues.
Definition queues.c:169
static GQueue * history
history of displayed notifications
Definition queues.c:25
unsigned int queues_length_waiting(void)
Returns the current amount of notifications, which are waiting to get displayed.
Definition queues.c:55
static GQueue * waiting
all new notifications get into here
Definition queues.c:23
GList * queues_get_displayed(void)
Receive the current list of displayed notifications.
Definition queues.c:41
void queues_history_pop(void)
Pushes the latest notification of history to the displayed queue and removes it from history.
Definition queues.c:409
static GQueue * displayed
currently displayed notifications
Definition queues.c:24
guint queues_history_clear(void)
Removes all notifications from history Returns the number of removed notifications.
Definition queues.c:400
unsigned int queues_length_history(void)
Returns the current amount of notifications, which are already in history.
Definition queues.c:67
void queues_notification_close_id(gint id, enum reason reason)
Close the notification that has n->id == id.
Definition queues.c:354
unsigned int queues_length_displayed(void)
Returns the current amount of notifications, which are shown in the UI.
Definition queues.c:61
void queues_history_pop_by_id(gint id)
Pushes the latest notification found in the history buffer identified by it's assigned id.
Definition queues.c:421
void queues_history_push_all(void)
Push all waiting and displayed notifications to history.
Definition queues.c:465
struct notification * queues_get_by_id(gint id)
Get the notification which has the given id in the displayed and waiting queue or NULL if not found.
Definition queues.c:662
GList * queues_get_history(void)
Recieve the list of all notifications encountered.
Definition queues.c:73
bool queues_history_remove_by_id(gint id)
Removes an notification identified by the given id from the history.
Definition queues.c:477
Queues for history, waiting and displayed notifications.
struct rule * get_rule(const char *name)
Check if a rule exists with that name.
Definition rules.c:336
Rules managment and helpers.
Type definitions for settings.
List of all the valid settings and values.
Definition draw.h:48
char * stack_tag
stack notifications by tag
char * icon_path
Full path to the notification's icon.
bool receiving_raw_icon
Still waiting for raw icon to be received.
char * desktop_entry
The desktop entry hint sent via every GApplication.
char * msg
formatted message
char * iconname
plain icon information (may be a path or just a name) as recieved from dbus.
int progress
percentage (-1: undefined)
gint64 timestamp
arrival time (in milliseconds)
char * urls
urllist delimited by '\n'
bool transient
timeout albeit user is idle
char * default_action_name
The name of the action to be invoked on do_action.
gint64 timeout
time to display (in milliseconds)
gint64 dbus_timeout
time to display (in milliseconds) (set by dbus)
Definition rules.h:20
bool is_special_section(const char *s)
Some sections are handled differently in dunst.
Definition utils.c:398
String, time and other various helpers.
#define STR_EMPTY(s)
Test if a string is NULL or empty.
Definition utils.h:20
#define STR_EQ(a, b)
Test if string a and b contain the same chars.
Definition utils.h:26
#define ASSERT_OR_RET(expr, val)
Assert that expr evaluates to true, if not return val.
Definition utils.h:42
#define BOOL2G(x)
Make a gboolean from a boolean value.
Definition utils.h:55