Merge pull request #411 from bebehei/refactor-queues

Refactor queues
This commit is contained in:
Nikos Tsipinakis 2017-10-31 07:56:38 +02:00 committed by GitHub
commit e111f5393e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 574 additions and 379 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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