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 ### Added
- `fullscreen` rule to hide notifications when a fullscreen window is active - `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 ## 1.3.2 - 2018-05-06

View File

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

View File

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

View File

@ -77,6 +77,7 @@ typedef struct _notification {
int dup_count; /**< amount of duplicate notifications stacked onto this */ int dup_count; /**< amount of duplicate notifications stacked onto this */
int displayed_height; int displayed_height;
enum behavior_fullscreen fullscreen; //!< The instruction what to do with it, when desktop enters fullscreen 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 */ /* derived fields */
char *msg; /**< formatted message */ char *msg; /**< formatted message */
@ -139,6 +140,9 @@ int notification_is_duplicate(const notification *a, const notification *b);
/** /**
* Run the script associated with the * Run the script associated with the
* given notification. * 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); 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 *displayed = NULL; /**< currently displayed notifications */
static GQueue *history = NULL; /**< history of displayed notifications */ static GQueue *history = NULL; /**< history of displayed notifications */
unsigned int displayed_limit = 0;
int next_notification_id = 1; int next_notification_id = 1;
bool pause_displayed = false; bool pause_displayed = false;
@ -44,18 +43,20 @@ void queues_init(void)
waiting = g_queue_new(); waiting = g_queue_new();
} }
/* see queues.h */
void queues_displayed_limit(unsigned int limit)
{
displayed_limit = limit;
}
/* see queues.h */ /* see queues.h */
const GList *queues_get_displayed(void) const GList *queues_get_displayed(void)
{ {
return g_queue_peek_head_link(displayed); 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 */ /* see queues.h */
unsigned int queues_length_waiting(void) unsigned int queues_length_waiting(void)
{ {
@ -74,6 +75,51 @@ unsigned int queues_length_history(void)
return history->length; 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 */ /* see queues.h */
int queues_notification_insert(notification *n) int queues_notification_insert(notification *n)
{ {
@ -123,52 +169,31 @@ int queues_notification_insert(notification *n)
*/ */
static bool queues_stack_duplicate(notification *n) static bool queues_stack_duplicate(notification *n)
{ {
for (GList *iter = g_queue_peek_head_link(displayed); iter; GQueue *allqueues[] = { displayed, waiting };
iter = iter->next) { for (int i = 0; i < sizeof(allqueues)/sizeof(GList*); i++) {
notification *orig = iter->data; for (GList *iter = g_queue_peek_head_link(allqueues[i]); iter;
if (notification_is_duplicate(orig, n)) { iter = iter->next) {
/* If the progress differs, probably notify-send was used to update the notification notification *orig = iter->data;
* So only count it as a duplicate, if the progress was not the same. if (notification_is_duplicate(orig, n)) {
* */ /* If the progress differs, probably notify-send was used to update the notification
if (orig->progress == n->progress) { * So only count it as a duplicate, if the progress was not the same.
orig->dup_count++; * */
} else { if (orig->progress == n->progress) {
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 */ /* see queues.h */
bool queues_notification_replace_id(notification *new) 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); if ( allqueues[i] == displayed ) {
iter; new->start = time_monotonic_now();
iter = iter->next) { notification_run_script(new);
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;
}
}
for (GList *iter = g_queue_peek_head_link(waiting); notification_free(old);
iter; return true;
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;
} }
} }
return false; return false;
} }
@ -212,24 +232,16 @@ void queues_notification_close_id(int id, enum reason reason)
{ {
notification *target = NULL; notification *target = NULL;
for (GList *iter = g_queue_peek_head_link(displayed); iter; GQueue *allqueues[] = { displayed, waiting };
iter = iter->next) { for (int i = 0; i < sizeof(allqueues)/sizeof(GList*); i++) {
notification *n = iter->data; for (GList *iter = g_queue_peek_head_link(allqueues[i]); iter;
if (n->id == id) { iter = iter->next) {
g_queue_remove(displayed, n); notification *n = iter->data;
target = n; if (n->id == id) {
break; g_queue_remove(allqueues[i], 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;
} }
} }
@ -258,7 +270,7 @@ void queues_history_pop(void)
n->redisplayed = true; n->redisplayed = true;
n->start = 0; n->start = 0;
n->timeout = settings.sticky_history ? 0 : n->timeout; 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 */ /* 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 */ /* move notifications from queue to displayed */
GList *iter = g_queue_peek_head_link(waiting); GList *iter = g_queue_peek_head_link(waiting);
while (iter) { while (displayed->length < cur_displayed_limit && iter) {
notification *n = iter->data; notification *n = iter->data;
GList *nextiter = iter->next; GList *nextiter = iter->next;
if (displayed_limit > 0 && displayed->length >= displayed_limit) {
/* the list is full */
break;
}
if (!n) if (!n)
return; return;
if (fullscreen if (!queues_notification_is_ready(n, fullscreen, false)) {
&& (n->fullscreen == FS_DELAY || n->fullscreen == FS_PUSHBACK)) {
iter = nextiter; iter = nextiter;
continue; continue;
} }
n->start = time_monotonic_now(); n->start = time_monotonic_now();
notification_run_script(n);
if (!n->redisplayed && n->script) {
notification_run_script(n);
}
g_queue_delete_link(waiting, iter); g_queue_delete_link(waiting, iter);
g_queue_insert_sorted(displayed, n, notification_cmp_data, NULL); g_queue_insert_sorted(displayed, n, notification_cmp_data, NULL);
iter = nextiter; 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 */ /* see queues.h */

View File

@ -18,14 +18,6 @@
*/ */
void queues_init(void); 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 * Receive the current list of displayed notifications
* *
@ -33,6 +25,13 @@ void queues_displayed_limit(unsigned int limit);
*/ */
const GList *queues_get_displayed(void); 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, * Returns the current amount of notifications,
* which are waiting to get displayed * 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_x = mask & XNegative;
geometry.negative_y = mask & YNegative; 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; return geometry;
} }