Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libnotify for openSUSE:Factory checked in at 2022-08-05 19:50:15 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libnotify (Old) and /work/SRC/openSUSE:Factory/.libnotify.new.1521 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libnotify" Fri Aug 5 19:50:15 2022 rev:43 rq:992698 version:0.8.1 Changes: -------- --- /work/SRC/openSUSE:Factory/libnotify/libnotify.changes 2022-05-14 22:54:44.083183817 +0200 +++ /work/SRC/openSUSE:Factory/.libnotify.new.1521/libnotify.changes 2022-08-05 19:50:40.417404934 +0200 @@ -1,0 +2,19 @@ +Fri Jul 22 17:31:30 UTC 2022 - Emily Gonyer <emilyyr...@gmail.com> + +- Update to version 0.8.1: + + Keep version of bindings at 0.7. +- Changes from version 0.8.0: + + Use Desktop Portal Notification when running confined (snap and + flatpak). + + Now the library acts like a wrapper in such scenario, with some + limited. + + capabilities, but this will enforce security and user control + over the allowed notifications. + + notify-send: Handles SIGINT gracefully, closing waiting + notification. + + Use NotifyClosedReason enum as closed reason return value. + + Bump dependency on GLib 2.38. + + Various introspection docs improvements and fixes. +- Use ldconfig_scriptlets macro for post(un) handling. + +------------------------------------------------------------------- Old: ---- libnotify-0.7.12.tar.xz New: ---- libnotify-0.8.1.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libnotify.spec ++++++ --- /var/tmp/diff_new_pack.Bgd2Y4/_old 2022-08-05 19:50:41.921408818 +0200 +++ /var/tmp/diff_new_pack.Bgd2Y4/_new 2022-08-05 19:50:41.925408829 +0200 @@ -17,14 +17,15 @@ Name: libnotify -Version: 0.7.12 +Version: 0.8.1 Release: 0 Summary: Notifications Library License: LGPL-2.1-or-later Group: Development/Libraries/X11 URL: https://galago-project.org/ -Source: https://download.gnome.org/sources/libnotify/0.7/%{name}-%{version}.tar.xz +Source: https://download.gnome.org/sources/libnotify/0.8/%{name}-%{version}.tar.xz Source99: baselibs.conf + BuildRequires: docbook5-xsl-stylesheets BuildRequires: gobject-introspection-devel BuildRequires: gtk-doc @@ -91,13 +92,11 @@ %install %meson_install -%post -n libnotify4 -p /sbin/ldconfig -%postun -n libnotify4 -p /sbin/ldconfig +%ldconfig_scriptlets -n libnotify4 %files -n libnotify4 %license COPYING -# README is empty -%doc AUTHORS NEWS +%doc AUTHORS NEWS README.md %{_libdir}/*.so.* %files -n typelib-1_0-Notify-0_7 ++++++ libnotify-0.7.12.tar.xz -> libnotify-0.8.1.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libnotify-0.7.12/NEWS new/libnotify-0.8.1/NEWS --- old/libnotify-0.7.12/NEWS 2022-05-05 18:04:26.352672300 +0200 +++ new/libnotify-0.8.1/NEWS 2022-07-17 15:00:39.000000000 +0200 @@ -1,11 +1,34 @@ -New in 0.7.12 +New in 0.8.1 +============ + +* Keep version of bindings at 0.7 (#27) [Marco] + +Contributors: + Marco Trevisan + +New in 0.8.0 ============ +* Use Desktop Portal Notification when running confined (snap and flatpak) + Now the library acts like a wrapper in such scenario, with some limited + capabilities, but this will enforce security and user control over the + allowed notifications. [Marco] +* notify-send: Handles SIGINT gracefully, closing waiting notification [Marco] +* Use NotifyClosedReason enum as closed reason return value [Marco] +* Bump dependency on GLib 2.38 [Marco] +* Various introspection docs improvements and fixes [Marco] + +Contributors: + Marco Trevisan + +New in 0.7.12 +============= + * docs/notify-send: Add --transient option to manpage [Marco] * notify-send: Move server capabilities check to a separate function [Marco] * notify-send: Add debug message about server not supporting persistence [Marco] -* notification: Include sender-pid hint by default if not provided [Marco] +* notification: Include sender-pid hint by default if not provided [Marco] * Delete unused notifynotification.xml [Patrick; !25] * notification: Bookend calling NotifyActionCallback with temporary ref [Logan; #25, !26] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libnotify-0.7.12/README.md new/libnotify-0.8.1/README.md --- old/libnotify-0.7.12/README.md 2022-05-05 18:04:26.352672300 +0200 +++ new/libnotify-0.8.1/README.md 2022-07-17 15:00:39.000000000 +0200 @@ -7,6 +7,9 @@ Specification. These notifications can be used to inform the user about an event or display some form of information without getting in the user's way. +It is also a simple wrapper to send cross-desktop Notifications for sandboxed +applications using the [XDG Portal Notification API][portal]. + ## Notice For GLib based applications the [GNotification][gnotif] API should be used @@ -14,3 +17,4 @@ [fdo-spec]: https://specifications.freedesktop.org/notification-spec/notification-spec-latest.html [gnotif]: https://docs.gtk.org/gio/class.Notification.html +[portal]: https://github.com/flatpak/xdg-desktop-portal/blob/main/data/org.freedesktop.portal.Notification.xml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libnotify-0.7.12/docs/reference/libnotify-sections.txt new/libnotify-0.8.1/docs/reference/libnotify-sections.txt --- old/libnotify-0.7.12/docs/reference/libnotify-sections.txt 2022-05-05 18:04:26.354672200 +0200 +++ new/libnotify-0.8.1/docs/reference/libnotify-sections.txt 2022-07-17 15:00:39.000000000 +0200 @@ -4,6 +4,7 @@ NOTIFY_EXPIRES_NEVER <TITLE>NotifyNotification</TITLE> NotifyNotification +NotifyClosedReason NotifyUrgency NotifyActionCallback NOTIFY_ACTION_CALLBACK diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libnotify-0.7.12/libnotify/internal.h new/libnotify-0.8.1/libnotify/internal.h --- old/libnotify-0.7.12/libnotify/internal.h 2022-05-05 18:04:26.355672400 +0200 +++ new/libnotify-0.8.1/libnotify/internal.h 2022-07-17 15:00:39.000000000 +0200 @@ -26,6 +26,10 @@ #define NOTIFY_DBUS_CORE_INTERFACE "org.freedesktop.Notifications" #define NOTIFY_DBUS_CORE_OBJECT "/org/freedesktop/Notifications" +#define NOTIFY_PORTAL_DBUS_NAME "org.freedesktop.portal.Desktop" +#define NOTIFY_PORTAL_DBUS_CORE_INTERFACE "org.freedesktop.portal.Notification" +#define NOTIFY_PORTAL_DBUS_CORE_OBJECT "/org/freedesktop/portal/desktop" + G_BEGIN_DECLS GDBusProxy * _notify_get_proxy (GError **error); @@ -36,6 +40,14 @@ gboolean _notify_notification_has_nondefault_actions (const NotifyNotification *n); gboolean _notify_check_spec_version (int major, int minor); +const char * _notify_get_snap_name (void); +const char * _notify_get_snap_path (void); +const char * _notify_get_snap_app (void); + +const char * _notify_get_flatpak_app (void); + +gboolean _notify_uses_portal_notifications (void); + G_END_DECLS #endif /* _LIBNOTIFY_INTERNAL_H_ */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libnotify-0.7.12/libnotify/notification.c new/libnotify-0.8.1/libnotify/notification.c --- old/libnotify-0.7.12/libnotify/notification.c 2022-05-05 18:04:26.356672300 +0200 +++ new/libnotify-0.8.1/libnotify/notification.c 2022-07-17 15:00:39.000000000 +0200 @@ -53,6 +53,7 @@ static void notify_notification_class_init (NotifyNotificationClass *klass); static void notify_notification_init (NotifyNotification *sp); static void notify_notification_finalize (GObject *object); +static void notify_notification_dispose (GObject *object); typedef struct { @@ -70,12 +71,9 @@ char *body; char *activation_token; - const char *snap_path; - const char *snap_name; - char *snap_app; - /* NULL to use icon data. Anything else to have server lookup icon */ char *icon_name; + GdkPixbuf *icon_pixbuf; /* * -1 = use server default @@ -83,6 +81,7 @@ * > 0 = Number of milliseconds before we timeout */ gint timeout; + guint portal_timeout_id; GSList *actions; GHashTable *action_map; @@ -154,6 +153,7 @@ object_class->constructor = notify_notification_constructor; object_class->get_property = notify_notification_get_property; object_class->set_property = notify_notification_set_property; + object_class->dispose = notify_notification_dispose; object_class->finalize = notify_notification_finalize; /** @@ -238,9 +238,9 @@ g_param_spec_int ("closed-reason", "Closed Reason", "The reason code for why the notification was closed", - -1, + NOTIFY_CLOSED_REASON_UNSET, G_MAXINT32, - -1, + NOTIFY_CLOSED_REASON_UNSET, G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK @@ -357,109 +357,11 @@ } static void -maybe_initialize_snap (NotifyNotification *obj) -{ - NotifyNotificationPrivate *priv = obj->priv; - gchar *cgroup_contents = NULL; - - priv->snap_path = g_getenv ("SNAP"); - if (priv->snap_path == NULL) - return; - - if (*priv->snap_path == '\0' || - !strchr (priv->snap_path, G_DIR_SEPARATOR)) { - priv->snap_path = NULL; - return; - } - - priv->snap_name = g_getenv ("SNAP_NAME"); - if (priv->snap_name && *priv->snap_name == '\0') { - priv->snap_name = NULL; - } - - if (g_file_get_contents ("/proc/self/cgroup", &cgroup_contents, - NULL, NULL)) { - gchar **lines = g_strsplit (cgroup_contents, "\n", -1); - gchar *found_snap_name = NULL; - gint i; - - for (i = 0; lines[i]; ++i) { - gchar **parts = g_strsplit (lines[i], ":", 3); - gchar *basename; - gchar **ns; - guint ns_length; - - if (g_strv_length (parts) != 3) { - g_strfreev (parts); - continue; - } - - basename = g_path_get_basename (parts[2]); - g_strfreev (parts); - - if (!basename) { - continue; - } - - ns = g_strsplit (basename, ".", -1); - ns_length = g_strv_length (ns); - g_free (basename); - - if (ns_length < 2 || !g_str_equal (ns[0], "snap")) { - g_strfreev (ns); - continue; - } - - if (priv->snap_name == NULL) { - g_free (found_snap_name); - found_snap_name = g_strdup (ns[1]); - } - - if (ns_length < 3) { - g_strfreev (ns); - continue; - } - - if (priv->snap_name == NULL) { - priv->snap_name = found_snap_name; - found_snap_name = NULL; - } - - if (g_str_equal (ns[1], priv->snap_name)) { - priv->snap_app = g_strdup (ns[2]); - g_strfreev (ns); - break; - } - - g_strfreev (ns); - } - - if (priv->snap_name == NULL && found_snap_name != NULL) { - priv->snap_name = found_snap_name; - found_snap_name = NULL; - } - - g_strfreev (lines); - g_free (found_snap_name); - } - - if (priv->snap_app == NULL) { - priv->snap_app = g_strdup (priv->snap_name); - } - - g_debug ("SNAP path: %s", priv->snap_path); - g_debug ("SNAP name: %s", priv->snap_name); - g_debug ("SNAP app: %s", priv->snap_app); - - g_free (cgroup_contents); -} - -static void notify_notification_init (NotifyNotification *obj) { obj->priv = g_new0 (NotifyNotificationPrivate, 1); obj->priv->timeout = NOTIFY_EXPIRES_DEFAULT; - obj->priv->closed_reason = -1; + obj->priv->closed_reason = NOTIFY_CLOSED_REASON_UNSET; obj->priv->hints = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, @@ -469,8 +371,22 @@ g_str_equal, g_free, (GDestroyNotify) destroy_pair); +} + +static void +notify_notification_dispose (GObject *object) +{ + NotifyNotification *obj = NOTIFY_NOTIFICATION (object); + NotifyNotificationPrivate *priv = obj->priv; - maybe_initialize_snap (obj); + if (priv->portal_timeout_id) { + g_source_remove (priv->portal_timeout_id); + priv->portal_timeout_id = 0; + } + + g_clear_object (&priv->icon_pixbuf); + + G_OBJECT_CLASS (parent_class)->dispose (object); } static void @@ -487,7 +403,6 @@ g_free (priv->body); g_free (priv->icon_name); g_free (priv->activation_token); - g_free (priv->snap_app); if (priv->actions != NULL) { g_slist_foreach (priv->actions, (GFunc) g_free, NULL); @@ -510,6 +425,18 @@ G_OBJECT_CLASS (parent_class)->finalize (object); } +static gboolean +maybe_warn_portal_unsupported_feature (const char *feature_name) +{ + if (!_notify_uses_portal_notifications ()) { + return FALSE; + } + + g_message ("%s is not available when using Portal Notifications", + feature_name); + return TRUE; +} + /** * notify_notification_new: * @summary: The required summary text. @@ -596,7 +523,6 @@ try_prepend_snap_desktop (NotifyNotification *notification, const gchar *desktop) { - NotifyNotificationPrivate *priv = notification->priv; gchar *ret = NULL; /* @@ -604,11 +530,11 @@ * ${SNAP_NAME}_; snap .desktop files are in the format * ${SNAP_NAME}_desktop_file_name */ - ret = try_prepend_path (desktop, priv->snap_path); + ret = try_prepend_path (desktop, _notify_get_snap_path ()); - if (ret == NULL && priv->snap_name != NULL && + if (ret == NULL && _notify_get_snap_name () != NULL && strchr (desktop, G_DIR_SEPARATOR) == NULL) { - ret = g_strdup_printf ("%s_%s", priv->snap_name, desktop); + ret = g_strdup_printf ("%s_%s", _notify_get_snap_name (), desktop); } return ret; @@ -619,7 +545,7 @@ const gchar *value) { /* hardcoded paths to icons might be relocated under $SNAP */ - return try_prepend_path (value, notification->priv->snap_path); + return try_prepend_path (value, _notify_get_snap_path ()); } @@ -698,6 +624,80 @@ return TRUE; } +static char * +get_portal_notification_id (NotifyNotification *notification) +{ + char *app_id; + char *notification_id; + + g_assert (_notify_uses_portal_notifications ()); + + if (_notify_get_snap_name ()) { + app_id = g_strdup_printf ("snap.%s_%s", + _notify_get_snap_name (), + _notify_get_snap_app ()); + } else { + app_id = g_strdup_printf ("flatpak.%s", + _notify_get_flatpak_app ()); + } + + notification_id = g_strdup_printf ("libnotify-%s-%s-%u", + app_id, + notify_get_app_name (), + notification->priv->id); + + g_free (app_id); + + return notification_id; +} + +static gboolean +activate_action (NotifyNotification *notification, + const gchar *action) +{ + CallbackPair *pair; + + pair = g_hash_table_lookup (notification->priv->action_map, action); + + if (!pair) { + return FALSE; + } + + /* Some clients have assumed it is safe to unref the + * Notification at the end of their NotifyActionCallback + * so we add a temporary ref until we're done with it. + */ + g_object_ref (notification); + + notification->priv->activating = TRUE; + pair->cb (notification, (char *) action, pair->user_data); + notification->priv->activating = FALSE; + g_free (notification->priv->activation_token); + notification->priv->activation_token = NULL; + + g_object_unref (notification); + + return TRUE; +} + +static gboolean +close_notification (NotifyNotification *notification, + NotifyClosedReason reason) +{ + if (notification->priv->closed_reason != NOTIFY_CLOSED_REASON_UNSET || + reason == NOTIFY_CLOSED_REASON_UNSET) { + return FALSE; + } + + g_object_ref (G_OBJECT (notification)); + notification->priv->closed_reason = reason; + g_signal_emit (notification, signals[SIGNAL_CLOSED], 0); + notification->priv->id = 0; + g_object_unref (G_OBJECT (notification)); + + return TRUE; +} + static void proxy_g_signal_cb (GDBusProxy *proxy, const char *sender_name, @@ -705,8 +705,12 @@ GVariant *parameters, NotifyNotification *notification) { + const char *interface; + g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification)); + interface = g_dbus_proxy_get_interface_name (proxy); + if (g_strcmp0 (signal_name, "NotificationClosed") == 0 && g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(uu)"))) { guint32 id, reason; @@ -715,43 +719,21 @@ if (id != notification->priv->id) return; - g_object_ref (G_OBJECT (notification)); - notification->priv->closed_reason = reason; - g_signal_emit (notification, signals[SIGNAL_CLOSED], 0); - notification->priv->id = 0; - g_object_unref (G_OBJECT (notification)); + close_notification (notification, reason); } else if (g_strcmp0 (signal_name, "ActionInvoked") == 0 && + g_str_equal (interface, NOTIFY_DBUS_CORE_INTERFACE) && g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(us)"))) { guint32 id; const char *action; - CallbackPair *pair; g_variant_get (parameters, "(u&s)", &id, &action); if (id != notification->priv->id) return; - pair = (CallbackPair *) g_hash_table_lookup (notification->priv->action_map, - action); - - if (pair == NULL) { - if (g_ascii_strcasecmp (action, "default")) { - g_warning ("Received unknown action %s", action); - } - } else { - /* Some clients have assumed it is safe to unref the - * Notification at the end of their NotifyActionCallback - * so we add a temporary ref until we're done with it. - */ - g_object_ref (notification); - - notification->priv->activating = TRUE; - pair->cb (notification, (char *) action, pair->user_data); - notification->priv->activating = FALSE; - g_free (notification->priv->activation_token); - notification->priv->activation_token = NULL; - - g_object_unref (notification); + if (!activate_action (notification, action) && + g_ascii_strcasecmp (action, "default")) { + g_warning ("Received unknown action %s", action); } } else if (g_strcmp0 (signal_name, "ActivationToken") == 0 && g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(us)"))) { @@ -765,7 +747,304 @@ g_free (notification->priv->activation_token); notification->priv->activation_token = g_strdup (activation_token); + } else if (g_str_equal (signal_name, "ActionInvoked") && + g_str_equal (interface, NOTIFY_PORTAL_DBUS_CORE_INTERFACE) && + g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(ssav)"))) { + char *notification_id; + const char *id; + const char *action; + GVariant *parameter; + + g_variant_get (parameters, "(&s&s@av)", &id, &action, ¶meter); + g_variant_unref (parameter); + + notification_id = get_portal_notification_id (notification); + + if (!g_str_equal (notification_id, id)) { + g_free (notification_id); + return; + } + + if (!activate_action (notification, action) && + g_str_equal (action, "default-action") && + !_notify_get_snap_app ()) { + g_warning ("Received unknown action %s", action); + } + + close_notification (notification, NOTIFY_CLOSED_REASON_DISMISSED); + + g_free (notification_id); + } else { + g_debug ("Unhandled signal '%s.%s'", interface, signal_name); + } +} + +static gboolean +remove_portal_notification (GDBusProxy *proxy, + NotifyNotification *notification, + NotifyClosedReason reason, + GError **error) +{ + GVariant *ret; + gchar *notification_id; + + if (notification->priv->portal_timeout_id) { + g_source_remove (notification->priv->portal_timeout_id); + notification->priv->portal_timeout_id = 0; + } + + notification_id = get_portal_notification_id (notification); + + ret = g_dbus_proxy_call_sync (proxy, + "RemoveNotification", + g_variant_new ("(s)", notification_id), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); + + g_free (notification_id); + + if (!ret) { + return FALSE; + } + + close_notification (notification, reason); + + g_variant_unref (ret); + + return TRUE; +} + +static gboolean +on_portal_timeout (gpointer data) +{ + NotifyNotification *notification = data; + GDBusProxy *proxy; + + notification->priv->portal_timeout_id = 0; + + proxy = _notify_get_proxy (NULL); + if (proxy == NULL) { + return FALSE; + } + + remove_portal_notification (proxy, notification, + NOTIFY_CLOSED_REASON_EXPIRED, NULL); + return FALSE; +} + +static GIcon * +get_notification_gicon (NotifyNotification *notification, + GError **error) +{ + NotifyNotificationPrivate *priv = notification->priv; + GFileInputStream *input; + GFile *file = NULL; + GIcon *gicon = NULL; + + if (priv->icon_pixbuf) { + return G_ICON (g_object_ref (priv->icon_pixbuf)); + } + + if (!priv->icon_name) { + return NULL; + } + + if (strstr (priv->icon_name, "://")) { + file = g_file_new_for_uri (priv->icon_name); + } else if (g_file_test (priv->icon_name, G_FILE_TEST_EXISTS)) { + file = g_file_new_for_path (priv->icon_name); + } else { + gicon = g_themed_icon_new (priv->icon_name); + } + + if (!file) { + return gicon; + } + + input = g_file_read (file, NULL, error); + + if (input) { + GByteArray *bytes_array = g_byte_array_new (); + guint8 buf[1024]; + + while (TRUE) { + gssize read; + + read = g_input_stream_read (G_INPUT_STREAM (input), + buf, + G_N_ELEMENTS (buf), + NULL, NULL); + + if (read > 0) { + g_byte_array_append (bytes_array, buf, read); + } else { + if (read < 0) { + g_byte_array_unref (bytes_array); + bytes_array = NULL; + } + + break; + } + } + + if (bytes_array && bytes_array->len) { + GBytes *bytes; + + bytes = g_byte_array_free_to_bytes (bytes_array); + bytes_array = NULL; + + gicon = g_bytes_icon_new (bytes); + } else if (bytes_array) { + g_byte_array_unref (bytes_array); + } } + + g_clear_object (&input); + g_clear_object (&file); + + return gicon; +} + +static gboolean +add_portal_notification (GDBusProxy *proxy, + NotifyNotification *notification, + GError **error) +{ + GIcon *icon; + GVariant *urgency; + GVariant *ret; + GVariantBuilder builder; + NotifyNotificationPrivate *priv = notification->priv; + GError *local_error = NULL; + static guint32 portal_notification_count = 0; + char *notification_id; + + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + + g_variant_builder_add (&builder, "{sv}", "title", + g_variant_new_string (priv->summary ? priv->summary : "")); + g_variant_builder_add (&builder, "{sv}", "body", + g_variant_new_string (priv->body ? priv->body : "")); + + if (g_hash_table_lookup (priv->action_map, "default")) { + g_variant_builder_add (&builder, "{sv}", "default-action", + g_variant_new_string ("default")); + } else if (g_hash_table_lookup (priv->action_map, "DEFAULT")) { + g_variant_builder_add (&builder, "{sv}", "default-action", + g_variant_new_string ("DEFAULT")); + } else if (_notify_get_snap_app ()) { + /* In the snap case we may need to ensure that a default-action + * is set to ensure that we will use the FDO notification daemon + * and won't fallback to GTK one, as app-id won't match. + * See: https://github.com/flatpak/xdg-desktop-portal/issues/769 + */ + g_variant_builder_add (&builder, "{sv}", "default-action", + g_variant_new_string ("snap-fake-default-action")); + } + + if (priv->has_nondefault_actions) { + GVariantBuilder buttons; + GSList *l; + + g_variant_builder_init (&buttons, G_VARIANT_TYPE ("aa{sv}")); + + for (l = priv->actions; l && l->next; l = l->next->next) { + GVariantBuilder button; + const char *action; + const char *label; + + g_variant_builder_init (&button, G_VARIANT_TYPE_VARDICT); + + action = l->data; + label = l->next->data; + + g_variant_builder_add (&button, "{sv}", "action", + g_variant_new_string (action)); + g_variant_builder_add (&button, "{sv}", "label", + g_variant_new_string (label)); + + g_variant_builder_add (&buttons, "@a{sv}", + g_variant_builder_end (&button)); + } + + g_variant_builder_add (&builder, "{sv}", "buttons", + g_variant_builder_end (&buttons)); + } + + urgency = g_hash_table_lookup (notification->priv->hints, "urgency"); + if (urgency) { + switch (g_variant_get_byte (urgency)) { + case NOTIFY_URGENCY_LOW: + g_variant_builder_add (&builder, "{sv}", "priority", + g_variant_new_string ("low")); + break; + case NOTIFY_URGENCY_NORMAL: + g_variant_builder_add (&builder, "{sv}", "priority", + g_variant_new_string ("normal")); + break; + case NOTIFY_URGENCY_CRITICAL: + g_variant_builder_add (&builder, "{sv}", "priority", + g_variant_new_string ("urgent")); + break; + default: + g_warn_if_reached (); + } + } + + icon = get_notification_gicon (notification, &local_error); + if (icon) { + GVariant *serialized_icon = g_icon_serialize (icon); + + g_variant_builder_add (&builder, "{sv}", "icon", + serialized_icon); + g_variant_unref (serialized_icon); + g_clear_object (&icon); + } else if (local_error) { + g_propagate_error (error, local_error); + return FALSE; + } + + if (!priv->id) { + priv->id = ++portal_notification_count; + } else if (priv->closed_reason == NOTIFY_CLOSED_REASON_UNSET) { + remove_portal_notification (proxy, notification, + NOTIFY_CLOSED_REASON_UNSET, NULL); + } + + notification_id = get_portal_notification_id (notification); + + ret = g_dbus_proxy_call_sync (proxy, + "AddNotification", + g_variant_new ("(s@a{sv})", + notification_id, + g_variant_builder_end (&builder)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); + + if (priv->portal_timeout_id) { + g_source_remove (priv->portal_timeout_id); + priv->portal_timeout_id = 0; + } + + g_free (notification_id); + + if (!ret) { + return FALSE; + } + + if (priv->timeout > 0) { + priv->portal_timeout_id = g_timeout_add (priv->timeout, + on_portal_timeout, + notification); + } + + g_variant_unref (ret); + + return TRUE; } /** @@ -789,9 +1068,7 @@ GHashTableIter iter; gpointer key, data; GVariant *result; -#ifdef GLIB_VERSION_2_32 GApplication *application = NULL; -#endif g_return_val_if_fail (notification != NULL, FALSE); g_return_val_if_fail (NOTIFY_IS_NOTIFICATION (notification), FALSE); @@ -815,6 +1092,10 @@ notification); } + if (_notify_uses_portal_notifications ()) { + return add_portal_notification (proxy, notification, error); + } + g_variant_builder_init (&actions_builder, G_VARIANT_TYPE ("as")); for (l = priv->actions; l != NULL; l = l->next) { g_variant_builder_add (&actions_builder, "s", l->data); @@ -831,13 +1112,13 @@ g_variant_new_int64 (getpid ())); } - if (priv->snap_app && + if (_notify_get_snap_app () && g_hash_table_lookup (priv->hints, "desktop-entry") == NULL) { gchar *snap_desktop; snap_desktop = g_strdup_printf ("%s_%s", - priv->snap_name, - priv->snap_app); + _notify_get_snap_name (), + _notify_get_snap_app ()); g_debug ("Using desktop entry: %s", snap_desktop); g_variant_builder_add (&hints_builder, "{sv}", @@ -845,8 +1126,7 @@ g_variant_new_take_string (snap_desktop)); } -#ifdef GLIB_VERSION_2_32 - if (!priv->snap_app) { + if (!_notify_get_snap_app ()) { application = g_application_get_default (); } @@ -861,7 +1141,6 @@ g_variant_new_string (application_id)); } } -#endif /* TODO: make this nonblocking */ result = g_dbus_proxy_call_sync (proxy, @@ -940,6 +1219,10 @@ g_return_if_fail (notification != NULL); g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification)); + if (maybe_warn_portal_unsupported_feature ("Category")) { + return; + } + if (category != NULL && category[0] != '\0') { notify_notification_set_hint_string (notification, "category", @@ -1017,11 +1300,18 @@ hint_name = "icon_data"; } + g_clear_object (¬ification->priv->icon_pixbuf); + if (pixbuf == NULL) { notify_notification_set_hint (notification, hint_name, NULL); return; } + if (_notify_uses_portal_notifications ()) { + notification->priv->icon_pixbuf = g_object_ref (pixbuf); + return; + } + g_object_get (pixbuf, "width", &width, "height", &height, @@ -1078,7 +1368,7 @@ { StringParserFunc parse_func = NULL; - if (!notification->priv->snap_path) + if (!_notify_get_snap_path ()) return value; if (g_strcmp0 (key, "desktop-entry") == 0) { @@ -1145,6 +1435,10 @@ { g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification)); + if (maybe_warn_portal_unsupported_feature ("App Name")) { + return; + } + g_free (notification->priv->app_name); notification->priv->app_name = g_strdup (app_name); @@ -1385,6 +1679,7 @@ /** * notify_notification_get_activation_token: + * @notification: The notification. * * If an an action is currently being activated, return the activation token. * This function is intended to be used in a #NotifyActionCallback to get @@ -1399,11 +1694,9 @@ notify_notification_get_activation_token (NotifyNotification *notification) { g_return_val_if_fail (NOTIFY_IS_NOTIFICATION (notification), NULL); + g_return_val_if_fail (notification->priv->activating, NULL); - if (notification->priv->activating) - return notification->priv->activation_token; - - return NULL; + return notification->priv->activation_token; } gboolean @@ -1442,6 +1735,12 @@ return FALSE; } + if (_notify_uses_portal_notifications ()) { + return remove_portal_notification (proxy, notification, + NOTIFY_CLOSED_REASON_API_REQUEST, + error); + } + /* FIXME: make this nonblocking! */ result = g_dbus_proxy_call_sync (proxy, "CloseNotification", @@ -1466,13 +1765,17 @@ * Returns the closed reason code for the notification. This is valid only * after the "closed" signal is emitted. * - * Returns: The closed reason code. + * Since version 0.8.0 the returned value is of type #NotifyClosedReason. + * + * Returns: An integer representing the closed reason code + * (Since 0.8.0 it's also a #NotifyClosedReason). */ gint notify_notification_get_closed_reason (const NotifyNotification *notification) { - g_return_val_if_fail (notification != NULL, -1); - g_return_val_if_fail (NOTIFY_IS_NOTIFICATION (notification), -1); + g_return_val_if_fail (notification != NULL, NOTIFY_CLOSED_REASON_UNSET); + g_return_val_if_fail (NOTIFY_IS_NOTIFICATION (notification), + NOTIFY_CLOSED_REASON_UNSET); return notification->priv->closed_reason; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libnotify-0.7.12/libnotify/notification.h new/libnotify-0.8.1/libnotify/notification.h --- old/libnotify-0.7.12/libnotify/notification.h 2022-05-05 18:04:26.357672200 +0200 +++ new/libnotify-0.8.1/libnotify/notification.h 2022-07-17 15:00:39.000000000 +0200 @@ -55,6 +55,11 @@ typedef struct _NotifyNotificationClass NotifyNotificationClass; typedef struct _NotifyNotificationPrivate NotifyNotificationPrivate; +/** + * NotifyNotification: + * + * A passive pop-up notification. + */ struct _NotifyNotification { /*< private >*/ @@ -88,11 +93,34 @@ } NotifyUrgency; + +/** + * NotifyClosedReason: + * @NOTIFY_CLOSED_REASON_UNSET: Notification not closed. + * @NOTIFY_CLOSED_REASON_EXPIRED: Timeout has expired. + * @NOTIFY_CLOSED_REASON_DISMISSED: It has been dismissed by the user. + * @NOTIFY_CLOSED_REASON_API_REQUEST: It has been closed by a call to + * notify_notification_close(). + * @NOTIFY_CLOSED_REASON_UNDEFIEND: Closed by undefined/reserved reasons. + * + * The reason for which the notification has been closed. + * + * Since: 0.8.0 + */ +typedef enum +{ + NOTIFY_CLOSED_REASON_UNSET = -1, + NOTIFY_CLOSED_REASON_EXPIRED = 1, + NOTIFY_CLOSED_REASON_DISMISSED = 2, + NOTIFY_CLOSED_REASON_API_REQUEST = 3, + NOTIFY_CLOSED_REASON_UNDEFIEND = 4, +} NotifyClosedReason; + /** * NotifyActionCallback: - * @notification: - * @action: - * @user_data: + * @notification: a #NotifyActionCallback notification + * @action: (transfer none): The activated action name + * @user_data: (nullable) (transfer none): User provided data * * An action callback function. */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libnotify-0.7.12/libnotify/notify.c new/libnotify-0.8.1/libnotify/notify.c --- old/libnotify-0.7.12/libnotify/notify.c 2022-05-05 18:04:26.359672300 +0200 +++ new/libnotify-0.8.1/libnotify/notify.c 2022-07-17 15:00:39.000000000 +0200 @@ -43,10 +43,14 @@ static gboolean _initted = FALSE; static char *_app_name = NULL; +static char *_snap_name = NULL; +static char *_snap_app = NULL; +static char *_flatpak_app = NULL; static GDBusProxy *_proxy = NULL; static GList *_active_notifications = NULL; static int _spec_version_major = 0; static int _spec_version_minor = 0; +static int _portal_version = 0; gboolean _notify_check_spec_version (int major, @@ -74,6 +78,26 @@ return FALSE; } + if (_notify_uses_portal_notifications ()) { + if (ret_name) { + *ret_name = g_strdup ("Portal Notification"); + } + + if (ret_vendor) { + *ret_vendor = g_strdup ("Freedesktop"); + } + + if (ret_version) { + *ret_version = g_strdup_printf ("%u", _portal_version); + } + + if (ret_spec_version) { + *ret_spec_version = g_strdup ("1.2"); + } + + return TRUE; + } + result = g_dbus_proxy_call_sync (proxy, "GetServerInformation", g_variant_new ("()"), @@ -119,6 +143,18 @@ return TRUE; } +static gboolean +set_app_name (const char *app_name) +{ + g_return_val_if_fail (app_name != NULL, FALSE); + g_return_val_if_fail (*app_name != '\0', FALSE); + + g_free (_app_name); + _app_name = g_strdup (app_name); + + return TRUE; +} + /** * notify_set_app_name: @@ -130,46 +166,267 @@ void notify_set_app_name (const char *app_name) { - g_free (_app_name); - _app_name = g_strdup (app_name); + set_app_name (app_name); } /** * notify_init: - * @app_name: The name of the application initializing libnotify. + * @app_name: (nullable): The name of the application initializing libnotify. * * Initialized libnotify. This must be called before any other functions. * + * Starting from 0.8, if the provided @app_name is %NULL, libnotify will + * try to figure it out from the running application. + * Before it was not allowed, and was causing libnotify not to be initialized. + * * Returns: %TRUE if successful, or %FALSE on error. */ gboolean notify_init (const char *app_name) { - g_return_val_if_fail (app_name != NULL, FALSE); - g_return_val_if_fail (*app_name != '\0', FALSE); - if (_initted) return TRUE; -#ifdef GLIB_VERSION_2_32 - if (app_name == NULL && g_application_get_default ()) { - GApplication *application = g_application_get_default (); + if (app_name == NULL) { + GApplication *application; - app_name = g_application_get_application_id (application); - } -#endif + app_name = _notify_get_snap_app (); + if (app_name == NULL) { + app_name = _notify_get_flatpak_app (); + } - notify_set_app_name (app_name); + if (app_name == NULL && + (application = g_application_get_default ())) { + app_name = g_application_get_application_id (application); + } + } -#ifndef GLIB_VERSION_2_36 - g_type_init (); -#endif + if (!set_app_name (app_name)) { + return FALSE; + } _initted = TRUE; return TRUE; } +static void +_initialize_snap_names (void) +{ + gchar *cgroup_contents = NULL; + gchar *found_snap_name = NULL; + gchar **lines; + gint i; + + if (!g_file_get_contents ("/proc/self/cgroup", &cgroup_contents, + NULL, NULL)) { + g_free (cgroup_contents); + return; + } + + lines = g_strsplit (cgroup_contents, "\n", -1); + g_free (cgroup_contents); + + for (i = 0; lines[i]; ++i) { + gchar **parts = g_strsplit (lines[i], ":", 3); + gchar *basename; + gchar **ns; + guint ns_length; + + if (g_strv_length (parts) != 3) { + g_strfreev (parts); + continue; + } + + basename = g_path_get_basename (parts[2]); + g_strfreev (parts); + + if (!basename) { + continue; + } + + ns = g_strsplit (basename, ".", -1); + ns_length = g_strv_length (ns); + g_free (basename); + + if (ns_length < 2 || !g_str_equal (ns[0], "snap")) { + g_strfreev (ns); + continue; + } + + if (_snap_name == NULL) { + g_free (found_snap_name); + found_snap_name = g_strdup (ns[1]); + } + + if (ns_length < 3) { + g_strfreev (ns); + continue; + } + + if (_snap_name == NULL) { + _snap_name = found_snap_name; + found_snap_name = NULL; + g_debug ("SNAP name: %s", _snap_name); + } + + if (g_str_equal (ns[1], _snap_name)) { + _snap_app = g_strdup (ns[2]); + g_strfreev (ns); + break; + } + + g_strfreev (ns); + } + + if (_snap_name == NULL && found_snap_name != NULL) { + _snap_name = found_snap_name; + found_snap_name = NULL; + g_debug ("SNAP name: %s", _snap_name); + } + + if (_snap_app == NULL) { + _snap_app = g_strdup (_snap_name); + } + + g_debug ("SNAP app: %s", _snap_app); + + g_strfreev (lines); + g_free (found_snap_name); +} + +const char * +_notify_get_snap_path (void) +{ + static const char *snap_path = NULL; + static gsize snap_path_set = FALSE; + + if (g_once_init_enter (&snap_path_set)) { + snap_path = g_getenv ("SNAP"); + + if (!snap_path || *snap_path == '\0' || + !strchr (snap_path, G_DIR_SEPARATOR)) { + snap_path = NULL; + } else { + g_debug ("SNAP path: %s", snap_path); + } + + g_once_init_leave (&snap_path_set, TRUE); + } + + return snap_path; +} + +const char * +_notify_get_snap_name (void) +{ + static gsize snap_name_set = FALSE; + + if (g_once_init_enter (&snap_name_set)) { + if (!_snap_name) { + const char *snap_name_env = g_getenv ("SNAP_NAME"); + + if (!snap_name_env || *snap_name_env == '\0') + snap_name_env = NULL; + + _snap_name = g_strdup (snap_name_env); + g_debug ("SNAP name: %s", _snap_name); + } + + g_once_init_leave (&snap_name_set, TRUE); + } + + return _snap_name; +} + +const char * +_notify_get_snap_app (void) +{ + static gsize snap_app_set = FALSE; + + if (g_once_init_enter (&snap_app_set)) { + _initialize_snap_names (); + g_once_init_leave (&snap_app_set, TRUE); + } + + return _snap_app; +} + +const char * +_notify_get_flatpak_app (void) +{ + static gsize flatpak_app_set = FALSE; + + if (g_once_init_enter (&flatpak_app_set)) { + GKeyFile *info = g_key_file_new (); + + if (g_key_file_load_from_file (info, "/.flatpak-info", + G_KEY_FILE_NONE, NULL)) { + const char *group = "Application"; + + if (g_key_file_has_group (info, "Runtime")) { + group = "Runtime"; + } + + _flatpak_app = g_key_file_get_string (info, group, + "name", NULL); + } + + g_key_file_free (info); + g_once_init_leave (&flatpak_app_set, TRUE); + } + + return _flatpak_app; +} + +static gboolean +_notify_is_running_under_flatpak (void) +{ + return !!_notify_get_flatpak_app (); +} + +static gboolean +_notify_is_running_under_snap (void) +{ + return !!_notify_get_snap_app (); +} + +static gboolean +_notify_is_running_in_sandbox (void) +{ + static gsize use_portal = 0; + enum { + IGNORE_PORTAL = 1, + TRY_USE_PORTAL = 2, + FORCE_PORTAL = 3 + }; + + if (g_once_init_enter (&use_portal)) { + if (G_UNLIKELY (g_getenv ("NOTIFY_IGNORE_PORTAL"))) { + g_once_init_leave (&use_portal, IGNORE_PORTAL); + } else if (G_UNLIKELY (g_getenv ("NOTIFY_FORCE_PORTAL"))) { + g_once_init_leave (&use_portal, FORCE_PORTAL); + } else { + g_once_init_leave (&use_portal, TRY_USE_PORTAL); + } + } + + if (use_portal == IGNORE_PORTAL) { + return FALSE; + } + + return use_portal == FORCE_PORTAL || + _notify_is_running_under_flatpak () || + _notify_is_running_under_snap (); +} + +gboolean +_notify_uses_portal_notifications (void) +{ + return _portal_version != 0; +} + + /** * notify_get_app_name: * @@ -219,6 +476,15 @@ _proxy = NULL; } + g_free (_snap_name); + _snap_name = NULL; + + g_free (_snap_app); + _snap_app = NULL; + + g_free (_flatpak_app); + _flatpak_app = NULL; + _initted = FALSE; } @@ -235,6 +501,46 @@ return _initted; } +GDBusProxy * +_get_portal_proxy (GError **error) +{ + GError *local_error = NULL; + GDBusProxy *proxy; + GVariant *res; + + proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + NOTIFY_PORTAL_DBUS_NAME, + NOTIFY_PORTAL_DBUS_CORE_OBJECT, + NOTIFY_PORTAL_DBUS_CORE_INTERFACE, + NULL, + &local_error); + + if (proxy == NULL) { + g_debug ("Failed to get portal proxy: %s", local_error->message); + g_clear_error (&local_error); + + return NULL; + } + + res = g_dbus_proxy_get_cached_property (proxy, "version"); + if (!res) { + g_object_unref (proxy); + return NULL; + } + + _portal_version = g_variant_get_uint32 (res); + g_assert (_portal_version > 0); + + g_warning ("Running in confined mode, using Portal notifications. " + "Some features and hints won't be supported"); + + g_variant_unref (res); + + return proxy; +} + /* * _notify_get_proxy: * @error: (allow-none): a location to store a #GError, or %NULL @@ -250,6 +556,14 @@ if (_proxy != NULL) return _proxy; + if (_notify_is_running_in_sandbox ()) { + _proxy = _get_portal_proxy (error); + + if (_proxy != NULL) { + goto out; + } + } + _proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, @@ -258,6 +572,8 @@ NOTIFY_DBUS_CORE_INTERFACE, NULL, error); + +out: if (_proxy == NULL) { return NULL; } @@ -295,6 +611,15 @@ return NULL; } + if (_notify_uses_portal_notifications ()) { + list = g_list_prepend (list, g_strdup ("actions")); + list = g_list_prepend (list, g_strdup ("body")); + list = g_list_prepend (list, g_strdup ("body-images")); + list = g_list_prepend (list, g_strdup ("icon-static")); + + return list; + } + result = g_dbus_proxy_call_sync (proxy, "GetCapabilities", g_variant_new ("()"), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libnotify-0.7.12/meson.build new/libnotify-0.8.1/meson.build --- old/libnotify-0.7.12/meson.build 2022-05-05 18:04:26.360672200 +0200 +++ new/libnotify-0.8.1/meson.build 2022-07-17 15:00:39.000000000 +0200 @@ -1,6 +1,6 @@ project('libnotify', 'c', - version: '0.7.12', + version: '0.8.1', meson_version: '>= 0.47.0') gnome = import('gnome') @@ -22,8 +22,11 @@ LT_REVISION=0 LT_AGE=0 +API_VERSION = 7 + VERSION_ARRAY = meson.project_version().split('.') -MODULE_VERSION = '@0@.@1@'.format(VERSION_ARRAY[0], VERSION_ARRAY[1]) +# Minor version is hardcoded until we have a real API break +MODULE_VERSION = '@0@.@1@'.format(VERSION_ARRAY[0], API_VERSION) LIBNAME = meson.project_name().split('lib')[1] default_includes = include_directories('.') @@ -36,7 +39,7 @@ libnotify_deps = [] extra_deps = [] -glib_req_version = '>= 2.26.0' +glib_req_version = '>= 2.38.0' gdk_pixbuf_dep = dependency('gdk-pixbuf-2.0') glib_dep = dependency('glib-2.0', version: glib_req_version) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libnotify-0.7.12/tests/test-error.c new/libnotify-0.8.1/tests/test-error.c --- old/libnotify-0.7.12/tests/test-error.c 2022-05-05 18:04:26.363672300 +0200 +++ new/libnotify-0.8.1/tests/test-error.c 2022-07-17 15:00:39.000000000 +0200 @@ -29,10 +29,6 @@ { NotifyNotification *n; -#ifndef GLIB_VERSION_2_36 - g_type_init (); -#endif - notify_init ("Error Handling"); n = notify_notification_new ("Summary", "Content", NULL); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libnotify-0.7.12/tests/test-replace.c new/libnotify-0.8.1/tests/test-replace.c --- old/libnotify-0.7.12/tests/test-replace.c 2022-05-05 18:04:26.364672200 +0200 +++ new/libnotify-0.8.1/tests/test-replace.c 2022-07-17 15:00:39.000000000 +0200 @@ -28,10 +28,6 @@ GError *error; error = NULL; -#ifndef GLIB_VERSION_2_36 - g_type_init (); -#endif - notify_init ("Replace Test"); n = notify_notification_new ("Summary", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libnotify-0.7.12/tools/notify-send.c new/libnotify-0.8.1/tools/notify-send.c --- old/libnotify-0.7.12/tools/notify-send.c 2022-05-05 18:04:26.365672300 +0200 +++ new/libnotify-0.8.1/tools/notify-send.c 2022-07-17 15:00:39.000000000 +0200 @@ -26,6 +26,7 @@ #include <stdlib.h> #include <string.h> #include <glib.h> +#include <glib-unix.h> #include <glib/gprintf.h> #define N_(x) (x) @@ -148,6 +149,19 @@ g_main_loop_quit (loop); } +static gboolean +on_sigint (gpointer data) +{ + NotifyNotification *notification = data; + + g_printerr ("Wait cancelled, closing notification\n"); + + notify_notification_close (notification, NULL); + g_main_loop_quit (loop); + + return FALSE; +} + static void handle_action (NotifyNotification *notify, char *action, @@ -203,6 +217,10 @@ static char **n_text = NULL; static char **hints = NULL; static char **actions = NULL; + static char *server_name = NULL; + static char *server_vendor = NULL; + static char *server_version = NULL; + static char *server_spec_version = NULL; static gboolean print_id = FALSE; static gint notification_id = 0; static gboolean do_version = FALSE; @@ -265,10 +283,6 @@ setlocale (LC_ALL, ""); -#ifndef GLIB_VERSION_2_36 - g_type_init (); -#endif - g_set_prgname (argv[0]); g_log_set_always_fatal (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); @@ -310,6 +324,18 @@ if (!notify_init ("notify-send")) exit (1); + notify_get_server_info (&server_name, + &server_vendor, + &server_version, + &server_spec_version); + + g_debug ("Using sever %s %s, v%s - Supporting Notification Spec %s", + server_name, server_vendor, server_version, server_spec_version); + g_free (server_name); + g_free (server_vendor); + g_free (server_version); + g_free (server_spec_version); + notify = g_object_new (NOTIFY_TYPE_NOTIFICATION, "summary", summary, "body", body, @@ -442,6 +468,7 @@ } if (wait) { + g_unix_signal_add (SIGINT, on_sigint, notify); loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (loop); g_main_loop_unref (loop);