From f60261e08cb98f3b2a55a4be528471b22d829b21 Mon Sep 17 00:00:00 2001 From: bynect <68197565+bynect@users.noreply.github.com> Date: Thu, 2 May 2024 17:59:24 +0200 Subject: [PATCH 01/16] Allow multiple `-conf` values and refactor cmdline persing --- src/draw.c | 1 + src/dunst.c | 20 ++++++++++++++------ src/dunst.h | 2 +- src/option_parser.c | 41 ++++++++++++++++++++++++----------------- src/option_parser.h | 4 +++- src/settings.c | 31 +++++++++++++++++++------------ src/settings.h | 2 +- 7 files changed, 63 insertions(+), 38 deletions(-) diff --git a/src/draw.c b/src/draw.c index 58fc4119f..73eb6fb49 100644 --- a/src/draw.c +++ b/src/draw.c @@ -974,6 +974,7 @@ void draw(void) void draw_deinit(void) { + pango_font_description_free(pango_fdesc); output->win_destroy(win); output->deinit(); if (settings.enable_recursive_icon_lookup) diff --git a/src/dunst.c b/src/dunst.c index ae875ed3d..26e48aca1 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -25,6 +25,7 @@ GMainLoop *mainloop = NULL; static struct dunst_status status; static bool setup_done = false; +static char **config_paths = NULL; /* see dunst.h */ void dunst_status(const enum dunst_status_field field, @@ -207,7 +208,7 @@ static void teardown(void) draw_deinit(); } -int dunst_main(int argc, char *argv[]) +int dunst_main(int argc, const char *argv[]) { dunst_status_int(S_PAUSE_LEVEL, 0); @@ -229,10 +230,16 @@ int dunst_main(int argc, char *argv[]) log_set_level_from_string(verbosity); g_free(verbosity); - char *cmdline_config_path; - cmdline_config_path = - cmdline_get_string("-conf/-config", NULL, - "Path to configuration file"); + GStrvBuilder *builder = g_strv_builder_new(); + char *path = NULL; + int start = 1; + + while ((path = cmdline_get_string_offset("-conf/-config", NULL, + "Path to configuration file", start, &start))) + g_strv_builder_add(builder, path); + + config_paths = g_strv_builder_end(builder); + g_strv_builder_unref(builder); settings.print_notifications = cmdline_get_bool("-print/--print", false, "Print notifications to stdout"); @@ -244,7 +251,7 @@ int dunst_main(int argc, char *argv[]) usage(EXIT_SUCCESS); } - load_settings(cmdline_config_path); + load_settings(config_paths); int dbus_owner_id = dbus_init(); mainloop = g_main_loop_new(NULL, FALSE); @@ -278,6 +285,7 @@ int dunst_main(int argc, char *argv[]) run(GINT_TO_POINTER(DUNST_TIMER)); // The first run() is a scheduled one g_main_loop_run(mainloop); g_clear_pointer(&mainloop, g_main_loop_unref); + g_strfreev(config_paths); /* remove signal handler watches */ g_source_remove(pause_src); diff --git a/src/dunst.h b/src/dunst.h index dc5267e0f..545e86248 100644 --- a/src/dunst.h +++ b/src/dunst.h @@ -39,7 +39,7 @@ struct dunst_status dunst_status_get(void); void wake_up(void); -int dunst_main(int argc, char *argv[]); +int dunst_main(int argc, const char *argv[]); void usage(int exit_status); void print_version(void); diff --git a/src/option_parser.c b/src/option_parser.c index 53e3d1cba..468a19e24 100644 --- a/src/option_parser.c +++ b/src/option_parser.c @@ -17,13 +17,11 @@ #include "settings_data.h" static int cmdline_argc; -static char **cmdline_argv; +static const char **cmdline_argv; static char *usage_str = NULL; static void cmdline_usage_append(const char *key, const char *type, const char *description); -static int cmdline_find_option(const char *key); - #define STRING_PARSE_RET(string, value) if (STR_EQ(s, string)) { *ret = value; return true; } int string_parse_enum(const void *data, const char *s, void * ret) { @@ -511,20 +509,20 @@ void save_settings(struct ini *ini) { } } -void cmdline_load(int argc, char *argv[]) +void cmdline_load(int argc, const char *argv[]) { cmdline_argc = argc; cmdline_argv = argv; } -int cmdline_find_option(const char *key) +static int cmdline_find_option(const char *key, int start) { ASSERT_OR_RET(key, -1); gchar **keys = g_strsplit(key, "/", -1); for (int i = 0; keys[i] != NULL; i++) { - for (int j = 0; j < cmdline_argc; j++) { + for (int j = start; j < cmdline_argc; j++) { if (STR_EQ(keys[i], cmdline_argv[j])) { g_strfreev(keys); return j; @@ -536,13 +534,16 @@ int cmdline_find_option(const char *key) return -1; } -static const char *cmdline_get_value(const char *key) +static const char *cmdline_get_value(const char *key, int start, int *found) { - int idx = cmdline_find_option(key); + int idx = cmdline_find_option(key, start); if (idx < 0) { return NULL; } + if (found) + *found = idx + 1; + if (idx + 1 >= cmdline_argc) { /* the argument is missing */ LOG_W("%s: Missing argument. Ignoring.", key); @@ -551,10 +552,11 @@ static const char *cmdline_get_value(const char *key) return cmdline_argv[idx + 1]; } -char *cmdline_get_string(const char *key, const char *def, const char *description) +char *cmdline_get_string_offset(const char *key, const char *def, const char *description, + int start, int *found) { cmdline_usage_append(key, "string", description); - const char *str = cmdline_get_value(key); + const char *str = cmdline_get_value(key, start, found); if (str) return g_strdup(str); @@ -564,10 +566,15 @@ char *cmdline_get_string(const char *key, const char *def, const char *descripti return NULL; } +char *cmdline_get_string(const char *key, const char *def, const char *description) +{ + return cmdline_get_string_offset(key, def, description, 1, NULL); +} + char *cmdline_get_path(const char *key, const char *def, const char *description) { cmdline_usage_append(key, "string", description); - const char *str = cmdline_get_value(key); + const char *str = cmdline_get_value(key, 1, NULL); if (str) return string_to_path(g_strdup(str)); @@ -578,7 +585,7 @@ char *cmdline_get_path(const char *key, const char *def, const char *description char **cmdline_get_list(const char *key, const char *def, const char *description) { cmdline_usage_append(key, "list", description); - const char *str = cmdline_get_value(key); + const char *str = cmdline_get_value(key, 1, NULL); if (str) return string_to_array(str, ","); @@ -589,7 +596,7 @@ char **cmdline_get_list(const char *key, const char *def, const char *descriptio gint64 cmdline_get_time(const char *key, gint64 def, const char *description) { cmdline_usage_append(key, "time", description); - const char *timestring = cmdline_get_value(key); + const char *timestring = cmdline_get_value(key, 1, NULL); gint64 val = def; if (timestring) { @@ -602,7 +609,7 @@ gint64 cmdline_get_time(const char *key, gint64 def, const char *description) int cmdline_get_int(const char *key, int def, const char *description) { cmdline_usage_append(key, "int", description); - const char *str = cmdline_get_value(key); + const char *str = cmdline_get_value(key, 1, NULL); if (str) return atoi(str); @@ -613,7 +620,7 @@ int cmdline_get_int(const char *key, int def, const char *description) double cmdline_get_double(const char *key, double def, const char *description) { cmdline_usage_append(key, "double", description); - const char *str = cmdline_get_value(key); + const char *str = cmdline_get_value(key, 1, NULL); if (str) return atof(str); @@ -624,7 +631,7 @@ double cmdline_get_double(const char *key, double def, const char *description) int cmdline_get_bool(const char *key, int def, const char *description) { cmdline_usage_append(key, "", description); - int idx = cmdline_find_option(key); + int idx = cmdline_find_option(key, 1); if (idx > 0) return true; @@ -634,7 +641,7 @@ int cmdline_get_bool(const char *key, int def, const char *description) bool cmdline_is_set(const char *key) { - return cmdline_get_value(key) != NULL; + return cmdline_get_value(key, 1, NULL) != NULL; } void cmdline_usage_append(const char *key, const char *type, const char *description) diff --git a/src/option_parser.h b/src/option_parser.h index 14ceb6bce..d5574d961 100644 --- a/src/option_parser.h +++ b/src/option_parser.h @@ -20,8 +20,10 @@ int string_parse_maybe_int(const void *data, const char *s, void *ret); void set_defaults(void); void save_settings(struct ini *ini); -void cmdline_load(int argc, char *argv[]); +void cmdline_load(int argc, const char *argv[]); /* for all cmdline_get_* key can be either "-key" or "-key/-longkey" */ +char *cmdline_get_string_offset(const char *key, const char *def, const char *description, + int start, int *found); char *cmdline_get_string(const char *key, const char *def, const char *description); char *cmdline_get_path(const char *key, const char *def, const char *description); char **cmdline_get_list(const char *key, const char *def, const char *description); diff --git a/src/settings.c b/src/settings.c index 94367316c..71152db1e 100644 --- a/src/settings.c +++ b/src/settings.c @@ -110,13 +110,7 @@ static void config_files_add_drop_ins(GPtrArray *config_files, const char *path) * @param path The config path that overrides the default config path. No * drop-in files or other configs are searched. */ -static GPtrArray* get_conf_files(const char *path) { - if (path) { - GPtrArray *result = g_ptr_array_new_full(1, g_free); - g_ptr_array_add(result, g_strdup(path)); - return result; - } - +static GPtrArray* get_conf_files(void) { GPtrArray *config_locations = get_xdg_conf_basedirs(); GPtrArray *config_files = g_ptr_array_new_full(3, g_free); char *dunstrc_location = NULL; @@ -286,15 +280,27 @@ static void process_conf_file(const gpointer conf_fname, gpointer n_success) { ++(*(int *) n_success); } -void load_settings(const char *const path) { - // NOTE: settings_init should be called before if some settings are changed - // But having it here is useful for tests - // Potentially, we could update the settings code and move this somewhere else +void load_settings(char **const config_paths) +{ + // NOTE: settings_init should be called at the start of dunst_main, otherwise + // the cmdline settings would be reset settings_init(); + LOG_D("Setting defaults"); set_defaults(); - GPtrArray *conf_files = get_conf_files(path); + guint length = g_strv_length(config_paths); + + GPtrArray *conf_files; + + if (length != 0) { + conf_files = g_ptr_array_new_full(length, g_free); + for (int i = 0; config_paths[i]; i++) + g_ptr_array_add(conf_files, g_strdup(config_paths[i])); + } else { + // Use default locations + conf_files = get_conf_files(); + } /* Load all conf files and drop-ins, least important first. */ int n_loaded_confs = 0; @@ -309,4 +315,5 @@ void load_settings(const char *const path) { } g_ptr_array_unref(conf_files); } + /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/src/settings.h b/src/settings.h index fcefa5b59..c59c3e89f 100644 --- a/src/settings.h +++ b/src/settings.h @@ -183,7 +183,7 @@ extern struct settings settings; void settings_init(void); -void load_settings(const char *const path); +void load_settings(char **const config_paths); #endif /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ From 4d46e21673937d424edd364ed72a8d1bf11c557c Mon Sep 17 00:00:00 2001 From: bynect <68197565+bynect@users.noreply.github.com> Date: Thu, 2 May 2024 18:34:18 +0200 Subject: [PATCH 02/16] Implement config reloading --- dunstctl | 8 ++++++-- main.c | 2 +- src/dbus.c | 22 ++++++++++++++++++++++ src/dunst.c | 24 +++++++++++++++++++++++- src/dunst.h | 1 + src/log.c | 3 ++- src/notification.c | 2 ++ src/notification.h | 2 +- src/queues.c | 14 +++++++++++++- src/queues.h | 4 ++++ src/rules.c | 5 ++--- src/settings.c | 2 +- 12 files changed, 78 insertions(+), 11 deletions(-) diff --git a/dunstctl b/dunstctl index 97f2dc733..74b29fa95 100755 --- a/dunstctl +++ b/dunstctl @@ -40,6 +40,7 @@ show_help() { help Show this help EOH } + dbus_send_checked() { 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." @@ -60,7 +61,6 @@ property_set() { command -v dbus-send >/dev/null 2>/dev/null || \ die "Command dbus-send not found" - case "${1:-}" in "action") method_call "${DBUS_IFAC_DUNST}.NotificationAction" "uint32:${2:-0}" >/dev/null @@ -199,6 +199,10 @@ case "${1:-}" in busctl --user --json=pretty --no-pager call org.freedesktop.Notifications /org/freedesktop/Notifications org.dunstproject.cmd0 NotificationListHistory 2>/dev/null \ || die "Dunst is not running." ;; + "reload") + shift + method_call "${DBUS_IFAC_DUNST}.ReloadConfig" "array:string:$(for f in "$@"; do printf ",\"%s\"" "$f"; done | cut -c 2-)" >/dev/null + ;; "") die "dunstctl: No command specified. Please consult the usage." ;; @@ -206,5 +210,5 @@ case "${1:-}" in die "dunstctl: unrecognized command '${1:-}'. Please consult the usage." ;; esac -# vim: noexpandtab +# vim: noexpandtab diff --git a/main.c b/main.c index 0f4f6849c..25740f474 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,6 @@ #include "src/dunst.h" -int main(int argc, char *argv[]) +int main(int argc, const char *argv[]) { return dunst_main(argc, argv); } diff --git a/src/dbus.c b/src/dbus.c index e989d5caa..3f26431c3 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -100,6 +100,9 @@ static const char *introspection_xml = " " " " " " + " " + " " + " " " " " " @@ -193,7 +196,10 @@ DBUS_METHOD(dunst_NotificationRemoveFromHistory); DBUS_METHOD(dunst_NotificationShow); DBUS_METHOD(dunst_RuleEnable); DBUS_METHOD(dunst_RuleList); +DBUS_METHOD(dunst_ReloadConfig); DBUS_METHOD(dunst_Ping); + +// NOTE: Keep the names sorted alphabetically static struct dbus_method methods_dunst[] = { {"ContextMenuCall", dbus_cb_dunst_ContextMenuCall}, {"NotificationAction", dbus_cb_dunst_NotificationAction}, @@ -205,6 +211,7 @@ static struct dbus_method methods_dunst[] = { {"NotificationRemoveFromHistory", dbus_cb_dunst_NotificationRemoveFromHistory}, {"NotificationShow", dbus_cb_dunst_NotificationShow}, {"Ping", dbus_cb_dunst_Ping}, + {"ReloadConfig", dbus_cb_dunst_ReloadConfig}, {"RuleEnable", dbus_cb_dunst_RuleEnable}, {"RuleList", dbus_cb_dunst_RuleList}, }; @@ -603,6 +610,21 @@ static void dbus_cb_dunst_RuleEnable(GDBusConnection *connection, g_dbus_connection_flush(connection, NULL, NULL, NULL); } +static void dbus_cb_dunst_ReloadConfig(GDBusConnection *connection, + const gchar *sender, + GVariant *parameters, + GDBusMethodInvocation *invocation) +{ + gchar **configs = NULL; + g_variant_get(parameters, "(^as)", &configs); + + LOG_M("Reloading settings (with the %s files)", configs ? "new" : "old"); + reload(configs); + + g_dbus_method_invocation_return_value(invocation, NULL); + 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, diff --git a/src/dunst.c b/src/dunst.c index 26e48aca1..56ed0876d 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -206,6 +206,29 @@ static void teardown(void) queues_teardown(); draw_deinit(); + + g_strfreev(config_paths); +} + +void reload(char **const configs) +{ + if (configs) { + g_strfreev(config_paths); + config_paths = configs; + } + + pause_signal(NULL); + + setup_done = false; + draw_deinit(); + + load_settings(config_paths); + draw_setup(); + setup_done = true; + + queues_reapply_all_rules(); + + unpause_signal(NULL); } int dunst_main(int argc, const char *argv[]) @@ -285,7 +308,6 @@ int dunst_main(int argc, const char *argv[]) run(GINT_TO_POINTER(DUNST_TIMER)); // The first run() is a scheduled one g_main_loop_run(mainloop); g_clear_pointer(&mainloop, g_main_loop_unref); - g_strfreev(config_paths); /* remove signal handler watches */ g_source_remove(pause_src); diff --git a/src/dunst.h b/src/dunst.h index 545e86248..d4e3b3f85 100644 --- a/src/dunst.h +++ b/src/dunst.h @@ -38,6 +38,7 @@ void dunst_status_int(const enum dunst_status_field field, struct dunst_status dunst_status_get(void); void wake_up(void); +void reload(char **const configs); int dunst_main(int argc, const char *argv[]); diff --git a/src/log.c b/src/log.c index afaeecec9..76eca65ad 100644 --- a/src/log.c +++ b/src/log.c @@ -11,7 +11,8 @@ #include "utils.h" -static GLogLevelFlags log_level = G_LOG_LEVEL_WARNING; +// NOTE: Keep updated with the dunst manual +static GLogLevelFlags log_level = G_LOG_LEVEL_MESSAGE; /* see log.h */ static const char *log_level_to_string(GLogLevelFlags level) diff --git a/src/notification.c b/src/notification.c index 94411c232..961cde3c5 100644 --- a/src/notification.c +++ b/src/notification.c @@ -317,6 +317,8 @@ void notification_unref(struct notification *n) notification_private_free(n->priv); if (n->script_count > 0) { + for (int i = 0; i < n->script_count; i++) + g_free(n->scripts[i]); g_free(n->scripts); } diff --git a/src/notification.h b/src/notification.h index 3ef3ff6be..bd1fb435c 100644 --- a/src/notification.h +++ b/src/notification.h @@ -83,7 +83,7 @@ struct notification { enum markup_mode markup; const char *format; - const char **scripts; + char **scripts; int script_count; struct notification_colors colors; diff --git a/src/queues.c b/src/queues.c index 2849bd755..109babc72 100644 --- a/src/queues.c +++ b/src/queues.c @@ -25,7 +25,7 @@ #include "notification.h" #include "settings.h" #include "utils.h" -#include "output.h" // For checking if wayland is active. +#include "rules.h" /* notification lists */ static GQueue *waiting = NULL; /**< all new notifications get into here */ @@ -617,6 +617,18 @@ struct notification* queues_get_by_id(int id) return NULL; } +void queues_reapply_all_rules(void) +{ + GQueue *recqueues[] = { displayed, waiting, history }; + for (int i = 0; i < sizeof(recqueues)/sizeof(GQueue*); i++) { + for (GList *iter = g_queue_peek_head_link(recqueues[i]); iter; + iter = iter->next) { + struct notification *cur = iter->data; + rule_apply_all(cur); + } + } +} + /** * Helper function for queues_teardown() to free a single notification * diff --git a/src/queues.h b/src/queues.h index 387ef91ee..55e85baf0 100644 --- a/src/queues.h +++ b/src/queues.h @@ -179,6 +179,10 @@ gint64 queues_get_next_datachange(gint64 time); */ struct notification* queues_get_by_id(int id); +/** + * Reapply all rules to the queue (used when reloading configs) + */ +void queues_reapply_all_rules(void); /** * Remove all notifications from all list and free the notifications diff --git a/src/rules.c b/src/rules.c index b3dcf0ff7..630a82514 100644 --- a/src/rules.c +++ b/src/rules.c @@ -82,9 +82,8 @@ void rule_apply(struct rule *r, struct notification *n) n->receiving_raw_icon = false; } if (r->script) { - n->scripts = g_renew(const char*,n->scripts,n->script_count + 1); - n->scripts[n->script_count] = r->script; - + n->scripts = g_renew(char *, n->scripts, n->script_count + 1); + n->scripts[n->script_count] = g_strdup(r->script); n->script_count++; } if (r->set_stack_tag) { diff --git a/src/settings.c b/src/settings.c index 71152db1e..c884f83da 100644 --- a/src/settings.c +++ b/src/settings.c @@ -307,7 +307,7 @@ void load_settings(char **const config_paths) g_ptr_array_foreach(conf_files, process_conf_file, &n_loaded_confs); if (0 == n_loaded_confs) - LOG_I("No configuration file found, using defaults"); + LOG_M("No configuration file found, using defaults"); for (GSList *iter = rules; iter; iter = iter->next) { struct rule *r = iter->data; From 8556ba682c1c5bc21fbf111d2123293635d9ee87 Mon Sep 17 00:00:00 2001 From: bynect <68197565+bynect@users.noreply.github.com> Date: Thu, 2 May 2024 18:47:38 +0200 Subject: [PATCH 03/16] Add wayland cleanup --- src/wayland/wl.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/wayland/wl.c b/src/wayland/wl.c index e97e35662..ec6133e4b 100644 --- a/src/wayland/wl.c +++ b/src/wayland/wl.c @@ -332,10 +332,10 @@ void wl_deinit(void) { // could have been aborted half way through, or the compositor doesn't // support some of these features. if (ctx.layer_surface != NULL) { - zwlr_layer_surface_v1_destroy(ctx.layer_surface); + g_clear_pointer(&ctx.layer_surface, zwlr_layer_surface_v1_destroy); } if (ctx.surface != NULL) { - wl_surface_destroy(ctx.surface); + g_clear_pointer(&ctx.surface, wl_surface_destroy); } finish_buffer(&ctx.buffers[0]); finish_buffer(&ctx.buffers[1]); @@ -352,38 +352,41 @@ void wl_deinit(void) { destroy_seat(seat); } + ctx.outputs = (struct wl_list) {0}; + ctx.seats = (struct wl_list) {0}; + #ifdef HAVE_WL_CURSOR_SHAPE if (ctx.cursor_shape_manager) - wp_cursor_shape_manager_v1_destroy(ctx.cursor_shape_manager); + g_clear_pointer(&ctx.cursor_shape_manager, wp_cursor_shape_manager_v1_destroy); #endif #ifdef HAVE_WL_EXT_IDLE_NOTIFY if (ctx.ext_idle_notifier) - ext_idle_notifier_v1_destroy(ctx.ext_idle_notifier); + g_clear_pointer(&ctx.ext_idle_notifier, ext_idle_notifier_v1_destroy); #endif if (ctx.idle_handler) - org_kde_kwin_idle_destroy(ctx.idle_handler); + g_clear_pointer(&ctx.idle_handler, org_kde_kwin_idle_destroy); if (ctx.layer_shell) - zwlr_layer_shell_v1_destroy(ctx.layer_shell); + g_clear_pointer(&ctx.layer_shell, zwlr_layer_shell_v1_destroy); if (ctx.compositor) - wl_compositor_destroy(ctx.compositor); + g_clear_pointer(&ctx.compositor, wl_compositor_destroy); if (ctx.shm) - wl_shm_destroy(ctx.shm); + g_clear_pointer(&ctx.shm, wl_shm_destroy); if (ctx.registry) - wl_registry_destroy(ctx.registry); + g_clear_pointer(&ctx.registry, wl_registry_destroy); if (ctx.cursor_theme != NULL) { - wl_cursor_theme_destroy(ctx.cursor_theme); - wl_surface_destroy(ctx.cursor_surface); + g_clear_pointer(&ctx.cursor_theme, wl_cursor_theme_destroy); + g_clear_pointer(&ctx.cursor_surface, wl_surface_destroy); } // this also disconnects the wl_display - g_water_wayland_source_free(ctx.esrc); + g_clear_pointer(&ctx.esrc, g_water_wayland_source_free); } static void schedule_frame_and_commit(void); From 8c8011ba73f2afea7e69aaa3404ca9bbfad531c7 Mon Sep 17 00:00:00 2001 From: bynect <68197565+bynect@users.noreply.github.com> Date: Thu, 2 May 2024 19:00:21 +0200 Subject: [PATCH 04/16] Update tests --- test/option_parser.c | 2 +- test/setting.c | 20 ++++++++++---------- test/test.c | 7 ++++--- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/test/option_parser.c b/test/option_parser.c index 9f0b5fd2b..10ecee74f 100644 --- a/test/option_parser.c +++ b/test/option_parser.c @@ -965,7 +965,7 @@ SUITE(suite_option_parser) int argc; char **argv; g_shell_parse_argv(&cmdline[0], &argc, &argv, NULL); - cmdline_load(argc, argv); + cmdline_load(argc, (const char **)argv); RUN_TEST(test_cmdline_get_string); RUN_TEST(test_cmdline_get_list); RUN_TEST(test_cmdline_get_path); diff --git a/test/setting.c b/test/setting.c index 5b73c7094..5083cadb1 100644 --- a/test/setting.c +++ b/test/setting.c @@ -9,11 +9,11 @@ extern const char *base; // In this suite a few dunstrc's are tested to see if the settings code works // This file is called setting.c, since the name settings.c caused issues. -char *config_path; +char *config_paths[2] = {0}; TEST test_dunstrc_markup(void) { - config_path = g_strconcat(base, "/data/dunstrc.markup", NULL); - load_settings(config_path); + config_paths[0] = g_strconcat(base, "/data/dunstrc.markup", NULL); + load_settings(config_paths); ASSERT_STR_EQ(settings.font, "Monospace 8"); @@ -24,13 +24,13 @@ TEST test_dunstrc_markup(void) { ASSERT_STR_EQ(e_format, got_format); ASSERT(settings.indicate_hidden); - g_free(config_path); + g_clear_pointer(&config_paths[0], g_free); PASS(); } TEST test_dunstrc_nomarkup(void) { - config_path = g_strconcat(base, "/data/dunstrc.nomarkup", NULL); - load_settings(config_path); + config_paths[0] = g_strconcat(base, "/data/dunstrc.nomarkup", NULL); + load_settings(config_paths); ASSERT_STR_EQ(settings.font, "Monospace 8"); @@ -41,7 +41,7 @@ TEST test_dunstrc_nomarkup(void) { ASSERT_STR_EQ(e_format, got_format); ASSERT(settings.indicate_hidden); - g_free(config_path); + g_clear_pointer(&config_paths[0], g_free); PASS(); } @@ -50,11 +50,11 @@ TEST test_dunstrc_defaults(void) { struct settings s_default; struct settings s_dunstrc; - config_path = g_strconcat(base, "/data/dunstrc.default", NULL); + config_paths[0] = g_strconcat(base, "/data/dunstrc.default", NULL); set_defaults(); s_default = settings; - load_settings(config_path); + load_settings(config_paths); s_dunstrc = settings; ASSERT_EQ(s_default.corner_radius, s_dunstrc.corner_radius); @@ -105,7 +105,7 @@ TEST test_dunstrc_defaults(void) { /* printf("%zu\n", offset); */ } - g_free(config_path); + g_clear_pointer(&config_paths[0], g_free); PASS(); } diff --git a/test/test.c b/test/test.c index 0e6312548..5d4e42907 100644 --- a/test/test.c +++ b/test/test.c @@ -48,8 +48,9 @@ int main(int argc, char *argv[]) { // initialize settings - char *config_path = g_strconcat(base, "/data/dunstrc.default", NULL); - load_settings(config_path); + char **configs = g_malloc0(2 * sizeof(char *)); + configs[0] = g_strconcat(base, "/data/dunstrc.default", NULL); + load_settings(configs); GREATEST_MAIN_BEGIN(); RUN_SUITE(suite_utils); @@ -71,7 +72,7 @@ int main(int argc, char *argv[]) { RUN_SUITE(suite_input); base = NULL; - g_free(config_path); + g_strfreev(configs); free(prog); // this returns the error code From 467544dbf0b969c98e03e2679963bc8094519ad7 Mon Sep 17 00:00:00 2001 From: bynect <68197565+bynect@users.noreply.github.com> Date: Thu, 2 May 2024 19:25:26 +0200 Subject: [PATCH 05/16] Various fixes --- dunstctl | 2 +- src/dbus.c | 2 -- src/dunst.c | 22 ++++++++++++---------- src/option_parser.c | 9 +++------ src/option_parser.h | 4 ++-- src/settings.c | 5 +---- 6 files changed, 19 insertions(+), 25 deletions(-) diff --git a/dunstctl b/dunstctl index 74b29fa95..289ccc890 100755 --- a/dunstctl +++ b/dunstctl @@ -201,7 +201,7 @@ case "${1:-}" in ;; "reload") shift - method_call "${DBUS_IFAC_DUNST}.ReloadConfig" "array:string:$(for f in "$@"; do printf ",\"%s\"" "$f"; done | cut -c 2-)" >/dev/null + method_call "${DBUS_IFAC_DUNST}.ReloadConfig" "array:string:$(IFS=','; echo "$*")" >/dev/null ;; "") die "dunstctl: No command specified. Please consult the usage." diff --git a/src/dbus.c b/src/dbus.c index 3f26431c3..0bfdf38a5 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -617,8 +617,6 @@ static void dbus_cb_dunst_ReloadConfig(GDBusConnection *connection, { gchar **configs = NULL; g_variant_get(parameters, "(^as)", &configs); - - LOG_M("Reloading settings (with the %s files)", configs ? "new" : "old"); reload(configs); g_dbus_method_invocation_return_value(invocation, NULL); diff --git a/src/dunst.c b/src/dunst.c index 56ed0876d..56d013916 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -212,7 +212,10 @@ static void teardown(void) void reload(char **const configs) { - if (configs) { + guint length = g_strv_length(configs); + LOG_M("Reloading settings (with the %s files)", length != 0 ? "new" : "old"); + + if (length != 0) { g_strfreev(config_paths); config_paths = configs; } @@ -233,7 +236,6 @@ void reload(char **const configs) int dunst_main(int argc, const char *argv[]) { - dunst_status_int(S_PAUSE_LEVEL, 0); dunst_status(S_IDLE, false); @@ -253,16 +255,16 @@ int dunst_main(int argc, const char *argv[]) log_set_level_from_string(verbosity); g_free(verbosity); - GStrvBuilder *builder = g_strv_builder_new(); - char *path = NULL; - int start = 1; + cmdline_usage_append("-conf/-config", "string", "Path to configuration file"); + + int start = 1, count = 1; + while (cmdline_get_string_offset("-conf/-config", NULL, start, &start)) + count++; - while ((path = cmdline_get_string_offset("-conf/-config", NULL, - "Path to configuration file", start, &start))) - g_strv_builder_add(builder, path); + config_paths = g_malloc0(sizeof(char *) * count); + start = 1, count = 0; - config_paths = g_strv_builder_end(builder); - g_strv_builder_unref(builder); + while ((config_paths[count++] = cmdline_get_string_offset("-conf/-config", NULL, start, &start))); settings.print_notifications = cmdline_get_bool("-print/--print", false, "Print notifications to stdout"); diff --git a/src/option_parser.c b/src/option_parser.c index 468a19e24..ae7c98406 100644 --- a/src/option_parser.c +++ b/src/option_parser.c @@ -18,9 +18,7 @@ static int cmdline_argc; static const char **cmdline_argv; - static char *usage_str = NULL; -static void cmdline_usage_append(const char *key, const char *type, const char *description); #define STRING_PARSE_RET(string, value) if (STR_EQ(s, string)) { *ret = value; return true; } @@ -552,10 +550,8 @@ static const char *cmdline_get_value(const char *key, int start, int *found) return cmdline_argv[idx + 1]; } -char *cmdline_get_string_offset(const char *key, const char *def, const char *description, - int start, int *found) +char *cmdline_get_string_offset(const char *key, const char *def, int start, int *found) { - cmdline_usage_append(key, "string", description); const char *str = cmdline_get_value(key, start, found); if (str) @@ -568,7 +564,8 @@ char *cmdline_get_string_offset(const char *key, const char *def, const char *de char *cmdline_get_string(const char *key, const char *def, const char *description) { - return cmdline_get_string_offset(key, def, description, 1, NULL); + cmdline_usage_append(key, "string", description); + return cmdline_get_string_offset(key, def, 1, NULL); } char *cmdline_get_path(const char *key, const char *def, const char *description) diff --git a/src/option_parser.h b/src/option_parser.h index d5574d961..5a6ac5fe8 100644 --- a/src/option_parser.h +++ b/src/option_parser.h @@ -22,8 +22,7 @@ void save_settings(struct ini *ini); void cmdline_load(int argc, const char *argv[]); /* for all cmdline_get_* key can be either "-key" or "-key/-longkey" */ -char *cmdline_get_string_offset(const char *key, const char *def, const char *description, - int start, int *found); +char *cmdline_get_string_offset(const char *key, const char *def, int start, int *found); char *cmdline_get_string(const char *key, const char *def, const char *description); char *cmdline_get_path(const char *key, const char *def, const char *description); char **cmdline_get_list(const char *key, const char *def, const char *description); @@ -31,6 +30,7 @@ int cmdline_get_int(const char *key, int def, const char *description); double cmdline_get_double(const char *key, double def, const char *description); int cmdline_get_bool(const char *key, int def, const char *description); bool cmdline_is_set(const char *key); +void cmdline_usage_append(const char *key, const char *type, const char *description); const char *cmdline_create_usage(void); #endif diff --git a/src/settings.c b/src/settings.c index c884f83da..cf6b8302c 100644 --- a/src/settings.c +++ b/src/settings.c @@ -106,9 +106,6 @@ static void config_files_add_drop_ins(GPtrArray *config_files, const char *path) * drop-ins and puts their locations in a GPtrArray, @e most important last. * * The returned GPtrArray and it's elements are owned by the caller. - * - * @param path The config path that overrides the default config path. No - * drop-in files or other configs are searched. */ static GPtrArray* get_conf_files(void) { GPtrArray *config_locations = get_xdg_conf_basedirs(); @@ -298,7 +295,7 @@ void load_settings(char **const config_paths) for (int i = 0; config_paths[i]; i++) g_ptr_array_add(conf_files, g_strdup(config_paths[i])); } else { - // Use default locations + // Use default locations (and search drop-ins) conf_files = get_conf_files(); } From af141182e6ce79316c456dad355416d9dbdee0ea Mon Sep 17 00:00:00 2001 From: bynect <68197565+bynect@users.noreply.github.com> Date: Sat, 4 May 2024 00:37:34 +0200 Subject: [PATCH 06/16] Update completions and manual --- .github/workflows/main.yml | 1 + .valgrind.suppressions | 15 +++++++++++++++ completions/_dunstctl.zshcomp | 17 +++++++++-------- completions/dunstctl.bashcomp | 2 +- completions/dunstctl.fishcomp | 13 ++++++++----- docs/dunstctl.pod | 14 +++++++++++--- dunstctl | 28 +++++++++++++++++++--------- main.c | 2 +- src/dbus.c | 10 +++++----- 9 files changed, 70 insertions(+), 32 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bc140dd16..4840cbbbc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,6 +32,7 @@ jobs: - fedora - ubuntu-focal - ubuntu-jammy + - ubuntu-noble env: CC: ${{ matrix.CC }} diff --git a/.valgrind.suppressions b/.valgrind.suppressions index 50236d37f..28f4e9c4f 100644 --- a/.valgrind.suppressions +++ b/.valgrind.suppressions @@ -179,3 +179,18 @@ fun:notification_load_icon_with_scaling ... } + +{ + # Something new on Ubuntu Noble Numbat + rsvg_conditional_jump-ubuntu_focal + Memcheck:Cond + obj:/usr/lib/*/librsvg-2.so.2.50.0 + ... + fun:rsvg_handle_new + obj:/usr/lib/*/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-svg.so + obj:/usr/lib/*/libgdk_pixbuf-2.0.so.0.4200.10 + fun:gdk_pixbuf_new_from_file + fun:notification_setup_raw_image + fun:notification_load_icon_with_scaling + ... +} diff --git a/completions/_dunstctl.zshcomp b/completions/_dunstctl.zshcomp index 57a66604c..fb8fd2c33 100644 --- a/completions/_dunstctl.zshcomp +++ b/completions/_dunstctl.zshcomp @@ -1,6 +1,6 @@ #compdef _dunstctl dunstctl -# ZSH arguments completion script for the dunstctl commnd +# ZSH arguments completion script for the dunstctl command # Depends on: gAWK (rule), jq (history-pop) local curcontext="$curcontext" ret=1 @@ -17,20 +17,21 @@ case $state in local -a commands 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' + 'close:Close the last notification or optionally the notification with given ID' + 'close-all:Close all the notifications' 'context:Open context menu' 'count:Show the number of notifications' 'history:Display notification history (in JSON)' - 'history-pop:Pop the latest notification from history or optionally the notification with given ID.' - 'is-paused:Check if dunst is running or paused' + 'history-pop:Pop the latest notification from history or optionally the notification with given ID' + 'is-paused:Check if pause level is greater than 0' 'set-paused:Set the pause status' - 'get-pause-level:Get current dunst's pause level' - 'set-pause-level:Set current dunst's pause level' + 'get-pause-level:Get the current pause level' + 'set-pause-level:Set the pause level' 'rule:Enable or disable a rule by its name' 'rules:Displays configured rules' + 'reload:Reload the settings of the running instance, optionally with specific configuration files' 'debug:Print debugging information' - 'help:Show this help' + 'help:Show help' ) _describe commands commands && ret=0 ;; diff --git a/completions/dunstctl.bashcomp b/completions/dunstctl.bashcomp index 821ca021d..d59449fdb 100644 --- a/completions/dunstctl.bashcomp +++ b/completions/dunstctl.bashcomp @@ -2,7 +2,7 @@ _dunstctl() { local opts cur prev _get_comp_words_by_ref cur prev COMPREPLY=() - opts='action close close-all context count debug help history history-clear history-pop history-rm is-paused rule rules set-paused' + opts='action close close-all context count debug help history history-clear history-pop history-rm is-paused rule rules set-paused get-pause-level set-pause-level reload' case "$prev" in count) COMPREPLY=( $( compgen -W 'displayed history waiting' -- "$cur" ) ) diff --git a/completions/dunstctl.fishcomp b/completions/dunstctl.fishcomp index 6d4a99b22..7532dfca3 100644 --- a/completions/dunstctl.fishcomp +++ b/completions/dunstctl.fishcomp @@ -14,23 +14,26 @@ end # commands complete -c dunstctl -f -n __fish_use_subcommand -a action -d 'Perform the default action, or open the context menu of the notification at the given position' -complete -c dunstctl -f -n __fish_use_subcommand -a close -d 'Close the last notification' -complete -c dunstctl -f -n __fish_use_subcommand -a close-all -d 'Close the all notifications' +complete -c dunstctl -f -n __fish_use_subcommand -a close -d 'Close the last notification or optionally the notification with given ID' +complete -c dunstctl -f -n __fish_use_subcommand -a close-all -d 'Close all the notifications' complete -c dunstctl -f -n __fish_use_subcommand -a context -d 'Open context menu' complete -c dunstctl -f -n __fish_use_subcommand -a count -d 'Show the number of notifications' complete -c dunstctl -f -n __fish_use_subcommand -a history -d 'Display notification history (in JSON)' complete -c dunstctl -f -n __fish_use_subcommand -a history-clear -d 'Delete all notifications from history' complete -c dunstctl -f -n __fish_use_subcommand -a history-pop -d 'Pop the latest notification from history or optionally the notification with given ID' 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 is-paused -d 'Check if pause level is greater than 0' 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 get-pause-level -d 'Get the current pause level' +complete -c dunstctl -f -n __fish_use_subcommand -a set-pause-level -d 'Set the pause level' 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 reload -d 'Reload the settings of the running instance, optionally with specific configuration files' 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' +complete -c dunstctl -f -n __fish_use_subcommand -a help -d 'Show help' # command specific arguments -complete -c dunstctl -x -n '__fish_seen_subcommand_from action close close-all context history history-clear is-paused debug help' +complete -c dunstctl -x -n '__fish_seen_subcommand_from action close close-all context history history-clear is-paused get-pause-level set-pause-level reload debug help' complete -c dunstctl -x -n '__fish_seen_subcommand_from count' -a 'displayed history waiting' 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' diff --git a/docs/dunstctl.pod b/docs/dunstctl.pod index 626972674..909fd7d16 100644 --- a/docs/dunstctl.pod +++ b/docs/dunstctl.pod @@ -15,15 +15,16 @@ dunstctl COMMAND [PARAMETER] 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). +notification being 0). -=item B +=item B [ID] Close the topmost notification currently being displayed. +You can optionally pass an ID to close the matching notification (if present). =item B -Close all notifications currently being displayed +Close all notifications currently being displayed. =item B @@ -89,6 +90,13 @@ to temporarily activate or deactivate specific rules. Exports all currently configured rules (optionally JSON formatted). +=item B [dunstrc ...] + +Reload the settings of the running dunst instance. You can optionally specify +which configuration files to use. Otherwise, the previous one will be used. +When dunst is reloaded all the rules are reapplied, however previosly modified +notifications will not be reverted back. + =item B Tries to contact dunst and checks for common faults between dunstctl and dunst. diff --git a/dunstctl b/dunstctl index 289ccc890..5c82fa7d8 100755 --- a/dunstctl +++ b/dunstctl @@ -18,29 +18,32 @@ show_help() { 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 + close [ID] Close the last notification or the + notification with given ID + close-all Close all the notifications context Open context menu count [displayed|history|waiting] Show the number of notifications history Display notification history (in JSON) history-clear Delete all notifications from history history-pop [ID] Pop the latest notification from history or optionally the - notification with given ID. + notification with given ID history-rm ID Remove the notification from history with given ID. - is-paused Check if pause level is > 0 + is-paused Check if pause level is greater than 0 set-paused true|false|toggle Set the pause status 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 [--json] Displays configured rules (optionally in JSON) + reload [dunstrc ...] Reload the settings of the running + instance, optionally with specific + configuration files debug Print debugging information - help Show this help + help Show help EOH } - dbus_send_checked() { 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." @@ -61,12 +64,19 @@ property_set() { command -v dbus-send >/dev/null 2>/dev/null || \ die "Command dbus-send not found" + case "${1:-}" in "action") method_call "${DBUS_IFAC_DUNST}.NotificationAction" "uint32:${2:-0}" >/dev/null ;; "close") - method_call "${DBUS_IFAC_DUNST}.NotificationCloseLast" >/dev/null + if [ $# -eq 1 ]; then + method_call "${DBUS_IFAC_DUNST}.NotificationCloseLast" >/dev/null + elif [ $# -eq 2 ]; then + method_call "${DBUS_IFAC_FDN}.CloseNotification" "uint32:$2" >/dev/null + else + die "Please pass the right number of arguments. Close takes 0 or 1 arguments" + fi ;; "close-all") method_call "${DBUS_IFAC_DUNST}.NotificationCloseAll" >/dev/null @@ -201,7 +211,7 @@ case "${1:-}" in ;; "reload") shift - method_call "${DBUS_IFAC_DUNST}.ReloadConfig" "array:string:$(IFS=','; echo "$*")" >/dev/null + method_call "${DBUS_IFAC_DUNST}.ConfigReload" "array:string:$(IFS=','; echo "$*")" >/dev/null ;; "") die "dunstctl: No command specified. Please consult the usage." @@ -210,5 +220,5 @@ case "${1:-}" in die "dunstctl: unrecognized command '${1:-}'. Please consult the usage." ;; esac - # vim: noexpandtab + diff --git a/main.c b/main.c index 25740f474..0f4f6849c 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,6 @@ #include "src/dunst.h" -int main(int argc, const char *argv[]) +int main(int argc, char *argv[]) { return dunst_main(argc, argv); } diff --git a/src/dbus.c b/src/dbus.c index 0bfdf38a5..314fb7a7f 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -100,7 +100,7 @@ static const char *introspection_xml = " " " " " " - " " + " " " " " " " " @@ -196,11 +196,12 @@ DBUS_METHOD(dunst_NotificationRemoveFromHistory); DBUS_METHOD(dunst_NotificationShow); DBUS_METHOD(dunst_RuleEnable); DBUS_METHOD(dunst_RuleList); -DBUS_METHOD(dunst_ReloadConfig); +DBUS_METHOD(dunst_ConfigReload); DBUS_METHOD(dunst_Ping); // NOTE: Keep the names sorted alphabetically static struct dbus_method methods_dunst[] = { + {"ConfigReload", dbus_cb_dunst_ConfigReload}, {"ContextMenuCall", dbus_cb_dunst_ContextMenuCall}, {"NotificationAction", dbus_cb_dunst_NotificationAction}, {"NotificationClearHistory", dbus_cb_dunst_NotificationClearHistory}, @@ -211,7 +212,6 @@ static struct dbus_method methods_dunst[] = { {"NotificationRemoveFromHistory", dbus_cb_dunst_NotificationRemoveFromHistory}, {"NotificationShow", dbus_cb_dunst_NotificationShow}, {"Ping", dbus_cb_dunst_Ping}, - {"ReloadConfig", dbus_cb_dunst_ReloadConfig}, {"RuleEnable", dbus_cb_dunst_RuleEnable}, {"RuleList", dbus_cb_dunst_RuleList}, }; @@ -610,7 +610,7 @@ static void dbus_cb_dunst_RuleEnable(GDBusConnection *connection, g_dbus_connection_flush(connection, NULL, NULL, NULL); } -static void dbus_cb_dunst_ReloadConfig(GDBusConnection *connection, +static void dbus_cb_dunst_ConfigReload(GDBusConnection *connection, const gchar *sender, GVariant *parameters, GDBusMethodInvocation *invocation) @@ -1103,7 +1103,7 @@ gboolean dbus_cb_dunst_Properties_Set(GDBusConnection *connection, int targetPauseLevel = -1; if (STR_EQ(property_name, "paused")) { if (g_variant_get_boolean(value)) { - targetPauseLevel = MAX_PAUSE_LEVEL; + targetPauseLevel = MAX_PAUSE_LEVEL; } else { targetPauseLevel = 0; } From 996ba9ef0e27d26d49db9b9cfe17c84d387d417f Mon Sep 17 00:00:00 2001 From: bynect <68197565+bynect@users.noreply.github.com> Date: Tue, 7 May 2024 12:12:19 +0200 Subject: [PATCH 07/16] Readibility fixes --- main.c | 2 +- src/dunst.c | 9 +++++++-- src/dunst.h | 2 +- src/notification.c | 6 +----- src/option_parser.c | 4 ++-- src/option_parser.h | 2 +- src/rules.c | 3 ++- 7 files changed, 15 insertions(+), 13 deletions(-) diff --git a/main.c b/main.c index 25740f474..0f4f6849c 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,6 @@ #include "src/dunst.h" -int main(int argc, const char *argv[]) +int main(int argc, char *argv[]) { return dunst_main(argc, argv); } diff --git a/src/dunst.c b/src/dunst.c index 56d013916..cb14bfb14 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -234,7 +234,7 @@ void reload(char **const configs) unpause_signal(NULL); } -int dunst_main(int argc, const char *argv[]) +int dunst_main(int argc, char *argv[]) { dunst_status_int(S_PAUSE_LEVEL, 0); dunst_status(S_IDLE, false); @@ -261,10 +261,15 @@ int dunst_main(int argc, const char *argv[]) while (cmdline_get_string_offset("-conf/-config", NULL, start, &start)) count++; + // Leaves an extra space for the NULL config_paths = g_malloc0(sizeof(char *) * count); start = 1, count = 0; + char *path = NULL; - while ((config_paths[count++] = cmdline_get_string_offset("-conf/-config", NULL, start, &start))); + do { + path = cmdline_get_string_offset("-conf/-config", NULL, start, &start); + config_paths[count++] = path; + } while (path != NULL); settings.print_notifications = cmdline_get_bool("-print/--print", false, "Print notifications to stdout"); diff --git a/src/dunst.h b/src/dunst.h index d4e3b3f85..f2a4e4e3a 100644 --- a/src/dunst.h +++ b/src/dunst.h @@ -40,7 +40,7 @@ struct dunst_status dunst_status_get(void); void wake_up(void); void reload(char **const configs); -int dunst_main(int argc, const char *argv[]); +int dunst_main(int argc, char *argv[]); void usage(int exit_status); void print_version(void); diff --git a/src/notification.c b/src/notification.c index 961cde3c5..e8398bd16 100644 --- a/src/notification.c +++ b/src/notification.c @@ -316,11 +316,7 @@ void notification_unref(struct notification *n) notification_private_free(n->priv); - if (n->script_count > 0) { - for (int i = 0; i < n->script_count; i++) - g_free(n->scripts[i]); - g_free(n->scripts); - } + g_strfreev(n->scripts); g_free(n); } diff --git a/src/option_parser.c b/src/option_parser.c index ae7c98406..17192559e 100644 --- a/src/option_parser.c +++ b/src/option_parser.c @@ -17,7 +17,7 @@ #include "settings_data.h" static int cmdline_argc; -static const char **cmdline_argv; +static char **cmdline_argv; static char *usage_str = NULL; #define STRING_PARSE_RET(string, value) if (STR_EQ(s, string)) { *ret = value; return true; } @@ -507,7 +507,7 @@ void save_settings(struct ini *ini) { } } -void cmdline_load(int argc, const char *argv[]) +void cmdline_load(int argc, char *argv[]) { cmdline_argc = argc; cmdline_argv = argv; diff --git a/src/option_parser.h b/src/option_parser.h index 5a6ac5fe8..fe3f10c61 100644 --- a/src/option_parser.h +++ b/src/option_parser.h @@ -20,7 +20,7 @@ int string_parse_maybe_int(const void *data, const char *s, void *ret); void set_defaults(void); void save_settings(struct ini *ini); -void cmdline_load(int argc, const char *argv[]); +void cmdline_load(int argc, char *argv[]); /* for all cmdline_get_* key can be either "-key" or "-key/-longkey" */ char *cmdline_get_string_offset(const char *key, const char *def, int start, int *found); char *cmdline_get_string(const char *key, const char *def, const char *description); diff --git a/src/rules.c b/src/rules.c index 630a82514..63d682f49 100644 --- a/src/rules.c +++ b/src/rules.c @@ -82,8 +82,9 @@ void rule_apply(struct rule *r, struct notification *n) n->receiving_raw_icon = false; } if (r->script) { - n->scripts = g_renew(char *, n->scripts, n->script_count + 1); + n->scripts = g_renew(char *, n->scripts, n->script_count + 2); n->scripts[n->script_count] = g_strdup(r->script); + n->scripts[n->script_count + 1] = NULL; n->script_count++; } if (r->set_stack_tag) { From 9d693f5ffd2ee7d06a2658304e40109fbf87e08f Mon Sep 17 00:00:00 2001 From: bynect <68197565+bynect@users.noreply.github.com> Date: Tue, 7 May 2024 12:57:47 +0200 Subject: [PATCH 08/16] Add functional test for reload --- docs/dunstctl.pod | 2 +- test/functional-tests/script_test.sh | 2 +- test/functional-tests/test.sh | 40 +++++++++++++++++++++++++--- test/option_parser.c | 2 +- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/docs/dunstctl.pod b/docs/dunstctl.pod index 909fd7d16..3bddbe8f1 100644 --- a/docs/dunstctl.pod +++ b/docs/dunstctl.pod @@ -63,7 +63,7 @@ will be kept but not shown until it is unpaused. =item B true/false/toggle Set the paused status of dunst. If false, dunst is running normally, if true, -dunst is paused (with maximum pause level of 100). +dunst is paused (with maximum pause level of 100). See the is-paused command and the dunst man page for more information. =item B diff --git a/test/functional-tests/script_test.sh b/test/functional-tests/script_test.sh index 8e21d4023..705c3802d 100755 --- a/test/functional-tests/script_test.sh +++ b/test/functional-tests/script_test.sh @@ -1,4 +1,4 @@ #!/bin/bash echo "triggered script!" -$DUNSTIFY "Success" "ooooh yeah" +dunstify "Success" "ooooh yeah" diff --git a/test/functional-tests/test.sh b/test/functional-tests/test.sh index a66009a71..5150b97cf 100755 --- a/test/functional-tests/test.sh +++ b/test/functional-tests/test.sh @@ -3,11 +3,12 @@ # prefix should be the root of the repository PREFIX="${PREFIX:-../..}" TESTDIR="$PREFIX/test/functional-tests" +DUNST="${DUNST:-$PREFIX/dunst}" +DUNSTIFY="${DUNSTIFY:-$PREFIX/dunstify}" +DUNSTCTL="${DUSNTCTL:-$PREFIX/dunstctl}" # for run_script export PATH="$TESTDIR:$PATH" -export DUNST="${DUNST:-$PREFIX/dunst}" -export DUNSTIFY="${DUNSTIFY:-$PREFIX/dunstify}" function keypress { echo "press enter to continue..." @@ -20,7 +21,7 @@ function tmp_dunstrc { } function tmp_clean { - rm "$TESTDIR/dunstrc.tmp" + rm "$TESTDIR/dunstrc.tmp" } function start_dunst { @@ -371,6 +372,38 @@ function vertical_align { done } +function hot_reload { + echo "###################################" + echo "hot_reload" + echo "###################################" + + tmp_dunstrc dunstrc.default "background = \"#313131\"" + start_dunst dunstrc.tmp + + $DUNSTIFY -a "dunst tester" "Nice notification" "Will not change style after keypress" + $DUNSTIFY -a "dunst tester" --hints string:category:change "Change" "Will change only after the first keypress" + keypress + + tmp_dunstrc dunstrc.default " + [change] + category = change + background = \"#525\" + set_category = notchange + urgency = critical + " + $DUNSTCTL reload + keypress + + $DUNSTCTL reload + keypress + + $DUNSTCTL reload "$TESTDIR/dunstrc.hot_reload" + $DUNSTIFY -a "dunst tester" "New notification" "Now we are using another config file" + keypress + + tmp_clean +} + if [ -n "$1" ]; then while [ -n "$1" ]; do $1 @@ -393,6 +426,7 @@ else separator_click dynamic_height vertical_align + hot_reload fi killall dunst diff --git a/test/option_parser.c b/test/option_parser.c index 10ecee74f..9f0b5fd2b 100644 --- a/test/option_parser.c +++ b/test/option_parser.c @@ -965,7 +965,7 @@ SUITE(suite_option_parser) int argc; char **argv; g_shell_parse_argv(&cmdline[0], &argc, &argv, NULL); - cmdline_load(argc, (const char **)argv); + cmdline_load(argc, argv); RUN_TEST(test_cmdline_get_string); RUN_TEST(test_cmdline_get_list); RUN_TEST(test_cmdline_get_path); From 6053e03563a0bcf3af82c165c01e17fff8d5ae50 Mon Sep 17 00:00:00 2001 From: bynect <68197565+bynect@users.noreply.github.com> Date: Wed, 29 May 2024 01:01:54 +0200 Subject: [PATCH 09/16] Save original notification --- src/dbus.c | 20 ++++-- src/notification.c | 10 +++ src/notification.h | 7 +- src/rules.c | 160 ++++++++++++++++++++++++++++++++++---------- src/rules.h | 1 + src/settings.c | 14 +--- src/settings_data.h | 1 + 7 files changed, 158 insertions(+), 55 deletions(-) diff --git a/src/dbus.c b/src/dbus.c index 314fb7a7f..989698144 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -810,29 +810,41 @@ static struct notification *dbus_message_to_notification(const gchar *sender, GV // Modify these values after the notification is initialized and all rules are applied. if ((dict_value = g_variant_lookup_value(hints, "fgcolor", G_VARIANT_TYPE_STRING))) { struct color c; - if (string_parse_color(g_variant_get_string(dict_value, NULL), &c)) + if (string_parse_color(g_variant_get_string(dict_value, NULL), &c)) { + notification_keep_original(n); + if (!COLOR_VALID(n->original->fg)) n->original->fg = n->colors.fg; n->colors.fg = c; + } g_variant_unref(dict_value); } if ((dict_value = g_variant_lookup_value(hints, "bgcolor", G_VARIANT_TYPE_STRING))) { struct color c; - if (string_parse_color(g_variant_get_string(dict_value, NULL), &c)) + if (string_parse_color(g_variant_get_string(dict_value, NULL), &c)) { + notification_keep_original(n); + if (!COLOR_VALID(n->original->bg)) n->original->bg = n->colors.bg; n->colors.bg = c; + } g_variant_unref(dict_value); } if ((dict_value = g_variant_lookup_value(hints, "frcolor", G_VARIANT_TYPE_STRING))) { struct color c; - if (string_parse_color(g_variant_get_string(dict_value, NULL), &c)) + if (string_parse_color(g_variant_get_string(dict_value, NULL), &c)) { + notification_keep_original(n); + if (!COLOR_VALID(n->original->fc)) n->original->fc = n->colors.frame; n->colors.frame = c; + } g_variant_unref(dict_value); } if ((dict_value = g_variant_lookup_value(hints, "hlcolor", G_VARIANT_TYPE_STRING))) { struct color c; - if (string_parse_color(g_variant_get_string(dict_value, NULL), &c)) + if (string_parse_color(g_variant_get_string(dict_value, NULL), &c)) { + notification_keep_original(n); + if (!COLOR_VALID(n->original->highlight)) n->original->highlight = n->colors.highlight; n->colors.highlight = c; + } g_variant_unref(dict_value); } diff --git a/src/notification.c b/src/notification.c index e8398bd16..e8b0bf17a 100644 --- a/src/notification.c +++ b/src/notification.c @@ -25,6 +25,7 @@ #include "utils.h" #include "draw.h" #include "icon-lookup.h" +#include "settings_data.h" static void notification_extract_urls(struct notification *n); static void notification_format_message(struct notification *n); @@ -293,6 +294,8 @@ void notification_unref(struct notification *n) if (!g_atomic_int_dec_and_test(&n->priv->refcount)) return; + g_free(n->original); + g_free(n->appname); g_free(n->summary); g_free(n->body); @@ -768,4 +771,11 @@ void notification_invalidate_actions(struct notification *n) { g_hash_table_remove_all(n->actions); } +void notification_keep_original(struct notification *n) +{ + if (n->original) return; + n->original = g_malloc0(sizeof(struct rule)); + *n->original = empty_rule; +} + /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/src/notification.h b/src/notification.h index bd1fb435c..ab432efaa 100644 --- a/src/notification.h +++ b/src/notification.h @@ -51,6 +51,9 @@ struct notification { char *dbus_client; bool dbus_valid; + // We keep the original notification properties here when it is modified + struct rule *original; + char *appname; char *summary; char *body; @@ -242,7 +245,7 @@ void notification_open_url(struct notification *n); /** * Open the context menu for the notification. - * + * * Convenience function that creates the GList and passes it to context_menu_for(). */ void notification_open_context_menu(struct notification *n); @@ -266,5 +269,7 @@ const char *notification_urgency_to_string(const enum urgency urgency); */ const char *enum_to_string_fullscreen(enum behavior_fullscreen in); +void notification_keep_original(struct notification *n); + #endif /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/src/rules.c b/src/rules.c index 63d682f49..9a5c19fb3 100644 --- a/src/rules.c +++ b/src/rules.c @@ -14,66 +14,105 @@ GSList *rules = NULL; +#define APPLY(nprop, rprop, defval) \ + do { \ + if (n->original->rprop == (defval)) n->original->rprop = n->nprop; \ + n->nprop = r->rprop; \ + } while (0) + /* * Apply rule to notification. */ void rule_apply(struct rule *r, struct notification *n) { + notification_keep_original(n); + if (r->timeout != -1) - n->timeout = r->timeout; + APPLY(timeout, timeout, -1); if (r->override_dbus_timeout != -1) - n->dbus_timeout = r->override_dbus_timeout; + APPLY(dbus_timeout, override_dbus_timeout, -1); if (r->urgency != URG_NONE) - n->urgency = r->urgency; + APPLY(urgency, urgency, URG_NONE); if (r->fullscreen != FS_NULL) - n->fullscreen = r->fullscreen; + APPLY(fullscreen, fullscreen, FS_NULL); if (r->history_ignore != -1) - n->history_ignore = r->history_ignore; + APPLY(history_ignore, history_ignore, -1); if (r->set_transient != -1) - n->transient = r->set_transient; + APPLY(transient, set_transient, -1); if (r->skip_display != -1) - n->skip_display = r->skip_display; + APPLY(skip_display, skip_display, -1); if (r->word_wrap != -1) - n->word_wrap = r->word_wrap; + APPLY(word_wrap, word_wrap, -1); if (r->ellipsize != -1) - n->ellipsize = r->ellipsize; + APPLY(ellipsize, ellipsize, -1); if (r->alignment != -1) - n->alignment = r->alignment; + APPLY(alignment, alignment, -1); if (r->hide_text != -1) - n->hide_text = r->hide_text; + APPLY(hide_text, hide_text, -1); if (r->progress_bar_alignment != -1) - n->progress_bar_alignment = r->progress_bar_alignment; + APPLY(progress_bar_alignment, progress_bar_alignment, -1); if (r->min_icon_size != -1) - n->min_icon_size = r->min_icon_size; + APPLY(min_icon_size, min_icon_size, -1); if (r->max_icon_size != -1) - n->max_icon_size = r->max_icon_size; - if (r->action_name) { - g_free(n->default_action_name); - n->default_action_name = g_strdup(r->action_name); - } - if (r->set_category) { - g_free(n->category); - n->category = g_strdup(r->set_category); - } + APPLY(max_icon_size, max_icon_size, -1); if (r->markup != MARKUP_NULL) - n->markup = r->markup; + APPLY(markup, markup, MARKUP_NULL); if (r->icon_position != -1) - n->icon_position = r->icon_position; - if (COLOR_VALID(r->fg)) + APPLY(icon_position, icon_position, -1); + if (COLOR_VALID(r->fg)) { + if (!COLOR_VALID(n->original->fg)) n->original->fg = n->colors.fg; n->colors.fg = r->fg; - if (COLOR_VALID(r->bg)) + } + if (COLOR_VALID(r->bg)) { + if (!COLOR_VALID(n->original->bg)) n->original->bg = n->colors.bg; n->colors.bg = r->bg; - if (COLOR_VALID(r->highlight)) + } + if (COLOR_VALID(r->highlight)) { + if (!COLOR_VALID(n->original->highlight)) n->original->highlight = n->colors.highlight; n->colors.highlight = r->highlight; - if (COLOR_VALID(r->fc)) + } + if (COLOR_VALID(r->fc)) { + if (!COLOR_VALID(n->original->fc)) n->original->fc = n->colors.frame; n->colors.frame = r->fc; + } if (r->format) - n->format = r->format; + APPLY(format, format, NULL); + if (r->action_name) { + if (n->original->action_name) + g_free(n->default_action_name); + else + n->original->action_name = n->default_action_name; + + n->default_action_name = g_strdup(r->action_name); + } + if (r->set_category) { + if (n->original->set_category) + g_free(n->category); + else + n->original->set_category = n->category; + + n->category = g_strdup(r->set_category); + } if (r->default_icon) { - g_free(n->default_icon_name); + if (n->original->default_icon) + g_free(n->default_icon_name); + else + n->original->default_icon = n->default_icon_name; + n->default_icon_name = g_strdup(r->default_icon); } + if (r->set_stack_tag) { + if (n->original->set_stack_tag) + g_free(n->stack_tag); + else + n->original->set_stack_tag = n->stack_tag; + + n->stack_tag = g_strdup(r->set_stack_tag); + } if (r->new_icon) { + if (!n->original->new_icon) + n->original->new_icon = g_strdup(n->iconname); + // FIXME This is not efficient when the icon is replaced // multiple times for the same notification. To fix this, a // separate variable is needed to track if the icon is @@ -87,13 +126,60 @@ void rule_apply(struct rule *r, struct notification *n) n->scripts[n->script_count + 1] = NULL; n->script_count++; } - if (r->set_stack_tag) { - g_free(n->stack_tag); - n->stack_tag = g_strdup(r->set_stack_tag); - } - if (r->override_pause_level != -1) { - n->override_pause_level = r->override_pause_level; - } + if (r->override_pause_level != -1) + APPLY(override_pause_level, override_pause_level, -1); +} + +void rule_print(const struct rule *r) +{ + printf("{\n"); + printf("\tname: '%s'\n", STR_NN(r->name)); + printf("\tenabled: %d\n", r->enabled); + + // filters + if (r->appname != NULL) printf("\tappname: '%s'\n", r->appname); + if (r->summary != NULL) printf("\tsummary: '%s'\n", r->summary); + if (r->body != NULL) printf("\tbody: '%s'\n", r->body); + if (r->icon != NULL) printf("\ticon: '%s'\n", r->icon); + if (r->category != NULL) printf("\tcategory: '%s'\n", r->category); + if (r->msg_urgency != URG_NONE) printf("\tmsg_urgency: '%s'\n", notification_urgency_to_string(r->msg_urgency)); + if (r->stack_tag != NULL) printf("\tstack_tag: '%s'\n", r->stack_tag); + if (r->desktop_entry != NULL) printf("\tdesktop_entry: '%s'\n", r->desktop_entry); + if (r->match_dbus_timeout != -1) printf("\tmatch_dbus_timeout: %ld\n", r->match_dbus_timeout); + if (r->match_transient != -1) printf("\tmatch_transient: %d\n", r->match_transient); + + // modifiers + if (r->timeout != -1) printf("\ttimeout: %ld\n", r->timeout); + if (r->override_dbus_timeout != -1) printf("\toverride_dbus_timeout: %ld\n", r->override_dbus_timeout); + if (r->markup != -1) printf("\tmarkup: %d\n", r->markup); + if (r->action_name != NULL) printf("\taction_name: '%s'\n", r->action_name); + if (r->urgency != URG_NONE) printf("\turgency: '%s'\n", notification_urgency_to_string(r->urgency)); + if (r->history_ignore != -1) printf("\thistory_ignore: %d\n", r->history_ignore); + if (r->set_transient != -1) printf("\tset_transient: %d\n", r->set_transient); + if (r->skip_display != -1) printf("\tskip_display: %d\n", r->skip_display); + if (r->word_wrap != -1) printf("\tword_wrap: %d\n", r->word_wrap); + if (r->ellipsize != -1) printf("\tellipsize: %d\n", r->ellipsize); + if (r->alignment != -1) printf("\talignment: %d\n", r->alignment); + if (r->hide_text != -1) printf("\thide_text: %d\n", r->hide_text); + if (r->icon_position != -1) printf("\ticon_position: %d\n", r->icon_position); + if (r->min_icon_size != -1) printf("\tmin_icon_size: %d\n", r->min_icon_size); + if (r->max_icon_size != -1) printf("\tmax_icon_size: %d\n", r->max_icon_size); + if (r->override_pause_level != -1) printf("\toverride_pause_level: %d\n", r->override_pause_level); + if (r->new_icon != NULL) printf("\tnew_icon: '%s'\n", r->new_icon); + if (r->default_icon != NULL) printf("\tdefault_icon: '%s'\n", r->default_icon); + + char buf[10]; + if (COLOR_VALID(r->fg)) printf("\tfg: %s\n", color_to_string(r->fg, buf)); + if (COLOR_VALID(r->bg)) printf("\tbg: %s\n", color_to_string(r->bg, buf)); + if (COLOR_VALID(r->highlight)) printf("\thighlight: %s\n", color_to_string(r->highlight, buf)); + if (COLOR_VALID(r->fc)) printf("\tframe: %s\n", color_to_string(r->fc, buf)); + if (r->set_category != NULL) printf("\tset_category: '%s'\n", r->set_category); + if (r->format != NULL) printf("\tformat: '%s'\n", r->format); + if (r->script != NULL) printf("\tscript: '%s'\n", r->script); + if (r->fullscreen != FS_NULL) printf("\tfullscreen: %s\n", enum_to_string_fullscreen(r->fullscreen)); + if (r->progress_bar_alignment != -1) printf("\tprogress_bar_alignment: %d\n", r->progress_bar_alignment); + if (r->set_stack_tag != NULL) printf("\tset_stack_tag: %s\n", r->set_stack_tag); + printf("}\n"); } /* diff --git a/src/rules.h b/src/rules.h index b8e1339b0..f5684ac81 100644 --- a/src/rules.h +++ b/src/rules.h @@ -75,6 +75,7 @@ extern GSList *rules; */ struct rule *rule_new(const char *name); +void rule_print(const struct rule *r); void rule_apply(struct rule *r, struct notification *n); void rule_apply_all(struct notification *n); bool rule_matches_notification(struct rule *r, struct notification *n); diff --git a/src/settings.c b/src/settings.c index cb1fe814a..b33e01ce9 100644 --- a/src/settings.c +++ b/src/settings.c @@ -151,18 +151,6 @@ void settings_init(void) { } } -// NOTE: This function is probably outdated and no longer relevant -// Since it does not even fully display rule information we -// may want to remove it or change it -void print_rule(struct rule* r) { - LOG_D("Rule %s", STR_NN(r->name)); - LOG_D("summary %s", STR_NN(r->summary)); - LOG_D("appname %s", STR_NN(r->appname)); - LOG_D("script %s", STR_NN(r->script)); - char buf[10]; - LOG_D("frame %s", STR_NN(color_to_string(r->fc, buf))); -} - void check_and_correct_settings(struct settings *s) { #ifndef ENABLE_WAYLAND @@ -311,7 +299,7 @@ void load_settings(char **const config_paths) for (GSList *iter = rules; iter; iter = iter->next) { struct rule *r = iter->data; - print_rule(r); + rule_print(r); } g_ptr_array_unref(conf_files); } diff --git a/src/settings_data.h b/src/settings_data.h index 93fd092e9..ece96b798 100644 --- a/src/settings_data.h +++ b/src/settings_data.h @@ -168,6 +168,7 @@ static const struct rule empty_rule = { .progress_bar_alignment = -1, .min_icon_size = -1, .max_icon_size = -1, + .fullscreen = FS_NULL, .override_pause_level = -1 }; From 1d05558e9f50603d671ed0c90ce209ff6c015f94 Mon Sep 17 00:00:00 2001 From: bynect <68197565+bynect@users.noreply.github.com> Date: Wed, 29 May 2024 01:10:36 +0200 Subject: [PATCH 10/16] Prevent memleak --- src/notification.c | 9 ++++++++- src/queues.c | 1 + src/settings.c | 9 +++++---- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/notification.c b/src/notification.c index e8b0bf17a..1d2689680 100644 --- a/src/notification.c +++ b/src/notification.c @@ -294,7 +294,14 @@ void notification_unref(struct notification *n) if (!g_atomic_int_dec_and_test(&n->priv->refcount)) return; - g_free(n->original); + if (n->original) { + g_free(n->original->action_name); + g_free(n->original->set_category); + g_free(n->original->default_icon); + g_free(n->original->set_stack_tag); + g_free(n->original->new_icon); + g_free(n->original); + } g_free(n->appname); g_free(n->summary); diff --git a/src/queues.c b/src/queues.c index 109babc72..a0e6bb5cf 100644 --- a/src/queues.c +++ b/src/queues.c @@ -624,6 +624,7 @@ void queues_reapply_all_rules(void) for (GList *iter = g_queue_peek_head_link(recqueues[i]); iter; iter = iter->next) { struct notification *cur = iter->data; + //if (cur->original) rule_print(cur->original); rule_apply_all(cur); } } diff --git a/src/settings.c b/src/settings.c index b33e01ce9..4b141a893 100644 --- a/src/settings.c +++ b/src/settings.c @@ -297,10 +297,11 @@ void load_settings(char **const config_paths) if (0 == n_loaded_confs) LOG_M("No configuration file found, using defaults"); - for (GSList *iter = rules; iter; iter = iter->next) { - struct rule *r = iter->data; - rule_print(r); - } + // Maybe add an option to display the rules + //for (GSList *iter = rules; iter; iter = iter->next) { + // struct rule *r = iter->data; + // rule_print(r); + //} g_ptr_array_unref(conf_files); } From b6829d9ba99b6dd2b82b45ba7aebd2587dfa2791 Mon Sep 17 00:00:00 2001 From: bynect <68197565+bynect@users.noreply.github.com> Date: Wed, 29 May 2024 01:24:29 +0200 Subject: [PATCH 11/16] Use original notification when reloading --- src/queues.c | 5 ++++- src/rules.c | 43 ++++++++++++++++++++++--------------------- src/rules.h | 2 +- test/icon-lookup.c | 2 +- 4 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/queues.c b/src/queues.c index a0e6bb5cf..287e9baad 100644 --- a/src/queues.c +++ b/src/queues.c @@ -624,7 +624,10 @@ void queues_reapply_all_rules(void) for (GList *iter = g_queue_peek_head_link(recqueues[i]); iter; iter = iter->next) { struct notification *cur = iter->data; - //if (cur->original) rule_print(cur->original); + if (cur->original) { + //rule_print(cur->original); + rule_apply(cur->original, cur, false); + } rule_apply_all(cur); } } diff --git a/src/rules.c b/src/rules.c index 9a5c19fb3..ed94e7948 100644 --- a/src/rules.c +++ b/src/rules.c @@ -16,16 +16,17 @@ GSList *rules = NULL; #define APPLY(nprop, rprop, defval) \ do { \ - if (n->original->rprop == (defval)) n->original->rprop = n->nprop; \ + if (save && n->original->rprop == (defval)) n->original->rprop = n->nprop; \ n->nprop = r->rprop; \ } while (0) /* * Apply rule to notification. + * If save is true the original value will be saved in the notification. */ -void rule_apply(struct rule *r, struct notification *n) +void rule_apply(struct rule *r, struct notification *n, bool save) { - notification_keep_original(n); + if (save) notification_keep_original(n); if (r->timeout != -1) APPLY(timeout, timeout, -1); @@ -60,57 +61,57 @@ void rule_apply(struct rule *r, struct notification *n) if (r->icon_position != -1) APPLY(icon_position, icon_position, -1); if (COLOR_VALID(r->fg)) { - if (!COLOR_VALID(n->original->fg)) n->original->fg = n->colors.fg; + if (save && !COLOR_VALID(n->original->fg)) n->original->fg = n->colors.fg; n->colors.fg = r->fg; } if (COLOR_VALID(r->bg)) { - if (!COLOR_VALID(n->original->bg)) n->original->bg = n->colors.bg; + if (save && !COLOR_VALID(n->original->bg)) n->original->bg = n->colors.bg; n->colors.bg = r->bg; } if (COLOR_VALID(r->highlight)) { - if (!COLOR_VALID(n->original->highlight)) n->original->highlight = n->colors.highlight; + if (save && !COLOR_VALID(n->original->highlight)) n->original->highlight = n->colors.highlight; n->colors.highlight = r->highlight; } if (COLOR_VALID(r->fc)) { - if (!COLOR_VALID(n->original->fc)) n->original->fc = n->colors.frame; + if (save && !COLOR_VALID(n->original->fc)) n->original->fc = n->colors.frame; n->colors.frame = r->fc; } if (r->format) APPLY(format, format, NULL); if (r->action_name) { - if (n->original->action_name) - g_free(n->default_action_name); - else + if (save && n->original->action_name) n->original->action_name = n->default_action_name; + else + g_free(n->default_action_name); n->default_action_name = g_strdup(r->action_name); } if (r->set_category) { - if (n->original->set_category) - g_free(n->category); - else + if (save && n->original->set_category) n->original->set_category = n->category; + else + g_free(n->category); n->category = g_strdup(r->set_category); } if (r->default_icon) { - if (n->original->default_icon) - g_free(n->default_icon_name); - else + if (save && n->original->default_icon) n->original->default_icon = n->default_icon_name; + else + g_free(n->default_icon_name); n->default_icon_name = g_strdup(r->default_icon); } if (r->set_stack_tag) { - if (n->original->set_stack_tag) - g_free(n->stack_tag); - else + if (save && !n->original->set_stack_tag) n->original->set_stack_tag = n->stack_tag; + else + g_free(n->stack_tag); n->stack_tag = g_strdup(r->set_stack_tag); } if (r->new_icon) { - if (!n->original->new_icon) + if (save && !n->original->new_icon) n->original->new_icon = g_strdup(n->iconname); // FIXME This is not efficient when the icon is replaced @@ -190,7 +191,7 @@ void rule_apply_all(struct notification *n) for (GSList *iter = rules; iter; iter = iter->next) { struct rule *r = iter->data; if (rule_matches_notification(r, n)) { - rule_apply(r, n); + rule_apply(r, n, true); } } } diff --git a/src/rules.h b/src/rules.h index f5684ac81..e676f3a5b 100644 --- a/src/rules.h +++ b/src/rules.h @@ -76,7 +76,7 @@ extern GSList *rules; struct rule *rule_new(const char *name); void rule_print(const struct rule *r); -void rule_apply(struct rule *r, struct notification *n); +void rule_apply(struct rule *r, struct notification *n, bool save); void rule_apply_all(struct notification *n); bool rule_matches_notification(struct rule *r, struct notification *n); diff --git a/test/icon-lookup.c b/test/icon-lookup.c index 4dac8aea5..0acb9693f 100644 --- a/test/icon-lookup.c +++ b/test/icon-lookup.c @@ -80,7 +80,7 @@ TEST test_new_icon_overrides_raw_icon(void) { ASSERT(n->icon); int old_width = cairo_image_surface_get_width(n->icon); - rule_apply(rule, n); + rule_apply(rule, n, true); ASSERT(n->icon); ASSERT(old_width != cairo_image_surface_get_width(n->icon)); From e8c7a2c8e3037223ac8766722bbb00ddf4808b83 Mon Sep 17 00:00:00 2001 From: bynect <68197565+bynect@users.noreply.github.com> Date: Wed, 29 May 2024 10:56:28 +0200 Subject: [PATCH 12/16] Update docs --- docs/dunstctl.pod | 4 ++-- src/rules.c | 3 +++ test/functional-tests/test.sh | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/dunstctl.pod b/docs/dunstctl.pod index 3bddbe8f1..e08262566 100644 --- a/docs/dunstctl.pod +++ b/docs/dunstctl.pod @@ -94,8 +94,8 @@ Exports all currently configured rules (optionally JSON formatted). Reload the settings of the running dunst instance. You can optionally specify which configuration files to use. Otherwise, the previous one will be used. -When dunst is reloaded all the rules are reapplied, however previosly modified -notifications will not be reverted back. +When dunst is reloaded all the rules are reapplied to the original notification, +so modifications made by previous rules are not taken into account. =item B diff --git a/src/rules.c b/src/rules.c index ed94e7948..8dfd57667 100644 --- a/src/rules.c +++ b/src/rules.c @@ -122,6 +122,9 @@ void rule_apply(struct rule *r, struct notification *n, bool save) n->receiving_raw_icon = false; } if (r->script) { + if (save && !n->original->script && n->script_count > 0) + n->original->script = n->scripts[0]; + n->scripts = g_renew(char *, n->scripts, n->script_count + 2); n->scripts[n->script_count] = g_strdup(r->script); n->scripts[n->script_count + 1] = NULL; diff --git a/test/functional-tests/test.sh b/test/functional-tests/test.sh index 5150b97cf..a401eac61 100755 --- a/test/functional-tests/test.sh +++ b/test/functional-tests/test.sh @@ -380,8 +380,8 @@ function hot_reload { tmp_dunstrc dunstrc.default "background = \"#313131\"" start_dunst dunstrc.tmp - $DUNSTIFY -a "dunst tester" "Nice notification" "Will not change style after keypress" - $DUNSTIFY -a "dunst tester" --hints string:category:change "Change" "Will change only after the first keypress" + $DUNSTIFY -a "dunst tester" "Nice notification" "This will change once" + $DUNSTIFY -a "dunst tester" --hints string:category:change "Change" "And this too" keypress tmp_dunstrc dunstrc.default " From 98a516b7ddf71da4625d2e2705088f3cddfccd64 Mon Sep 17 00:00:00 2001 From: bynect <68197565+bynect@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:29:07 +0200 Subject: [PATCH 13/16] Cleanup --- completions/dunstctl.fishcomp | 3 +- dunstctl | 4 +-- src/queues.c | 1 - src/rules.c | 68 +++++++++++++++-------------------- src/settings.c | 5 --- 5 files changed, 33 insertions(+), 48 deletions(-) diff --git a/completions/dunstctl.fishcomp b/completions/dunstctl.fishcomp index 7532dfca3..d20f7428a 100644 --- a/completions/dunstctl.fishcomp +++ b/completions/dunstctl.fishcomp @@ -33,11 +33,12 @@ complete -c dunstctl -f -n __fish_use_subcommand -a debug -d 'Print debugging in complete -c dunstctl -f -n __fish_use_subcommand -a help -d 'Show help' # command specific arguments -complete -c dunstctl -x -n '__fish_seen_subcommand_from action close close-all context history history-clear is-paused get-pause-level set-pause-level reload debug help' +complete -c dunstctl -x -n '__fish_seen_subcommand_from action close close-all context history history-clear is-paused get-pause-level set-pause-level debug help' complete -c dunstctl -x -n '__fish_seen_subcommand_from count' -a 'displayed history waiting' 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 +complete -c dunstctl -n '__fish_seen_subcommand_from reload' # ex: filetype=fish diff --git a/dunstctl b/dunstctl index 5c82fa7d8..eecd4695b 100755 --- a/dunstctl +++ b/dunstctl @@ -39,7 +39,7 @@ show_help() { in JSON) reload [dunstrc ...] Reload the settings of the running instance, optionally with specific - configuration files + config files (space,comma-separated) debug Print debugging information help Show help EOH @@ -214,7 +214,7 @@ case "${1:-}" in method_call "${DBUS_IFAC_DUNST}.ConfigReload" "array:string:$(IFS=','; echo "$*")" >/dev/null ;; "") - die "dunstctl: No command specified. Please consult the usage." + die "dunstctl: No command specified. Use dunstctl help" ;; *) die "dunstctl: unrecognized command '${1:-}'. Please consult the usage." diff --git a/src/queues.c b/src/queues.c index 287e9baad..64a218022 100644 --- a/src/queues.c +++ b/src/queues.c @@ -625,7 +625,6 @@ void queues_reapply_all_rules(void) iter = iter->next) { struct notification *cur = iter->data; if (cur->original) { - //rule_print(cur->original); rule_apply(cur->original, cur, false); } rule_apply_all(cur); diff --git a/src/rules.c b/src/rules.c index 8dfd57667..e369cf622 100644 --- a/src/rules.c +++ b/src/rules.c @@ -14,11 +14,16 @@ GSList *rules = NULL; -#define APPLY(nprop, rprop, defval) \ - do { \ - if (save && n->original->rprop == (defval)) n->original->rprop = n->nprop; \ +// NOTE: Internal, only for rule_apply(...) + +#define RULE_APPLY2(nprop, rprop, defval) \ + if (r->rprop != (defval)) { \ + if (save && n->original->rprop == (defval)) \ + n->original->rprop = n->nprop; \ n->nprop = r->rprop; \ - } while (0) + } + +#define RULE_APPLY(prop, defval) RULE_APPLY2(prop, prop, defval) /* * Apply rule to notification. @@ -28,38 +33,25 @@ void rule_apply(struct rule *r, struct notification *n, bool save) { if (save) notification_keep_original(n); - if (r->timeout != -1) - APPLY(timeout, timeout, -1); - if (r->override_dbus_timeout != -1) - APPLY(dbus_timeout, override_dbus_timeout, -1); - if (r->urgency != URG_NONE) - APPLY(urgency, urgency, URG_NONE); - if (r->fullscreen != FS_NULL) - APPLY(fullscreen, fullscreen, FS_NULL); - if (r->history_ignore != -1) - APPLY(history_ignore, history_ignore, -1); - if (r->set_transient != -1) - APPLY(transient, set_transient, -1); - if (r->skip_display != -1) - APPLY(skip_display, skip_display, -1); - if (r->word_wrap != -1) - APPLY(word_wrap, word_wrap, -1); - if (r->ellipsize != -1) - APPLY(ellipsize, ellipsize, -1); - if (r->alignment != -1) - APPLY(alignment, alignment, -1); - if (r->hide_text != -1) - APPLY(hide_text, hide_text, -1); - if (r->progress_bar_alignment != -1) - APPLY(progress_bar_alignment, progress_bar_alignment, -1); - if (r->min_icon_size != -1) - APPLY(min_icon_size, min_icon_size, -1); - if (r->max_icon_size != -1) - APPLY(max_icon_size, max_icon_size, -1); - if (r->markup != MARKUP_NULL) - APPLY(markup, markup, MARKUP_NULL); - if (r->icon_position != -1) - APPLY(icon_position, icon_position, -1); + RULE_APPLY2(dbus_timeout, override_dbus_timeout, -1); + RULE_APPLY2(transient, set_transient, -1); + + RULE_APPLY(timeout, -1); + RULE_APPLY(urgency, URG_NONE); + RULE_APPLY(fullscreen, FS_NULL); + RULE_APPLY(history_ignore, -1); + RULE_APPLY(skip_display, -1); + RULE_APPLY(word_wrap, -1); + RULE_APPLY(ellipsize, -1); + RULE_APPLY(alignment, -1); + RULE_APPLY(hide_text, -1); + RULE_APPLY(progress_bar_alignment, -1); + RULE_APPLY(min_icon_size, -1); + RULE_APPLY(max_icon_size, -1); + RULE_APPLY(markup, MARKUP_NULL); + RULE_APPLY(icon_position, -1); + RULE_APPLY(override_pause_level, -1); + if (COLOR_VALID(r->fg)) { if (save && !COLOR_VALID(n->original->fg)) n->original->fg = n->colors.fg; n->colors.fg = r->fg; @@ -77,7 +69,7 @@ void rule_apply(struct rule *r, struct notification *n, bool save) n->colors.frame = r->fc; } if (r->format) - APPLY(format, format, NULL); + RULE_APPLY(format, NULL); if (r->action_name) { if (save && n->original->action_name) n->original->action_name = n->default_action_name; @@ -130,8 +122,6 @@ void rule_apply(struct rule *r, struct notification *n, bool save) n->scripts[n->script_count + 1] = NULL; n->script_count++; } - if (r->override_pause_level != -1) - APPLY(override_pause_level, override_pause_level, -1); } void rule_print(const struct rule *r) diff --git a/src/settings.c b/src/settings.c index 4b141a893..6982271ac 100644 --- a/src/settings.c +++ b/src/settings.c @@ -297,11 +297,6 @@ void load_settings(char **const config_paths) if (0 == n_loaded_confs) LOG_M("No configuration file found, using defaults"); - // Maybe add an option to display the rules - //for (GSList *iter = rules; iter; iter = iter->next) { - // struct rule *r = iter->data; - // rule_print(r); - //} g_ptr_array_unref(conf_files); } From bbf6d519b78742c7839a150ea9785fa357c3aea4 Mon Sep 17 00:00:00 2001 From: bynect <68197565+bynect@users.noreply.github.com> Date: Tue, 2 Jul 2024 13:27:16 +0200 Subject: [PATCH 14/16] Save only the original configs --- src/dunst.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/dunst.c b/src/dunst.c index cb14bfb14..8d8bdd4e5 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -215,17 +215,12 @@ void reload(char **const configs) guint length = g_strv_length(configs); LOG_M("Reloading settings (with the %s files)", length != 0 ? "new" : "old"); - if (length != 0) { - g_strfreev(config_paths); - config_paths = configs; - } - pause_signal(NULL); setup_done = false; draw_deinit(); - load_settings(config_paths); + load_settings(configs); draw_setup(); setup_done = true; From 2ee7449a65549284d00c59f6f8beb10744977450 Mon Sep 17 00:00:00 2001 From: bynect <68197565+bynect@users.noreply.github.com> Date: Wed, 10 Jul 2024 12:41:51 +0200 Subject: [PATCH 15/16] Update dunstctl.pod --- docs/dunstctl.pod | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/dunstctl.pod b/docs/dunstctl.pod index e08262566..a07eeb48d 100644 --- a/docs/dunstctl.pod +++ b/docs/dunstctl.pod @@ -93,7 +93,8 @@ Exports all currently configured rules (optionally JSON formatted). =item B [dunstrc ...] Reload the settings of the running dunst instance. You can optionally specify -which configuration files to use. Otherwise, the previous one will be used. +which configuration files to use. Otherwise, the files specified by the first invocation +of dunst will be reloaded. When dunst is reloaded all the rules are reapplied to the original notification, so modifications made by previous rules are not taken into account. From 703bc5a9ac511f7f2a76e69fa848021f62500bad Mon Sep 17 00:00:00 2001 From: nect <68197565+bynect@users.noreply.github.com> Date: Mon, 15 Jul 2024 09:23:02 +0000 Subject: [PATCH 16/16] Fix --- docs/dunstctl.pod | 2 +- dunstctl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dunstctl.pod b/docs/dunstctl.pod index a07eeb48d..f6c93cef4 100644 --- a/docs/dunstctl.pod +++ b/docs/dunstctl.pod @@ -93,7 +93,7 @@ Exports all currently configured rules (optionally JSON formatted). =item B [dunstrc ...] Reload the settings of the running dunst instance. You can optionally specify -which configuration files to use. Otherwise, the files specified by the first invocation +which configuration files to use. Otherwise, the config specified by the first invocation of dunst will be reloaded. When dunst is reloaded all the rules are reapplied to the original notification, so modifications made by previous rules are not taken into account. diff --git a/dunstctl b/dunstctl index eecd4695b..b78cd1b4f 100755 --- a/dunstctl +++ b/dunstctl @@ -39,7 +39,7 @@ show_help() { in JSON) reload [dunstrc ...] Reload the settings of the running instance, optionally with specific - config files (space,comma-separated) + config files (space/comma-separated) debug Print debugging information help Show help EOH