Merge pull request #541 from bebehei/concatenated-queues

Concatenated queues
This commit is contained in:
Nikos Tsipinakis 2018-09-14 16:47:22 +03:00 committed by GitHub
commit fab2543f06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 176 additions and 130 deletions

View File

@ -5,6 +5,10 @@
### Added
- `fullscreen` rule to hide notifications when a fullscreen window is active
- When new notifications arrive, but display is full, important notifications don't
have to wait for a timeout in a displayed notification (#541)
- `<I> more` notifications don't occupy space anymore, if there is only a single
notification waiting to get displayed. The notification gets displayed directly (#467)
## 1.3.2 - 2018-05-06

View File

@ -26,7 +26,7 @@ typedef struct {
char *text;
PangoAttrList *attr;
cairo_surface_t *icon;
notification *n;
const notification *n;
} colored_layout;
window_x11 *win;
@ -245,7 +245,7 @@ static PangoLayout *layout_create(cairo_t *c)
return layout;
}
static colored_layout *layout_init_shared(cairo_t *c, notification *n)
static colored_layout *layout_init_shared(cairo_t *c, const notification *n)
{
colored_layout *cl = g_malloc(sizeof(colored_layout));
cl->l = layout_create(c);
@ -301,7 +301,7 @@ static colored_layout *layout_init_shared(cairo_t *c, notification *n)
return cl;
}
static colored_layout *layout_derive_xmore(cairo_t *c, notification *n, int qlen)
static colored_layout *layout_derive_xmore(cairo_t *c, const notification *n, int qlen)
{
colored_layout *cl = layout_init_shared(c, n);
cl->text = g_strdup_printf("(%d more)", qlen);
@ -350,12 +350,10 @@ static GSList *create_layouts(cairo_t *c)
int qlen = queues_length_waiting();
bool xmore_is_needed = qlen > 0 && settings.indicate_hidden;
notification *last = NULL;
for (const GList *iter = queues_get_displayed();
iter; iter = iter->next)
{
notification *n = iter->data;
last = n;
notification_update_text_to_render(n);
@ -371,7 +369,7 @@ static GSList *create_layouts(cairo_t *c)
if (xmore_is_needed && settings.geometry.h != 1) {
/* append xmore message as new message */
layouts = g_slist_append(layouts,
layout_derive_xmore(c, last, qlen));
layout_derive_xmore(c, queues_get_head_waiting(), qlen));
}
return layouts;

View File

@ -92,6 +92,11 @@ void notification_run_script(notification *n)
if (!n->script || strlen(n->script) < 1)
return;
if (n->script_run && !settings.always_run_script)
return;
n->script_run = true;
const char *appname = n->appname ? n->appname : "";
const char *summary = n->summary ? n->summary : "";
const char *body = n->body ? n->body : "";
@ -268,6 +273,8 @@ notification *notification_create(void)
n->transient = false;
n->progress = -1;
n->script_run = false;
n->fullscreen = FS_SHOW;
return n;

View File

@ -77,6 +77,7 @@ typedef struct _notification {
int dup_count; /**< amount of duplicate notifications stacked onto this */
int displayed_height;
enum behavior_fullscreen fullscreen; //!< The instruction what to do with it, when desktop enters fullscreen
bool script_run; /**< Has the script been executed already? */
/* derived fields */
char *msg; /**< formatted message */
@ -139,6 +140,9 @@ int notification_is_duplicate(const notification *a, const notification *b);
/**
* Run the script associated with the
* given notification.
*
* If the script of the notification has been executed already and
* settings.always_run_script is not set, do nothing.
*/
void notification_run_script(notification *n);
/**

View File

@ -30,7 +30,6 @@ 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;
@ -44,18 +43,20 @@ void queues_init(void)
waiting = g_queue_new();
}
/* see queues.h */
void queues_displayed_limit(unsigned int limit)
{
displayed_limit = limit;
}
/* see queues.h */
const GList *queues_get_displayed(void)
{
return g_queue_peek_head_link(displayed);
}
/* see queues.h */
const notification *queues_get_head_waiting(void)
{
if (waiting->length == 0)
return NULL;
return g_queue_peek_head(waiting);
}
/* see queues.h */
unsigned int queues_length_waiting(void)
{
@ -74,6 +75,51 @@ unsigned int queues_length_history(void)
return history->length;
}
/**
* Swap two given queue elements. The element's data has to be a notification.
*
* @pre { elemA has to be part of queueA. }
* @pre { elemB has to be part of queueB. }
*
* @param queueA The queue, which elemB's data will get inserted
* @param elemA The element, which will get removed from queueA
* @param queueB The queue, which elemA's data will get inserted
* @param elemB The element, which will get removed from queueB
*/
static void queues_swap_notifications(GQueue *queueA,
GList *elemA,
GQueue *queueB,
GList *elemB)
{
notification *toB = elemA->data;
notification *toA = elemB->data;
g_queue_delete_link(queueA, elemA);
g_queue_delete_link(queueB, elemB);
if (toA)
g_queue_insert_sorted(queueA, toA, notification_cmp_data, NULL);
if (toB)
g_queue_insert_sorted(queueB, toB, notification_cmp_data, NULL);
}
/**
* Check if a notification is eligible to get shown.
*
* @param n The notification to check
* @param fullscreen True if a fullscreen window is currently active
* @param visible True if the notification is currently displayed
*/
static bool queues_notification_is_ready(const notification *n, bool fullscreen, bool visible)
{
if (fullscreen && visible)
return n && n->fullscreen != FS_PUSHBACK;
else if (fullscreen && !visible)
return n && n->fullscreen == FS_SHOW;
else
return true;
}
/* see queues.h */
int queues_notification_insert(notification *n)
{
@ -123,52 +169,31 @@ int queues_notification_insert(notification *n)
*/
static bool 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;
GQueue *allqueues[] = { displayed, waiting };
for (int i = 0; i < sizeof(allqueues)/sizeof(GList*); i++) {
for (GList *iter = g_queue_peek_head_link(allqueues[i]); 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;
}
iter->data = n;
n->dup_count = orig->dup_count;
signal_notification_closed(orig, 1);
if ( allqueues[i] == displayed )
n->start = time_monotonic_now();
notification_free(orig);
return true;
}
iter->data = n;
n->start = time_monotonic_now();
n->dup_count = orig->dup_count;
signal_notification_closed(orig, 1);
notification_free(orig);
return true;
}
}
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;
}
iter->data = n;
n->dup_count = orig->dup_count;
signal_notification_closed(orig, 1);
notification_free(orig);
return true;
}
}
@ -178,31 +203,26 @@ static bool queues_stack_duplicate(notification *n)
/* see queues.h */
bool queues_notification_replace_id(notification *new)
{
GQueue *allqueues[] = { displayed, waiting };
for (int i = 0; i < sizeof(allqueues)/sizeof(GList*); i++) {
for (GList *iter = g_queue_peek_head_link(allqueues[i]);
iter;
iter = iter->next) {
notification *old = iter->data;
if (old->id == new->id) {
iter->data = new;
new->dup_count = old->dup_count;
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_monotonic_now();
new->dup_count = old->dup_count;
notification_run_script(new);
notification_free(old);
return true;
}
}
if ( allqueues[i] == displayed ) {
new->start = time_monotonic_now();
notification_run_script(new);
}
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;
notification_free(old);
return true;
notification_free(old);
return true;
}
}
}
return false;
}
@ -212,24 +232,16 @@ void queues_notification_close_id(int id, enum reason 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);
target = n;
break;
}
}
for (GList *iter = g_queue_peek_head_link(waiting); iter;
iter = iter->next) {
notification *n = iter->data;
if (n->id == id) {
assert(target == NULL);
g_queue_remove(waiting, n);
target = n;
break;
GQueue *allqueues[] = { displayed, waiting };
for (int i = 0; i < sizeof(allqueues)/sizeof(GList*); i++) {
for (GList *iter = g_queue_peek_head_link(allqueues[i]); iter;
iter = iter->next) {
notification *n = iter->data;
if (n->id == id) {
g_queue_remove(allqueues[i], n);
target = n;
break;
}
}
}
@ -258,7 +270,7 @@ void queues_history_pop(void)
n->redisplayed = true;
n->start = 0;
n->timeout = settings.sticky_history ? 0 : n->timeout;
g_queue_push_head(waiting, n);
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL);
}
/* see queues.h */
@ -353,37 +365,70 @@ void queues_update(bool fullscreen)
}
}
int cur_displayed_limit;
if (settings.geometry.h == 0)
cur_displayed_limit = INT_MAX;
else if ( settings.indicate_hidden
&& settings.geometry.h > 1
&& displayed->length + waiting->length > settings.geometry.h)
cur_displayed_limit = settings.geometry.h-1;
else
cur_displayed_limit = settings.geometry.h;
/* move notifications from queue to displayed */
GList *iter = g_queue_peek_head_link(waiting);
while (iter) {
while (displayed->length < cur_displayed_limit && iter) {
notification *n = iter->data;
GList *nextiter = iter->next;
if (displayed_limit > 0 && displayed->length >= displayed_limit) {
/* the list is full */
break;
}
if (!n)
return;
if (fullscreen
&& (n->fullscreen == FS_DELAY || n->fullscreen == FS_PUSHBACK)) {
if (!queues_notification_is_ready(n, fullscreen, false)) {
iter = nextiter;
continue;
}
n->start = time_monotonic_now();
if (!n->redisplayed && n->script) {
notification_run_script(n);
}
notification_run_script(n);
g_queue_delete_link(waiting, iter);
g_queue_insert_sorted(displayed, n, notification_cmp_data, NULL);
iter = nextiter;
}
/* if necessary, push the overhanging notifications from displayed to waiting again */
while (displayed->length > cur_displayed_limit) {
notification *n = g_queue_pop_tail(displayed);
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL); //TODO: actually it should be on the head if unsorted
}
/* If displayed is actually full, let the more important notifications
* from waiting seep into displayed.
*/
if (settings.sort) {
GList *i_waiting, *i_displayed;
while ( (i_waiting = g_queue_peek_head_link(waiting))
&& (i_displayed = g_queue_peek_tail_link(displayed))) {
while (i_waiting && ! queues_notification_is_ready(i_waiting->data, fullscreen, true)) {
i_waiting = i_waiting->prev;
}
if (i_waiting && notification_cmp(i_displayed->data, i_waiting->data) > 0) {
notification *todisp = i_waiting->data;
todisp->start = time_monotonic_now();
notification_run_script(todisp);
queues_swap_notifications(displayed, i_displayed, waiting, i_waiting);
} else {
break;
}
}
}
}
/* see queues.h */

View File

@ -18,14 +18,6 @@
*/
void queues_init(void);
/**
* Set maximum notification count to display
* and store in displayed queue
*
* @param limit The maximum amount
*/
void queues_displayed_limit(unsigned int limit);
/**
* Receive the current list of displayed notifications
*
@ -33,6 +25,13 @@ void queues_displayed_limit(unsigned int limit);
*/
const GList *queues_get_displayed(void);
/**
* Get the highest notification in line
*
* @return a notification or NULL, if waiting is empty
*/
const notification *queues_get_head_waiting(void);
/**
* Returns the current amount of notifications,
* which are waiting to get displayed

View File

@ -516,17 +516,6 @@ struct geometry x_parse_geometry(const char *geom_str)
geometry.negative_x = mask & XNegative;
geometry.negative_y = mask & YNegative;
/* calculate maximum notification count and push information to queue */
if (geometry.h == 0) {
queues_displayed_limit(0);
} else if (geometry.h == 1) {
queues_displayed_limit(1);
} else if (settings.indicate_hidden) {
queues_displayed_limit(geometry.h - 1);
} else {
queues_displayed_limit(geometry.h);
}
return geometry;
}