Merge pull request #552 from Gravemind/x-canonical-private-synchronous

Implement x-canonical-private-synchronous hint
This commit is contained in:
Nikos Tsipinakis 2018-11-11 19:55:05 +02:00 committed by GitHub
commit 27c6a1682d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 114 additions and 28 deletions

View File

@ -611,9 +611,9 @@ matched.
=item B<filtering> =item B<filtering>
Notifications can be matched for any of the following attributes: appname, Notifications can be matched for any of the following attributes: appname,
summary, body, icon, category, match_transient and msg_urgency where each is summary, body, icon, category, match_transient, msg_urgency, and stack_tag where
the respective notification attribute to be matched and 'msg_urgency' is the each is the respective notification attribute to be matched and 'msg_urgency' is
urgency of the notification, it is named so to not conflict with trying to the urgency of the notification, it is named so to not conflict with trying to
modify the urgency. modify the urgency.
To define a matching rule simply assign the specified value to the value that To define a matching rule simply assign the specified value to the value that
@ -630,9 +630,9 @@ Shell-like globing is supported.
=item B<modifying> =item B<modifying>
The following attributes can be overridden: timeout, urgency, foreground, The following attributes can be overridden: timeout, urgency, foreground,
background, frame_color, new_icon, set_transient, format, fullscreen where, background, frame_color, new_icon, set_transient, format, fullscreen,
as with the filtering attributes, each one corresponds to the respective set_stack_tag where, as with the filtering attributes, each one corresponds to
notification attribute to be modified. the respective notification attribute to be modified.
As with filtering, to make a rule modify an attribute simply assign it in the As with filtering, to make a rule modify an attribute simply assign it in the
rule definition. rule definition.
@ -640,6 +640,11 @@ rule definition.
If the format is set to an empty string, the notification will not be If the format is set to an empty string, the notification will not be
suppressed. suppressed.
Notifications with the same non-empty stack_tag value will be stacked
together. The default stack_stag value is set from the string hints
"synchronous", "private-synchronous", "x-canonical-private-synchronous", and
"x-dunst-stack-tag".
=back =back
=head2 SCRIPTING =head2 SCRIPTING

11
dunstrc
View File

@ -147,10 +147,10 @@
# Ignore newlines '\n' in notifications. # Ignore newlines '\n' in notifications.
ignore_newline = no ignore_newline = no
# Merge multiple notifications with the same content # Stack together notifications with the same content
stack_duplicates = true stack_duplicates = true
# Hide the count of merged notifications with the same content # Hide the count of stacked notifications with the same content
hide_duplicate_count = false hide_duplicate_count = false
# Display indicators for URLs (U) and actions (A). # Display indicators for URLs (U) and actions (A).
@ -299,7 +299,8 @@
# override settings for certain messages. # override settings for certain messages.
# Messages can be matched by "appname", "summary", "body", "icon", "category", # Messages can be matched by "appname", "summary", "body", "icon", "category",
# "msg_urgency" and you can override the "timeout", "urgency", "foreground", # "msg_urgency" and you can override the "timeout", "urgency", "foreground",
# "background", "frame_color", "new_icon" and "format", "fullscreen". # "background", "frame_color", "new_icon" and "format", "fullscreen",
# "stack_tag".
# Shell-like globbing will get expanded. # Shell-like globbing will get expanded.
# #
# SCRIPTING # SCRIPTING
@ -365,4 +366,8 @@
# summary = *twitter.com* # summary = *twitter.com*
# urgency = normal # urgency = normal
# #
#[stack-volumes]
# appname = "some_volume_notifiers"
# set_stack_tag = "volume"
#
# vim: ft=cfg # vim: ft=cfg

View File

