summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Kareh <[email protected]>2025-08-20 20:49:51 -0400
committerLuke from DC <[email protected]>2025-08-25 21:50:51 +0000
commitd348da7ea809e1bcfb9e408455271395a64c877a (patch)
tree888416a5b29ef342bb2fd49fb255b05a7e1cfc0f
parent7ed54960995ef18d5fff9092b839565080456a40 (diff)
downloadmate-notification-daemon-d348da7ea809e1bcfb9e408455271395a64c877a.tar.bz2
mate-notification-daemon-d348da7ea809e1bcfb9e408455271395a64c877a.tar.xz
daemon: implement Desktop Notifications Specification 1.3
Add persistence support with resident/transient hints, fix icon precedence order (according to spec), and implement desktop-entry icons. Also added new 'default-timeout' and 'enable-persistence' gsettings to allow users to control timeout and persistence behavior. Spec is on https://specifications.freedesktop.org/notification-spec/1.3/ Fixes https://github.com/mate-desktop/mate-notification-daemon/issues/132 Fixes https://github.com/mate-desktop/mate-notification-daemon/issues/137 Fixes https://github.com/mate-desktop/mate-notification-daemon/issues/138 Fixes https://github.com/mate-desktop/mate-notification-daemon/issues/149
-rw-r--r--data/org.mate.NotificationDaemon.gschema.xml.in11
-rw-r--r--src/daemon/daemon.c91
-rw-r--r--src/daemon/daemon.h2
3 files changed, 86 insertions, 18 deletions
diff --git a/data/org.mate.NotificationDaemon.gschema.xml.in b/data/org.mate.NotificationDaemon.gschema.xml.in
index 17ea761..0cd797f 100644
--- a/data/org.mate.NotificationDaemon.gschema.xml.in
+++ b/data/org.mate.NotificationDaemon.gschema.xml.in
@@ -30,5 +30,16 @@
<summary>Do not disturb</summary>
<description>When enabled, notifications are not shown.</description>
</key>
+ <key name="default-timeout" type="i">
+ <range min="0" max="300000"/>
+ <default>7000</default>
+ <summary>Default timeout</summary>
+ <description>Default timeout for notifications in milliseconds. Use 0 for no timeout (persistent).</description>
+ </key>
+ <key name="enable-persistence" type="b">
+ <default>true</default>
+ <summary>Enable persistence</summary>
+ <description>Allow notifications to be persistent when requested by applications.</description>
+ </key>
</schema>
</schemalist>
diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c
index 2064c46..cc38e0f 100644
--- a/src/daemon/daemon.c
+++ b/src/daemon/daemon.c
@@ -93,6 +93,7 @@ typedef struct {
GtkWindow *nw;
guint has_timeout : 1;
guint paused : 1;
+ guint resident : 1;
#ifdef HAVE_X11
Window src_window_xid;
#endif /* HAVE_X11 */
@@ -869,9 +870,22 @@ static gboolean _check_expiration(NotifyDaemon* daemon)
return has_more_timeouts;
}
-static void _calculate_timeout(NotifyDaemon* daemon, NotifyTimeout* nt, int timeout)
+static void _calculate_timeout(NotifyDaemon* daemon, NotifyTimeout* nt, int timeout, gboolean resident)
{
- if (timeout == 0)
+ GSettings *gsettings = g_settings_new ("org.mate.NotificationDaemon");
+ gboolean persistence_enabled = g_settings_get_boolean (gsettings, "enable-persistence");
+ g_object_unref (gsettings);
+
+ nt->resident = resident && persistence_enabled;
+
+ if (timeout == -1)
+ {
+ GSettings *gsettings = g_settings_new ("org.mate.NotificationDaemon");
+ timeout = g_settings_get_int (gsettings, "default-timeout");
+ g_object_unref (gsettings);
+ }
+
+ if (timeout == 0 || nt->resident)
{
nt->has_timeout = FALSE;
}
@@ -881,11 +895,6 @@ static void _calculate_timeout(NotifyDaemon* daemon, NotifyTimeout* nt, int time
nt->has_timeout = TRUE;
- if (timeout == -1)
- {
- timeout = NOTIFY_DAEMON_DEFAULT_TIMEOUT;
- }
-
theme_set_notification_timeout(nt->nw, timeout);
usec = (gint64) timeout * G_TIME_SPAN_MILLISECOND; /* convert from msec to usec */
@@ -925,7 +934,7 @@ static guint _generate_id(NotifyDaemon* daemon)
return id;
}
-static NotifyTimeout* _store_notification(NotifyDaemon* daemon, GtkWindow* nw, int timeout)
+static NotifyTimeout* _store_notification(NotifyDaemon* daemon, GtkWindow* nw, int timeout, gboolean resident)
{
NotifyTimeout* nt;
guint id = _generate_id(daemon);
@@ -935,7 +944,7 @@ static NotifyTimeout* _store_notification(NotifyDaemon* daemon, GtkWindow* nw, i
nt->nw = nw;
nt->daemon = daemon;
- _calculate_timeout(daemon, nt, timeout);
+ _calculate_timeout(daemon, nt, timeout, resident);
#if GLIB_CHECK_VERSION (2, 68, 0)
g_hash_table_insert(daemon->notification_hash, g_memdup2(&id, sizeof(guint)), nt);
@@ -1363,6 +1372,10 @@ static gboolean notify_daemon_notify_handler(NotifyDaemonNotifications *object,
GdkPixbuf* pixbuf;
GSettings* gsettings;
gboolean fullscreen_window;
+ gboolean resident = FALSE;
+ gboolean transient = FALSE;
+ const gchar *desktop_entry = NULL;
+ const gchar *category = NULL;
#ifdef HAVE_X11
Window window_xid = None;
@@ -1472,6 +1485,14 @@ static gboolean notify_daemon_notify_handler(NotifyDaemonNotifications *object,
}
}
+ /* process resident and transient hints for persistence */
+ g_variant_lookup(hints, "resident", "b", &resident);
+ g_variant_lookup(hints, "transient", "b", &transient);
+
+ g_variant_lookup(hints, "desktop-entry", "s", &desktop_entry);
+ // TODO: Use 'category' for future category-based notification settings
+ g_variant_lookup(hints, "category", "s", &category);
+
/* set up action buttons */
for (i = 0; actions[i] != NULL; i += 2)
{
@@ -1490,23 +1511,29 @@ static gboolean notify_daemon_notify_handler(NotifyDaemonNotifications *object,
pixbuf = NULL;
- if (g_variant_lookup(hints, "image_data", "@(iiibiiay)", &data))
+ /* According to the Desktop Notifications Specification 1.3 spec, icons have the following precedence:
+ * 1. image-data (or image_data)
+ * 2. image-path (or image_path)
+ * 3. icon (which falls back to the desktop entry)
+ * 4. icon_data (for backward compatibility)
+ */
+ if (g_variant_lookup(hints, "image-data", "@(iiibiiay)", &data))
{
pixbuf = _notify_daemon_pixbuf_from_data_hint (data);
g_variant_unref(data);
}
- else if (g_variant_lookup(hints, "image-data", "@(iiibiiay)", &data))
+ else if (g_variant_lookup(hints, "image_data", "@(iiibiiay)", &data))
{
pixbuf = _notify_daemon_pixbuf_from_data_hint (data);
g_variant_unref(data);
}
- else if (g_variant_lookup(hints, "image_path", "@s", &data))
+ else if (g_variant_lookup(hints, "image-path", "@s", &data))
{
const char *path = g_variant_get_string (data, NULL);
pixbuf = _notify_daemon_pixbuf_from_path (path);
g_variant_unref(data);
}
- else if (g_variant_lookup(hints, "image-path", "@s", &data))
+ else if (g_variant_lookup(hints, "image_path", "@s", &data))
{
const char *path = g_variant_get_string (data, NULL);
pixbuf = _notify_daemon_pixbuf_from_path (path);
@@ -1516,6 +1543,37 @@ static gboolean notify_daemon_notify_handler(NotifyDaemonNotifications *object,
{
pixbuf = _notify_daemon_pixbuf_from_path (icon);
}
+ else if (desktop_entry != NULL && *desktop_entry != '\0')
+ {
+ /* Use desktop-entry to resolve application icon as fallback */
+ gchar *desktop_file = g_strdup_printf("%s.desktop", desktop_entry);
+ gchar *icon_name = NULL;
+
+ /* Try to get icon from desktop file */
+ GKeyFile *key_file = g_key_file_new();
+ const gchar * const *search_dirs = g_get_system_data_dirs();
+
+ for (const gchar * const *dir = search_dirs; *dir != NULL; dir++)
+ {
+ gchar *desktop_path = g_build_filename(*dir, "applications", desktop_file, NULL);
+ if (g_key_file_load_from_file(key_file, desktop_path, G_KEY_FILE_NONE, NULL))
+ {
+ icon_name = g_key_file_get_string(key_file, "Desktop Entry", "Icon", NULL);
+ g_free(desktop_path);
+ break;
+ }
+ g_free(desktop_path);
+ }
+
+ if (icon_name != NULL)
+ {
+ pixbuf = _notify_daemon_pixbuf_from_path (icon_name);
+ g_free(icon_name);
+ }
+
+ g_key_file_free(key_file);
+ g_free(desktop_file);
+ }
else if (g_variant_lookup(hints, "icon_data", "@(iiibiiay)", &data))
{
g_warning("\"icon_data\" hint is deprecated, please use \"image_data\" instead");
@@ -1604,7 +1662,7 @@ static gboolean notify_daemon_notify_handler(NotifyDaemonNotifications *object,
if (id == 0)
{
- nt = _store_notification (daemon, nw, timeout);
+ nt = _store_notification (daemon, nw, timeout, resident && !transient);
return_id = nt->id;
}
else
@@ -1664,7 +1722,7 @@ static gboolean notify_daemon_notify_handler(NotifyDaemonNotifications *object,
if (nt)
{
- _calculate_timeout (daemon, nt, timeout);
+ _calculate_timeout (daemon, nt, timeout, resident && !transient);
}
notify_daemon_notifications_complete_notify ( object, invocation, return_id);
@@ -1701,6 +1759,7 @@ static gboolean notify_daemon_get_capabilities( NotifyDaemonNotifications *objec
g_variant_builder_add (builder, "s", "body-markup");
g_variant_builder_add (builder, "s", "icon-static");
g_variant_builder_add (builder, "s", "sound");
+ g_variant_builder_add (builder, "s", "persistence");
value = g_variant_new ("as", builder);
g_variant_builder_unref (builder);
notify_daemon_notifications_complete_get_capabilities (
@@ -1718,7 +1777,7 @@ static gboolean notify_daemon_get_server_information (NotifyDaemonNotifications
"Notification Daemon",
"MATE",
PACKAGE_VERSION,
- "1.1");
+ "1.3");
return TRUE;
}
diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h
index 4c32e12..1b6ddf8 100644
--- a/src/daemon/daemon.h
+++ b/src/daemon/daemon.h
@@ -34,8 +34,6 @@
#define NOTIFY_TYPE_DAEMON (notify_daemon_get_type())
G_DECLARE_FINAL_TYPE (NotifyDaemon, notify_daemon, NOTIFY, DAEMON, GObject)
-#define NOTIFY_DAEMON_DEFAULT_TIMEOUT 7000
-
enum {
URGENCY_LOW,
URGENCY_NORMAL,