diff options
| author | Victor Kareh <[email protected]> | 2025-08-20 20:49:51 -0400 |
|---|---|---|
| committer | Luke from DC <[email protected]> | 2025-08-25 21:50:51 +0000 |
| commit | d348da7ea809e1bcfb9e408455271395a64c877a (patch) | |
| tree | 888416a5b29ef342bb2fd49fb255b05a7e1cfc0f | |
| parent | 7ed54960995ef18d5fff9092b839565080456a40 (diff) | |
| download | mate-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.in | 11 | ||||
| -rw-r--r-- | src/daemon/daemon.c | 91 | ||||
| -rw-r--r-- | src/daemon/daemon.h | 2 |
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, |
