Merge pull request #379 from bebehei/timeout-in-msecs

Timeout in msecs
This commit is contained in:
Nikos Tsipinakis 2017-10-07 18:59:00 +03:00 committed by GitHub
commit fe2a3b5049
15 changed files with 182 additions and 105 deletions

View File

@ -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 */

View File

@ -230,7 +230,8 @@ If set to true, display notifications with higher urgency above the others.
=item B<idle_threshold> (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<show_age_threshold> (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

View File

@ -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;

View File

@ -12,7 +12,6 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#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,61 +172,45 @@ void wake_up(void)
run(NULL);
}
static int get_sleep_time(void)
static gint64 get_sleep_time(void)
{
gint64 time = g_get_monotonic_time();
gint64 sleep = G_MAXINT64;
if (settings.show_age_threshold == 0) {
/* we need to update every second */
return 1;
}
bool have_ttl = false;
int min_ttl = 0;
int 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));
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;
}
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);
}
}
int min_timeout;
int show_age_timeout = settings.show_age_threshold - max_age;
if (show_age_timeout < 1) {
return 1;
}
if (!have_ttl) {
min_timeout = show_age_timeout;
} else {
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 != G_MAXINT64 ? sleep : -1;
}
gboolean run(void *data)
{
update_lists();
static int timeout_cnt = 0;
static int next_timeout = 0;
static gint64 next_timeout = 0;
if (data) {
if (data && timeout_cnt > 0) {
timeout_cnt--;
}
@ -244,13 +227,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 (sleep >= 0) {
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++;
}

View File

@ -11,7 +11,6 @@
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <libgen.h>
@ -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,18 +699,6 @@ void notification_update_text_to_render(notification *n)
n->text_to_render = buf;
}
int notification_get_ttl(notification *n) {
if (n->timeout == 0) {
return -1;
} else {
return n->timeout - (time(NULL) - n->start);
}
}
int notification_get_age(notification *n) {
return time(NULL) - 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

View File

@ -4,7 +4,6 @@
#include <glib.h>
#include <stdbool.h>
#include <time.h>
#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,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);
int notification_get_ttl(notification *n);
int notification_get_age(notification *n);
void notification_do_action(notification *n);
#endif

View File

@ -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,

View File

@ -2,12 +2,14 @@
#ifndef DUNST_OPTION_PARSER_H
#define DUNST_OPTION_PARSER_H
#include <glib.h>
#include <stdbool.h>
#include <stdio.h>
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,

View File

@ -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;

View File

@ -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 = 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 = 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] = 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] = 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] = 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 = ini_get_int(cur_section, "timeout", r->timeout);
r->timeout = ini_get_time(cur_section, "timeout", r->timeout);
{
char *c = ini_get_string(

View File

@ -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;

View File

@ -2,7 +2,9 @@
#define _GNU_SOURCE
#include "utils.h"
#include <assert.h>
#include <glib.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -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);

View File

@ -2,6 +2,8 @@
#ifndef DUNST_UTILS_H
#define DUNST_UTILS_H
#include <glib.h>
/* 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: */

View File

@ -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_* */

View File

@ -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: */