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,