From 55f4971a929eb939614bd36fa60f2cb3c30f2ba1 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Tue, 18 Sep 2018 09:19:37 +0200 Subject: [PATCH 01/21] Add DBus interface to control dunst --- src/dbus.c | 251 +++++++++++++++++++++++++++++++++++++++++++++------ src/dbus.h | 8 ++ src/dunst.c | 6 ++ test/dbus.c | 6 ++ test/dunst.c | 9 ++ 5 files changed, 252 insertions(+), 28 deletions(-) diff --git a/src/dbus.c b/src/dbus.c index 5bb6210..0f8f93a 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -8,6 +8,7 @@ #include "dunst.h" #include "log.h" +#include "menu.h" #include "notification.h" #include "queues.h" #include "settings.h" @@ -17,6 +18,12 @@ #define FDN_IFAC "org.freedesktop.Notifications" #define FDN_NAME "org.freedesktop.Notifications" +#define DUNST_PATH "/org/freedesktop/Notifications" +#define DUNST_IFAC "org.dunstproject.cmd0" +#define DUNST_NAME "org.freedesktop.Notifications" + +#define PROPERTIES_IFAC "org.freedesktop.DBus.Properties" + GDBusConnection *dbus_conn; static GDBusNodeInfo *introspection_data = NULL; @@ -62,7 +69,19 @@ static const char *introspection_xml = " " " " " " - " " + " " + " " + + " " + " " + " " + " " + + " " + " " + " " + + " " ""; static const char *stack_tag_hints[] = { @@ -80,6 +99,7 @@ struct dbus_method { GDBusMethodInvocation *invocation); }; +// TODO: call it interface methods #define DBUS_METHOD(name) static void dbus_cb_##name( \ GDBusConnection *connection, \ const gchar *sender, \ @@ -98,7 +118,6 @@ DBUS_METHOD(Notify); DBUS_METHOD(CloseNotification); DBUS_METHOD(GetCapabilities); DBUS_METHOD(GetServerInformation); - static struct dbus_method methods_fdn[] = { {"CloseNotification", dbus_cb_CloseNotification}, {"GetCapabilities", dbus_cb_GetCapabilities}, @@ -106,7 +125,7 @@ static struct dbus_method methods_fdn[] = { {"Notify", dbus_cb_Notify}, }; -void handle_method_call(GDBusConnection *connection, +void dbus_cb_fdn_methods(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, @@ -115,12 +134,12 @@ void handle_method_call(GDBusConnection *connection, GDBusMethodInvocation *invocation, gpointer user_data) { - struct dbus_method *m = bsearch( - method_name, - &methods_fdn, - G_N_ELEMENTS(methods_fdn), - sizeof(struct dbus_method), - cmp_methods); + + struct dbus_method *m = bsearch(method_name, + methods_fdn, + G_N_ELEMENTS(methods_fdn), + sizeof(struct dbus_method), + cmp_methods); if (m) { m->method(connection, sender, parameters, invocation); @@ -131,6 +150,98 @@ void handle_method_call(GDBusConnection *connection, } } +DBUS_METHOD(dunst_ContextMenuCall); +DBUS_METHOD(dunst_NotificationCloseAll); +DBUS_METHOD(dunst_NotificationCloseLast); +DBUS_METHOD(dunst_NotificationShow); +static struct dbus_method methods_dunst[] = { + {"ContextMenuCall", dbus_cb_dunst_ContextMenuCall}, + {"NotificationCloseAll", dbus_cb_dunst_NotificationCloseAll}, + {"NotificationCloseLast", dbus_cb_dunst_NotificationCloseLast}, + {"NotificationShow", dbus_cb_dunst_NotificationShow}, +}; + +void dbus_cb_dunst_methods(GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + + struct dbus_method *m = bsearch(method_name, + methods_dunst, + G_N_ELEMENTS(methods_dunst), + sizeof(struct dbus_method), + cmp_methods); + + if (m) { + m->method(connection, sender, parameters, invocation); + } else { + LOG_M("Unknown method name: '%s' (sender: '%s').", + method_name, + sender); + } +} + +static void dbus_cb_dunst_ContextMenuCall(GDBusConnection *connection, + const gchar *sender, + GVariant *parameters, + GDBusMethodInvocation *invocation) +{ + LOG_D("CMD: Calling context menu"); + context_menu(); + + g_dbus_method_invocation_return_value(invocation, NULL); + g_dbus_connection_flush(connection, NULL, NULL, NULL); +} + +static void dbus_cb_dunst_NotificationCloseAll(GDBusConnection *connection, + const gchar *sender, + GVariant *parameters, + GDBusMethodInvocation *invocation) +{ + LOG_D("CMD: Pushing all to history"); + queues_history_push_all(); + wake_up(); + + g_dbus_method_invocation_return_value(invocation, NULL); + g_dbus_connection_flush(connection, NULL, NULL, NULL); +} + +static void dbus_cb_dunst_NotificationCloseLast(GDBusConnection *connection, + const gchar *sender, + GVariant *parameters, + GDBusMethodInvocation *invocation) +{ + LOG_D("CMD: Closing last notification"); + const GList *list = queues_get_displayed(); + if (list && list->data) { + struct notification *n = list->data; + queues_notification_close_id(n->id, REASON_USER); + wake_up(); + } + + g_dbus_method_invocation_return_value(invocation, NULL); + g_dbus_connection_flush(connection, NULL, NULL, NULL); +} + +static void dbus_cb_dunst_NotificationShow(GDBusConnection *connection, + const gchar *sender, + GVariant *parameters, + GDBusMethodInvocation *invocation) +{ + LOG_D("CMD: Showing last notification from history"); + queues_history_pop(); + wake_up(); + + g_dbus_method_invocation_return_value(invocation, NULL); + g_dbus_connection_flush(connection, NULL, NULL, NULL); +} + + static void dbus_cb_GetCapabilities( GDBusConnection *connection, const gchar *sender, @@ -347,11 +458,9 @@ static void dbus_cb_GetServerInformation( GVariant *parameters, GDBusMethodInvocation *invocation) { - GVariant *value; - - value = g_variant_new("(ssss)", "dunst", "knopwob", VERSION, "1.2"); - g_dbus_method_invocation_return_value(invocation, value); + GVariant *answer = g_variant_new("(ssss)", "dunst", "knopwob", VERSION, "1.2"); + g_dbus_method_invocation_return_value(invocation, answer); g_dbus_connection_flush(connection, NULL, NULL, NULL); } @@ -420,28 +529,112 @@ void signal_action_invoked(const struct notification *n, const char *identifier) } } -static const GDBusInterfaceVTable interface_vtable = { - handle_method_call +//FIXME: Is this necessary or alternative question: Is this implemented correctl? +// This had been an old relict from the manual times, when I haven't used the +// interface vtable of GLib +void dbus_signal_status_changed(struct dunst_status status) +{ + // We might have not a working connection yet, so just ignore it. + if (!dbus_conn) + return; + + //TODO: I'm pretty sure this is the right format string, but I don't know how to verify it + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE("(sa{sv}as)")); + g_variant_builder_add(&builder, "s", DUNST_IFAC); + + g_variant_builder_open(&builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add(&builder, "{sv}", "running", g_variant_new_boolean(status.running)); + g_variant_builder_close(&builder); + + g_variant_builder_open(&builder, G_VARIANT_TYPE ("as")); + g_variant_builder_add(&builder, "s", "unrelated"); + g_variant_builder_close(&builder); + + GError *err = NULL; + g_dbus_connection_emit_signal(dbus_conn, + NULL, + DUNST_PATH, + PROPERTIES_IFAC, + "PropertiesChanged", + g_variant_builder_end(&builder), + &err); + + if (err) { + LOG_W("Unable send signal 'PropertiesChanged': %s", err->message); + g_error_free(err); + } +} + +GVariant *dbus_cb_dunst_Properties_Get(GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + struct dunst_status status = dunst_status_get(); + + if (STR_EQ(property_name, "running")) + return g_variant_new_boolean(status.running); + else + //TODO: is NULL as return value allowed? + return NULL; +} + +gboolean dbus_cb_dunst_Properties_Set(GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GVariant *value, + GError **error, + gpointer user_data) +{ + if (STR_EQ(property_name, "running")) + dunst_status(S_RUNNING, g_variant_get_boolean(value)); + + return true; +} + + +static const GDBusInterfaceVTable interface_vtable_fdn = { + dbus_cb_fdn_methods +}; + +static const GDBusInterfaceVTable interface_vtable_dunst = { + dbus_cb_dunst_methods, + dbus_cb_dunst_Properties_Get, + dbus_cb_dunst_Properties_Set, }; static void dbus_cb_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data) { - guint registration_id; - + // TODO: deduplicate the code GError *err = NULL; + if(!g_dbus_connection_register_object( + connection, + FDN_PATH, + introspection_data->interfaces[0], + &interface_vtable_fdn, + NULL, + NULL, + &err)) { + DIE("Unable to register dbus connection interface '%s': %s", introspection_data->interfaces[0]->name, err->message); + } - registration_id = g_dbus_connection_register_object(connection, - FDN_PATH, - introspection_data->interfaces[0], - &interface_vtable, - NULL, - NULL, - &err); - - if (registration_id == 0) { - DIE("Unable to register dbus connection: %s", err->message); + if(!g_dbus_connection_register_object( + connection, + FDN_PATH, + introspection_data->interfaces[1], + &interface_vtable_dunst, + NULL, + NULL, + &err)) { + DIE("Unable to register dbus connection interface '%s': %s", introspection_data->interfaces[1]->name, err->message); } } @@ -449,7 +642,9 @@ static void dbus_cb_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data) { - dbus_conn = connection; + // If we're not able to get org.fd.N bus, we've still got a problem + if (STR_EQ(name, FDN_NAME)) + dbus_conn = connection; } /** diff --git a/src/dbus.h b/src/dbus.h index 4a72479..9301055 100644 --- a/src/dbus.h +++ b/src/dbus.h @@ -3,6 +3,7 @@ #ifndef DUNST_DBUS_H #define DUNST_DBUS_H +#include "dunst.h" #include "notification.h" /// The reasons according to the notification spec @@ -20,5 +21,12 @@ void dbus_teardown(int id); void signal_notification_closed(struct notification *n, enum reason reason); void signal_action_invoked(const struct notification *n, const char *identifier); +/** + * Signal a changed status via DBus + * + * @param status The current status of dunst + * */ +void dbus_signal_status_changed(struct dunst_status status); + #endif /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/src/dunst.c b/src/dunst.c index 8ba2d15..85b900d 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -31,20 +31,26 @@ static struct dunst_status status; void dunst_status(const enum dunst_status_field field, bool value) { + bool changed; switch (field) { case S_FULLSCREEN: + changed = status.fullscreen != value; status.fullscreen = value; break; case S_IDLE: + changed = status.idle != value; status.idle = value; break; case S_RUNNING: + changed = status.running != value; status.running = value; break; default: LOG_E("Invalid %s enum value in %s:%d", "dunst_status", __FILE__, __LINE__); break; } + if (changed) + dbus_signal_status_changed(status); } /* see dunst.h */ diff --git a/test/dbus.c b/test/dbus.c index 659731a..5db9a7b 100644 --- a/test/dbus.c +++ b/test/dbus.c @@ -799,6 +799,12 @@ TEST assert_methodlists_sorted(void) methods_fdn[i+1].method_name)); } + for (size_t i = 0; i+1 < G_N_ELEMENTS(methods_dunst); i++) { + ASSERT(0 > strcmp( + methods_dunst[i].method_name, + methods_dunst[i+1].method_name)); + } + PASS(); } diff --git a/test/dunst.c b/test/dunst.c index 827f98d..476c518 100644 --- a/test/dunst.c +++ b/test/dunst.c @@ -1,6 +1,15 @@ +#define dbus_signal_status_changed(status) signal_sent_stub(status) #include "../src/dunst.c" #include "greatest.h" +static bool signal_sent = false; + +void signal_sent_stub(struct dunst_status status) +{ + signal_sent = true; + return; +} + TEST test_dunst_status(void) { status = (struct dunst_status) {false, false, false}; From 305ab6c4e636d4c24824faeb6b0b5c73db212b0f Mon Sep 17 00:00:00 2001 From: Felix Buehler Date: Sat, 10 Aug 2019 19:18:43 +0200 Subject: [PATCH 02/21] Add dunstctl shell script --- dunstctl | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100755 dunstctl diff --git a/dunstctl b/dunstctl new file mode 100755 index 0000000..c5f706c --- /dev/null +++ b/dunstctl @@ -0,0 +1,89 @@ +#!/bin/sh -eu + +dbus_name="org.freedesktop.Notifications" +dbus_path="/org/freedesktop/Notifications" +dbus_ifac="org.dunstproject.cmd0" +dbus_property="org.freedesktop.DBus.Properties" + +usage() { + echo "Usage: dunstctl [options...]" + echo "" + echo "Commands:" + echo " close [-a|--all] Close the last or all notifications" + echo " context Open context menu of latest notification" + echo " display Redisplay closed notifications" + echo " status Reload the configuration file" + echo " help Show this help" +} + +method_call() { + dbus-send --print-reply=literal --dest=$dbus_name $dbus_path $dbus_ifac."$*" +} + +property_get() { + result=$(dbus-send --print-reply=literal --dest=$dbus_name $dbus_path $dbus_property.Get string:$dbus_ifac string:"$*") + if test "${result#*'true'}" != "$result"; then + echo "dunst notifications are enabled" + else + echo "dunst notifications are disabled" + fi +} + +property_set() { + dbus-send --print-reply=literal --dest=$dbus_name $dbus_path $dbus_property.Set string:$dbus_ifac string:"$@" +} + +if [ $# -eq 0 ] || [ $# -gt 3 ]; then + usage + exit 1 +fi + +case "$1" in +"close") + [ $# -lt 2 ] && param="" || param="$2" + case "$param" in + "-a"|"--all") + method_call NotificationCloseAll + ;; + "") + method_call NotificationCloseLast + ;; + *) + echo "dunstctl: unrecognized option '$2'" + exit 1 + ;; + esac + ;; +"context") + method_call ContextMenuCall + ;; +"display") + method_call NotificationShow + ;; +"status") + [ $# -lt 2 ] && action="" || action="$2" + case "$action" in + "disable") + property_set running variant:boolean:false + ;; + "enable") + property_set running variant:boolean:true + ;; + ""|"get") + property_get running + ;; + *) + echo "dunstctl status: unrecognized option '$2'" + exit 1 + ;; + esac + ;; +"help"|"--help"|"-h") + usage + ;; +*) + echo "dunstctl: unrecognized command '$1'" + exit 1 + ;; +esac + From 72d68fcec44ee13d86ac81208a152f211f47f336 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Sun, 11 Aug 2019 15:13:16 +0200 Subject: [PATCH 03/21] Overhaul dunstctl --- dunstctl | 130 +++++++++++++++++++++++++------------------------------ 1 file changed, 58 insertions(+), 72 deletions(-) diff --git a/dunstctl b/dunstctl index c5f706c..2e1f7db 100755 --- a/dunstctl +++ b/dunstctl @@ -1,89 +1,75 @@ -#!/bin/sh -eu +#!/bin/sh -dbus_name="org.freedesktop.Notifications" -dbus_path="/org/freedesktop/Notifications" -dbus_ifac="org.dunstproject.cmd0" -dbus_property="org.freedesktop.DBus.Properties" +set -eu -usage() { - echo "Usage: dunstctl [options...]" - echo "" - echo "Commands:" - echo " close [-a|--all] Close the last or all notifications" - echo " context Open context menu of latest notification" - echo " display Redisplay closed notifications" - echo " status Reload the configuration file" - echo " help Show this help" +DBUS_NAME="org.freedesktop.Notifications" +DBUS_PATH="/org/freedesktop/Notifications" +DBUS_IFAC_DUNST="org.dunstproject.cmd0" +DBUS_IFAC_PROP="org.freedesktop.DBus.Properties" +DBUS_IFAC_FDN="org.freedesktop.Notifications" + +function die(){ printf "%s\n" "${1}" >&2; exit 1; } + +function show_help() { + cat <<-EOH + Usage: dunstctl [parameters]" + Commands: + close Close the last notification + close-all Close the all notifications + context Open context menu of latest notification + history-pop Pop one notification from history + status Check if + status-set [true|false] Set the status + help Show this help + EOH } -method_call() { - dbus-send --print-reply=literal --dest=$dbus_name $dbus_path $dbus_ifac."$*" +function method_call() { + dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${1}" } -property_get() { - result=$(dbus-send --print-reply=literal --dest=$dbus_name $dbus_path $dbus_property.Get string:$dbus_ifac string:"$*") - if test "${result#*'true'}" != "$result"; then - echo "dunst notifications are enabled" - else - echo "dunst notifications are disabled" - fi +function property_get() { + dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_PROP}.Get" "string:${DBUS_IFAC_DUNST}" "string:${1}" } -property_set() { - dbus-send --print-reply=literal --dest=$dbus_name $dbus_path $dbus_property.Set string:$dbus_ifac string:"$@" +function property_set() { + dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_PROP}.Set" "string:${DBUS_IFAC_DUNST}" "string:${1}" "${2}" } -if [ $# -eq 0 ] || [ $# -gt 3 ]; then - usage - exit 1 -fi +command -v dbus-send >/dev/null 2>/dev/null || \ + die "Command dbus-send not found" -case "$1" in -"close") - [ $# -lt 2 ] && param="" || param="$2" - case "$param" in - "-a"|"--all") - method_call NotificationCloseAll + +case "${1:-}" in + "close") + method_call "${DBUS_IFAC_DUNST}.NotificationCloseLast" >/dev/null + ;; + "close-all") + method_call "${DBUS_IFAC_DUNST}.NotificationCloseAll" >/dev/null + ;; + "context") + method_call "${DBUS_IFAC_DUNST}.ContextMenuCall" >/dev/null + ;; + "history-pop") + method_call "${DBUS_IFAC_DUNST}.NotificationShow" >/dev/null + ;; + "status") + property_get running | ( read _ _ status; printf "%s\n" "${status}"; ) + ;; + "status-set") + [ "${2:-}" ] \ + || die "No status parameter specified. Please give either 'true' or 'false' as running parameter." + [ "${2}" == "true" ] || [ "${2}" == "false" ] \ + || die "Please give either 'true' or 'false' as running parameter." + property_set running variant:boolean:"${2}" + ;; + "help"|"--help"|"-h") + show_help ;; "") - method_call NotificationCloseLast + die "dunstctl: No command specified. Please consult the usage." ;; *) - echo "dunstctl: unrecognized option '$2'" - exit 1 + die "dunstctl: unrecognized command '${1:-}'. Please consult the usage." ;; - esac - ;; -"context") - method_call ContextMenuCall - ;; -"display") - method_call NotificationShow - ;; -"status") - [ $# -lt 2 ] && action="" || action="$2" - case "$action" in - "disable") - property_set running variant:boolean:false - ;; - "enable") - property_set running variant:boolean:true - ;; - ""|"get") - property_get running - ;; - *) - echo "dunstctl status: unrecognized option '$2'" - exit 1 - ;; - esac - ;; -"help"|"--help"|"-h") - usage - ;; -*) - echo "dunstctl: unrecognized command '$1'" - exit 1 - ;; esac - From e80e8e9eb53756d5ae9d1e20373665b7b0183027 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Sun, 11 Aug 2019 15:28:03 +0200 Subject: [PATCH 04/21] Install dunstctl via make automatically --- Makefile | 14 ++++++++++---- test/test-install.sh | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 347cc3c..7b26c41 100644 --- a/Makefile +++ b/Makefile @@ -151,16 +151,19 @@ clean-coverage-run: ${FIND} . -type f -name '*.gcov' -delete ${FIND} . -type f -name '*.gcda' -delete -.PHONY: install install-dunst install-doc \ +.PHONY: install install-dunst install-dunstctl install-doc \ install-service install-service-dbus install-service-systemd \ - uninstall \ + uninstall uninstall-dunstctl \ uninstall-service uninstall-service-dbus uninstall-service-systemd -install: install-dunst install-doc install-service install-dunstify +install: install-dunst install-dunstctl install-doc install-service install-dunstify install-dunst: dunst doc install -Dm755 dunst ${DESTDIR}${BINDIR}/dunst install -Dm644 docs/dunst.1 ${DESTDIR}${MANPREFIX}/man1/dunst.1 +install-dunstctl: dunstctl + install -Dm755 dunstctl ${DESTDIR}${BINDIR}/dunstctl + install-doc: install -Dm644 dunstrc ${DESTDIR}${DATADIR}/dunst/dunstrc @@ -176,12 +179,15 @@ endif install-dunstify: dunstify install -Dm755 dunstify ${DESTDIR}${BINDIR}/dunstify -uninstall: uninstall-service +uninstall: uninstall-service uninstall-dunstctl rm -f ${DESTDIR}${BINDIR}/dunst rm -f ${DESTDIR}${BINDIR}/dunstify rm -f ${DESTDIR}${MANPREFIX}/man1/dunst.1 rm -rf ${DESTDIR}${DATADIR}/dunst +uninstall-dunstctl: + rm -f ${DESTDIR}${BINDIR}/dunstctl + uninstall-service: uninstall-service-dbus uninstall-service-dbus: rm -f ${DESTDIR}${SERVICEDIR_DBUS}/org.knopwob.dunst.service diff --git a/test/test-install.sh b/test/test-install.sh index 50e7336..f4bb8ad 100755 --- a/test/test-install.sh +++ b/test/test-install.sh @@ -9,6 +9,7 @@ make -C "${BASE}" SYSTEMD=1 SERVICEDIR_SYSTEMD="${PREFIX}/systemd" SERVICEDIR_DB diff -u <(find "${PREFIX}" -type f -printf "%P\n" | sort) - < Date: Mon, 12 Aug 2019 20:47:13 +0200 Subject: [PATCH 05/21] Add debug command --- dunstctl | 16 ++++++++++++++++ src/dbus.c | 14 ++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/dunstctl b/dunstctl index 2e1f7db..bce3028 100755 --- a/dunstctl +++ b/dunstctl @@ -20,6 +20,7 @@ function show_help() { history-pop Pop one notification from history status Check if status-set [true|false] Set the status + debug Print debugging information help Show this help EOH } @@ -66,6 +67,21 @@ case "${1:-}" in "help"|"--help"|"-h") show_help ;; + "debug") + dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_FDN}.GetServerInformation" >/dev/null 2>/dev/null \ + || die "Dunst is not running." + + dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_FDN}.GetServerInformation" \ + | ( + read name vendor version protocol_version + [ "${name}" == dunst ] + printf "dunst version: %s\n" "${version}" + ) \ + || die "Another notification manager is running. It's not dunst" + + dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_DUNST}.Ping" >/dev/null 2>/dev/null \ + || die "Dunst controlling interface not available. Is the version too old?" + ;; "") die "dunstctl: No command specified. Please consult the usage." ;; diff --git a/src/dbus.c b/src/dbus.c index 0f8f93a..d554bcc 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -76,6 +76,7 @@ static const char *introspection_xml = " " " " " " + " " " " " " @@ -154,11 +155,13 @@ DBUS_METHOD(dunst_ContextMenuCall); DBUS_METHOD(dunst_NotificationCloseAll); DBUS_METHOD(dunst_NotificationCloseLast); DBUS_METHOD(dunst_NotificationShow); +DBUS_METHOD(dunst_Ping); static struct dbus_method methods_dunst[] = { {"ContextMenuCall", dbus_cb_dunst_ContextMenuCall}, {"NotificationCloseAll", dbus_cb_dunst_NotificationCloseAll}, {"NotificationCloseLast", dbus_cb_dunst_NotificationCloseLast}, {"NotificationShow", dbus_cb_dunst_NotificationShow}, + {"Ping", dbus_cb_dunst_Ping}, }; void dbus_cb_dunst_methods(GDBusConnection *connection, @@ -241,6 +244,17 @@ static void dbus_cb_dunst_NotificationShow(GDBusConnection *connection, g_dbus_connection_flush(connection, NULL, NULL, NULL); } +/* Just a simple Ping command to give the ability to dunstctl to test for the existence of this interface + * Any other way requires parsing the XML of the Introspection or other foo. Just calling the Ping on an old dunst version will fail. */ +static void dbus_cb_dunst_Ping(GDBusConnection *connection, + const gchar *sender, + GVariant *parameters, + GDBusMethodInvocation *invocation) +{ + g_dbus_method_invocation_return_value(invocation, NULL); + g_dbus_connection_flush(connection, NULL, NULL, NULL); +} + static void dbus_cb_GetCapabilities( GDBusConnection *connection, From 7e92619967f21b6744776ddc5e165be720c6df09 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Thu, 22 Aug 2019 23:28:51 +0200 Subject: [PATCH 06/21] Add ability to trigger action of latest notification Currently triggers only the latest notification. TODO: implement it either with ActionsAll or more favorable: with an optional paramter, which should trigger only the action of a single notification. The problem currently: I have got no ability to check the DBus docs, as I'm on a bad internet connection. --- dunstctl | 5 ++++- src/dbus.c | 43 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/dunstctl b/dunstctl index bce3028..d8b7f16 100755 --- a/dunstctl +++ b/dunstctl @@ -26,7 +26,7 @@ function show_help() { } function method_call() { - dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${1}" + dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" $* } function property_get() { @@ -42,6 +42,9 @@ command -v dbus-send >/dev/null 2>/dev/null || \ case "${1:-}" in + "action") + method_call "${DBUS_IFAC_DUNST}.NotificationAction" "int32:${2:-0}" >/dev/null + ;; "close") method_call "${DBUS_IFAC_DUNST}.NotificationCloseLast" >/dev/null ;; diff --git a/src/dbus.c b/src/dbus.c index d554bcc..39867af 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -73,6 +73,10 @@ static const char *introspection_xml = " " " " +// TODO: add an optional parmater definining the action of notification number X to invoke + " " + " " + " " " " " " " " @@ -152,12 +156,14 @@ void dbus_cb_fdn_methods(GDBusConnection *connection, } DBUS_METHOD(dunst_ContextMenuCall); +DBUS_METHOD(dunst_NotificationAction); DBUS_METHOD(dunst_NotificationCloseAll); DBUS_METHOD(dunst_NotificationCloseLast); DBUS_METHOD(dunst_NotificationShow); DBUS_METHOD(dunst_Ping); static struct dbus_method methods_dunst[] = { {"ContextMenuCall", dbus_cb_dunst_ContextMenuCall}, + {"NotificationAction", dbus_cb_dunst_NotificationAction}, {"NotificationCloseAll", dbus_cb_dunst_NotificationCloseAll}, {"NotificationCloseLast", dbus_cb_dunst_NotificationCloseLast}, {"NotificationShow", dbus_cb_dunst_NotificationShow}, @@ -201,6 +207,33 @@ static void dbus_cb_dunst_ContextMenuCall(GDBusConnection *connection, g_dbus_connection_flush(connection, NULL, NULL, NULL); } +static void dbus_cb_dunst_NotificationAction(GDBusConnection *connection, + const gchar *sender, + GVariant *parameters, + GDBusMethodInvocation *invocation) +{ + int notification_nr = 0; + g_variant_get(parameters, "(i)", ¬ification_nr); + + LOG_D("CMD: Calling action for notification %d", notification_nr); + + if (notification_nr < 0 || queues_length_waiting() < notification_nr) + return; //FIXME return error + + const GList *list = g_list_nth_data(queues_get_displayed(), notification_nr); + + if (list && list->data) { + struct notification *n = list->data; + LOG_D("CMD: Calling action for notification %s", n->summary); + notification_do_action(n); + // TODO: do we need to wake up after notification action? + wake_up(); + } + + g_dbus_method_invocation_return_value(invocation, NULL); + g_dbus_connection_flush(connection, NULL, NULL, NULL); +} + static void dbus_cb_dunst_NotificationCloseAll(GDBusConnection *connection, const gchar *sender, GVariant *parameters, @@ -606,10 +639,16 @@ gboolean dbus_cb_dunst_Properties_Set(GDBusConnection *connection, GError **error, gpointer user_data) { - if (STR_EQ(property_name, "running")) + if (STR_EQ(property_name, "running")) { dunst_status(S_RUNNING, g_variant_get_boolean(value)); + return true; + } + - return true; + //FIXME: don't we have to return true on successful setting, but return false, if e.g. the parameter name is wrong? + //return true; + // so like this? + return false; } From d45d26e2579b03731bb92a6754b969c31dc88614 Mon Sep 17 00:00:00 2001 From: Nikos Tsipinakis Date: Thu, 9 Apr 2020 17:19:46 +0200 Subject: [PATCH 07/21] dunstctl: Don't use function keyword Plain `foo()` is more portable, `function` doesn't appear to be supported in sh. --- dunstctl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dunstctl b/dunstctl index d8b7f16..fa824bc 100755 --- a/dunstctl +++ b/dunstctl @@ -8,9 +8,9 @@ DBUS_IFAC_DUNST="org.dunstproject.cmd0" DBUS_IFAC_PROP="org.freedesktop.DBus.Properties" DBUS_IFAC_FDN="org.freedesktop.Notifications" -function die(){ printf "%s\n" "${1}" >&2; exit 1; } +die(){ printf "%s\n" "${1}" >&2; exit 1; } -function show_help() { +show_help() { cat <<-EOH Usage: dunstctl [parameters]" Commands: @@ -25,15 +25,15 @@ function show_help() { EOH } -function method_call() { +method_call() { dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" $* } -function property_get() { +property_get() { dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_PROP}.Get" "string:${DBUS_IFAC_DUNST}" "string:${1}" } -function property_set() { +property_set() { dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_PROP}.Set" "string:${DBUS_IFAC_DUNST}" "string:${1}" "${2}" } From 6a8401d85ad717eb882f5d9ca3cb61812b48621e Mon Sep 17 00:00:00 2001 From: Nikos Tsipinakis Date: Thu, 9 Apr 2020 17:23:56 +0200 Subject: [PATCH 08/21] dunstctl: Use = rather than == for string equality `==` is not supported in plain sh --- dunstctl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dunstctl b/dunstctl index fa824bc..c8f3b60 100755 --- a/dunstctl +++ b/dunstctl @@ -63,7 +63,7 @@ case "${1:-}" in "status-set") [ "${2:-}" ] \ || die "No status parameter specified. Please give either 'true' or 'false' as running parameter." - [ "${2}" == "true" ] || [ "${2}" == "false" ] \ + [ "${2}" = "true" ] || [ "${2}" = "false" ] \ || die "Please give either 'true' or 'false' as running parameter." property_set running variant:boolean:"${2}" ;; @@ -77,7 +77,7 @@ case "${1:-}" in dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_FDN}.GetServerInformation" \ | ( read name vendor version protocol_version - [ "${name}" == dunst ] + [ "${name}" = "dunst" ] printf "dunst version: %s\n" "${version}" ) \ || die "Another notification manager is running. It's not dunst" From e14160bda6c18369b82e990e7e8aef3a4e491a77 Mon Sep 17 00:00:00 2001 From: Nikos Tsipinakis Date: Thu, 9 Apr 2020 17:27:37 +0200 Subject: [PATCH 09/21] dunstctl: Apply shellcheck suggestions --- dunstctl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dunstctl b/dunstctl index c8f3b60..070eaf3 100755 --- a/dunstctl +++ b/dunstctl @@ -26,7 +26,7 @@ show_help() { } method_call() { - dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" $* + dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "$@" } property_get() { @@ -58,7 +58,7 @@ case "${1:-}" in method_call "${DBUS_IFAC_DUNST}.NotificationShow" >/dev/null ;; "status") - property_get running | ( read _ _ status; printf "%s\n" "${status}"; ) + property_get running | ( read -r _ _ status; printf "%s\n" "${status}"; ) ;; "status-set") [ "${2:-}" ] \ @@ -76,7 +76,7 @@ case "${1:-}" in dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_FDN}.GetServerInformation" \ | ( - read name vendor version protocol_version + read -r name _ version _ [ "${name}" = "dunst" ] printf "dunst version: %s\n" "${version}" ) \ From b1f64b266b7cacb7cd718b4925e87477aa05c997 Mon Sep 17 00:00:00 2001 From: Nikos Tsipinakis Date: Thu, 9 Apr 2020 17:30:36 +0200 Subject: [PATCH 10/21] Wake up after changing status via dunstctl --- src/dbus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dbus.c b/src/dbus.c index 39867af..51a06a3 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -641,6 +641,7 @@ gboolean dbus_cb_dunst_Properties_Set(GDBusConnection *connection, { if (STR_EQ(property_name, "running")) { dunst_status(S_RUNNING, g_variant_get_boolean(value)); + wake_up(); return true; } From 936dbb039c2ac8b992b75b5f6720cd3eb62d0549 Mon Sep 17 00:00:00 2001 From: Nikos Tsipinakis Date: Thu, 9 Apr 2020 17:43:34 +0200 Subject: [PATCH 11/21] dunstctl: Print help message on dbus communication failure --- dunstctl | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/dunstctl b/dunstctl index 070eaf3..fca714c 100755 --- a/dunstctl +++ b/dunstctl @@ -1,6 +1,6 @@ #!/bin/sh -set -eu +set -u DBUS_NAME="org.freedesktop.Notifications" DBUS_PATH="/org/freedesktop/Notifications" @@ -24,17 +24,26 @@ show_help() { help Show this help EOH } +dbus_send_checked() { + dbus-send "$@" + rc="$?" + if [ "$rc" -eq "1" ]; then + echo "Failed to communicate with dunst, is it running? Or maybe the version is outdated." >&2 + echo "Hint: You can try 'dunstctl debug' as a next debugging step." >&2 + exit 1 + fi +} method_call() { - dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "$@" + dbus_send_checked --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "$@" } property_get() { - dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_PROP}.Get" "string:${DBUS_IFAC_DUNST}" "string:${1}" + dbus_send_checked --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_PROP}.Get" "string:${DBUS_IFAC_DUNST}" "string:${1}" } property_set() { - dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_PROP}.Set" "string:${DBUS_IFAC_DUNST}" "string:${1}" "${2}" + dbus_send_checked --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_PROP}.Set" "string:${DBUS_IFAC_DUNST}" "string:${1}" "${2}" } command -v dbus-send >/dev/null 2>/dev/null || \ From ee11dfa8feff4cd151b590c7022174dbe80c9c43 Mon Sep 17 00:00:00 2001 From: Nikos Tsipinakis Date: Thu, 9 Apr 2020 17:50:43 +0200 Subject: [PATCH 12/21] Don't wake up after doing an action --- src/dbus.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/dbus.c b/src/dbus.c index 51a06a3..1fbf69c 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -73,7 +73,6 @@ static const char *introspection_xml = " " " " -// TODO: add an optional parmater definining the action of notification number X to invoke " " " " " " @@ -226,8 +225,6 @@ static void dbus_cb_dunst_NotificationAction(GDBusConnection *connection, struct notification *n = list->data; LOG_D("CMD: Calling action for notification %s", n->summary); notification_do_action(n); - // TODO: do we need to wake up after notification action? - wake_up(); } g_dbus_method_invocation_return_value(invocation, NULL); From 9049bf15b262f0922641deee80fa563d7fe60327 Mon Sep 17 00:00:00 2001 From: Nikos Tsipinakis Date: Thu, 9 Apr 2020 18:15:01 +0200 Subject: [PATCH 13/21] Remove live status checking (for now) --- src/dbus.c | 37 ------------------------------------- src/dbus.h | 7 ------- src/dunst.c | 6 ------ 3 files changed, 50 deletions(-) diff --git a/src/dbus.c b/src/dbus.c index 1fbf69c..ee44c04 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -573,43 +573,6 @@ void signal_action_invoked(const struct notification *n, const char *identifier) } } -//FIXME: Is this necessary or alternative question: Is this implemented correctl? -// This had been an old relict from the manual times, when I haven't used the -// interface vtable of GLib -void dbus_signal_status_changed(struct dunst_status status) -{ - // We might have not a working connection yet, so just ignore it. - if (!dbus_conn) - return; - - //TODO: I'm pretty sure this is the right format string, but I don't know how to verify it - GVariantBuilder builder; - g_variant_builder_init(&builder, G_VARIANT_TYPE("(sa{sv}as)")); - g_variant_builder_add(&builder, "s", DUNST_IFAC); - - g_variant_builder_open(&builder, G_VARIANT_TYPE ("a{sv}")); - g_variant_builder_add(&builder, "{sv}", "running", g_variant_new_boolean(status.running)); - g_variant_builder_close(&builder); - - g_variant_builder_open(&builder, G_VARIANT_TYPE ("as")); - g_variant_builder_add(&builder, "s", "unrelated"); - g_variant_builder_close(&builder); - - GError *err = NULL; - g_dbus_connection_emit_signal(dbus_conn, - NULL, - DUNST_PATH, - PROPERTIES_IFAC, - "PropertiesChanged", - g_variant_builder_end(&builder), - &err); - - if (err) { - LOG_W("Unable send signal 'PropertiesChanged': %s", err->message); - g_error_free(err); - } -} - GVariant *dbus_cb_dunst_Properties_Get(GDBusConnection *connection, const gchar *sender, const gchar *object_path, diff --git a/src/dbus.h b/src/dbus.h index 9301055..b74d95c 100644 --- a/src/dbus.h +++ b/src/dbus.h @@ -21,12 +21,5 @@ void dbus_teardown(int id); void signal_notification_closed(struct notification *n, enum reason reason); void signal_action_invoked(const struct notification *n, const char *identifier); -/** - * Signal a changed status via DBus - * - * @param status The current status of dunst - * */ -void dbus_signal_status_changed(struct dunst_status status); - #endif /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/src/dunst.c b/src/dunst.c index 85b900d..8ba2d15 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -31,26 +31,20 @@ static struct dunst_status status; void dunst_status(const enum dunst_status_field field, bool value) { - bool changed; switch (field) { case S_FULLSCREEN: - changed = status.fullscreen != value; status.fullscreen = value; break; case S_IDLE: - changed = status.idle != value; status.idle = value; break; case S_RUNNING: - changed = status.running != value; status.running = value; break; default: LOG_E("Invalid %s enum value in %s:%d", "dunst_status", __FILE__, __LINE__); break; } - if (changed) - dbus_signal_status_changed(status); } /* see dunst.h */ From ec421f1ea1f78f40b93c476c7c4c948657e3bc52 Mon Sep 17 00:00:00 2001 From: Nikos Tsipinakis Date: Thu, 9 Apr 2020 19:38:27 +0200 Subject: [PATCH 14/21] Properly handle error conditions from dunstctl --- src/dbus.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/dbus.c b/src/dbus.c index ee44c04..6f288c6 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -583,11 +583,13 @@ GVariant *dbus_cb_dunst_Properties_Get(GDBusConnection *connection, { struct dunst_status status = dunst_status_get(); - if (STR_EQ(property_name, "running")) + if (STR_EQ(property_name, "running")) { return g_variant_new_boolean(status.running); - else - //TODO: is NULL as return value allowed? + } else { + LOG_W("Unknown property!\n"); + *error = g_error_new(G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property"); return NULL; + } } gboolean dbus_cb_dunst_Properties_Set(GDBusConnection *connection, @@ -604,11 +606,9 @@ gboolean dbus_cb_dunst_Properties_Set(GDBusConnection *connection, wake_up(); return true; } - - //FIXME: don't we have to return true on successful setting, but return false, if e.g. the parameter name is wrong? - //return true; - // so like this? + *error = g_error_new(G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property"); + return false; } From 36eed785d28c86acb62d664a8ef11142041dab99 Mon Sep 17 00:00:00 2001 From: Nikos Tsipinakis Date: Thu, 9 Apr 2020 20:01:48 +0200 Subject: [PATCH 15/21] dunstctl: Fix alignment in help message --- dunstctl | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/dunstctl b/dunstctl index fca714c..48a6178 100755 --- a/dunstctl +++ b/dunstctl @@ -12,16 +12,19 @@ die(){ printf "%s\n" "${1}" >&2; exit 1; } show_help() { cat <<-EOH - Usage: dunstctl [parameters]" - Commands: - close Close the last notification - close-all Close the all notifications - context Open context menu of latest notification - history-pop Pop one notification from history - status Check if - status-set [true|false] Set the status - debug Print debugging information - help Show this help + Usage: dunstctl [parameters]" + Commands: + action Perform the default action, or open the + context menu of the notification at the + given position + close Close the last notification + close-all Close the all notifications + context Open context menu + history-pop Pop one notification from history + status Check if + status-set [true|false] Set the status + debug Print debugging information + help Show this help EOH } dbus_send_checked() { From a91adf9c80b113702115a4ff23d503f7fbf2ecda Mon Sep 17 00:00:00 2001 From: Nikos Tsipinakis Date: Thu, 9 Apr 2020 20:02:16 +0200 Subject: [PATCH 16/21] Implement error handling in NotificationAction call --- src/dbus.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/dbus.c b/src/dbus.c index 6f288c6..e3427b8 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -103,7 +103,6 @@ struct dbus_method { GDBusMethodInvocation *invocation); }; -// TODO: call it interface methods #define DBUS_METHOD(name) static void dbus_cb_##name( \ GDBusConnection *connection, \ const gchar *sender, \ @@ -216,8 +215,14 @@ static void dbus_cb_dunst_NotificationAction(GDBusConnection *connection, LOG_D("CMD: Calling action for notification %d", notification_nr); - if (notification_nr < 0 || queues_length_waiting() < notification_nr) - return; //FIXME return error + if (notification_nr < 0 || queues_length_waiting() < notification_nr) { + g_dbus_method_invocation_return_error(invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + "Couldn't activate action for notification in position %d, %d notifications currently open", + notification_nr, queues_length_waiting()); + return; + } const GList *list = g_list_nth_data(queues_get_displayed(), notification_nr); @@ -627,7 +632,6 @@ static void dbus_cb_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data) { - // TODO: deduplicate the code GError *err = NULL; if(!g_dbus_connection_register_object( connection, From 23fa204e6921bb029d6c406c1b25948093e5b430 Mon Sep 17 00:00:00 2001 From: Nikos Tsipinakis Date: Fri, 10 Apr 2020 12:06:15 +0200 Subject: [PATCH 17/21] Use set -e --- dunstctl | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/dunstctl b/dunstctl index 48a6178..db481e5 100755 --- a/dunstctl +++ b/dunstctl @@ -1,6 +1,6 @@ #!/bin/sh -set -u +set -eu DBUS_NAME="org.freedesktop.Notifications" DBUS_PATH="/org/freedesktop/Notifications" @@ -28,13 +28,8 @@ show_help() { EOH } dbus_send_checked() { - dbus-send "$@" - rc="$?" - if [ "$rc" -eq "1" ]; then - echo "Failed to communicate with dunst, is it running? Or maybe the version is outdated." >&2 - echo "Hint: You can try 'dunstctl debug' as a next debugging step." >&2 - exit 1 - fi + dbus-send "$@" \ + || die "Failed to communicate with dunst, is it running? Or maybe the version is outdated. You can try 'dunstctl debug' as a next debugging step." } method_call() { From 94aa427a1bc95ea0e78aa631473209617d383dbd Mon Sep 17 00:00:00 2001 From: Nikos Tsipinakis Date: Fri, 10 Apr 2020 12:06:34 +0200 Subject: [PATCH 18/21] Fix dunstctl help alignment --- dunstctl | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/dunstctl b/dunstctl index db481e5..d549e71 100755 --- a/dunstctl +++ b/dunstctl @@ -12,19 +12,19 @@ die(){ printf "%s\n" "${1}" >&2; exit 1; } show_help() { cat <<-EOH - Usage: dunstctl [parameters]" - Commands: - action Perform the default action, or open the - context menu of the notification at the - given position - close Close the last notification - close-all Close the all notifications - context Open context menu - history-pop Pop one notification from history - status Check if - status-set [true|false] Set the status - debug Print debugging information - help Show this help + Usage: dunstctl [parameters]" + Commands: + action Perform the default action, or open the + context menu of the notification at the + given position + close Close the last notification + close-all Close the all notifications + context Open context menu + history-pop Pop one notification from history + status Check if + status-set [true|false] Set the status + debug Print debugging information + help Show this help EOH } dbus_send_checked() { From 5aa1863762ff8994f90310b50915a09a36261fe8 Mon Sep 17 00:00:00 2001 From: Nikos Tsipinakis Date: Tue, 14 Apr 2020 11:34:18 +0200 Subject: [PATCH 19/21] Rename status commands to running --- dunstctl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dunstctl b/dunstctl index d549e71..347c77c 100755 --- a/dunstctl +++ b/dunstctl @@ -21,8 +21,8 @@ show_help() { close-all Close the all notifications context Open context menu history-pop Pop one notification from history - status Check if - status-set [true|false] Set the status + running Check if dunst is running or paused + set-running [true|false] Set the pause status debug Print debugging information help Show this help EOH @@ -64,10 +64,10 @@ case "${1:-}" in "history-pop") method_call "${DBUS_IFAC_DUNST}.NotificationShow" >/dev/null ;; - "status") - property_get running | ( read -r _ _ status; printf "%s\n" "${status}"; ) + "running") + property_get running | ( read -r _ _ paused; printf "%s\n" "${paused}"; ) ;; - "status-set") + "set-running") [ "${2:-}" ] \ || die "No status parameter specified. Please give either 'true' or 'false' as running parameter." [ "${2}" = "true" ] || [ "${2}" = "false" ] \ From 6ba430a81872c5c3240b538b161ae5fd2c5e0aeb Mon Sep 17 00:00:00 2001 From: Nikos Tsipinakis Date: Tue, 14 Apr 2020 12:57:22 +0200 Subject: [PATCH 20/21] Add dunstctl man page --- Makefile | 11 ++++++++- docs/dunstctl.pod | 59 ++++++++++++++++++++++++++++++++++++++++++++ test/test-install.sh | 1 + 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 docs/dunstctl.pod diff --git a/Makefile b/Makefile index 7b26c41..2db753c 100644 --- a/Makefile +++ b/Makefile @@ -106,9 +106,15 @@ test/test: ${OBJ} ${TEST_OBJ} ${CC} -o ${@} ${TEST_OBJ} $(filter-out ${TEST_OBJ:test/%=src/%},${OBJ}) ${CFLAGS} ${LDFLAGS} .PHONY: doc doc-doxygen -doc: docs/dunst.1 +doc: docs/dunst.1 docs/dunstctl.1 + +# Can't dedup this as we need to explicitly provide the name and title text to +# pod2man :( docs/dunst.1: docs/dunst.pod ${POD2MAN} --name=dunst -c "Dunst Reference" --section=1 --release=${VERSION} $< > $@ +docs/dunstctl.1: docs/dunstctl.pod + ${POD2MAN} --name=dunstctl -c "dunstctl reference" --section=1 --release=${VERSION} $< > $@ + doc-doxygen: ${DOXYGEN} docs/internal/Doxyfile @@ -137,6 +143,7 @@ clean-dunstify: clean-doc: rm -f docs/dunst.1 + rm -f docs/dunstctl.1 rm -fr docs/internal/html rm -fr docs/internal/coverage @@ -160,6 +167,7 @@ install: install-dunst install-dunstctl install-doc install-service install-duns install-dunst: dunst doc install -Dm755 dunst ${DESTDIR}${BINDIR}/dunst install -Dm644 docs/dunst.1 ${DESTDIR}${MANPREFIX}/man1/dunst.1 + install -Dm644 docs/dunstctl.1 ${DESTDIR}${MANPREFIX}/man1/dunstctl.1 install-dunstctl: dunstctl install -Dm755 dunstctl ${DESTDIR}${BINDIR}/dunstctl @@ -183,6 +191,7 @@ uninstall: uninstall-service uninstall-dunstctl rm -f ${DESTDIR}${BINDIR}/dunst rm -f ${DESTDIR}${BINDIR}/dunstify rm -f ${DESTDIR}${MANPREFIX}/man1/dunst.1 + rm -f ${DESTDIR}${MANPREFIX}/man1/dunstctl.1 rm -rf ${DESTDIR}${DATADIR}/dunst uninstall-dunstctl: diff --git a/docs/dunstctl.pod b/docs/dunstctl.pod new file mode 100644 index 0000000..c537349 --- /dev/null +++ b/docs/dunstctl.pod @@ -0,0 +1,59 @@ +=head1 NAME + +dunstctl - Command line control utility for dunst, a customizable and lightweight notification-daemon + +=head1 SYNOPSIS + +dunstctl COMMAND [PARAMETER] + +=head1 COMMANDS + +=over 4 + +=item B notification_position + +Performs the default action or, if not available, opens the context menu of the +notification at the given position (starting count at the top, first +notification being 0). + +=item B + +Close the topmost notification currently being displayed. + +=item B + +Close all notifications currently being displayed + +=item B + +Open the context menu, presenting all available actions and urls for the +currently open notifications. + +=item B + +Redisplay the notification that was most recently closed. This can be called +multiple times to show older notifications, up to the history limit configured +in dunst. + +=item B + +Check if dunst is currently running or paused. If dunst is paused notifications +will be kept but not shown until it is unpaused. + +=item B true/false + +Set the paused status of dunst. If true, dunst is running normally, if false, +dunst is paused. See the running command and the dunst man page for more +information. + +=item B + +Tries to contact dunst and checks for common faults between dunstctl and dunst. +Useful if something isn't working + +=item B + +Show all available commands with a brief description + +=back + diff --git a/test/test-install.sh b/test/test-install.sh index f4bb8ad..ab0d950 100755 --- a/test/test-install.sh +++ b/test/test-install.sh @@ -14,6 +14,7 @@ bin/dunstify dbus/org.knopwob.dunst.service share/dunst/dunstrc share/man/man1/dunst.1 +share/man/man1/dunstctl.1 systemd/dunst.service EOF From 50a55f2ff17dd72deacaa6db44711c899daa78f8 Mon Sep 17 00:00:00 2001 From: Nikos Tsipinakis Date: Tue, 14 Apr 2020 12:59:17 +0200 Subject: [PATCH 21/21] Mention dunstctl in dunst man page --- docs/dunst.pod | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/dunst.pod b/docs/dunst.pod index f94a7dd..b97e647 100644 --- a/docs/dunst.pod +++ b/docs/dunst.pod @@ -517,7 +517,7 @@ Close all notifications. =back -=head2 Shortcut section +=head2 Shortcut section B Keyboard shortcuts are defined in the following format: "Modifier+key" where the modifier is one of ctrl,mod1,mod2,mod3,mod4 and key is any keyboard key. @@ -602,6 +602,13 @@ See TIME FORMAT for valid times. =back +=head1 DUNSTCTL + +Dunst now contains a command line control command that can be used to interact +with it. It supports all functions previously done only via keyboard shortcuts +but also has a lot of extra functionality. So see more see the dunstctl man +page. + =head1 HISTORY Dunst saves a number of notifications (specified by B) in memory. @@ -865,9 +872,8 @@ Example time: "1000ms" "10m" =head1 MISCELLANEOUS -Dunst can be paused by sending a notification with a summary of -"DUNST_COMMAND_PAUSE", resumed with a summary of "DUNST_COMMAND_RESUME" and -toggled with a summary of "DUNST_COMMAND_TOGGLE". +Dunst can be paused via the `dunstctl set-running false` command. To unpause dunst use +`dunstctl set-status true` and to unpause `dunstctl set-status false`. Alternatively you can send SIGUSR1 and SIGUSR2 to pause and unpause respectively. For Example: @@ -908,4 +914,4 @@ If you feel that copyrights are violated, please send me an email. =head1 SEE ALSO -dwm(1), dmenu(1), twmn(1), notify-send(1) +dunstctl(1), dwm(1), dmenu(1), twmn(1), notify-send(1)