@ -65,6 +65,13 @@ static const char *introspection_xml =
" </interface>" " </interface>"
"</node>"; "</node>";
static const char *stack_tag_hints[] = {
"synchronous",
"private-synchronous",
"x-canonical-private-synchronous",
"x-dunst-stack-tag"
};
static void on_get_capabilities(GDBusConnection *connection, static void on_get_capabilities(GDBusConnection *connection,
const gchar *sender, const gchar *sender,
const GVariant *parameters, const GVariant *parameters,
@ -120,6 +127,9 @@ static void on_get_capabilities(GDBusConnection *connection,
g_variant_builder_add(builder, "s", "body"); g_variant_builder_add(builder, "s", "body");
g_variant_builder_add(builder, "s", "body-hyperlinks"); g_variant_builder_add(builder, "s", "body-hyperlinks");
for (int i = 0; i < sizeof(stack_tag_hints)/sizeof(*stack_tag_hints); ++i)
g_variant_builder_add(builder, "s", stack_tag_hints[i]);
if (settings.markup != MARKUP_NO) if (settings.markup != MARKUP_NO)
g_variant_builder_add(builder, "s", "body-markup"); g_variant_builder_add(builder, "s", "body-markup");
@ -245,6 +255,20 @@ static struct notification *dbus_message_to_notification(const gchar *sender, GV
n->progress = g_variant_get_uint32(dict_value); n->progress = g_variant_get_uint32(dict_value);
g_variant_unref(dict_value); g_variant_unref(dict_value);
} }
/* Check for hints that define the stack_tag
*
* Only accept to first one we find.
*/
for (int i = 0; i < sizeof(stack_tag_hints)/sizeof(*stack_tag_hints); ++i) {
dict_value = g_variant_lookup_value(content, stack_tag_hints[i], G_VARIANT_TYPE_STRING);
if (dict_value) {
n->stack_tag = g_variant_dup_string(dict_value, NULL);
g_variant_unref(dict_value);
break;
}
}
} }
break; break;
case 7: case 7:

View File

