commit
e111f5393e
16
src/dbus.c
16
src/dbus.c
@ -8,6 +8,7 @@
|
||||
|
||||
#include "dunst.h"
|
||||
#include "notification.h"
|
||||
#include "queues.h"
|
||||
#include "settings.h"
|
||||
#include "utils.h"
|
||||
|
||||
@ -280,14 +281,20 @@ static void on_notify(GDBusConnection *connection,
|
||||
n->color_strings[ColFG] = fgcolor;
|
||||
n->color_strings[ColBG] = bgcolor;
|
||||
|
||||
int id = notification_init(n, replaces_id);
|
||||
wake_up();
|
||||
notification_init(n);
|
||||
int id = queues_notification_insert(n, replaces_id);
|
||||
|
||||
GVariant *reply = g_variant_new("(u)", id);
|
||||
g_dbus_method_invocation_return_value(invocation, reply);
|
||||
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
||||
|
||||
run(NULL);
|
||||
// The message got discarded
|
||||
if (id == 0) {
|
||||
notification_closed(n, 2);
|
||||
notification_free(n);
|
||||
}
|
||||
|
||||
wake_up();
|
||||
}
|
||||
|
||||
static void on_close_notification(GDBusConnection *connection,
|
||||
@ -297,7 +304,8 @@ static void on_close_notification(GDBusConnection *connection,
|
||||
{
|
||||
guint32 id;
|
||||
g_variant_get(parameters, "(u)", &id);
|
||||
notification_close_by_id(id, 3);
|
||||
queues_notification_close_id(id, 3);
|
||||
wake_up();
|
||||
g_dbus_method_invocation_return_value(invocation, NULL);
|
||||
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
||||
}
|
||||
|
187
src/dunst.c
187
src/dunst.c
@ -16,6 +16,7 @@
|
||||
#include "menu.h"
|
||||
#include "notification.h"
|
||||
#include "option_parser.h"
|
||||
#include "queues.h"
|
||||
#include "settings.h"
|
||||
#include "x11/x.h"
|
||||
#include "x11/screen.h"
|
||||
@ -35,175 +36,23 @@ typedef struct _x11_source {
|
||||
} x11_source_t;
|
||||
|
||||
/* index of colors fit to urgency level */
|
||||
bool pause_display = false;
|
||||
|
||||
GMainLoop *mainloop = NULL;
|
||||
|
||||
/* notification lists */
|
||||
GQueue *queue = NULL; /* all new notifications get into here */
|
||||
GQueue *displayed = NULL; /* currently displayed notifications */
|
||||
GQueue *history = NULL; /* history of displayed notifications */
|
||||
GSList *rules = NULL;
|
||||
|
||||
/* misc funtions */
|
||||
|
||||
void check_timeouts(void)
|
||||
{
|
||||
/* nothing to do */
|
||||
if (displayed->length == 0)
|
||||
return;
|
||||
|
||||
GList *iter = g_queue_peek_head_link(displayed);
|
||||
while (iter) {
|
||||
notification *n = iter->data;
|
||||
|
||||
/*
|
||||
* Update iter to the next item before we either exit the
|
||||
* current iteration of the loop or potentially delete the
|
||||
* notification which would invalidate the pointer.
|
||||
*/
|
||||
iter = iter->next;
|
||||
|
||||
/* don't timeout when user is idle */
|
||||
if (x_is_idle() && !n->transient) {
|
||||
n->start = g_get_monotonic_time();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* skip hidden and sticky messages */
|
||||
if (n->start == 0 || n->timeout == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* remove old message */
|
||||
if (g_get_monotonic_time() - n->start > n->timeout) {
|
||||
notification_close(n, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_lists()
|
||||
{
|
||||
int limit;
|
||||
|
||||
check_timeouts();
|
||||
|
||||
if (pause_display) {
|
||||
while (displayed->length > 0) {
|
||||
g_queue_insert_sorted(queue, g_queue_pop_head(displayed),
|
||||
notification_cmp_data, NULL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (xctx.geometry.h == 0) {
|
||||
limit = 0;
|
||||
} else if (xctx.geometry.h == 1) {
|
||||
limit = 1;
|
||||
} else if (settings.indicate_hidden) {
|
||||
limit = xctx.geometry.h - 1;
|
||||
} else {
|
||||
limit = xctx.geometry.h;
|
||||
}
|
||||
|
||||
/* move notifications from queue to displayed */
|
||||
while (queue->length > 0) {
|
||||
|
||||
if (limit > 0 && displayed->length >= limit) {
|
||||
/* the list is full */
|
||||
break;
|
||||
}
|
||||
|
||||
notification *n = g_queue_pop_head(queue);
|
||||
|
||||
if (!n)
|
||||
return;
|
||||
n->start = g_get_monotonic_time();
|
||||
if (!n->redisplayed && n->script) {
|
||||
notification_run_script(n);
|
||||
}
|
||||
|
||||
g_queue_insert_sorted(displayed, n, notification_cmp_data,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void move_all_to_history()
|
||||
{
|
||||
while (displayed->length > 0) {
|
||||
notification_close(g_queue_peek_head_link(displayed)->data, 2);
|
||||
}
|
||||
|
||||
while (queue->length > 0) {
|
||||
notification_close(g_queue_peek_head_link(queue)->data, 2);
|
||||
}
|
||||
}
|
||||
|
||||
void history_pop(void)
|
||||
{
|
||||
if (g_queue_is_empty(history))
|
||||
return;
|
||||
|
||||
notification *n = g_queue_pop_tail(history);
|
||||
n->redisplayed = true;
|
||||
n->start = 0;
|
||||
n->timeout = settings.sticky_history ? 0 : n->timeout;
|
||||
g_queue_push_head(queue, n);
|
||||
|
||||
wake_up();
|
||||
}
|
||||
|
||||
void history_push(notification *n)
|
||||
{
|
||||
if (settings.history_length > 0 && history->length >= settings.history_length) {
|
||||
notification *to_free = g_queue_pop_head(history);
|
||||
notification_free(to_free);
|
||||
}
|
||||
|
||||
if (!n->history_ignore)
|
||||
g_queue_push_tail(history, n);
|
||||
}
|
||||
|
||||
void wake_up(void)
|
||||
{
|
||||
run(NULL);
|
||||
}
|
||||
|
||||
static gint64 get_sleep_time(void)
|
||||
{
|
||||
gint64 time = g_get_monotonic_time();
|
||||
gint64 sleep = G_MAXINT64;
|
||||
|
||||
for (GList *iter = g_queue_peek_head_link(displayed); iter;
|
||||
iter = iter->next) {
|
||||
notification *n = iter->data;
|
||||
gint64 ttl = n->timeout - (time - n->start);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return sleep != G_MAXINT64 ? sleep : -1;
|
||||
}
|
||||
|
||||
gboolean run(void *data)
|
||||
{
|
||||
update_lists();
|
||||
queues_check_timeouts(x_is_idle());
|
||||
queues_update();
|
||||
|
||||
static int timeout_cnt = 0;
|
||||
static gint64 next_timeout = 0;
|
||||
|
||||
@ -211,11 +60,11 @@ gboolean run(void *data)
|
||||
timeout_cnt--;
|
||||
}
|
||||
|
||||
if (displayed->length > 0 && !xctx.visible && !pause_display) {
|
||||
if (queues_length_displayed() > 0 && !xctx.visible) {
|
||||
x_win_show();
|
||||
}
|
||||
|
||||
if (xctx.visible && (pause_display || displayed->length == 0)) {
|
||||
if (xctx.visible && queues_length_displayed() == 0) {
|
||||
x_win_hide();
|
||||
}
|
||||
|
||||
@ -225,7 +74,7 @@ gboolean run(void *data)
|
||||
|
||||
if (xctx.visible) {
|
||||
gint64 now = g_get_monotonic_time();
|
||||
gint64 sleep = get_sleep_time();
|
||||
gint64 sleep = queues_get_next_datachange(now);
|
||||
gint64 timeout_at = now + sleep;
|
||||
|
||||
if (sleep >= 0) {
|
||||
@ -243,7 +92,7 @@ gboolean run(void *data)
|
||||
|
||||
gboolean pause_signal(gpointer data)
|
||||
{
|
||||
pause_display = true;
|
||||
queues_pause_on();
|
||||
wake_up();
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
@ -251,7 +100,7 @@ gboolean pause_signal(gpointer data)
|
||||
|
||||
gboolean unpause_signal(gpointer data)
|
||||
{
|
||||
pause_display = false;
|
||||
queues_pause_off();
|
||||
wake_up();
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
@ -264,19 +113,11 @@ gboolean quit_signal(gpointer data)
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void teardown_notification(gpointer data)
|
||||
{
|
||||
notification *n = data;
|
||||
notification_free(n);
|
||||
}
|
||||
|
||||
static void teardown(void)
|
||||
{
|
||||
regex_teardown();
|
||||
|
||||
g_queue_free_full(history, teardown_notification);
|
||||
g_queue_free_full(displayed, teardown_notification);
|
||||
g_queue_free_full(queue, teardown_notification);
|
||||
teardown_queues();
|
||||
|
||||
x_free();
|
||||
}
|
||||
@ -284,9 +125,7 @@ static void teardown(void)
|
||||
int dunst_main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
history = g_queue_new();
|
||||
displayed = g_queue_new();
|
||||
queue = g_queue_new();
|
||||
queues_init();
|
||||
|
||||
cmdline_load(argc, argv);
|
||||
|
||||
@ -319,7 +158,9 @@ int dunst_main(int argc, char *argv[])
|
||||
n->timeout = 10 * G_USEC_PER_SEC;
|
||||
n->markup = MARKUP_NO;
|
||||
n->urgency = LOW;
|
||||
notification_init(n, 0);
|
||||
notification_init(n);
|
||||
queues_notification_insert(n, 0);
|
||||
// we do not call wakeup now, wake_up does not work here yet
|
||||
}
|
||||
|
||||
mainloop = g_main_loop_new(NULL, FALSE);
|
||||
|
@ -16,11 +16,7 @@
|
||||
#define ColFG 1
|
||||
#define ColBG 0
|
||||
|
||||
extern GQueue *queue;
|
||||
extern GQueue *displayed;
|
||||
extern GQueue *history;
|
||||
extern GSList *rules;
|
||||
extern bool pause_display;
|
||||
extern const char *color_strings[3][3];
|
||||
|
||||
/* return id of notification */
|
||||
@ -30,10 +26,7 @@ void wake_up(void);
|
||||
int dunst_main(int argc, char *argv[]);
|
||||
|
||||
void check_timeouts(void);
|
||||
void history_pop(void);
|
||||
void history_push(notification *n);
|
||||
void usage(int exit_status);
|
||||
void move_all_to_history(void);
|
||||
void print_version(void);
|
||||
char *extract_urls(const char *str);
|
||||
void context_menu(void);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "dunst.h"
|
||||
#include "settings.h"
|
||||
#include "notification.h"
|
||||
#include "queues.h"
|
||||
#include "utils.h"
|
||||
|
||||
static bool is_initialized = false;
|
||||
@ -136,7 +137,7 @@ void invoke_action(const char *action)
|
||||
int appname_len = strlen(appname_begin) - 1; // remove ]
|
||||
int action_len = strlen(action) - appname_len - 3; // remove space, [, ]
|
||||
|
||||
for (GList *iter = g_queue_peek_head_link(displayed); iter;
|
||||
for (const GList *iter = queues_get_displayed(); iter;
|
||||
iter = iter->next) {
|
||||
notification *n = iter->data;
|
||||
if (g_str_has_prefix(appname_begin, n->appname) && strlen(n->appname) == appname_len) {
|
||||
@ -188,7 +189,7 @@ void context_menu(void)
|
||||
}
|
||||
char *dmenu_input = NULL;
|
||||
|
||||
for (GList *iter = g_queue_peek_head_link(displayed); iter;
|
||||
for (const GList *iter = queues_get_displayed(); iter;
|
||||
iter = iter->next) {
|
||||
notification *n = iter->data;
|
||||
|
||||
|
@ -19,10 +19,10 @@
|
||||
#include "menu.h"
|
||||
#include "rules.h"
|
||||
#include "settings.h"
|
||||
#include "queues.h"
|
||||
#include "utils.h"
|
||||
#include "x11/x.h"
|
||||
|
||||
int next_notification_id = 1;
|
||||
|
||||
/*
|
||||
* print a human readable representation
|
||||
@ -279,30 +279,19 @@ void notification_init_defaults(notification *n)
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the given notification and add it to
|
||||
* the queue. Replace notification with id if id > 0.
|
||||
* Initialize the given notification
|
||||
*
|
||||
* n should be a pointer to a notification allocated with
|
||||
* notification_create, it is undefined behaviour to pass a notification
|
||||
* allocated some other way.
|
||||
*/
|
||||
int notification_init(notification *n, int id)
|
||||
void notification_init(notification *n)
|
||||
{
|
||||
assert(n != NULL);
|
||||
|
||||
//Prevent undefined behaviour by initialising required fields
|
||||
notification_init_defaults(n);
|
||||
|
||||
if (strcmp("DUNST_COMMAND_PAUSE", n->summary) == 0) {
|
||||
pause_display = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp("DUNST_COMMAND_RESUME", n->summary) == 0) {
|
||||
pause_display = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
n->script = NULL;
|
||||
n->text_to_render = NULL;
|
||||
|
||||
@ -422,64 +411,8 @@ int notification_init(notification *n, int id)
|
||||
n->msg = buffer;
|
||||
}
|
||||
|
||||
if (id == 0) {
|
||||
n->id = ++next_notification_id;
|
||||
} else {
|
||||
n->id = id;
|
||||
}
|
||||
|
||||
n->dup_count = 0;
|
||||
|
||||
/* check if n is a duplicate */
|
||||
if (settings.stack_duplicates) {
|
||||
for (GList *iter = g_queue_peek_head_link(queue); iter;
|
||||
iter = iter->next) {
|
||||
notification *orig = iter->data;
|
||||
if (notification_is_duplicate(orig, n)) {
|
||||
/* If the progress differs this was probably intended to replace the notification
|
||||
* but notify-send was used. So don't increment dup_count in this case
|
||||
*/
|
||||
if (orig->progress == n->progress) {
|
||||
orig->dup_count++;
|
||||
} else {
|
||||
orig->progress = n->progress;
|
||||
}
|
||||
/* notifications that differ only in progress hints should be expected equal,
|
||||
* but we want the latest message, with the latest hint value
|
||||
*/
|
||||
g_free(orig->msg);
|
||||
orig->msg = g_strdup(n->msg);
|
||||
notification_free(n);
|
||||
wake_up();
|
||||
return orig->id;
|
||||
}
|
||||
}
|
||||
|
||||
for (GList *iter = g_queue_peek_head_link(displayed); iter;
|
||||
iter = iter->next) {
|
||||
notification *orig = iter->data;
|
||||
if (notification_is_duplicate(orig, n)) {
|
||||
/* notifications that differ only in progress hints should be expected equal,
|
||||
* but we want the latest message, with the latest hint value
|
||||
*/
|
||||
g_free(orig->msg);
|
||||
orig->msg = g_strdup(n->msg);
|
||||
/* If the progress differs this was probably intended to replace the notification
|
||||
* but notify-send was used. So don't increment dup_count in this case
|
||||
*/
|
||||
if (orig->progress == n->progress) {
|
||||
orig->dup_count++;
|
||||
} else {
|
||||
orig->progress = n->progress;
|
||||
}
|
||||
orig->start = g_get_monotonic_time();
|
||||
notification_free(n);
|
||||
wake_up();
|
||||
return orig->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* urgency > CRIT -> array out of range */
|
||||
n->urgency = n->urgency > CRIT ? CRIT : n->urgency;
|
||||
|
||||
@ -505,17 +438,6 @@ int notification_init(notification *n, int id)
|
||||
|
||||
n->first_render = true;
|
||||
|
||||
if (strlen(n->msg) == 0) {
|
||||
notification_close(n, 2);
|
||||
if (settings.always_run_script) {
|
||||
notification_run_script(n);
|
||||
}
|
||||
printf("skipping notification: %s %s\n", n->body, n->summary);
|
||||
} else {
|
||||
if (id == 0 || !notification_replace_by_id(n))
|
||||
g_queue_insert_sorted(queue, n, notification_cmp_data, NULL);
|
||||
}
|
||||
|
||||
char *tmp = g_strconcat(n->summary, " ", n->body, NULL);
|
||||
|
||||
char *tmp_urls = extract_urls(tmp);
|
||||
@ -538,102 +460,6 @@ int notification_init(notification *n, int id)
|
||||
}
|
||||
|
||||
g_free(tmp);
|
||||
|
||||
if (settings.print_notifications)
|
||||
notification_print(n);
|
||||
|
||||
return n->id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the notification that has id.
|
||||
*
|
||||
* reasons:
|
||||
* -1 -> notification is a replacement, no NotificationClosed signal emitted
|
||||
* 1 -> the notification expired
|
||||
* 2 -> the notification was dismissed by the user_data
|
||||
* 3 -> The notification was closed by a call to CloseNotification
|
||||
*/
|
||||
int notification_close_by_id(int id, int reason)
|
||||
{
|
||||
notification *target = NULL;
|
||||
|
||||
for (GList *iter = g_queue_peek_head_link(displayed); iter;
|
||||
iter = iter->next) {
|
||||
notification *n = iter->data;
|
||||
if (n->id == id) {
|
||||
g_queue_remove(displayed, n);
|
||||
history_push(n);
|
||||
target = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (GList *iter = g_queue_peek_head_link(queue); iter;
|
||||
iter = iter->next) {
|
||||
notification *n = iter->data;
|
||||
if (n->id == id) {
|
||||
g_queue_remove(queue, n);
|
||||
history_push(n);
|
||||
target = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reason > 0 && reason < 4 && target != NULL) {
|
||||
notification_closed(target, reason);
|
||||
}
|
||||
|
||||
wake_up();
|
||||
return reason;
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the given notification. SEE notification_close_by_id.
|
||||
*/
|
||||
int notification_close(notification *n, int reason)
|
||||
{
|
||||
assert(n != NULL);
|
||||
return notification_close_by_id(n->id, reason);
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace the notification which matches the id field of
|
||||
* the new notification. The given notification is inserted
|
||||
* right in the same position as the old notification.
|
||||
*
|
||||
* Returns true, if a matching notification has been found
|
||||
* and is replaced. Else false.
|
||||
*/
|
||||
bool notification_replace_by_id(notification *new)
|
||||
{
|
||||
|
||||
for (GList *iter = g_queue_peek_head_link(displayed);
|
||||
iter;
|
||||
iter = iter->next) {
|
||||
notification *old = iter->data;
|
||||
if (old->id == new->id) {
|
||||
iter->data = new;
|
||||
new->start = time(NULL);
|
||||
new->dup_count = old->dup_count;
|
||||
notification_run_script(new);
|
||||
history_push(old);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (GList *iter = g_queue_peek_head_link(queue);
|
||||
iter;
|
||||
iter = iter->next) {
|
||||
notification *old = iter->data;
|
||||
if (old->id == new->id) {
|
||||
iter->data = new;
|
||||
new->dup_count = old->dup_count;
|
||||
history_push(old);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void notification_update_text_to_render(notification *n)
|
||||
|
@ -62,15 +62,12 @@ typedef struct _notification {
|
||||
} notification;
|
||||
|
||||
notification *notification_create(void);
|
||||
int notification_init(notification *n, int id);
|
||||
void notification_init(notification *n);
|
||||
void notification_free(notification *n);
|
||||
int notification_close_by_id(int id, int reason);
|
||||
bool notification_replace_by_id(notification *n);
|
||||
int notification_cmp(const void *a, const void *b);
|
||||
int notification_cmp_data(const void *a, const void *b, void *data);
|
||||
int notification_is_duplicate(const notification *a, const notification *b);
|
||||
void notification_run_script(notification *n);
|
||||
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);
|
||||
|
381
src/queues.c
Normal file
381
src/queues.c
Normal file
@ -0,0 +1,381 @@
|
||||
/* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
|
||||
|
||||
#include "queues.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dbus.h"
|
||||
#include "notification.h"
|
||||
#include "settings.h"
|
||||
|
||||
/* notification lists */
|
||||
static GQueue *waiting = NULL; /* all new notifications get into here */
|
||||
static GQueue *displayed = NULL; /* currently displayed notifications */
|
||||
static GQueue *history = NULL; /* history of displayed notifications */
|
||||
|
||||
unsigned int displayed_limit = 0;
|
||||
int next_notification_id = 1;
|
||||
bool pause_displayed = false;
|
||||
|
||||
static int queues_stack_duplicate(notification *n);
|
||||
|
||||
void queues_init(void)
|
||||
{
|
||||
history = g_queue_new();
|
||||
displayed = g_queue_new();
|
||||
waiting = g_queue_new();
|
||||
}
|
||||
|
||||
void queues_displayed_limit(unsigned int limit)
|
||||
{
|
||||
displayed_limit = limit;
|
||||
}
|
||||
|
||||
/* misc getter functions */
|
||||
const GList *queues_get_displayed()
|
||||
{
|
||||
return g_queue_peek_head_link(displayed);
|
||||
}
|
||||
unsigned int queues_length_waiting()
|
||||
{
|
||||
return waiting->length;
|
||||
}
|
||||
unsigned int queues_length_displayed()
|
||||
{
|
||||
return displayed->length;
|
||||
}
|
||||
unsigned int queues_length_history()
|
||||
{
|
||||
return history->length;
|
||||
}
|
||||
|
||||
int queues_notification_insert(notification *n, int replaces_id)
|
||||
{
|
||||
/* do not display the message, if the message is empty */
|
||||
if (strlen(n->msg) == 0) {
|
||||
if (settings.always_run_script) {
|
||||
notification_run_script(n);
|
||||
}
|
||||
printf("skipping notification: %s %s\n", n->body, n->summary);
|
||||
return 0;
|
||||
}
|
||||
/* Do not insert the message if it's a command */
|
||||
if (strcmp("DUNST_COMMAND_PAUSE", n->summary) == 0) {
|
||||
pause_displayed = true;
|
||||
return 0;
|
||||
}
|
||||
if (strcmp("DUNST_COMMAND_RESUME", n->summary) == 0) {
|
||||
pause_displayed = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (replaces_id == 0) {
|
||||
|
||||
if (settings.stack_duplicates) {
|
||||
int stacked = queues_stack_duplicate(n);
|
||||
if (stacked > 0) {
|
||||
// notification got stacked
|
||||
return stacked;
|
||||
}
|
||||
}
|
||||
|
||||
n->id = ++next_notification_id;
|
||||
|
||||
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL);
|
||||
|
||||
} else {
|
||||
n->id = replaces_id;
|
||||
if (!queues_notification_replace_id(n))
|
||||
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL);
|
||||
}
|
||||
|
||||
if (settings.print_notifications)
|
||||
notification_print(n);
|
||||
|
||||
return n->id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Replaces duplicate notification and stacks it
|
||||
*
|
||||
* Returns the notification id of the stacked notification
|
||||
* Returns -1 if not notification could be stacked
|
||||
*/
|
||||
static int queues_stack_duplicate(notification *n)
|
||||
{
|
||||
for (GList *iter = g_queue_peek_head_link(displayed); iter;
|
||||
iter = iter->next) {
|
||||
notification *orig = iter->data;
|
||||
if (notification_is_duplicate(orig, n)) {
|
||||
/* If the progress differs, probably notify-send was used to update the notification
|
||||
* So only count it as a duplicate, if the progress was not the same.
|
||||
* */
|
||||
if (orig->progress == n->progress) {
|
||||
orig->dup_count++;
|
||||
} else {
|
||||
orig->progress = n->progress;
|
||||
}
|
||||
orig->start = g_get_monotonic_time();
|
||||
g_free(orig->msg);
|
||||
orig->msg = g_strdup(n->msg);
|
||||
notification_free(n);
|
||||
return orig->id;
|
||||
}
|
||||
}
|
||||
|
||||
for (GList *iter = g_queue_peek_head_link(waiting); iter;
|
||||
iter = iter->next) {
|
||||
notification *orig = iter->data;
|
||||
if (notification_is_duplicate(orig, n)) {
|
||||
/* If the progress differs, probably notify-send was used to update the notification
|
||||
* So only count it as a duplicate, if the progress was not the same.
|
||||
* */
|
||||
if (orig->progress == n->progress) {
|
||||
orig->dup_count++;
|
||||
} else {
|
||||
orig->progress = n->progress;
|
||||
}
|
||||
g_free(orig->msg);
|
||||
orig->msg = g_strdup(n->msg);
|
||||
notification_free(n);
|
||||
return orig->id;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool queues_notification_replace_id(notification *new)
|
||||
{
|
||||
|
||||
for (GList *iter = g_queue_peek_head_link(displayed);
|
||||
iter;
|
||||
iter = iter->next) {
|
||||
notification *old = iter->data;
|
||||
if (old->id == new->id) {
|
||||
iter->data = new;
|
||||
new->start = time(NULL);
|
||||
new->dup_count = old->dup_count;
|
||||
notification_run_script(new);
|
||||
queues_history_push(old);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (GList *iter = g_queue_peek_head_link(waiting);
|
||||
iter;
|
||||
iter = iter->next) {
|
||||
notification *old = iter->data;
|
||||
if (old->id == new->id) {
|
||||
iter->data = new;
|
||||
new->dup_count = old->dup_count;
|
||||
queues_history_push(old);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int queues_notification_close_id(int id, int reason)
|
||||
{
|
||||
notification *target = NULL;
|
||||
|
||||
for (GList *iter = g_queue_peek_head_link(displayed); iter;
|
||||
iter = iter->next) {
|
||||
notification *n = iter->data;
|
||||
if (n->id == id) {
|
||||
g_queue_remove(displayed, n);
|
||||
queues_history_push(n);
|
||||
target = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (GList *iter = g_queue_peek_head_link(waiting); iter;
|
||||
iter = iter->next) {
|
||||
notification *n = iter->data;
|
||||
if (n->id == id) {
|
||||
g_queue_remove(waiting, n);
|
||||
queues_history_push(n);
|
||||
target = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reason > 0 && reason < 4 && target != NULL) {
|
||||
notification_closed(target, reason);
|
||||
}
|
||||
|
||||
return reason;
|
||||
}
|
||||
|
||||
int queues_notification_close(notification *n, int reason)
|
||||
{
|
||||
assert(n != NULL);
|
||||
return queues_notification_close_id(n->id, reason);
|
||||
}
|
||||
|
||||
void queues_history_pop(void)
|
||||
{
|
||||
if (g_queue_is_empty(history))
|
||||
return;
|
||||
|
||||
notification *n = g_queue_pop_tail(history);
|
||||
n->redisplayed = true;
|
||||
n->start = 0;
|
||||
n->timeout = settings.sticky_history ? 0 : n->timeout;
|
||||
g_queue_push_head(waiting, n);
|
||||
}
|
||||
|
||||
void queues_history_push(notification *n)
|
||||
{
|
||||
if (settings.history_length > 0 && history->length >= settings.history_length) {
|
||||
notification *to_free = g_queue_pop_head(history);
|
||||
notification_free(to_free);
|
||||
}
|
||||
|
||||
if (!n->history_ignore)
|
||||
g_queue_push_tail(history, n);
|
||||
}
|
||||
|
||||
void queues_history_push_all(void)
|
||||
{
|
||||
while (displayed->length > 0) {
|
||||
queues_notification_close(g_queue_peek_head_link(displayed)->data, 2);
|
||||
}
|
||||
|
||||
while (waiting->length > 0) {
|
||||
queues_notification_close(g_queue_peek_head_link(waiting)->data, 2);
|
||||
}
|
||||
}
|
||||
|
||||
void queues_check_timeouts(bool idle)
|
||||
{
|
||||
/* nothing to do */
|
||||
if (displayed->length == 0)
|
||||
return;
|
||||
|
||||
GList *iter = g_queue_peek_head_link(displayed);
|
||||
while (iter) {
|
||||
notification *n = iter->data;
|
||||
|
||||
/*
|
||||
* Update iter to the next item before we either exit the
|
||||
* current iteration of the loop or potentially delete the
|
||||
* notification which would invalidate the pointer.
|
||||
*/
|
||||
iter = iter->next;
|
||||
|
||||
/* don't timeout when user is idle */
|
||||
if (idle && !n->transient) {
|
||||
n->start = g_get_monotonic_time();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* skip hidden and sticky messages */
|
||||
if (n->start == 0 || n->timeout == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* remove old message */
|
||||
if (g_get_monotonic_time() - n->start > n->timeout) {
|
||||
queues_notification_close(n, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void queues_update()
|
||||
{
|
||||
if (pause_displayed) {
|
||||
while (displayed->length > 0) {
|
||||
g_queue_insert_sorted(
|
||||
waiting, g_queue_pop_head(displayed), notification_cmp_data, NULL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* move notifications from queue to displayed */
|
||||
while (waiting->length > 0) {
|
||||
|
||||
if (displayed_limit > 0 && displayed->length >= displayed_limit) {
|
||||
/* the list is full */
|
||||
break;
|
||||
}
|
||||
|
||||
notification *n = g_queue_pop_head(waiting);
|
||||
|
||||
if (!n)
|
||||
return;
|
||||
|
||||
n->start = g_get_monotonic_time();
|
||||
|
||||
if (!n->redisplayed && n->script) {
|
||||
notification_run_script(n);
|
||||
}
|
||||
|
||||
g_queue_insert_sorted(displayed, n, notification_cmp_data, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
gint64 queues_get_next_datachange(gint64 time)
|
||||
{
|
||||
gint64 sleep = G_MAXINT64;
|
||||
|
||||
for (GList *iter = g_queue_peek_head_link(displayed); iter;
|
||||
iter = iter->next) {
|
||||
notification *n = iter->data;
|
||||
gint64 ttl = n->timeout - (time - n->start);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return sleep != G_MAXINT64 ? sleep : -1;
|
||||
}
|
||||
|
||||
void queues_pause_on(void)
|
||||
{
|
||||
pause_displayed = true;
|
||||
}
|
||||
|
||||
void queues_pause_off(void)
|
||||
{
|
||||
pause_displayed = false;
|
||||
}
|
||||
|
||||
bool queues_pause_status(void)
|
||||
{
|
||||
return pause_displayed;
|
||||
}
|
||||
|
||||
static void teardown_notification(gpointer data)
|
||||
{
|
||||
notification *n = data;
|
||||
notification_free(n);
|
||||
}
|
||||
|
||||
void teardown_queues(void)
|
||||
{
|
||||
g_queue_free_full(history, teardown_notification);
|
||||
g_queue_free_full(displayed, teardown_notification);
|
||||
g_queue_free_full(waiting, teardown_notification);
|
||||
}
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
132
src/queues.h
Normal file
132
src/queues.h
Normal file
@ -0,0 +1,132 @@
|
||||
/* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
|
||||
|
||||
#ifndef DUNST_QUEUE_H
|
||||
#define DUNST_QUEUE_H
|
||||
|
||||
#include "notification.h"
|
||||
|
||||
/*
|
||||
* Initialise neccessary queues
|
||||
*/
|
||||
void queues_init(void);
|
||||
|
||||
/*
|
||||
* Set maximum notification count to display
|
||||
* and store in displayed queue
|
||||
*/
|
||||
void queues_displayed_limit(unsigned int limit);
|
||||
|
||||
/*
|
||||
* Return read only list of notifications
|
||||
*/
|
||||
const GList *queues_get_displayed();
|
||||
|
||||
/*
|
||||
* Returns the current amount of notifications,
|
||||
* which are shown, waiting or already in history
|
||||
*/
|
||||
unsigned int queues_length_waiting();
|
||||
unsigned int queues_length_displayed();
|
||||
unsigned int queues_length_history();
|
||||
|
||||
/*
|
||||
* Insert a fully initialized notification into queues
|
||||
* Respects stack_duplicates, and notification replacement
|
||||
*
|
||||
* If replaces_id != 0, n replaces notification with id replaces_id
|
||||
* If replaces_id == 0, n gets occupies a new position
|
||||
*
|
||||
* Returns the assigned notification id
|
||||
* If returned id == 0, the message was dismissed
|
||||
*/
|
||||
int queues_notification_insert(notification *n, int replaces_id);
|
||||
|
||||
/*
|
||||
* Replace the notification which matches the id field of
|
||||
* the new notification. The given notification is inserted
|
||||
* right in the same position as the old notification.
|
||||
*
|
||||
* Returns true, if a matching notification has been found
|
||||
* and is replaced. Else false.
|
||||
*/
|
||||
bool queues_notification_replace_id(notification *new);
|
||||
|
||||
/*
|
||||
* Close the notification that has n->id == id
|
||||
*
|
||||
* Sends a signal and pushes it automatically to history.
|
||||
*
|
||||
* After closing, call wake_up to synchronize the queues with the UI
|
||||
* (which closes the notification on screen)
|
||||
*
|
||||
* reasons:
|
||||
* -1 -> notification is a replacement, no NotificationClosed signal emitted
|
||||
* 1 -> the notification expired
|
||||
* 2 -> the notification was dismissed by the user_data
|
||||
* 3 -> The notification was closed by a call to CloseNotification
|
||||
*/
|
||||
int queues_notification_close_id(int id, int reason);
|
||||
|
||||
/* Close the given notification. SEE queues_notification_close_id. */
|
||||
int queues_notification_close(notification *n, int reason);
|
||||
|
||||
/*
|
||||
* Pushed the latest notification of history to the displayed queue
|
||||
* and removes it from history
|
||||
*/
|
||||
void queues_history_pop(void);
|
||||
|
||||
/*
|
||||
* Push a single notification to history
|
||||
* The given notification has to be removed its queue
|
||||
*/
|
||||
void queues_history_push(notification *n);
|
||||
|
||||
/*
|
||||
* Push all waiting and displayed notifications to history
|
||||
*/
|
||||
void queues_history_push_all(void);
|
||||
|
||||
/*
|
||||
* Check timeout of each notification and close it, if neccessary
|
||||
*/
|
||||
void queues_check_timeouts(bool idle);
|
||||
|
||||
/*
|
||||
* Move inserted notifications from waiting queue to displayed queue
|
||||
* and show them. In displayed queue, the amount of elements is limited
|
||||
* to the amount set via queues_displayed_limit
|
||||
*/
|
||||
void queues_update();
|
||||
|
||||
/*
|
||||
* Return the distance to the next event in the queue,
|
||||
* which forces an update visible to the user
|
||||
*
|
||||
* This may be:
|
||||
*
|
||||
* - notification hits timeout
|
||||
* - notification's age second changes
|
||||
* - notification's age threshold is hit
|
||||
*/
|
||||
gint64 queues_get_next_datachange(gint64 time);
|
||||
|
||||
/*
|
||||
* Pause queue-management of dunst
|
||||
* pause_on = paused (no notifications displayed)
|
||||
* pause_off = running
|
||||
*
|
||||
* Calling update_lists is neccessary
|
||||
*/
|
||||
void queues_pause_on(void);
|
||||
void queues_pause_off(void);
|
||||
bool queues_pause_status(void);
|
||||
|
||||
/*
|
||||
* Remove all notifications from all lists
|
||||
* and free the notifications
|
||||
*/
|
||||
void teardown_queues(void);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
40
src/x11/x.c
40
src/x11/x.c
@ -29,6 +29,7 @@
|
||||
#include "src/markup.h"
|
||||
#include "src/notification.h"
|
||||
#include "src/settings.h"
|
||||
#include "src/queues.h"
|
||||
#include "src/utils.h"
|
||||
|
||||
#include "screen.h"
|
||||
@ -532,11 +533,11 @@ static GSList *r_create_layouts(cairo_t *c)
|
||||
{
|
||||
GSList *layouts = NULL;
|
||||
|
||||
int qlen = g_list_length(g_queue_peek_head_link(queue));
|
||||
int qlen = queues_length_waiting();
|
||||
bool xmore_is_needed = qlen > 0 && settings.indicate_hidden;
|
||||
|
||||
notification *last = NULL;
|
||||
for (GList *iter = g_queue_peek_head_link(displayed);
|
||||
for (const GList *iter = queues_get_displayed();
|
||||
iter; iter = iter->next)
|
||||
{
|
||||
notification *n = iter->data;
|
||||
@ -842,6 +843,7 @@ gboolean x_mainloop_fd_dispatch(GSource *source, GSourceFunc callback,
|
||||
case ButtonRelease:
|
||||
if (ev.xbutton.window == xctx.win) {
|
||||
x_handle_click(ev);
|
||||
wake_up();
|
||||
}
|
||||
break;
|
||||
case KeyPress:
|
||||
@ -852,29 +854,32 @@ gboolean x_mainloop_fd_dispatch(GSource *source, GSourceFunc callback,
|
||||
&& XLookupKeysym(&ev.xkey,
|
||||
0) == settings.close_ks.sym
|
||||
&& settings.close_ks.mask == state) {
|
||||
if (displayed) {
|
||||
notification *n = g_queue_peek_head(displayed);
|
||||
if (n)
|
||||
notification_close(n, 2);
|
||||
const GList *displayed = queues_get_displayed();
|
||||
if (displayed && displayed->data) {
|
||||
queues_notification_close(displayed->data, 2);
|
||||
wake_up();
|
||||
}
|
||||
}
|
||||
if (settings.history_ks.str
|
||||
&& XLookupKeysym(&ev.xkey,
|
||||
0) == settings.history_ks.sym
|
||||
&& settings.history_ks.mask == state) {
|
||||
history_pop();
|
||||
queues_history_pop();
|
||||
wake_up();
|
||||
}
|
||||
if (settings.close_all_ks.str
|
||||
&& XLookupKeysym(&ev.xkey,
|
||||
0) == settings.close_all_ks.sym
|
||||
&& settings.close_all_ks.mask == state) {
|
||||
move_all_to_history();
|
||||
queues_history_push_all();
|
||||
wake_up();
|
||||
}
|
||||
if (settings.context_ks.str
|
||||
&& XLookupKeysym(&ev.xkey,
|
||||
0) == settings.context_ks.sym
|
||||
&& settings.context_ks.mask == state) {
|
||||
context_menu();
|
||||
wake_up();
|
||||
}
|
||||
break;
|
||||
case FocusIn:
|
||||
@ -910,7 +915,7 @@ bool x_is_idle(void)
|
||||
static void x_handle_click(XEvent ev)
|
||||
{
|
||||
if (ev.xbutton.button == Button3) {
|
||||
move_all_to_history();
|
||||
queues_history_push_all();
|
||||
|
||||
return;
|
||||
}
|
||||
@ -919,7 +924,7 @@ static void x_handle_click(XEvent ev)
|
||||
int y = settings.separator_height;
|
||||
notification *n = NULL;
|
||||
int first = true;
|
||||
for (GList *iter = g_queue_peek_head_link(displayed); iter;
|
||||
for (const GList *iter = queues_get_displayed(); iter;
|
||||
iter = iter->next) {
|
||||
n = iter->data;
|
||||
if (ev.xbutton.y > y && ev.xbutton.y < y + n->displayed_height)
|
||||
@ -932,7 +937,7 @@ static void x_handle_click(XEvent ev)
|
||||
|
||||
if (n) {
|
||||
if (ev.xbutton.button == Button1)
|
||||
notification_close(n, 2);
|
||||
queues_notification_close(n, 2);
|
||||
else
|
||||
notification_do_action(n);
|
||||
}
|
||||
@ -1008,6 +1013,17 @@ void x_setup(void)
|
||||
&xctx.geometry.x, &xctx.geometry.y,
|
||||
&xctx.geometry.w, &xctx.geometry.h);
|
||||
|
||||
/* calculate maximum notification count and push information to queue */
|
||||
if (xctx.geometry.h == 0) {
|
||||
queues_displayed_limit(0);
|
||||
} else if (xctx.geometry.h == 1) {
|
||||
queues_displayed_limit(1);
|
||||
} else if (settings.indicate_hidden) {
|
||||
queues_displayed_limit(xctx.geometry.h - 1);
|
||||
} else {
|
||||
queues_displayed_limit(xctx.geometry.h);
|
||||
}
|
||||
|
||||
xctx.screensaver_info = XScreenSaverAllocInfo();
|
||||
|
||||
init_screens();
|
||||
@ -1109,7 +1125,7 @@ static void x_win_setup(void)
|
||||
void x_win_show(void)
|
||||
{
|
||||
/* window is already mapped or there's nothing to show */
|
||||
if (xctx.visible || g_queue_is_empty(displayed)) {
|
||||
if (xctx.visible || queues_length_displayed() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user