diff --git a/docs/dunst.pod b/docs/dunst.pod index b8470d8..0973fa8 100644 --- a/docs/dunst.pod +++ b/docs/dunst.pod @@ -230,7 +230,8 @@ If set to true, display notifications with higher urgency above the others. =item B (default: 0) -Don't timeout notifications if user is idle longer than this value (in seconds). +Don't timeout notifications if user is idle longer than this time. +See TIME FORMAT for valid times. Set to 0 to disable. @@ -338,7 +339,8 @@ Defines how the text should be aligned within the notification. =item B (default: -1) -Show age of message if message is older than this value (in seconds). +Show age of message if message is older than this time. +See TIME FORMAT for valid times. Set to -1 to disable. @@ -533,8 +535,9 @@ See COLORS for more information =item B<-lto/nto/cto secs> -Defines the timeout time(in seconds) for low, normal and critical notifications +Defines the timeout time for low, normal and critical notifications respectively. +See TIME FORMAT for valid times. =back @@ -661,6 +664,15 @@ If there is exactly one associated action, or one is marked as default, that one is invoked. If there are multiple, the context menu is shown. The same applies to URLs when there are no actions. +=head1 TIME FORMAT + +A time can be any decimal integer value suffixed with a time unit. If no unit +given, seconds ("s") is taken as default. + +Time units understood by dunst are "ms", "s", "m", "h" and "d". + +Example time: "1000ms" "10m" + =head1 MISCELLANEOUS Dunst can be paused by sending a notification with a summary of diff --git a/src/option_parser.c b/src/option_parser.c index de76ef5..bbab16c 100644 --- a/src/option_parser.c +++ b/src/option_parser.c @@ -123,6 +123,18 @@ char *ini_get_string(const char *section, const char *key, const char *def) return def ? g_strdup(def) : NULL; } +gint64 ini_get_time(const char *section, const char *key, gint64 def) +{ + const char *timestring = get_value(section, key); + gint64 val = def; + + if (timestring) { + val = string_to_time(timestring); + } + + return val; +} + int ini_get_int(const char *section, const char *key, int def) { const char *value = get_value(section, key); @@ -371,6 +383,19 @@ char *cmdline_get_path(const char *key, const char *def, const char *description return string_to_path(g_strdup(def)); } +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); + gint64 val = def; + + if (timestring) { + val = string_to_time(timestring); + } + + return val; +} + int cmdline_get_int(const char *key, int def, const char *description) { cmdline_usage_append(key, "int", description); @@ -451,6 +476,16 @@ char *option_get_string(const char *ini_section, } +gint64 option_get_time(const char *ini_section, + const char *ini_key, + const char *cmdline_key, + gint64 def, + const char *description) +{ + gint64 ini_val = ini_get_time(ini_section, ini_key, def); + return cmdline_get_time(cmdline_key, ini_val, description); +} + int option_get_int(const char *ini_section, const char *ini_key, const char *cmdline_key, diff --git a/src/option_parser.h b/src/option_parser.h index b499434..e816b7e 100644 --- a/src/option_parser.h +++ b/src/option_parser.h @@ -2,12 +2,14 @@ #ifndef DUNST_OPTION_PARSER_H #define DUNST_OPTION_PARSER_H +#include #include #include int load_ini_file(FILE *); char *ini_get_path(const char *section, const char *key, const char *def); char *ini_get_string(const char *section, const char *key, const char *def); +gint64 ini_get_time(const char *section, const char *key, gint64 def); int ini_get_int(const char *section, const char *key, int def); double ini_get_double(const char *section, const char *key, double def); int ini_get_bool(const char *section, const char *key, int def); @@ -34,6 +36,11 @@ char *option_get_path(const char *ini_section, const char *cmdline_key, const char *def, const char *description); +gint64 option_get_time(const char *ini_section, + const char *ini_key, + const char *cmdline_key, + gint64 def, + const char *description); int option_get_int(const char *ini_section, const char *ini_key, const char *cmdline_key, diff --git a/src/settings.c b/src/settings.c index e81221b..4f66bc9 100644 --- a/src/settings.c +++ b/src/settings.c @@ -197,7 +197,7 @@ void load_settings(char *cmdline_config_path) "Ignore newline characters in notifications" ); - settings.idle_threshold = G_USEC_PER_SEC * option_get_int( + settings.idle_threshold = option_get_time( "global", "idle_threshold", "-idle_threshold", idle_threshold, "Don't timeout notifications if user is longer idle than threshold" @@ -279,7 +279,7 @@ void load_settings(char *cmdline_config_path) } } - settings.show_age_threshold = G_USEC_PER_SEC * option_get_int( + settings.show_age_threshold = option_get_time( "global", "show_age_threshold", "-show_age_threshold", show_age_threshold, "When should the age of the notification be displayed?" @@ -490,7 +490,7 @@ void load_settings(char *cmdline_config_path) "Frame color for notifications with low urgency" ); - settings.timeouts[LOW] = G_USEC_PER_SEC * option_get_int( + settings.timeouts[LOW] = option_get_time( "urgency_low", "timeout", "-lto", timeouts[LOW], "Timeout for notifications with low urgency" @@ -520,7 +520,7 @@ void load_settings(char *cmdline_config_path) "Frame color for notifications with normal urgency" ); - settings.timeouts[NORM] = G_USEC_PER_SEC * option_get_int( + settings.timeouts[NORM] = option_get_time( "urgency_normal", "timeout", "-nto", timeouts[NORM], "Timeout for notifications with normal urgency" @@ -550,7 +550,7 @@ void load_settings(char *cmdline_config_path) "Frame color for notifications with critical urgency" ); - settings.timeouts[CRIT] = G_USEC_PER_SEC * option_get_int( + settings.timeouts[CRIT] = option_get_time( "urgency_critical", "timeout", "-cto", timeouts[CRIT], "Timeout for notifications with critical urgency" @@ -637,7 +637,7 @@ void load_settings(char *cmdline_config_path) r->body = ini_get_string(cur_section, "body", r->body); r->icon = ini_get_string(cur_section, "icon", r->icon); r->category = ini_get_string(cur_section, "category", r->category); - r->timeout = G_USEC_PER_SEC * ini_get_int(cur_section, "timeout", r->timeout); + r->timeout = ini_get_time(cur_section, "timeout", r->timeout); { char *c = ini_get_string( diff --git a/src/utils.c b/src/utils.c index d838c53..417cfe8 100644 --- a/src/utils.c +++ b/src/utils.c @@ -2,7 +2,9 @@ #define _GNU_SOURCE #include "utils.h" +#include #include +#include #include #include #include @@ -122,6 +124,48 @@ char *string_to_path(char *string) { return string; } +gint64 string_to_time(const char *string) { + + assert(string); + + errno = 0; + char *endptr; + gint64 val = strtoll(string, &endptr, 10); + + if (errno != 0) { + fprintf(stderr, "ERROR: Time: '%s': %s.\n", string, strerror(errno)); + return 0; + } + else if (string == endptr) { + fprintf(stderr, "ERROR: Time: No digits found.\n"); + return 0; + } + else if (errno != 0 && val == 0) { + fprintf(stderr, "ERROR: Time: '%s' unknown error.\n", string); + return 0; + } + else if (errno == 0 && !*endptr) { + return val * G_USEC_PER_SEC; + } + + // endptr may point to a separating space + while (*endptr == ' ') + endptr++; + + if (0 == strncmp(endptr, "ms", 2)) + return val * 1000; + else if (0 == strncmp(endptr, "s", 1)) + return val * G_USEC_PER_SEC; + else if (0 == strncmp(endptr, "m", 1)) + return val * G_USEC_PER_SEC * 60; + else if (0 == strncmp(endptr, "h", 1)) + return val * G_USEC_PER_SEC * 60 * 60; + else if (0 == strncmp(endptr, "d", 1)) + return val * G_USEC_PER_SEC * 60 * 60 * 24; + else + return 0; +} + void die(char *text, int exit_value) { fputs(text, stderr); diff --git a/src/utils.h b/src/utils.h index 9b46a1b..ab45a74 100644 --- a/src/utils.h +++ b/src/utils.h @@ -2,6 +2,8 @@ #ifndef DUNST_UTILS_H #define DUNST_UTILS_H +#include + /* replace all occurences of the character needle with the character replacement in haystack */ char *string_replace_char(char needle, char replacement, char *haystack); @@ -27,5 +29,8 @@ void die(char *msg, int exit_value); /* replace tilde and path-specific values with its equivalents */ char *string_to_path(char *string); +/* convert time units (ms, s, m) to internal gint64 microseconds */ +gint64 string_to_time(const char *string); + #endif /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/test/utils.c b/test/utils.c index f0b7969..64b9231 100644 --- a/test/utils.c +++ b/test/utils.c @@ -158,6 +158,20 @@ TEST test_string_to_path(void) PASS(); } +TEST test_string_to_time(void) +{ + char *input[] = { "5000 ms", "5000ms", "100", "10s", "2m", "11h", "9d", "d9", " 5 ms ", NULL }; + gint64 exp[] = { 5000, 5000, 100000, 10000, 120000, 39600000, 777600000, 0, 5, }; + + int i = 0; + while (input[i]){ + ASSERT_EQ_FMT(string_to_time(input[i]), exp[i]*1000, "%ld"); + i++; + } + + PASS(); +} + SUITE(suite_utils) { RUN_TEST(test_string_replace_char); @@ -166,5 +180,6 @@ SUITE(suite_utils) RUN_TEST(test_string_append); RUN_TEST(test_string_strip_delimited); RUN_TEST(test_string_to_path); + RUN_TEST(test_string_to_time); } /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */