From 7a02d8e48a9e6886349314f0cd216d91baffc7cc Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Tue, 29 Aug 2017 01:11:53 +0200 Subject: [PATCH 1/5] Microoptimisation in get_sleep_time Setting the value to INT_MAX instead of 0 allows to remove the boolean check if value even has been set. --- src/dunst.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/dunst.c b/src/dunst.c index 26257ef..d571625 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -181,8 +181,7 @@ static int get_sleep_time(void) return 1; } - bool have_ttl = false; - int min_ttl = 0; + int min_ttl = INT_MAX; int max_age = 0; for (GList *iter = g_queue_peek_head_link(displayed); iter; iter = iter->next) { @@ -191,12 +190,7 @@ static int get_sleep_time(void) max_age = MAX(max_age, notification_get_age(n)); int ttl = notification_get_ttl(n); if (ttl >= 0) { - if (have_ttl) { - min_ttl = MIN(min_ttl, ttl); - } else { - min_ttl = ttl; - have_ttl = true; - } + min_ttl = MIN(min_ttl, ttl); } } @@ -207,11 +201,7 @@ static int get_sleep_time(void) return 1; } - if (!have_ttl) { - min_timeout = show_age_timeout; - } else { - min_timeout = MIN(show_age_timeout, min_ttl); - } + min_timeout = MIN(show_age_timeout, min_ttl); /* show_age_timeout might be negative */ if (min_timeout < 1) { From 956b5c640177dfb20484b925108ffa8c0573330f Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Sun, 17 Sep 2017 01:26:53 +0200 Subject: [PATCH 2/5] Use gint64 as microseconds for internal timeunit time(NULL) is vulnerable against timeshifts by NTP, timezones,... g_get_monotonic_time() is not, but counts its in microseconds with the gint64 datatype. To prevent any conversion bugs, all internal timeformats are now using a gint64 and only get converted during import or export (DBus notification arrives, configuration reading, age threshold display). --- config.def.h | 6 ++--- src/dbus.c | 10 +-------- src/dunst.c | 55 ++++++++++++++++++---------------------------- src/notification.c | 35 ++++++++++++++--------------- src/notification.h | 11 +++++----- src/rules.h | 2 +- src/settings.c | 12 +++++----- src/settings.h | 6 ++--- src/x11/x.c | 2 +- 9 files changed, 58 insertions(+), 81 deletions(-) diff --git a/config.def.h b/config.def.h index 6061851..18411b4 100644 --- a/config.def.h +++ b/config.def.h @@ -10,7 +10,7 @@ char *lowbgcolor = "#aaaaff"; char *lowfgcolor = "#000000"; char *format = "%s %b"; /* default format */ -int timeouts[] = { 10, 10, 0 }; /* low, normal, critical */ +gint64 timeouts[] = { 10*G_USEC_PER_SEC, 10*G_USEC_PER_SEC, 0 }; /* low, normal, critical */ char *icons[] = { "dialog-information", "dialog-information", "dialog-warning" }; /* low, normal, critical */ unsigned int transparency = 0; /* transparency */ @@ -20,8 +20,8 @@ char *class = "Dunst"; /* the class of dunst notification windows */ int shrink = false; /* shrinking */ int sort = true; /* sort messages by urgency */ int indicate_hidden = true; /* show count of hidden messages */ -int idle_threshold = 0; /* don't timeout notifications when idle for x seconds */ -int show_age_threshold = -1; /* show age of notification, when notification is older than x seconds */ +gint64 idle_threshold = 0; /* don't timeout notifications when idle for x seconds */ +gint64 show_age_threshold = -1; /* show age of notification, when notification is older than x seconds */ enum alignment align = left; /* text alignment [left/center/right] */ int sticky_history = true; int history_length = 20; /* max amount of notifications kept in history */ diff --git a/src/dbus.c b/src/dbus.c index 064c233..4893193 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -236,21 +236,13 @@ static void on_notify(GDBusConnection *connection, fflush(stdout); - if (timeout > 0) { - /* do some rounding */ - timeout = (timeout + 500) / 1000; - if (timeout < 1) { - timeout = 1; - } - } - notification *n = notification_create(); n->appname = appname; n->summary = summary; n->body = body; n->icon = icon; n->raw_icon = raw_icon; - n->timeout = timeout; + n->timeout = timeout < 0 ? -1 : timeout * 1000; n->markup = settings.markup; n->progress = (progress < 0 || progress > 100) ? 0 : progress + 1; n->urgency = urgency; diff --git a/src/dunst.c b/src/dunst.c index d571625..7e55f0d 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -12,7 +12,6 @@ #include #include #include -#include #include "dbus.h" #include "menu.h" @@ -70,7 +69,7 @@ void check_timeouts(void) /* don't timeout when user is idle */ if (x_is_idle() && !n->transient) { - n->start = time(NULL); + n->start = g_get_monotonic_time(); continue; } @@ -80,7 +79,7 @@ void check_timeouts(void) } /* remove old message */ - if (difftime(time(NULL), n->start) > n->timeout) { + if (g_get_monotonic_time() - n->start > n->timeout) { notification_close(n, 1); } } @@ -122,7 +121,7 @@ void update_lists() if (!n) return; - n->start = time(NULL); + n->start = g_get_monotonic_time(); if (!n->redisplayed && n->script) { notification_run_script(n); } @@ -173,49 +172,37 @@ void wake_up(void) run(NULL); } -static int get_sleep_time(void) +static gint64 get_sleep_time(void) { + gint64 sleep = G_MAXINT64; - if (settings.show_age_threshold == 0) { - /* we need to update every second */ - return 1; - } - - int min_ttl = INT_MAX; - int max_age = 0; + gint64 max_age = 0; for (GList *iter = g_queue_peek_head_link(displayed); iter; iter = iter->next) { notification *n = iter->data; max_age = MAX(max_age, notification_get_age(n)); - int ttl = notification_get_ttl(n); - if (ttl >= 0) { - min_ttl = MIN(min_ttl, ttl); - } + gint64 ttl = notification_get_ttl(n); + if (ttl >= 0) + sleep = MIN(sleep, ttl); } - int min_timeout; - int show_age_timeout = settings.show_age_threshold - max_age; - - if (show_age_timeout < 1) { - return 1; + /* if age_threshold is hit, seconds have to get updated every second */ + if (settings.show_age_threshold >= 0) { + if (settings.show_age_threshold > max_age) + sleep = MIN(sleep, settings.show_age_threshold - max_age); + else + sleep = MIN(sleep, 1000*1000); } - min_timeout = MIN(show_age_timeout, min_ttl); - - /* show_age_timeout might be negative */ - if (min_timeout < 1) { - return 1; - } else { - return min_timeout; - } + return sleep; } gboolean run(void *data) { update_lists(); static int timeout_cnt = 0; - static int next_timeout = 0; + static gint64 next_timeout = 0; if (data) { timeout_cnt--; @@ -234,13 +221,13 @@ gboolean run(void *data) } if (xctx.visible) { - int now = time(NULL); - int sleep = get_sleep_time(); + gint64 now = g_get_monotonic_time(); + gint64 sleep = get_sleep_time(); + gint64 timeout_at = now + sleep; if (sleep > 0) { - int timeout_at = now + sleep; if (timeout_cnt == 0 || timeout_at < next_timeout) { - g_timeout_add_seconds(sleep, run, mainloop); + g_timeout_add(sleep/1000, run, mainloop); next_timeout = timeout_at; timeout_cnt++; } diff --git a/src/notification.c b/src/notification.c index 6d9a8c0..7e77f9a 100644 --- a/src/notification.c +++ b/src/notification.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -39,7 +38,7 @@ void notification_print(notification *n) printf("\ticon: '%s'\n", n->icon); printf("\traw_icon set: %s\n", (n->raw_icon ? "true" : "false")); printf("\tcategory: %s\n", n->category); - printf("\ttimeout: %d\n", n->timeout); + printf("\ttimeout: %ld\n", n->timeout/1000); printf("\turgency: %d\n", n->urgency); printf("\ttransient: %d\n", n->transient); printf("\tformatted: '%s'\n", n->msg); @@ -478,7 +477,7 @@ int notification_init(notification *n, int id) } else { orig->progress = n->progress; } - orig->start = time(NULL); + orig->start = g_get_monotonic_time(); notification_free(n); wake_up(); return orig->id; @@ -502,10 +501,10 @@ int notification_init(notification *n, int id) } n->timeout = - n->timeout == -1 ? settings.timeouts[n->urgency] : n->timeout; + n->timeout < 0 ? settings.timeouts[n->urgency] : n->timeout; n->start = 0; - n->timestamp = time(NULL); + n->timestamp = g_get_monotonic_time(); n->redisplayed = false; @@ -671,26 +670,26 @@ void notification_update_text_to_render(notification *n) } /* print age */ - int hours, minutes, seconds; - time_t t_delta = time(NULL) - n->timestamp; + gint64 hours, minutes, seconds; + gint64 t_delta = g_get_monotonic_time() - n->timestamp; if (settings.show_age_threshold >= 0 && t_delta >= settings.show_age_threshold) { - hours = t_delta / 3600; - minutes = t_delta / 60 % 60; - seconds = t_delta % 60; + hours = t_delta / G_USEC_PER_SEC / 3600; + minutes = t_delta / G_USEC_PER_SEC / 60 % 60; + seconds = t_delta / G_USEC_PER_SEC % 60; char *new_buf; if (hours > 0) { new_buf = - g_strdup_printf("%s (%dh %dm %ds old)", buf, hours, + g_strdup_printf("%s (%ldh %ldm %lds old)", buf, hours, minutes, seconds); } else if (minutes > 0) { new_buf = - g_strdup_printf("%s (%dm %ds old)", buf, minutes, + g_strdup_printf("%s (%ldm %lds old)", buf, minutes, seconds); } else { - new_buf = g_strdup_printf("%s (%ds old)", buf, seconds); + new_buf = g_strdup_printf("%s (%lds old)", buf, seconds); } g_free(buf); @@ -700,16 +699,16 @@ void notification_update_text_to_render(notification *n) n->text_to_render = buf; } -int notification_get_ttl(notification *n) { - if (n->timeout == 0) { +gint64 notification_get_ttl(notification *n) { + if (n->timeout < 0) { return -1; } else { - return n->timeout - (time(NULL) - n->start); + return n->timeout - (g_get_monotonic_time() - n->start); } } -int notification_get_age(notification *n) { - return time(NULL) - n->timestamp; +gint64 notification_get_age(notification *n) { + return g_get_monotonic_time() - n->timestamp; } /* diff --git a/src/notification.h b/src/notification.h index a76b19d..befb9ce 100644 --- a/src/notification.h +++ b/src/notification.h @@ -4,7 +4,6 @@ #include #include -#include #include "settings.h" @@ -42,9 +41,9 @@ typedef struct _notification { char *text_to_render; const char *format; char *dbus_client; - time_t start; - time_t timestamp; - int timeout; + gint64 start; + gint64 timestamp; + gint64 timeout; int urgency; enum markup_mode markup; bool redisplayed; /* has been displayed before? */ @@ -75,8 +74,8 @@ int notification_close(notification *n, int reason); void notification_print(notification *n); void notification_replace_single_field(char **haystack, char **needle, const char *replacement, enum markup_mode markup_mode); void notification_update_text_to_render(notification *n); -int notification_get_ttl(notification *n); -int notification_get_age(notification *n); +gint64 notification_get_ttl(notification *n); +gint64 notification_get_age(notification *n); void notification_do_action(notification *n); #endif diff --git a/src/rules.h b/src/rules.h index b692b98..a813d98 100644 --- a/src/rules.h +++ b/src/rules.h @@ -19,7 +19,7 @@ typedef struct _rule_t { int msg_urgency; /* actions */ - int timeout; + gint64 timeout; int urgency; enum markup_mode markup; int history_ignore; diff --git a/src/settings.c b/src/settings.c index cee42e0..e81221b 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 = option_get_int( + settings.idle_threshold = G_USEC_PER_SEC * option_get_int( "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 = option_get_int( + settings.show_age_threshold = G_USEC_PER_SEC * option_get_int( "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] = option_get_int( + settings.timeouts[LOW] = G_USEC_PER_SEC * option_get_int( "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] = option_get_int( + settings.timeouts[NORM] = G_USEC_PER_SEC * option_get_int( "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] = option_get_int( + settings.timeouts[CRIT] = G_USEC_PER_SEC * option_get_int( "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 = ini_get_int(cur_section, "timeout", r->timeout); + r->timeout = G_USEC_PER_SEC * ini_get_int(cur_section, "timeout", r->timeout); { char *c = ini_get_string( diff --git a/src/settings.h b/src/settings.h index 337b2d7..0577f42 100644 --- a/src/settings.h +++ b/src/settings.h @@ -30,7 +30,7 @@ typedef struct _settings { char *lowfgcolor; char *lowframecolor; char *format; - int timeouts[3]; + gint64 timeouts[3]; char *icons[3]; unsigned int transparency; char *geom; @@ -39,8 +39,8 @@ typedef struct _settings { int shrink; int sort; int indicate_hidden; - int idle_threshold; - int show_age_threshold; + gint64 idle_threshold; + gint64 show_age_threshold; enum alignment align; int sticky_history; int history_length; diff --git a/src/x11/x.c b/src/x11/x.c index b8a98b8..45dae1c 100644 --- a/src/x11/x.c +++ b/src/x11/x.c @@ -900,7 +900,7 @@ bool x_is_idle(void) if (settings.idle_threshold == 0) { return false; } - return xctx.screensaver_info->idle / 1000 > settings.idle_threshold; + return xctx.screensaver_info->idle > settings.idle_threshold / 1000; } /* TODO move to x_mainloop_* */ From 5d46cd700c79170cc6ebf56f9186b6f338918973 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Tue, 19 Sep 2017 15:11:36 +0200 Subject: [PATCH 3/5] Add option_get_time functions --- docs/dunst.pod | 18 +++++++++++++++--- src/option_parser.c | 35 +++++++++++++++++++++++++++++++++++ src/option_parser.h | 7 +++++++ src/settings.c | 12 ++++++------ src/utils.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/utils.h | 5 +++++ test/utils.c | 15 +++++++++++++++ 7 files changed, 127 insertions(+), 9 deletions(-) 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: */ From 1f0e8d21818690f82589cb8746f1c327061359c1 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Thu, 21 Sep 2017 15:32:54 +0200 Subject: [PATCH 4/5] overhaul get_sleep_time --- src/dunst.c | 34 ++++++++++++++++++++-------------- src/notification.c | 12 ------------ src/notification.h | 2 -- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/src/dunst.c b/src/dunst.c index 7e55f0d..9ed2551 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -174,28 +174,34 @@ void wake_up(void) static gint64 get_sleep_time(void) { + gint64 time = g_get_monotonic_time(); gint64 sleep = G_MAXINT64; - gint64 max_age = 0; for (GList *iter = g_queue_peek_head_link(displayed); iter; iter = iter->next) { notification *n = iter->data; + gint64 ttl = n->timeout - (time - n->start); - max_age = MAX(max_age, notification_get_age(n)); - gint64 ttl = notification_get_ttl(n); - if (ttl >= 0) - sleep = MIN(sleep, ttl); + if (n->timeout > 0) { + if (ttl > 0) + sleep = MIN(sleep, ttl); + else + // while we're processing, the notification already timed out + return 0; + } + + if (settings.show_age_threshold >= 0) { + gint64 age = time - n->timestamp; + + if (age > settings.show_age_threshold) + // sleep exactly until the next shift of the second happens + sleep = MIN(sleep, ((G_USEC_PER_SEC) - (age % (G_USEC_PER_SEC)))); + else if (ttl > settings.show_age_threshold) + sleep = MIN(sleep, settings.show_age_threshold); + } } - /* if age_threshold is hit, seconds have to get updated every second */ - if (settings.show_age_threshold >= 0) { - if (settings.show_age_threshold > max_age) - sleep = MIN(sleep, settings.show_age_threshold - max_age); - else - sleep = MIN(sleep, 1000*1000); - } - - return sleep; + return sleep != G_MAXINT64 ? sleep : -1; } gboolean run(void *data) diff --git a/src/notification.c b/src/notification.c index 7e77f9a..4a063ce 100644 --- a/src/notification.c +++ b/src/notification.c @@ -699,18 +699,6 @@ void notification_update_text_to_render(notification *n) n->text_to_render = buf; } -gint64 notification_get_ttl(notification *n) { - if (n->timeout < 0) { - return -1; - } else { - return n->timeout - (g_get_monotonic_time() - n->start); - } -} - -gint64 notification_get_age(notification *n) { - return g_get_monotonic_time() - n->timestamp; -} - /* * If the notification has exactly one action, or one is marked as default, * invoke it. If there are multiple and no default, open the context menu. If diff --git a/src/notification.h b/src/notification.h index befb9ce..edd051b 100644 --- a/src/notification.h +++ b/src/notification.h @@ -74,8 +74,6 @@ int notification_close(notification *n, int reason); void notification_print(notification *n); void notification_replace_single_field(char **haystack, char **needle, const char *replacement, enum markup_mode markup_mode); void notification_update_text_to_render(notification *n); -gint64 notification_get_ttl(notification *n); -gint64 notification_get_age(notification *n); void notification_do_action(notification *n); #endif From 81ff86299f4e2df0c02c634c1fa7f415bc2781dc Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Thu, 21 Sep 2017 15:33:42 +0200 Subject: [PATCH 5/5] Change boundary conditions in run() --- src/dunst.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dunst.c b/src/dunst.c index 9ed2551..20a6415 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -210,7 +210,7 @@ gboolean run(void *data) static int timeout_cnt = 0; static gint64 next_timeout = 0; - if (data) { + if (data && timeout_cnt > 0) { timeout_cnt--; } @@ -231,7 +231,7 @@ gboolean run(void *data) gint64 sleep = get_sleep_time(); gint64 timeout_at = now + sleep; - if (sleep > 0) { + if (sleep >= 0) { if (timeout_cnt == 0 || timeout_at < next_timeout) { g_timeout_add(sleep/1000, run, mainloop); next_timeout = timeout_at;