34static void notification_extract_urls(
struct notification *n);
35static void notification_format_message(
struct notification *n);
46 LOG_E(
"Invalid %s enum value in %s:%d",
"fullscreen", __FILE__, __LINE__);
60 printf(
"\tappname: '%s'\n",
STR_NN(n->appname));
61 printf(
"\tsummary: '%s'\n",
STR_NN(n->summary));
62 printf(
"\tbody: '%s'\n",
STR_NN(n->body));
66 printf(
"\ticon_time: %"G_GINT64_FORMAT
"\n", n->
icon_time);
68 printf(
"\tcategory: '%s'\n",
STR_NN(n->category));
69 printf(
"\ttimeout: %"G_GINT64_FORMAT
"\n", n->
timeout/1000);
70 printf(
"\tstart: %"G_GINT64_FORMAT
"\n", n->
start);
71 printf(
"\ttimestamp: %"G_GINT64_FORMAT
"\n", n->
timestamp);
72 printf(
"\turgency: %s\n", notification_urgency_to_string(n->urgency));
73 printf(
"\ttransient: %d\n", n->
transient);
74 printf(
"\tformatted: '%s'\n",
STR_NN(n->
msg));
80 char *grad = gradient_to_string(n->colors.highlight);
81 printf(
"\thighlight: %s\n",
STR_NN(grad));
85 printf(
"\tformat: '%s'\n",
STR_NN(n->format));
86 printf(
"\tprogress: %d\n", n->
progress);
88 printf(
"\tid: %d\n", n->id);
93 printf(
"\t\t'%s'\n",
STR_NN(urls));
97 printf(
"\turls: {}\n");
99 if (g_hash_table_size(n->actions) == 0) {
100 printf(
"\tactions: {}\n");
102 gpointer p_key, p_value;
104 g_hash_table_iter_init(&iter, n->actions);
105 printf(
"\tactions: {\n");
106 while (g_hash_table_iter_next(&iter, &p_key, &p_value))
107 printf(
"\t\t\"%s\": \"%s\"\n", (
char*)p_key,
STR_NN((
char*)p_value));
110 printf(
"\tscript_count: %d\n", n->script_count);
111 if (n->script_count > 0) {
112 printf(
"\tscripts: ");
113 for (
int i = 0; i < n->script_count; i++) {
114 printf(
"'%s' ",
STR_NN(n->scripts[i]));
130 const char *appname = n->appname ? n->appname :
"";
131 const char *summary = n->summary ? n->summary :
"";
132 const char *body = n->body ? n->body :
"";
135 const char *
urgency = notification_urgency_to_string(n->urgency);
137 for(
int i = 0; i < n->script_count; i++) {
139 const char *script = n->scripts[i];
148 waitpid(pid1, &status, 0);
156 gchar *n_id_str = g_strdup_printf(
"%i", n->id);
157 gchar *n_progress_str = g_strdup_printf(
"%i", n->
progress);
158 gchar *n_timeout_str = g_strdup_printf(
"%"G_GINT64_FORMAT, n->
timeout/1000);
159 gchar *n_timestamp_str = g_strdup_printf(
"%"G_GINT64_FORMAT, n->
timestamp / 1000);
183 LOG_W(
"Unable to run script %s: %s", n->scripts[i], strerror(errno));
193const char *notification_urgency_to_string(
const enum urgency urgency)
222 if(
settings.sort == SORT_TYPE_URGENCY_ASCENDING){
223 if(a_order->urgency != b_order->urgency){
224 return a_order->urgency - b_order->urgency;
226 }
else if (
settings.sort == SORT_TYPE_URGENCY_DESCENDING) {
227 if(a_order->urgency != b_order->urgency){
228 return b_order->urgency - a_order->urgency;
230 }
else if(
settings.sort == SORT_TYPE_UPDATE){
234 return a_order->id - b_order->id;
250 return STR_EQ(a->appname, b->appname)
251 &&
STR_EQ(a->summary, b->summary)
252 &&
STR_EQ(a->body, b->body)
253 && a->urgency == b->urgency
262 return g_atomic_int_get(&n->
locked) != 0;
268 g_atomic_int_set(&n->
locked, 1);
277 g_atomic_int_set(&n->
locked, 0);
283static void notification_private_free(NotificationPrivate *p)
291 assert(n->priv->refcount > 0);
292 return g_atomic_int_get(&n->priv->refcount);
298 assert(n->priv->refcount > 0);
299 g_atomic_int_inc(&n->priv->refcount);
307 assert(n->priv->refcount > 0);
308 if (!g_atomic_int_dec_and_test(&n->priv->refcount))
312 rule_free(n->original);
314 g_free(n->dbus_client);
326 g_hash_table_unref(n->actions);
330 cairo_surface_destroy(n->
icon);
332 notification_private_free(n->priv);
334 gradient_release(n->colors.highlight);
337 g_strfreev(n->scripts);
373 cairo_surface_destroy(n->
icon);
375 g_clear_pointer(&n->
icon_id, g_free);
384 n->
icon = gdk_pixbuf_to_cairo_surface(pixbuf);
386 g_object_unref(pixbuf);
388 LOG_W(
"Failed to load icon from path: '%s'", n->
icon_path);
398 cairo_surface_destroy(n->
icon);
400 g_clear_pointer(&n->
icon_id, g_free);
404 n->
icon = gdk_pixbuf_to_cairo_surface(icon);
407 g_object_unref(icon);
411void notification_replace_format(
struct notification *n,
const char *format)
414 n->format = g_strdup(format);
420 const char *replacement,
421 enum markup_mode markup_mode)
424 assert(*needle[0] ==
'%');
426 assert(*needle >= *haystack);
427 assert(*needle - *haystack < strlen(*haystack) - 1);
429 int pos = *needle - *haystack;
436 *needle = *haystack + pos + strlen(input);
441static NotificationPrivate *notification_private_create(
void)
443 NotificationPrivate *priv = g_malloc0(
sizeof(NotificationPrivate));
444 g_atomic_int_set(&priv->refcount, 1);
454 n->priv = notification_private_create();
458 n->markup = MARKUP_FULL;
459 n->format = g_strdup(
settings.format);
470 n->ellipsize = PANGO_ELLIPSIZE_MIDDLE;
471 n->alignment = PANGO_ALIGN_LEFT;
473 n->hide_text =
false;
479 struct color invalid = COLOR_UNINIT;
480 n->colors.fg = invalid;
481 n->colors.bg = invalid;
482 n->colors.frame = invalid;
483 n->colors.highlight = NULL;
486 n->dbus_valid =
false;
492 n->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
503 n->appname = n->appname ? n->appname : g_strdup(
"unknown");
504 n->summary = n->summary ? n->summary : g_strdup(
"");
505 n->body = n->body ? n->body : g_strdup(
"");
506 n->category = n->category ? n->category : g_strdup(
"");
520 switch (n->urgency) {
531 g_error(
"Unhandled urgency type: %d", n->urgency);
533 if (!COLOR_VALID(n->colors.fg)) n->colors.fg = defcolors.fg;
534 if (!COLOR_VALID(n->colors.bg)) n->colors.bg = defcolors.bg;
535 if (!COLOR_VALID(n->colors.frame)) n->colors.frame = defcolors.frame;
537 if (!GRADIENT_VALID(n->colors.highlight)) {
538 gradient_release(n->colors.highlight);
539 n->colors.highlight = gradient_acquire(defcolors.highlight);
546 n->override_pause_level = 0;
551 if (g_str_has_prefix(n->summary,
"DUNST_COMMAND_")) {
552 const char *msg =
"DUNST_COMMAND_* has been removed, please switch to dunstctl. See #830 for more details. https://github.com/dunst-project/dunst/pull/830";
559 g_clear_pointer(&n->
iconname, g_free);
567 notification_extract_urls(n);
568 notification_format_message(n);
576static void notification_format_message(
struct notification *n)
578 g_clear_pointer(&n->
msg, g_free);
584 for(
char *substr = strchr(n->
msg,
'%');
586 substr = strchr(substr,
'%')) {
632 icon_tmp ? basename(icon_tmp) :
"",
645 sprintf(pg,
"[%3d%%]", n->
progress);
671 LOG_W(
"format_string has trailing %% character. "
672 "To escape it use %%%%.");
676 LOG_W(
"format_string %%%c is unknown.", substr[1]);
684 n->
msg = g_strchomp(n->
msg);
687 if (strnlen(n->
msg, DUNST_NOTIF_MAX_CHARS + 1) > DUNST_NOTIF_MAX_CHARS) {
688 char * buffer = g_strndup(n->
msg, DUNST_NOTIF_MAX_CHARS);
694static void notification_extract_urls(
struct notification *n)
696 g_clear_pointer(&n->
urls, g_free);
698 char *urls_in =
string_append(g_strdup(n->summary), n->body,
" ");
701 char *urls_img = NULL;
719void notification_update_text_to_render(
struct notification *n)
725 char *msg = g_strchomp(n->
msg);
729 && (g_hash_table_size(n->actions) || n->
urls) &&
settings.show_indicators) {
730 buf = g_strdup_printf(
"(%d%s%s) %s",
732 g_hash_table_size(n->actions) ?
"A" :
"",
733 n->
urls ?
"U" :
"", msg);
734 }
else if ((g_hash_table_size(n->actions) || n->
urls) &&
settings.show_indicators) {
735 buf = g_strdup_printf(
"(%s%s) %s",
736 g_hash_table_size(n->actions) ?
"A" :
"",
737 n->
urls ?
"U" :
"", msg);
739 buf = g_strdup_printf(
"(%d) %s", n->
dup_count, msg);
745 gint64 hours, minutes, seconds;
749 if (
settings.show_age_threshold >= 0
750 && t_delta >=
settings.show_age_threshold) {
751 hours =
US2S(t_delta) / 3600;
752 minutes =
US2S(t_delta) / 60 % 60;
753 seconds =
US2S(t_delta) % 60;
757 new_buf = g_strdup_printf(
"%s (%"G_GINT64_FORMAT
"h %"G_GINT64_FORMAT
"m %"G_GINT64_FORMAT
"s old)",
758 buf, hours, minutes, seconds);
759 }
else if (minutes > 0) {
760 new_buf = g_strdup_printf(
"%s (%"G_GINT64_FORMAT
"m %"G_GINT64_FORMAT
"s old)",
761 buf, minutes, seconds);
763 new_buf = g_strdup_printf(
"%s (%"G_GINT64_FORMAT
"s old)",
779 if (g_hash_table_size(n->actions)) {
784 if (strcmp(n->
default_action_name,
"default") == 0 && g_hash_table_size(n->actions) == 1) {
785 GList *keys = g_hash_table_get_keys(n->actions);
786 signal_action_invoked(n, keys->data);
792 }
else if (n->
urls) {
802 LOG_W(
"There are no URL's in this notification");
805 if (strstr(n->
urls,
"\n"))
808 open_browser(n->
urls);
814 GList *notifications = NULL;
815 notifications = g_list_append(notifications, n);
816 notification_lock(n);
822 g_hash_table_remove_all(n->actions);
827 if (n->original)
return;
828 n->original = g_malloc0(
sizeof(
struct rule));
829 *n->original = empty_rule;
830 n->original->name = g_strdup(
"original");
DBus support and implementation of the Desktop Notifications Specification.
char * color_to_string(struct color c, char buf[10])
Stringify a color struct to a RRGGBBAA string.
Layout and render notifications.
Recursive icon lookup in theme directories.
char * get_path_from_icon_name(const char *iconname, int size)
Retrieve a path from an icon name.
GdkPixbuf * icon_get_for_data(GVariant *data, char **id, double dpi_scale, int min_size, int max_size)
Convert a GVariant like described in GdkPixbuf, scaled according to settings.
GdkPixbuf * get_pixbuf_from_file(const char *filename, char **id, int min_size, int max_size, double scale)
Retrieve an icon by its full filepath, scaled according to settings.
Notification images loading.
Logging subsystem and helpers.
#define LOG_E
Prefix message with "[<source path>:<function name>:<line number>] ".
void markup_strip_img(char **str, char **urls)
Remove img-tags of a string.
void markup_strip_a(char **str, char **urls)
Remove HTML hyperlinks of a string.
char * markup_transform(char *str, enum markup_mode markup_mode)
Transform the string in accordance with markup_mode and settings.ignore_newline
Markup handling for notifications body.
void notification_do_action(struct notification *n)
If the notification has an action named n->default_action_name or there is only one action and n->def...
void notification_ref(struct notification *n)
Increase the reference counter of the notification.
int notification_cmp_data(const void *va, const void *vb, void *data)
Wrapper for notification_cmp to match glib's compare functions signature.
int notification_cmp(const struct notification *a, const struct notification *b)
Helper function to compare two given notifications.
void notification_invalidate_actions(struct notification *n)
Remove all client action data from the notification.
void notification_open_url(struct notification *n)
If the notification has exactly one url, invoke it.
void notification_run_script(struct notification *n)
Run the script associated with the given notification.
gint notification_refcount_get(struct notification *n)
Retrieve the current reference count of the notification.
void notification_icon_replace_path(struct notification *n, const char *new_icon)
Replace the current notification's icon with the icon specified by path.
void notification_init(struct notification *n)
Sanitize values of notification, apply all matching rules and generate derived fields.
void notification_transfer_icon(struct notification *from, struct notification *to)
Transfer the image surface of from to to.
void notification_open_context_menu(struct notification *n)
Open the context menu for the notification.
void notification_replace_single_field(char **haystack, char **needle, const char *replacement, enum markup_mode markup_mode)
Replace the two chars where **needle points with a quoted "replacement", according to the markup sett...
void notification_print(const struct notification *n)
print a human readable representation of the given notification to stdout.
void notification_unref(struct notification *n)
Decrease the reference counter of the notification.
const char * enum_to_string_fullscreen(enum behavior_fullscreen in)
Return the string representation for fullscreen behavior.
struct notification * notification_create(void)
Create notification struct and initialise all fields with either.
void notification_icon_replace_data(struct notification *n, GVariant *new_icon)
Replace the current notification's icon with the raw icon given in the GVariant.
Notification type definitions.
urgency
Representing the urgencies according to the notification spec.
@ URG_NONE
Urgency not set (invalid)
@ URG_NORM
Normal urgency.
@ URG_MIN
Minimum value, useful for boundary checking.
@ URG_CRIT
Critical urgency.
@ URG_MAX
Maximum value, useful for boundary checking.
@ FS_SHOW
Show the message when in fullscreen mode.
@ FS_PUSHBACK
When entering fullscreen mode, push the notification back to waiting.
@ FS_DELAY
Delay the notification until leaving fullscreen mode.
Queues for history, waiting and displayed notifications.
@ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM
the bottom edge of the anchor rectangle
List of all the valid settings and values.
char * stack_tag
stack notifications by tag
char * icon_path
Full path to the notification's icon.
bool receiving_raw_icon
Still waiting for raw icon to be received.
PangoAlignment progress_bar_alignment
Horizontal alignment of the progress bar.
int min_icon_size
Minimum icon size.
bool script_run
Has the script been executed already?
char * desktop_entry
The desktop entry hint sent via every GApplication.
enum icon_position icon_position
Icon position (enum left,right,top,off).
char * msg
formatted message
int locked
If non-zero the notification is locked.
char * iconname
plain icon information (may be a path or just a name) as recieved from dbus.
int progress
percentage (-1: undefined)
gint64 icon_time
Time of reception of the icon (or opening of the file in case of a path)
int max_icon_size
Maximum icon size.
cairo_surface_t * icon
The raw cached icon data used to draw.
gint64 timestamp
arrival time (in milliseconds)
char * text_to_render
formatted message (with age and action indicators)
char * urls
urllist delimited by '\n'
bool first_render
markup has been rendered before?
char * icon_id
Plain icon information, which acts as the icon's id.
enum behavior_fullscreen fullscreen
The instruction what to do with it, when desktop enters fullscreen.
gint64 start
begin of current display (in milliseconds)
bool transient
timeout albeit user is idle
char * default_icon_name
The icon that is used when no other icon is available.
char * default_action_name
The name of the action to be invoked on do_action.
gint64 timeout
time to display (in milliseconds)
gint64 dbus_timeout
time to display (in milliseconds) (set by dbus)
int dup_count
amount of duplicate notifications stacked onto this
gint64 time_monotonic_now(void)
Get the current monotonic time.
bool safe_setenv(const char *key, const char *value)
Try to set an environment variable safely.
char * string_replace_at(char *buf, int pos, int len, const char *repl)
Replace a substring inside a string with another string.
gint64 time_now(void)
Get the current real time.
char * string_replace_all(const char *needle, const char *replacement, char *haystack)
Replace all occurences of a substring.
char * string_append(char *a, const char *b, const char *sep)
Append b to string a, then concatenate both with sep (if they are non-empty).
String, time and other various helpers.
#define STR_EMPTY(s)
Test if a string is NULL or empty.
#define S2US(s)
Convert seconds into microseconds.
#define STR_EQ(a, b)
Test if string a and b contain the same chars.
#define STR_NN(s)
Get a non null string from a possibly null one.
#define ASSERT_OR_RET(expr, val)
Assert that expr evaluates to true, if not return val.
#define US2S(s)
Convert microseconds into seconds.