diff --git a/contrib/dunstctl.fishcomp b/contrib/dunstctl.fishcomp index 7b59c9ba6..fc63aece3 100644 --- a/contrib/dunstctl.fishcomp +++ b/contrib/dunstctl.fishcomp @@ -1,17 +1,17 @@ if command -q jq function __fish_dunstctl_info - dunstctl $argv[1] | jq -r ".data[][] | \"\(.$argv[2].data)\t\(.$argv[3].data)\"" + dunstctl (string split ' ' $argv[1]) | jq -r ".data[][] | \"\(.$argv[2].data)\t\(.$argv[3].data)\"" end else function __fish_dunstctl_info - dunstctl $argv[1] | awk -vpattern="\"$argv[2]\" :" '$0 ~ pattern {getline; getline; gsub("\"", "", $3); print $3}' + dunstctl (string split ' ' $argv[1]) | awk -vpattern="\"$argv[2]\" :" '$0 ~ pattern {getline; getline; gsub("\"", "", $3); print $3}' end end function __fish_dunstctl_rule_complete set -l parts (string split ' ' $argv[1]) if test (count $parts[-1]) -eq 0 || test $parts[-2] = rule - __fish_dunstctl_info rules name enabled + __fish_dunstctl_info 'rules --json' name enabled return end # TODO? enable disable might not make sense when the rule is already in the correct state @@ -30,7 +30,7 @@ complete -c dunstctl -f -n __fish_use_subcommand -a history-pop -d 'Pop the late complete -c dunstctl -f -n __fish_use_subcommand -a history-rm -d 'Remove the notification from history with given ID' complete -c dunstctl -f -n __fish_use_subcommand -a is-paused -d 'Check if dunst is running or paused' complete -c dunstctl -f -n __fish_use_subcommand -a set-paused -d 'Set the pause status' -complete -c dunstctl -f -n __fish_use_subcommand -a rules -d 'Displays configured rules (in JSON)' +complete -c dunstctl -f -n __fish_use_subcommand -a rules -d 'Displays configured rules (optionally in JSON)' complete -c dunstctl -f -n __fish_use_subcommand -a rule -d 'Enable or disable a rule by its name' complete -c dunstctl -f -n __fish_use_subcommand -a debug -d 'Print debugging information' complete -c dunstctl -f -n __fish_use_subcommand -a help -d 'Show this help' @@ -41,5 +41,6 @@ complete -c dunstctl -x -n '__fish_seen_subcommand_from count' -a 'displayed his complete -c dunstctl -x -n '__fish_seen_subcommand_from history-pop history-rm' -a '(__fish_dunstctl_info history id appname)' complete -c dunstctl -x -n '__fish_seen_subcommand_from set-paused' -a 'true false toggle' complete -c dunstctl -x -n '__fish_seen_subcommand_from rule' -a '(__fish_dunstctl_rule_complete (commandline -c))' +complete -c dunstctl -x -n '__fish_seen_subcommand_from rules' -a --json # ex: filetype=fish diff --git a/dunstctl b/dunstctl index 410582ba6..a42a7197c 100755 --- a/dunstctl +++ b/dunstctl @@ -34,7 +34,8 @@ show_help() { get-pause-level Get the current pause level set-pause-level level Set the pause level rule name enable|disable|toggle Enable or disable a rule by its name - rules Displays configured rules (in JSON) + rules [--json] Displays configured rules (optionally + in JSON) debug Print debugging information help Show this help EOH @@ -125,8 +126,18 @@ case "${1:-}" in fi ;; "rules") - busctl --user --json=pretty --no-pager call org.freedesktop.Notifications /org/freedesktop/Notifications org.dunstproject.cmd0 RuleList 2>/dev/null \ - || die "Dunst is not running." + case "${2:-}" in + "") + method_call "${DBUS_IFAC_DUNST}.RuleListHumanReadable" | sed '1s/^[[:space:]]\+//' + ;; + --json) + busctl --user --json=pretty --no-pager call "${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_DUNST}" RuleList 2>/dev/null \ + || die "Dunst is not running." + ;; + *) + die "Unknown format \"${2}\". Please use either \"as-json\" or \"human-readable\"." + ;; + esac ;; "rule") [ "${2:-}" ] \ diff --git a/src/dbus.c b/src/dbus.c index cabb64d2a..1dad6673b 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -98,6 +98,9 @@ static const char *introspection_xml = " " " " " " + " " + " " + " " " " " " @@ -191,6 +194,7 @@ DBUS_METHOD(dunst_NotificationRemoveFromHistory); DBUS_METHOD(dunst_NotificationShow); DBUS_METHOD(dunst_RuleEnable); DBUS_METHOD(dunst_RuleList); +DBUS_METHOD(dunst_RuleListHumanReadable); DBUS_METHOD(dunst_Ping); static struct dbus_method methods_dunst[] = { {"ContextMenuCall", dbus_cb_dunst_ContextMenuCall}, @@ -205,6 +209,7 @@ static struct dbus_method methods_dunst[] = { {"Ping", dbus_cb_dunst_Ping}, {"RuleEnable", dbus_cb_dunst_RuleEnable}, {"RuleList", dbus_cb_dunst_RuleList}, + {"RuleListHumanReadable", dbus_cb_dunst_RuleListHumanReadable}, }; void dbus_cb_dunst_methods(GDBusConnection *connection, @@ -634,6 +639,142 @@ static void dbus_cb_dunst_RuleList(GDBusConnection *connection, g_dbus_connection_flush(connection, NULL, NULL, NULL); } +static char *add_rule_entry_bool(char *old, const char *name, int value) +{ + return (value > -1) + ? string_append(old, g_strdup_printf(" %s = %s", name, value ? "yes" : "no"), "\n") + : old; +} + +static char *add_rule_entry_string(char *old, const char *name, const char *value) +{ + return value ? string_append(old, g_strdup_printf(" %s = %s", name, value), "\n") : old; +} + +static void dbus_cb_dunst_RuleListHumanReadable(GDBusConnection *connection, + const gchar *sender, + GVariant *parameters, + GDBusMethodInvocation *invocation) +{ + LOG_D("CMD: Listing all configured rules in human readable format"); + + char *answer = g_strdup(""); + + for (GSList *iter = rules; iter; iter = iter->next) { + struct rule *r = iter->data; + + if (is_special_section(r->name)) { + continue; + } + + answer = string_append(answer, g_strdup_printf("[%s]", r->name), "\n"); + + // filters - order according to rule_matches_notification + answer = string_append(answer, g_strdup_printf(" enabled = %s", r->enabled ? "yes" : "no"), "\n"); + // undocumented filter? + if (r->match_dbus_timeout > -1) + answer = string_append(answer, + g_strdup_printf(" match_dbus_timeout = %ld", r->match_dbus_timeout), + "\n"); + if (r->msg_urgency != URG_NONE) + answer = string_append(answer, + g_strdup_printf(" msg_urgency = %s", + enum_to_string(urgency_enum_data, r->msg_urgency)), + "\n"); + answer = add_rule_entry_bool(answer, "match_transient", r->match_transient); + answer = add_rule_entry_string(answer, "appname", r->appname); + answer = add_rule_entry_string(answer, "desktop_entry", r->desktop_entry); + answer = add_rule_entry_string(answer, "summary", r->summary); + answer = add_rule_entry_string(answer, "body", r->body); + answer = add_rule_entry_string(answer, "category", r->category); + answer = add_rule_entry_string(answer, "stack_tag", r->stack_tag); + + // settings to apply - order according to rule_apply + if (r->timeout != -1) + answer = string_append(answer, g_strdup_printf(" timeout = %ld", r->timeout), "\n"); + if (r->override_dbus_timeout != -1) + answer = string_append(answer, + g_strdup_printf(" override_dbus_timeout = %ld", + r->override_dbus_timeout), + "\n"); + if (r->urgency != URG_NONE) + answer = string_append(answer, + g_strdup_printf(" urgency = %s", + enum_to_string(urgency_enum_data, r->urgency)), + "\n"); + if (r->fullscreen != FS_NULL) + answer = string_append(answer, + g_strdup_printf(" fullscreen = %s", + enum_to_string(fullscreen_enum_data, r->fullscreen)), + "\n"); + answer = add_rule_entry_bool(answer, "history_ignore", r->history_ignore); + answer = add_rule_entry_bool(answer, "set_transient", r->set_transient); + answer = add_rule_entry_bool(answer, "skip_display", r->skip_display); + answer = add_rule_entry_bool(answer, "word_wrap", r->word_wrap); + if (r->ellipsize != -1) + answer = string_append(answer, + g_strdup_printf(" ellipsize = %s", + enum_to_string(ellipsize_enum_data, r->ellipsize)), + "\n"); + if (r->alignment != -1) + answer = string_append(answer, + g_strdup_printf(" alignment = %s", + enum_to_string(horizontal_alignment_enum_data, + r->alignment)), + "\n"); + answer = add_rule_entry_bool(answer, "hide_text", r->hide_text); + if (r->progress_bar_alignment != -1) + answer = string_append(answer, + g_strdup_printf(" progress_bar_alignment = %s", + enum_to_string(horizontal_alignment_enum_data, + r->progress_bar_alignment)), + "\n"); + if (r->min_icon_size != -1) + answer = string_append(answer, + g_strdup_printf(" min_icon_size = %d", r->min_icon_size), + "\n"); + if (r->max_icon_size != -1) + answer = string_append(answer, + g_strdup_printf(" max_icon_size = %d", r->max_icon_size), + "\n"); + answer = add_rule_entry_string(answer, "action_name", r->action_name); + answer = add_rule_entry_string(answer, "set_category", r->set_category); + if (r->markup != MARKUP_NULL) + answer = string_append(answer, + g_strdup_printf(" markup = %s", + enum_to_string(markup_mode_enum_data, + r->markup)), + "\n"); + if (r->icon_position != -1) + answer = string_append(answer, + g_strdup_printf(" icon_position = %s", + enum_to_string(icon_position_enum_data, + r->icon_position)), + "\n"); + answer = add_rule_entry_string(answer, "fg", r->fg); + answer = add_rule_entry_string(answer, "bg", r->bg); + answer = add_rule_entry_string(answer, "highlight", r->highlight); + answer = add_rule_entry_string(answer, "fc", r->fc); + answer = add_rule_entry_string(answer, "format", r->format); + answer = add_rule_entry_string(answer, "default_icon", r->default_icon); + answer = add_rule_entry_string(answer, "new_icon", r->new_icon); + answer = add_rule_entry_string(answer, "script", r->script); + answer = add_rule_entry_string(answer, "set_stack_tag", r->set_stack_tag); + if (r->override_pause_level != -1) + answer = string_append(answer, + g_strdup_printf(" override_pause_level = %d", + r->override_pause_level), + "\n"); + + // add an empty line after each rule to make it easier to read + answer = string_append(answer, "\n", NULL); + } + + g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", answer)); + g_dbus_connection_flush(connection, NULL, NULL, NULL); + g_free(answer); +} + static void dbus_cb_dunst_RuleEnable(GDBusConnection *connection, const gchar *sender, GVariant *parameters,