[libnotify] notification: Use Desktop Portal notification when running in sandbox



commit f3eb807464e06eb68b2901e553a21e31620aba53
Author: Marco Trevisan (TreviƱo) <mail 3v1n0 net>
Date:   Tue May 17 03:33:21 2022 +0200

    notification: Use Desktop Portal notification when running in sandbox
    
    When an application is running from a snap or a flatpak, libnotify
    should just work as a FDO Portal Notification wrapper.
    
    As per this, make the library to try use the portal API in sandboxed
    applications, by emulating the needed missing pieces.
    
    Not everything can be achieved 1:1, but it's just safer to use in such
    contexts.

 libnotify/internal.h     |   8 +
 libnotify/notification.c | 386 +++++++++++++++++++++++++++++++++++++++++++++++
 libnotify/notify.c       | 159 +++++++++++++++++++
 3 files changed, 553 insertions(+)
---
diff --git a/libnotify/internal.h b/libnotify/internal.h
index 35683d6..4d7ceb0 100644
--- a/libnotify/internal.h
+++ b/libnotify/internal.h
@@ -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);
@@ -40,6 +44,10 @@ 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 --git a/libnotify/notification.c b/libnotify/notification.c
index f20414d..de563d2 100644
--- a/libnotify/notification.c
+++ b/libnotify/notification.c
@@ -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
 {
@@ -72,6 +73,7 @@ struct _NotifyNotificationPrivate
 
         /* NULL to use icon data. Anything else to have server lookup icon */
         char           *icon_name;
+        GdkPixbuf      *icon_pixbuf;
 
         /*
          * -1   = use server default
@@ -79,6 +81,7 @@ struct _NotifyNotificationPrivate
          *  > 0 = Number of milliseconds before we timeout
          */
         gint            timeout;
+        guint           portal_timeout_id;
 
         GSList         *actions;
         GHashTable     *action_map;
@@ -150,6 +153,7 @@ notify_notification_class_init (NotifyNotificationClass *klass)
         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;
 
         /**
@@ -369,6 +373,22 @@ notify_notification_init (NotifyNotification *obj)
                                                        (GDestroyNotify) destroy_pair);
 }
 
+static void
+notify_notification_dispose (GObject *object)
+{
+        NotifyNotification        *obj = NOTIFY_NOTIFICATION (object);
+        NotifyNotificationPrivate *priv = obj->priv;
+
+        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
 notify_notification_finalize (GObject *object)
 {
@@ -405,6 +425,18 @@ notify_notification_finalize (GObject *object)
         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.
@@ -592,6 +624,33 @@ notify_notification_update (NotifyNotification *notification,
         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)
@@ -646,8 +705,12 @@ proxy_g_signal_cb (GDBusProxy *proxy,
                    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;
@@ -658,6 +721,7 @@ proxy_g_signal_cb (GDBusProxy *proxy,
 
                 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;
@@ -683,9 +747,306 @@ proxy_g_signal_cb (GDBusProxy *proxy,
 
                 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, &parameter);
+                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;
+}
+
 /**
  * notify_notification_show:
  * @notification: The notification.
@@ -731,6 +1092,10 @@ notify_notification_show (NotifyNotification *notification,
                                                                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);
@@ -854,6 +1219,10 @@ notify_notification_set_category (NotifyNotification *notification,
         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",
@@ -931,11 +1300,18 @@ notify_notification_set_image_from_pixbuf (NotifyNotification *notification,
                 hint_name = "icon_data";
         }
 
+        g_clear_object (&notification->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,
@@ -1059,6 +1435,10 @@ notify_notification_set_app_name (NotifyNotification *notification,
 {
         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);
 
@@ -1357,6 +1737,12 @@ notify_notification_close (NotifyNotification *notification,
                 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",
diff --git a/libnotify/notify.c b/libnotify/notify.c
index e6214c7..92b4010 100644
--- a/libnotify/notify.c
+++ b/libnotify/notify.c
@@ -45,10 +45,12 @@ 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,
@@ -76,6 +78,26 @@ _notify_get_server_info (char **ret_name,
                 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 ("()"),
@@ -327,6 +349,81 @@ _notify_get_snap_app (void)
         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:
  *
@@ -382,6 +479,9 @@ notify_uninit (void)
         g_free (_snap_app);
         _snap_app = NULL;
 
+        g_free (_flatpak_app);
+        _flatpak_app = NULL;
+
         _initted = FALSE;
 }
 
@@ -398,6 +498,46 @@ notify_is_initted (void)
         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
@@ -413,6 +553,14 @@ _notify_get_proxy (GError **error)
         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,
@@ -421,6 +569,8 @@ _notify_get_proxy (GError **error)
                                                 NOTIFY_DBUS_CORE_INTERFACE,
                                                 NULL,
                                                 error);
+
+out:
         if (_proxy == NULL) {
                 return NULL;
         }
@@ -458,6 +608,15 @@ notify_get_server_caps (void)
                 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 ("()"),


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]