@ -66,6 +66,7 @@ void notification_print(const struct notification *n)
printf("\tframe: %s\n", n->colors[ColFrame]); printf("\tframe: %s\n", n->colors[ColFrame]);
printf("\tfullscreen: %s\n", enum_to_string_fullscreen(n->fullscreen)); printf("\tfullscreen: %s\n", enum_to_string_fullscreen(n->fullscreen));
printf("\tprogress: %d\n", n->progress); printf("\tprogress: %d\n", n->progress);
printf("\tstack_tag: %s\n", (n->stack_tag ? n->stack_tag : ""));
printf("\tid: %d\n", n->id); printf("\tid: %d\n", n->id);
if (n->urls) { if (n->urls) {
char *urls = string_replace_all("\n", "\t\t\n", g_strdup(n->urls)); char *urls = string_replace_all("\n", "\t\t\n", g_strdup(n->urls));
@ -251,6 +252,7 @@ void notification_unref(struct notification *n)
g_free(n->colors[ColFG]); g_free(n->colors[ColFG]);
g_free(n->colors[ColBG]); g_free(n->colors[ColBG]);
g_free(n->colors[ColFrame]); g_free(n->colors[ColFrame]);
g_free(n->stack_tag);
actions_free(n->actions); actions_free(n->actions);
rawimage_free(n->raw_icon); rawimage_free(n->raw_icon);

View File

@ -70,6 +70,8 @@ struct notification {
const char *script; const char *script;
char *colors[3]; char *colors[3];
char *stack_tag; /**< stack notifications by tag */
/* Hints */ /* Hints */
bool transient; /**< timeout albeit user is idle */ bool transient; /**< timeout albeit user is idle */
int progress; /**< percentage (-1: undefined) */ int progress; /**< percentage (-1: undefined) */

View File

@ -34,6 +34,7 @@ int next_notification_id = 1;
bool pause_displayed = false; bool pause_displayed = false;
static bool queues_stack_duplicate(struct notification *n); static bool queues_stack_duplicate(struct notification *n);
static bool queues_stack_by_tag(struct notification *n);
/* see queues.h */ /* see queues.h */
void queues_init(void) void queues_init(void)
@ -123,6 +124,7 @@ static bool queues_notification_is_ready(const struct notification *n, bool full
/* see queues.h */ /* see queues.h */
int queues_notification_insert(struct notification *n) int queues_notification_insert(struct notification *n)
{ {
bool inserted = false;
/* do not display the message, if the message is empty */ /* do not display the message, if the message is empty */
if (STR_EMPTY(n->msg)) { if (STR_EMPTY(n->msg)) {
@ -146,14 +148,17 @@ int queues_notification_insert(struct notification *n)
return 0; return 0;
} }
if (n->id == 0) { if (!inserted && n->id != 0 && queues_notification_replace_id(n))
inserted = true;
else
n->id = ++next_notification_id; n->id = ++next_notification_id;
if (!settings.stack_duplicates || !queues_stack_duplicate(n))
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL); if (!inserted && STR_FULL(n->stack_tag) && queues_stack_by_tag(n))
} else { inserted = true;
if (!queues_notification_replace_id(n)) if (!inserted && settings.stack_duplicates && queues_stack_duplicate(n))
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL); inserted = true;
} if (!inserted)
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL);
if (settings.print_notifications) if (settings.print_notifications)
notification_print(n); notification_print(n);
@ -170,7 +175,7 @@ int queues_notification_insert(struct notification *n)
static bool queues_stack_duplicate(struct notification *n) static bool queues_stack_duplicate(struct notification *n)
{ {
GQueue *allqueues[] = { displayed, waiting }; GQueue *allqueues[] = { displayed, waiting };
for (int i = 0; i < sizeof(allqueues)/sizeof(GList*); i++) { for (int i = 0; i < sizeof(allqueues)/sizeof(GQueue*); i++) {
for (GList *iter = g_queue_peek_head_link(allqueues[i]); iter; for (GList *iter = g_queue_peek_head_link(allqueues[i]); iter;
iter = iter->next) { iter = iter->next) {
struct notification *orig = iter->data; struct notification *orig = iter->data;
@ -188,7 +193,7 @@ static bool queues_stack_duplicate(struct notification *n)
n->dup_count = orig->dup_count; n->dup_count = orig->dup_count;
signal_notification_closed(orig, 1); signal_notification_closed(orig, 1);
if ( allqueues[i] == displayed ) if (allqueues[i] == displayed)
n->start = time_monotonic_now(); n->start = time_monotonic_now();
notification_unref(orig); notification_unref(orig);
@ -200,11 +205,43 @@ static bool queues_stack_duplicate(struct notification *n)
return false; return false;
} }
/**
* Replaces the first notification of the same stack_tag
*
* @return true, if notification got stacked
* @return false, if notification did not get stacked
*/
static bool queues_stack_by_tag(struct notification *new)
{
GQueue *allqueues[] = { displayed, waiting };
for (int i = 0; i < sizeof(allqueues)/sizeof(GQueue*); i++) {
for (GList *iter = g_queue_peek_head_link(allqueues[i]); iter;
iter = iter->next) {
struct notification *old = iter->data;
if (STR_FULL(old->stack_tag) && STR_EQ(old->stack_tag, new->stack_tag)) {
iter->data = new;
new->dup_count = old->dup_count;
signal_notification_closed(old, 1);
if (allqueues[i] == displayed) {
new->start = time_monotonic_now();
notification_run_script(new);
}
notification_unref(old);
return true;
}
}
}
return false;
}
/* see queues.h */ /* see queues.h */
bool queues_notification_replace_id(struct notification *new) bool queues_notification_replace_id(struct notification *new)
{ {
GQueue *allqueues[] = { displayed, waiting }; GQueue *allqueues[] = { displayed, waiting };
for (int i = 0; i < sizeof(allqueues)/sizeof(GList*); i++) { for (int i = 0; i < sizeof(allqueues)/sizeof(GQueue*); i++) {
for (GList *iter = g_queue_peek_head_link(allqueues[i]); for (GList *iter = g_queue_peek_head_link(allqueues[i]);
iter; iter;
iter = iter->next) { iter = iter->next) {
@ -213,7 +250,7 @@ bool queues_notification_replace_id(struct notification *new)
iter->data = new; iter->data = new;
new->dup_count = old->dup_count; new->dup_count = old->dup_count;
if ( allqueues[i] == displayed ) { if (allqueues[i] == displayed) {
new->start = time_monotonic_now(); new->start = time_monotonic_now();
notification_run_script(new); notification_run_script(new);
} }
@ -232,7 +269,7 @@ void queues_notification_close_id(int id, enum reason reason)
struct notification *target = NULL; struct notification *target = NULL;
GQueue *allqueues[] = { displayed, waiting }; GQueue *allqueues[] = { displayed, waiting };
for (int i = 0; i < sizeof(allqueues)/sizeof(GList*); i++) { for (int i = 0; i < sizeof(allqueues)/sizeof(GQueue*); i++) {
for (GList *iter = g_queue_peek_head_link(allqueues[i]); iter; for (GList *iter = g_queue_peek_head_link(allqueues[i]); iter;
iter = iter->next) { iter = iter->next) {
struct notification *n = iter->data; struct notification *n = iter->data;

View File

@ -47,6 +47,10 @@ void rule_apply(struct rule *r, struct notification *n)
n->format = r->format; n->format = r->format;
if (r->script) if (r->script)
n->script = r->script; n->script = r->script;
if (r->set_stack_tag) {
g_free(n->stack_tag);
n->stack_tag = g_strdup(r->set_stack_tag);
}
} }
/* /*
@ -73,6 +77,7 @@ void rule_init(struct rule *r)
r->body = NULL; r->body = NULL;
r->icon = NULL; r->icon = NULL;
r->category = NULL; r->category = NULL;
r->stack_tag = NULL;
r->msg_urgency = URG_NONE; r->msg_urgency = URG_NONE;
r->timeout = -1; r->timeout = -1;
r->urgency = URG_NONE; r->urgency = URG_NONE;
@ -86,6 +91,7 @@ void rule_init(struct rule *r)
r->bg = NULL; r->bg = NULL;
r->fc = NULL; r->fc = NULL;
r->format = NULL; r->format = NULL;
r->set_stack_tag = NULL;
} }
/* /*
@ -93,11 +99,12 @@ void rule_init(struct rule *r)
*/ */
bool rule_matches_notification(struct rule *r, struct notification *n) bool rule_matches_notification(struct rule *r, struct notification *n)
{ {
return ( (!r->appname || (n->appname && !fnmatch(r->appname, n->appname, 0))) return ( (!r->appname || (n->appname && !fnmatch(r->appname, n->appname, 0)))
&& (!r->summary || (n->summary && !fnmatch(r->summary, n->summary, 0))) && (!r->summary || (n->summary && !fnmatch(r->summary, n->summary, 0)))
&& (!r->body || (n->body && !fnmatch(r->body, n->body, 0))) && (!r->body || (n->body && !fnmatch(r->body, n->body, 0)))
&& (!r->icon || (n->icon && !fnmatch(r->icon, n->icon, 0))) && (!r->icon || (n->icon && !fnmatch(r->icon, n->icon, 0)))
&& (!r->category || (n->category && !fnmatch(r->category, n->category, 0))) && (!r->category || (n->category && !fnmatch(r->category, n->category, 0)))
&& (!r->stack_tag || (n->stack_tag && !fnmatch(r->stack_tag, n->stack_tag, 0)))
&& (r->match_transient == -1 || (r->match_transient == n->transient)) && (r->match_transient == -1 || (r->match_transient == n->transient))
&& (r->msg_urgency == URG_NONE || r->msg_urgency == n->urgency)); && (r->msg_urgency == URG_NONE || r->msg_urgency == n->urgency));
} }

View File

@ -16,6 +16,7 @@ struct rule {
char *body; char *body;
char *icon; char *icon;
char *category; char *category;
char *stack_tag;
int msg_urgency; int msg_urgency;
/* actions */ /* actions */
@ -32,6 +33,7 @@ struct rule {
const char *format; const char *format;
const char *script; const char *script;
enum behavior_fullscreen fullscreen; enum behavior_fullscreen fullscreen;
char *set_stack_tag;
}; };
extern GSList *rules; extern GSList *rules;

View File

@ -368,7 +368,7 @@ void load_settings(char *cmdline_config_path)
settings.hide_duplicate_count = option_get_bool( settings.hide_duplicate_count = option_get_bool(
"global", "global",
"hide_duplicate_count", "-hide_duplicate_count", false, "hide_duplicate_count", "-hide_duplicate_count", false,
"Hide the count of merged notifications with the same content" "Hide the count of stacked notifications with the same content"
); );
settings.sticky_history = option_get_bool( settings.sticky_history = option_get_bool(
@ -444,7 +444,7 @@ void load_settings(char *cmdline_config_path)
settings.stack_duplicates = option_get_bool( settings.stack_duplicates = option_get_bool(
"global", "global",
"stack_duplicates", "-stack_duplicates", true, "stack_duplicates", "-stack_duplicates", true,
"Merge multiple notifications with the same content" "Stack together notifications with the same content"
); );
settings.startup_notification = option_get_bool( settings.startup_notification = option_get_bool(
@ -785,6 +785,7 @@ void load_settings(char *cmdline_config_path)
r->body = ini_get_string(cur_section, "body", r->body); r->body = ini_get_string(cur_section, "body", r->body);
r->icon = ini_get_string(cur_section, "icon", r->icon); r->icon = ini_get_string(cur_section, "icon", r->icon);
r->category = ini_get_string(cur_section, "category", r->category); r->category = ini_get_string(cur_section, "category", r->category);
r->stack_tag = ini_get_string(cur_section, "stack_tag", r->stack_tag);
r->timeout = ini_get_time(cur_section, "timeout", r->timeout); r->timeout = ini_get_time(cur_section, "timeout", r->timeout);
{ {
@ -819,6 +820,7 @@ void load_settings(char *cmdline_config_path)
g_free(c); g_free(c);
} }
r->script = ini_get_path(cur_section, "script", NULL); r->script = ini_get_path(cur_section, "script", NULL);
r->set_stack_tag = ini_get_string(cur_section, "set_stack_tag", r->set_stack_tag);
} }
#ifndef STATIC_CONFIG #ifndef STATIC_CONFIG