25#define FDN_PATH "/org/freedesktop/Notifications"
26#define FDN_IFAC "org.freedesktop.Notifications"
27#define FDN_NAME "org.freedesktop.Notifications"
29#define DUNST_PATH "/org/freedesktop/Notifications"
30#define DUNST_IFAC "org.dunstproject.cmd0"
31#define DUNST_NAME "org.freedesktop.Notifications"
33#define PROPERTIES_IFAC "org.freedesktop.DBus.Properties"
35GDBusConnection *dbus_conn = NULL;
37static GDBusNodeInfo *introspection_data = NULL;
39static const char *introspection_xml =
40 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
41 "<node name=\""FDN_PATH
"\">"
42 " <interface name=\""FDN_IFAC
"\">"
44 " <method name=\"GetCapabilities\">"
45 " <arg direction=\"out\" name=\"capabilities\" type=\"as\"/>"
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\"/>"
60 " <method name=\"CloseNotification\">"
61 " <arg direction=\"in\" name=\"id\" type=\"u\"/>"
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\"/>"
71 " <signal name=\"NotificationClosed\">"
72 " <arg name=\"id\" type=\"u\"/>"
73 " <arg name=\"reason\" type=\"u\"/>"
76 " <signal name=\"ActionInvoked\">"
77 " <arg name=\"id\" type=\"u\"/>"
78 " <arg name=\"action_key\" type=\"s\"/>"
81 " <interface name=\""DUNST_IFAC
"\">"
83 " <method name=\"ContextMenuCall\"/>"
84 " <method name=\"NotificationAction\">"
85 " <arg name=\"number\" type=\"u\"/>"
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}\"/>"
93 " <method name=\"NotificationPopHistory\">"
94 " <arg direction=\"in\" name=\"id\" type=\"u\"/>"
96 " <method name=\"NotificationRemoveFromHistory\">"
97 " <arg direction=\"in\" name=\"id\" type=\"u\"/>"
99 " <method name=\"NotificationShow\"/>"
100 " <method name=\"RuleEnable\">"
101 " <arg name=\"name\" type=\"s\"/>"
102 " <arg name=\"state\" type=\"i\"/>"
104 " <method name=\"RuleList\">"
105 " <arg direction=\"out\" name=\"rules\" type=\"aa{sv}\"/>"
107 " <method name=\"ConfigReload\">"
108 " <arg direction=\"in\" name=\"configs\" type=\"as\"/>"
110 " <method name=\"Ping\"/>"
112 " <property name=\"paused\" type=\"b\" access=\"readwrite\">"
113 " <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"true\"/>"
116 " <property name=\"pauseLevel\" type=\"u\" access=\"readwrite\">"
117 " <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"true\"/>"
120 " <property name=\"displayedLength\" type=\"u\" access=\"read\">"
121 " <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"true\"/>"
123 " <property name=\"historyLength\" type=\"u\" access=\"read\">"
124 " <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"true\"/>"
127 " <property name=\"waitingLength\" type=\"u\" access=\"read\">"
128 " <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"true\"/>"
132 " <signal name=\"NotificationHistoryRemoved\">"
133 " <arg name=\"id\" type=\"u\"/>"
136 " <signal name=\"NotificationHistoryCleared\">"
137 " <arg name=\"count\" type=\"u\"/>"
140 " <signal name=\"ConfigReloaded\">"
141 " <arg name=\"configs\" type=\"as\"/>"
147static const char *stack_tag_hints[] = {
149 "private-synchronous",
150 "x-canonical-private-synchronous",
155 const char *method_name;
156 void (*method) (GDBusConnection *connection,
158 GVariant *parameters,
159 GDBusMethodInvocation *invocation);
162#define DBUS_METHOD(name) static void dbus_cb_##name( \
163 GDBusConnection *connection, \
164 const gchar *sender, \
165 GVariant *parameters, \
166 GDBusMethodInvocation *invocation)
168int cmp_methods(
const void *vkey,
const void *velem)
170 const char *key = (
const char*)vkey;
173 return strcmp(key, m->method_name);
177DBUS_METHOD(CloseNotification);
178DBUS_METHOD(GetCapabilities);
179DBUS_METHOD(GetServerInformation);
181 {
"CloseNotification", dbus_cb_CloseNotification},
182 {
"GetCapabilities", dbus_cb_GetCapabilities},
183 {
"GetServerInformation", dbus_cb_GetServerInformation},
184 {
"Notify", dbus_cb_Notify},
187void dbus_cb_fdn_methods(GDBusConnection *connection,
189 const gchar *object_path,
190 const gchar *interface_name,
191 const gchar *method_name,
192 GVariant *parameters,
193 GDBusMethodInvocation *invocation,
198 G_N_ELEMENTS(methods_fdn),
203 m->method(connection, sender, parameters, invocation);
205 LOG_M(
"Unknown method name: '%s' (sender: '%s').",
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);
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},
242void dbus_cb_dunst_methods(GDBusConnection *connection,
244 const gchar *object_path,
245 const gchar *interface_name,
246 const gchar *method_name,
247 GVariant *parameters,
248 GDBusMethodInvocation *invocation,
253 G_N_ELEMENTS(methods_dunst),
258 m->method(connection, sender, parameters, invocation);
260 LOG_M(
"Unknown method name: '%s' (sender: '%s').",
261 method_name, sender);
265static void dbus_cb_dunst_ContextMenuCall(GDBusConnection *connection,
267 GVariant *parameters,
268 GDBusMethodInvocation *invocation)
270 LOG_D(
"CMD: Calling context menu");
273 g_dbus_method_invocation_return_value(invocation, NULL);
274 g_dbus_connection_flush(connection, NULL, NULL, NULL);
277static void dbus_cb_dunst_NotificationAction(GDBusConnection *connection,
279 GVariant *parameters,
280 GDBusMethodInvocation *invocation)
282 guint32 notification_nr = 0;
283 g_variant_get(parameters,
"(u)", ¬ification_nr);
285 LOG_D(
"CMD: Calling action for notification %d", notification_nr);
288 g_dbus_method_invocation_return_error(invocation,
290 G_DBUS_ERROR_INVALID_ARGS,
291 "Couldn't activate action for notification in position %d, %d notifications currently open",
299 LOG_D(
"CMD: Calling action for notification %s", n->summary);
303 g_dbus_method_invocation_return_value(invocation, NULL);
304 g_dbus_connection_flush(connection, NULL, NULL, NULL);
307static void dbus_cb_dunst_NotificationClearHistory(GDBusConnection *connection,
309 GVariant *parameters,
310 GDBusMethodInvocation *invocation)
312 LOG_D(
"CMD: Clearing the history");
316 signal_history_cleared(n);
318 g_dbus_method_invocation_return_value(invocation, NULL);
319 g_dbus_connection_flush(connection, NULL, NULL, NULL);
322static void dbus_cb_dunst_NotificationCloseAll(GDBusConnection *connection,
324 GVariant *parameters,
325 GDBusMethodInvocation *invocation)
327 LOG_D(
"CMD: Pushing all to history");
331 g_dbus_method_invocation_return_value(invocation, NULL);
332 g_dbus_connection_flush(connection, NULL, NULL, NULL);
335static void dbus_cb_dunst_NotificationCloseLast(GDBusConnection *connection,
337 GVariant *parameters,
338 GDBusMethodInvocation *invocation)
340 LOG_D(
"CMD: Closing last notification");
342 if (list && list->data) {
348 g_dbus_method_invocation_return_value(invocation, NULL);
349 g_dbus_connection_flush(connection, NULL, NULL, NULL);
352static void dbus_cb_dunst_NotificationShow(GDBusConnection *connection,
354 GVariant *parameters,
355 GDBusMethodInvocation *invocation)
357 LOG_D(
"CMD: Showing last notification from history");
361 g_dbus_method_invocation_return_value(invocation, NULL);
362 g_dbus_connection_flush(connection, NULL, NULL, NULL);
365static void dbus_cb_dunst_NotificationListHistory(GDBusConnection *connection,
367 GVariant *parameters,
368 GDBusMethodInvocation *invocation)
370 LOG_D(
"CMD: Listing all notifications from history");
372 GVariantBuilder builder;
373 g_variant_builder_init(&builder, G_VARIANT_TYPE(
"aa{sv}"));
380 n = g_list_nth_data(notification_list, i-1);
382 GVariantBuilder n_builder;
383 g_variant_builder_init(&n_builder, G_VARIANT_TYPE(
"a{sv}"));
385 char *body, *
msg, *summary, *appname, *category;
390 body = (n->body == NULL) ?
"" : n->body;
392 summary = (n->summary == NULL) ?
"" : n->summary;
393 appname = (n->appname == NULL) ?
"" : n->appname;
394 category = (n->category == NULL) ?
"" : n->category;
398 urgency = notification_urgency_to_string(n->urgency);
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",
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));
418 g_variant_builder_add(&builder,
"a{sv}", &n_builder);
421 g_dbus_method_invocation_return_value(invocation, g_variant_new(
"(aa{sv})", &builder));
422 g_dbus_connection_flush(connection, NULL, NULL, NULL);
425static void dbus_cb_dunst_NotificationPopHistory(GDBusConnection *connection,
427 GVariant *parameters,
428 GDBusMethodInvocation *invocation)
430 LOG_D(
"CMD: Popping notification from history");
433 g_variant_get(parameters,
"(u)", &
id);
438 g_dbus_method_invocation_return_value(invocation, NULL);
439 g_dbus_connection_flush(connection, NULL, NULL, NULL);
442static void dbus_cb_dunst_NotificationRemoveFromHistory(GDBusConnection *connection,
444 GVariant *parameters,
445 GDBusMethodInvocation *invocation)
447 LOG_D(
"CMD: Removing notification from history");
450 g_variant_get(parameters,
"(u)", &
id);
453 signal_history_removed(
id);
457 g_dbus_method_invocation_return_value(invocation, NULL);
458 g_dbus_connection_flush(connection, NULL, NULL, NULL);
461static const char *enum_to_string(
const struct string_to_enum_def values[],
int enum_value)
463 for (
size_t i = 0; values[i].string != NULL; i++) {
464 if (values[i].enum_value == enum_value) {
465 return values[i].string;
471static void color_entry(
const struct color c, GVariantDict *dict,
const char *field_name) {
474 g_variant_dict_insert(dict, field_name,
"s", buf);
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);
485 char **strv = g_malloc((grad->length + 1) *
sizeof(
char *));
486 for (
size_t i = 0; i < grad->length; i++) {
489 strv[i] = g_strdup(buf);
491 strv[grad->length] = NULL;
493 g_variant_dict_insert(dict, field_name,
"^as", strv);
497static void dbus_cb_dunst_RuleList(GDBusConnection *connection,
499 GVariant *parameters,
500 GDBusMethodInvocation *invocation)
502 LOG_D(
"CMD: Listing all configured rules");
504 GVariantBuilder builder;
505 g_variant_builder_init(&builder, G_VARIANT_TYPE(
"aa{sv}"));
507 for (GSList *iter = rules; iter; iter = iter->next) {
508 struct rule *r = iter->data;
515 g_variant_dict_init(&dict, NULL);
516 g_variant_dict_insert(&dict,
"name",
"s", r->name);
519 g_variant_dict_insert(&dict,
"enabled",
"b",
BOOL2G(r->enabled));
521 if (r->match_dbus_timeout > -1)
522 g_variant_dict_insert(&dict,
"match_dbus_timeout",
"i", r->match_dbus_timeout);
524 g_variant_dict_insert(&dict,
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));
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);
535 g_variant_dict_insert(&dict,
"summary",
"s", r->summary);
537 g_variant_dict_insert(&dict,
"body",
"s", r->body);
539 g_variant_dict_insert(&dict,
"category",
"s", r->category);
541 g_variant_dict_insert(&dict,
"stack_tag",
"s", r->stack_tag);
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);
549 g_variant_dict_insert(&dict,
"urgency",
"s", enum_to_string(urgency_enum_data, r->urgency));
551 g_variant_dict_insert(&dict,
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,
567 enum_to_string(ellipsize_enum_data, r->ellipsize));
568 if (r->alignment != -1)
569 g_variant_dict_insert(&dict,
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",
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);
586 g_variant_dict_insert(&dict,
"action_name",
"s", r->action_name);
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,
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");
601 g_variant_dict_insert(&dict,
"format",
"s", r->format);
603 g_variant_dict_insert(&dict,
"default_icon",
"s", r->default_icon);
605 g_variant_dict_insert(&dict,
"new_icon",
"s", r->new_icon);
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);
613 g_variant_builder_add_value(&builder, g_variant_dict_end(&dict));
616 g_dbus_method_invocation_return_value(invocation, g_variant_new(
"(aa{sv})", &builder));
617 g_dbus_connection_flush(connection, NULL, NULL, NULL);
620static void dbus_cb_dunst_RuleEnable(GDBusConnection *connection,
622 GVariant *parameters,
623 GDBusMethodInvocation *invocation)
629 g_variant_get(parameters,
"(si)", &name, &state);
631 LOG_D(
"CMD: Changing rule \"%s\" enable state to %d", name, state);
633 if (state < 0 || state > 2) {
634 g_dbus_method_invocation_return_error(invocation,
636 G_DBUS_ERROR_INVALID_ARGS,
637 "Couldn't understand state %d. It must be 0, 1 or 2",
643 if (target_rule == NULL) {
644 g_dbus_method_invocation_return_error(invocation,
646 G_DBUS_ERROR_INVALID_ARGS,
647 "There is no rule named \"%s\"",
655 target_rule->enabled =
false;
657 target_rule->enabled =
true;
659 target_rule->enabled = !target_rule->enabled;
661 g_dbus_method_invocation_return_value(invocation, NULL);
662 g_dbus_connection_flush(connection, NULL, NULL, NULL);
665static void dbus_cb_dunst_ConfigReload(GDBusConnection *connection,
667 GVariant *parameters,
668 GDBusMethodInvocation *invocation)
670 gchar **configs = NULL;
671 g_variant_get(parameters,
"(^as)", &configs);
674 signal_config_reloaded(configs);
676 g_dbus_method_invocation_return_value(invocation, NULL);
677 g_dbus_connection_flush(connection, NULL, NULL, NULL);
682static void dbus_cb_dunst_Ping(GDBusConnection *connection,
684 GVariant *parameters,
685 GDBusMethodInvocation *invocation)
687 g_dbus_method_invocation_return_value(invocation, NULL);
688 g_dbus_connection_flush(connection, NULL, NULL, NULL);
692static void dbus_cb_GetCapabilities(
693 GDBusConnection *connection,
695 GVariant *parameters,
696 GDBusMethodInvocation *invocation)
698 GVariantBuilder *builder;
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");
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]);
713 if (global_rule && global_rule->markup != MARKUP_NO)
714 g_variant_builder_add(builder,
"s",
"body-markup");
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);
720 g_dbus_connection_flush(connection, NULL, NULL, NULL);
723static struct notification *dbus_message_to_notification(
const gchar *sender, GVariant *parameters)
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);
734 n->dbus_client = g_strdup(sender);
735 n->dbus_valid =
true;
742 g_variant_iter_init(&i, parameters);
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);
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]));
761 LOG_W(
"Odd length in actions array. Ignoring element: %s", actions[num]);
766 GVariant *dict_value;
767 GVariant *icon_value = NULL;
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);
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);
780 if ((dict_value = g_variant_lookup_value(hints,
"desktop-entry", G_VARIANT_TYPE_STRING))) {
782 g_variant_unref(dict_value);
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);
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);
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);
824 dict_value = g_variant_lookup_value(hints,
"image-path", G_VARIANT_TYPE_STRING);
826 dict_value = g_variant_lookup_value(hints,
"image_path", G_VARIANT_TYPE_STRING);
830 n->
iconname = g_variant_dup_string(dict_value, NULL);
831 g_variant_unref(dict_value);
838 dict_value = g_variant_lookup_value(hints,
"image-data", G_VARIANT_TYPE(
"(iiibiiay)"));
840 dict_value = g_variant_lookup_value(hints,
"image_data", G_VARIANT_TYPE(
"(iiibiiay)"));
842 dict_value = g_variant_lookup_value(hints,
"icon_data", G_VARIANT_TYPE(
"(iiibiiay)"));
849 icon_value = dict_value;
865 g_variant_unref(icon_value);
869 if ((dict_value = g_variant_lookup_value(hints,
"fgcolor", G_VARIANT_TYPE_STRING))) {
872 notification_keep_original(n);
873 if (!COLOR_VALID(n->original->fg)) n->original->fg = n->colors.fg;
876 g_variant_unref(dict_value);
879 if ((dict_value = g_variant_lookup_value(hints,
"bgcolor", G_VARIANT_TYPE_STRING))) {
882 notification_keep_original(n);
883 if (!COLOR_VALID(n->original->bg)) n->original->bg = n->colors.bg;
886 g_variant_unref(dict_value);
889 if ((dict_value = g_variant_lookup_value(hints,
"frcolor", G_VARIANT_TYPE_STRING))) {
892 notification_keep_original(n);
893 if (!COLOR_VALID(n->original->fc)) n->original->fc = n->colors.frame;
896 g_variant_unref(dict_value);
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);
904 for (
size_t i = 0; i <
length; i++) {
911 gradient_pattern(grad);
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);
919 g_variant_unref(dict_value);
920 }
else if ((dict_value = g_variant_lookup_value(hints,
"hlcolor", G_VARIANT_TYPE_STRING))) {
923 struct gradient *grad = gradient_alloc(1);
925 gradient_pattern(grad);
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);
932 g_variant_unref(dict_value);
935 g_variant_unref(hints);
936 g_variant_type_free(required_type);
942void signal_length_propertieschanged(
void)
944 static unsigned int last_displayed = 0;
945 static unsigned int last_history = 0;
946 static unsigned int last_waiting = 0;
951 GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE_VARDICT);
952 GVariantBuilder *invalidated_builder = g_variant_builder_new(G_VARIANT_TYPE_STRING_ARRAY);
957 bool properties_changed =
false;
960 g_variant_builder_add(builder,
962 "displayedLength", g_variant_new_uint32(
displayed));
964 properties_changed =
true;
968 g_variant_builder_add(builder,
970 "historyLength", g_variant_new_uint32(
history));
972 properties_changed =
true;
975 g_variant_builder_add(builder,
977 "waitingLength", g_variant_new_uint32(
waiting));
979 properties_changed =
true;
982 if (properties_changed) {
983 GVariant *body = g_variant_new(
"(sa{sv}as)",
986 invalidated_builder);
990 g_dbus_connection_emit_signal(dbus_conn,
999 LOG_W(
"Unable to emit signal: %s", err->message);
1004 g_clear_pointer(&builder, g_variant_builder_unref);
1005 g_clear_pointer(&invalidated_builder, g_variant_builder_unref);
1008void signal_paused_propertieschanged(
void)
1015 GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE_VARDICT);
1016 GVariantBuilder *invalidated_builder = g_variant_builder_new(G_VARIANT_TYPE_STRING_ARRAY);
1018 g_variant_builder_add(builder,
1020 "paused", g_variant_new_boolean(status.pause_level != 0));
1021 g_variant_builder_add(builder,
1023 "pauseLevel", g_variant_new_uint32(status.pause_level));
1025 g_dbus_connection_emit_signal(dbus_conn,
1029 "PropertiesChanged",
1030 g_variant_new(
"(sa{sv}as)",
1033 invalidated_builder),
1036 g_clear_pointer(&builder, g_variant_builder_unref);
1037 g_clear_pointer(&invalidated_builder, g_variant_builder_unref);
1041static void dbus_cb_Notify(
1042 GDBusConnection *connection,
1043 const gchar *sender,
1044 GVariant *parameters,
1045 GDBusMethodInvocation *invocation)
1047 struct notification *n = dbus_message_to_notification(sender, parameters);
1049 LOG_W(
"A notification failed to decode.");
1050 g_dbus_method_invocation_return_dbus_error(
1053 "Cannot decode notification!");
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);
1072static void dbus_cb_CloseNotification(
1073 GDBusConnection *connection,
1074 const gchar *sender,
1075 GVariant *parameters,
1076 GDBusMethodInvocation *invocation)
1079 g_variant_get(parameters,
"(u)", &
id);
1081 LOG_D(
"Ignoring CloseNotification message");
1092 g_dbus_method_invocation_return_value(invocation, NULL);
1093 g_dbus_connection_flush(connection, NULL, NULL, NULL);
1096static void dbus_cb_GetServerInformation(
1097 GDBusConnection *connection,
1098 const gchar *sender,
1099 GVariant *parameters,
1100 GDBusMethodInvocation *invocation)
1102 GVariant *answer = g_variant_new(
"(ssss)",
"dunst",
"knopwob", VERSION,
"1.2");
1104 g_dbus_method_invocation_return_value(invocation, answer);
1105 g_dbus_connection_flush(connection, NULL, NULL, NULL);
1110 if (!n->dbus_valid) {
1115 LOG_W(
"Closing notification with reason '%d' not supported. "
1121 LOG_E(
"Unable to close notification: No DBus connection.");
1124 GVariant *body = g_variant_new(
"(uu)", n->id,
reason);
1127 g_dbus_connection_emit_signal(dbus_conn,
1131 "NotificationClosed",
1137 n->dbus_valid =
false;
1140 LOG_W(
"Unable to close notification: %s", err->message);
1143 char* reason_string;
1146 reason_string=
"time";
1149 reason_string=
"user";
1152 reason_string=
"signal";
1155 reason_string=
"undefined";
1158 reason_string=
"unknown";
1161 LOG_D(
"Queues: Closing notification for reason: %s", reason_string);
1165void signal_action_invoked(
const struct notification *n,
const char *identifier)
1167 if (!n->dbus_valid) {
1168 LOG_W(
"Invoking action '%s' not supported. "
1169 "Notification already closed.", identifier);
1173 GVariant *body = g_variant_new(
"(us)", n->id, identifier);
1176 g_dbus_connection_emit_signal(dbus_conn,
1185 LOG_W(
"Unable to invoke action: %s", err->message);
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,
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")) {
1207 }
else if (
STR_EQ(property_name,
"historyLength")) {
1209 return g_variant_new_uint32(
history);
1210 }
else if (
STR_EQ(property_name,
"waitingLength")) {
1212 return g_variant_new_uint32(
waiting);
1214 LOG_W(
"Unknown property!\n");
1215 *error = g_error_new(G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY,
"Unknown property");
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,
1229 int targetPauseLevel = -1;
1230 if (
STR_EQ(property_name,
"paused")) {
1231 if (g_variant_get_boolean(value)) {
1234 targetPauseLevel = 0;
1236 }
else if STR_EQ(property_name,
"pauseLevel") {
1237 targetPauseLevel = g_variant_get_uint32(value);
1243 if (targetPauseLevel >= 0) {
1244 dunst_status_int(S_PAUSE_LEVEL, targetPauseLevel);
1247 signal_paused_propertieschanged();
1252 *error = g_error_new(G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY,
"Unknown property");
1258static const GDBusInterfaceVTable interface_vtable_fdn = {
1259 dbus_cb_fdn_methods,
1262static const GDBusInterfaceVTable interface_vtable_dunst = {
1263 dbus_cb_dunst_methods,
1264 dbus_cb_dunst_Properties_Get,
1265 dbus_cb_dunst_Properties_Set,
1268static void dbus_cb_bus_acquired(GDBusConnection *connection,
1273 if(!g_dbus_connection_register_object(
1276 introspection_data->interfaces[0],
1277 &interface_vtable_fdn,
1281 DIE(
"Unable to register dbus connection interface '%s': %s", introspection_data->interfaces[0]->name, err->message);
1284 if(!g_dbus_connection_register_object(
1287 introspection_data->interfaces[1],
1288 &interface_vtable_dunst,
1292 DIE(
"Unable to register dbus connection interface '%s': %s", introspection_data->interfaces[1]->name, err->message);
1296static void dbus_cb_name_acquired(GDBusConnection *connection,
1301 if (
STR_EQ(name, FDN_NAME))
1302 dbus_conn = connection;
1328 GError *error = NULL;
1330 GDBusProxy *proxy_fdn;
1331 GDBusProxy *proxy_dbus;
1333 proxy_fdn = g_dbus_proxy_new_sync(
1336 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
1345 g_error_free(error);
1349 GVariant *daemoninfo = NULL;
1350 if (name || vendor) {
1351 daemoninfo = g_dbus_proxy_call_sync(
1353 FDN_IFAC
".GetServerInformation",
1355 G_DBUS_CALL_FLAGS_NONE,
1365 g_clear_pointer(&error, g_error_free);
1367 g_variant_get(daemoninfo,
"(ssss)", name, vendor, NULL, NULL);
1370 owner = g_dbus_proxy_get_name_owner(proxy_fdn);
1372 proxy_dbus = g_dbus_proxy_new_sync(
1374 G_DBUS_PROXY_FLAGS_NONE,
1376 "org.freedesktop.DBus",
1377 "/org/freedesktop/DBus",
1378 "org.freedesktop.DBus",
1383 g_error_free(error);
1387 GVariant *pidinfo = g_dbus_proxy_call_sync(
1389 "org.freedesktop.DBus.GetConnectionUnixProcessID",
1390 g_variant_new(
"(s)", owner),
1391 G_DBUS_CALL_FLAGS_NONE,
1399 g_error_free(error);
1403 g_object_unref(proxy_fdn);
1404 g_object_unref(proxy_dbus);
1407 g_variant_unref(daemoninfo);
1410 g_variant_get(pidinfo,
"(u)", pid);
1411 g_variant_unref(pidinfo);
1419static void dbus_cb_name_lost(GDBusConnection *connection,
1427 DIE(
"Cannot acquire '"FDN_NAME
"': "
1428 "Name is acquired by '%s' with PID '%d'.", name, pid);
1430 DIE(
"Cannot acquire '"FDN_NAME
"'.");
1433 const char *env = getenv(
"DBUS_SESSION_BUS_ADDRESS");
1435 LOG_W(
"DBUS_SESSION_BUS_ADDRESS is blank. Is the dbus session configured correctly?");
1438 DIE(
"Cannot connect to DBus.");
1447 introspection_data = g_dbus_node_info_new_for_xml(introspection_xml,
1450 owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
1452 G_BUS_NAME_OWNER_FLAGS_NONE,
1453 dbus_cb_bus_acquired,
1454 dbus_cb_name_acquired,
1462void signal_history_removed(guint
id)
1465 LOG_E(
"Unable to send signal: No DBus connection.");
1468 GVariant *body = g_variant_new(
"(u)",
id);
1471 g_dbus_connection_emit_signal(dbus_conn,
1475 "NotificationHistoryRemoved",
1480 LOG_W(
"Unable to send NotificationHistoryRemoved signal: %s", err->message);
1485void signal_history_cleared(guint n)
1488 LOG_E(
"Unable to send signal: No DBus connection.");
1491 GVariant *body = g_variant_new(
"(u)", n);
1494 g_dbus_connection_emit_signal(dbus_conn,
1498 "NotificationHistoryCleared",
1503 LOG_W(
"Unable to send NotificationHistoryCleared signal: %s", err->message);
1508void signal_config_reloaded(
char **
const configs)
1511 LOG_E(
"Unable to send signal: No DBus connection.");
1514 guint
length = g_strv_length(configs);
1515 GVariant *body = g_variant_new(
"(^as)",
length != 0 ? configs : config_paths);
1518 g_dbus_connection_emit_signal(dbus_conn,
1527 LOG_W(
"Unable to send ConfigReloaded signal: %s", err->message);
1532void dbus_teardown(
int owner_id)
1534 g_clear_pointer(&introspection_data, g_dbus_node_info_unref);
1536 g_bus_unown_name(owner_id);
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.
DBus support and implementation of the Desktop Notifications Specification.
reason
The reasons according to the notification spec.
@ REASON_UNDEF
Undefined reason not matching the previous ones.
@ REASON_SIG
The daemon received a NotificationClose signal.
@ REASON_MAX
Maximum value, useful for boundary checking.
@ REASON_TIME
The notification timed out.
@ REASON_USER
The user closed the notification.
@ REASON_MIN
Minimum value, useful for boundary checking.
char * color_to_string(struct color c, char buf[10])
Stringify a color struct to a RRGGBBAA string.
Layout and render notifications.
#define MAX_PAUSE_LEVEL
A structure to describe dunst's global window status.
Logging subsystem and helpers.
#define LOG_E
Prefix message with "[<source path>:<function name>:<line number>] ".
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)
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.
static GQueue * history
history of displayed notifications
unsigned int queues_length_waiting(void)
Returns the current amount of notifications, which are waiting to get displayed.
static GQueue * waiting
all new notifications get into here
GList * queues_get_displayed(void)
Receive the current list of displayed notifications.
void queues_history_pop(void)
Pushes the latest notification of history to the displayed queue and removes it from history.
static GQueue * displayed
currently displayed notifications
guint queues_history_clear(void)
Removes all notifications from history Returns the number of removed notifications.
unsigned int queues_length_history(void)
Returns the current amount of notifications, which are already in history.
void queues_notification_close_id(gint id, enum reason reason)
Close the notification that has n->id == id.
unsigned int queues_length_displayed(void)
Returns the current amount of notifications, which are shown in the UI.
void queues_history_pop_by_id(gint id)
Pushes the latest notification found in the history buffer identified by it's assigned id.
void queues_history_push_all(void)
Push all waiting and displayed notifications to history.
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.
GList * queues_get_history(void)
Recieve the list of all notifications encountered.
bool queues_history_remove_by_id(gint id)
Removes an notification identified by the given id from the history.
Queues for history, waiting and displayed notifications.
struct rule * get_rule(const char *name)
Check if a rule exists with that name.
Rules managment and helpers.
Type definitions for settings.
List of all the valid settings and values.
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)
bool is_special_section(const char *s)
Some sections are handled differently in dunst.
String, time and other various helpers.
#define STR_EMPTY(s)
Test if a string is NULL or empty.
#define STR_EQ(a, b)
Test if string a and b contain the same chars.
#define ASSERT_OR_RET(expr, val)
Assert that expr evaluates to true, if not return val.
#define BOOL2G(x)
Make a gboolean from a boolean value.