Compare commits
32 Commits
Author | SHA1 | Date |
---|---|---|
|
80121a3b8c | |
|
650f2f123e | |
|
a8374698c4 | |
|
359443f70f | |
|
3d81394cd2 | |
|
5e6a52fa4f | |
|
532608248e | |
|
adb3e0c7bf | |
|
13718b6ea0 | |
|
f3eb807464 | |
|
c6534a144c | |
|
543d636153 | |
|
9e9cb423b9 | |
|
cc30d8b8dd | |
|
e8ea0c39d0 | |
|
74f87250a3 | |
|
07a2b2e32c | |
|
8ce5daac4f | |
|
9d28fa3600 | |
|
4e40a460ce | |
|
8162b1d397 | |
|
4d68baf5ef | |
|
596d09a78f | |
|
f5b9619b2f | |
|
afef1e98b3 | |
|
ecb1d4ec08 | |
|
6d8e38bc69 | |
|
6e6d52340b | |
|
1fba04bc03 | |
|
a674e610ee | |
|
c99aacdb91 | |
|
997115b459 |
38
NEWS
38
NEWS
|
@ -1,3 +1,41 @@
|
||||||
|
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]
|
||||||
|
* Delete unused notifynotification.xml [Patrick; !25]
|
||||||
|
* notification: Bookend calling NotifyActionCallback with temporary ref
|
||||||
|
[Logan; #25, !26]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Marco Trevisan, Patrick Griffis, Logan Rathbone
|
||||||
|
|
||||||
New in 0.7.11
|
New in 0.7.11
|
||||||
=============
|
=============
|
||||||
* Fix potential build errors with old glib version we require
|
* Fix potential build errors with old glib version we require
|
||||||
|
|
|
@ -7,6 +7,9 @@ daemon, as defined in the [org.freedesktop.Notifications][fdo-spec] Desktop
|
||||||
Specification. These notifications can be used to inform the user about an event
|
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.
|
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
|
## Notice
|
||||||
|
|
||||||
For GLib based applications the [GNotification][gnotif] API should be used
|
For GLib based applications the [GNotification][gnotif] API should be used
|
||||||
|
@ -14,3 +17,4 @@ instead.
|
||||||
|
|
||||||
[fdo-spec]: https://specifications.freedesktop.org/notification-spec/notification-spec-latest.html
|
[fdo-spec]: https://specifications.freedesktop.org/notification-spec/notification-spec-latest.html
|
||||||
[gnotif]: https://docs.gtk.org/gio/class.Notification.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
|
||||||
|
|
|
@ -122,11 +122,17 @@
|
||||||
<para>Wait for the notification to be closed before exiting. If the <option>expire-time</option> is set, it will be used as the maximum waiting time.</para>
|
<para>Wait for the notification to be closed before exiting. If the <option>expire-time</option> is set, it will be used as the maximum waiting time.</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>-e</option>, <option>--transient</option></term>
|
||||||
|
<listitem>
|
||||||
|
<para>Show a transient notification. Transient notifications by-pass the server's persistence capability, if any. And so it won't be preserved until the user acknowledges it.</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</refsection>
|
</refsection>
|
||||||
<refsection>
|
<refsection>
|
||||||
<info><title>See also</title></info>
|
<info><title>See also</title></info>
|
||||||
|
|
||||||
<para>The Desktop Notification Spec on <link xlink:href="http://www.galago-project.org/specs/notification/">http://www.galago-project.org/specs/notification/</link>.</para>
|
<para>The Desktop Notification Spec on <link xlink:href="https://specifications.freedesktop.org/notification-spec/">https://specifications.freedesktop.org/notification-spec/</link>.</para>
|
||||||
</refsection>
|
</refsection>
|
||||||
</refentry>
|
</refentry>
|
||||||
|
|
|
@ -4,6 +4,7 @@ NOTIFY_EXPIRES_DEFAULT
|
||||||
NOTIFY_EXPIRES_NEVER
|
NOTIFY_EXPIRES_NEVER
|
||||||
<TITLE>NotifyNotification</TITLE>
|
<TITLE>NotifyNotification</TITLE>
|
||||||
NotifyNotification
|
NotifyNotification
|
||||||
|
NotifyClosedReason
|
||||||
NotifyUrgency
|
NotifyUrgency
|
||||||
NotifyActionCallback
|
NotifyActionCallback
|
||||||
NOTIFY_ACTION_CALLBACK
|
NOTIFY_ACTION_CALLBACK
|
||||||
|
|
|
@ -8,18 +8,21 @@
|
||||||
<category rdf:resource="http://api.gnome.org/doap-extensions#core" />
|
<category rdf:resource="http://api.gnome.org/doap-extensions#core" />
|
||||||
<programming-language>C</programming-language>
|
<programming-language>C</programming-language>
|
||||||
<shortdesc xml:lang="en">libnotify is a library for sending desktop notifications</shortdesc>
|
<shortdesc xml:lang="en">libnotify is a library for sending desktop notifications</shortdesc>
|
||||||
|
<description>Library to send desktop notifications that follows the FDO
|
||||||
|
specifications:
|
||||||
|
https://gitlab.freedesktop.org/xdg/xdg-specs/-/blob/master/notification/notification-spec.xml</description>
|
||||||
<maintainer>
|
<maintainer>
|
||||||
<foaf:Person>
|
<foaf:Person>
|
||||||
<foaf:name>Christian Hammond</foaf:name>
|
<foaf:name>Marco Trevisan</foaf:name>
|
||||||
<foaf:mbox rdf:resource="mailto:chipx86@chipx86.com" />
|
<foaf:mbox rdf:resource="mailto:mail@3v1n0.net" />
|
||||||
<gnome:userid>chammond</gnome:userid>
|
<gnome:userid>marcotrevi</gnome:userid>
|
||||||
</foaf:Person>
|
</foaf:Person>
|
||||||
</maintainer>
|
</maintainer>
|
||||||
<maintainer>
|
<maintainer>
|
||||||
<foaf:Person>
|
<foaf:Person>
|
||||||
<foaf:name>William Jon McCann</foaf:name>
|
<foaf:name>Logan Rathbone</foaf:name>
|
||||||
<foaf:mbox rdf:resource="mailto:william.jon.mccann@gmail.com" />
|
<foaf:mbox rdf:resource="mailto:poprocks@gmail.com" />
|
||||||
<gnome:userid>mccann</gnome:userid>
|
<gnome:userid>larathbone</gnome:userid>
|
||||||
</foaf:Person>
|
</foaf:Person>
|
||||||
</maintainer>
|
</maintainer>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -26,6 +26,10 @@
|
||||||
#define NOTIFY_DBUS_CORE_INTERFACE "org.freedesktop.Notifications"
|
#define NOTIFY_DBUS_CORE_INTERFACE "org.freedesktop.Notifications"
|
||||||
#define NOTIFY_DBUS_CORE_OBJECT "/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
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
GDBusProxy * _notify_get_proxy (GError **error);
|
GDBusProxy * _notify_get_proxy (GError **error);
|
||||||
|
@ -36,6 +40,14 @@ gint _notify_notification_get_timeout (const NotifyNotific
|
||||||
gboolean _notify_notification_has_nondefault_actions (const NotifyNotification *n);
|
gboolean _notify_notification_has_nondefault_actions (const NotifyNotification *n);
|
||||||
gboolean _notify_check_spec_version (int major, int minor);
|
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
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* _LIBNOTIFY_INTERNAL_H_ */
|
#endif /* _LIBNOTIFY_INTERNAL_H_ */
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
static void notify_notification_class_init (NotifyNotificationClass *klass);
|
static void notify_notification_class_init (NotifyNotificationClass *klass);
|
||||||
static void notify_notification_init (NotifyNotification *sp);
|
static void notify_notification_init (NotifyNotification *sp);
|
||||||
static void notify_notification_finalize (GObject *object);
|
static void notify_notification_finalize (GObject *object);
|
||||||
|
static void notify_notification_dispose (GObject *object);
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
@ -70,12 +71,9 @@ struct _NotifyNotificationPrivate
|
||||||
char *body;
|
char *body;
|
||||||
char *activation_token;
|
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 */
|
/* NULL to use icon data. Anything else to have server lookup icon */
|
||||||
char *icon_name;
|
char *icon_name;
|
||||||
|
GdkPixbuf *icon_pixbuf;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* -1 = use server default
|
* -1 = use server default
|
||||||
|
@ -83,6 +81,7 @@ struct _NotifyNotificationPrivate
|
||||||
* > 0 = Number of milliseconds before we timeout
|
* > 0 = Number of milliseconds before we timeout
|
||||||
*/
|
*/
|
||||||
gint timeout;
|
gint timeout;
|
||||||
|
guint portal_timeout_id;
|
||||||
|
|
||||||
GSList *actions;
|
GSList *actions;
|
||||||
GHashTable *action_map;
|
GHashTable *action_map;
|
||||||
|
@ -154,6 +153,7 @@ notify_notification_class_init (NotifyNotificationClass *klass)
|
||||||
object_class->constructor = notify_notification_constructor;
|
object_class->constructor = notify_notification_constructor;
|
||||||
object_class->get_property = notify_notification_get_property;
|
object_class->get_property = notify_notification_get_property;
|
||||||
object_class->set_property = notify_notification_set_property;
|
object_class->set_property = notify_notification_set_property;
|
||||||
|
object_class->dispose = notify_notification_dispose;
|
||||||
object_class->finalize = notify_notification_finalize;
|
object_class->finalize = notify_notification_finalize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -238,9 +238,9 @@ notify_notification_class_init (NotifyNotificationClass *klass)
|
||||||
g_param_spec_int ("closed-reason",
|
g_param_spec_int ("closed-reason",
|
||||||
"Closed Reason",
|
"Closed Reason",
|
||||||
"The reason code for why the notification was closed",
|
"The reason code for why the notification was closed",
|
||||||
-1,
|
NOTIFY_CLOSED_REASON_UNSET,
|
||||||
G_MAXINT32,
|
G_MAXINT32,
|
||||||
-1,
|
NOTIFY_CLOSED_REASON_UNSET,
|
||||||
G_PARAM_READABLE
|
G_PARAM_READABLE
|
||||||
| G_PARAM_STATIC_NAME
|
| G_PARAM_STATIC_NAME
|
||||||
| G_PARAM_STATIC_NICK
|
| G_PARAM_STATIC_NICK
|
||||||
|
@ -356,110 +356,12 @@ destroy_pair (CallbackPair *pair)
|
||||||
g_free (pair);
|
g_free (pair);
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
static void
|
||||||
notify_notification_init (NotifyNotification *obj)
|
notify_notification_init (NotifyNotification *obj)
|
||||||
{
|
{
|
||||||
obj->priv = g_new0 (NotifyNotificationPrivate, 1);
|
obj->priv = g_new0 (NotifyNotificationPrivate, 1);
|
||||||
obj->priv->timeout = NOTIFY_EXPIRES_DEFAULT;
|
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,
|
obj->priv->hints = g_hash_table_new_full (g_str_hash,
|
||||||
g_str_equal,
|
g_str_equal,
|
||||||
g_free,
|
g_free,
|
||||||
|
@ -469,8 +371,22 @@ notify_notification_init (NotifyNotification *obj)
|
||||||
g_str_equal,
|
g_str_equal,
|
||||||
g_free,
|
g_free,
|
||||||
(GDestroyNotify) destroy_pair);
|
(GDestroyNotify) destroy_pair);
|
||||||
|
}
|
||||||
|
|
||||||
maybe_initialize_snap (obj);
|
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
|
static void
|
||||||
|
@ -487,7 +403,6 @@ notify_notification_finalize (GObject *object)
|
||||||
g_free (priv->body);
|
g_free (priv->body);
|
||||||
g_free (priv->icon_name);
|
g_free (priv->icon_name);
|
||||||
g_free (priv->activation_token);
|
g_free (priv->activation_token);
|
||||||
g_free (priv->snap_app);
|
|
||||||
|
|
||||||
if (priv->actions != NULL) {
|
if (priv->actions != NULL) {
|
||||||
g_slist_foreach (priv->actions, (GFunc) g_free, NULL);
|
g_slist_foreach (priv->actions, (GFunc) g_free, NULL);
|
||||||
|
@ -510,6 +425,18 @@ notify_notification_finalize (GObject *object)
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (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:
|
* notify_notification_new:
|
||||||
* @summary: The required summary text.
|
* @summary: The required summary text.
|
||||||
|
@ -596,7 +523,6 @@ static gchar *
|
||||||
try_prepend_snap_desktop (NotifyNotification *notification,
|
try_prepend_snap_desktop (NotifyNotification *notification,
|
||||||
const gchar *desktop)
|
const gchar *desktop)
|
||||||
{
|
{
|
||||||
NotifyNotificationPrivate *priv = notification->priv;
|
|
||||||
gchar *ret = NULL;
|
gchar *ret = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -604,11 +530,11 @@ try_prepend_snap_desktop (NotifyNotification *notification,
|
||||||
* ${SNAP_NAME}_; snap .desktop files are in the format
|
* ${SNAP_NAME}_; snap .desktop files are in the format
|
||||||
* ${SNAP_NAME}_desktop_file_name
|
* ${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) {
|
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;
|
return ret;
|
||||||
|
@ -619,7 +545,7 @@ try_prepend_snap (NotifyNotification *notification,
|
||||||
const gchar *value)
|
const gchar *value)
|
||||||
{
|
{
|
||||||
/* hardcoded paths to icons might be relocated under $SNAP */
|
/* 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 @@ notify_notification_update (NotifyNotification *notification,
|
||||||
return TRUE;
|
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
|
static void
|
||||||
proxy_g_signal_cb (GDBusProxy *proxy,
|
proxy_g_signal_cb (GDBusProxy *proxy,
|
||||||
const char *sender_name,
|
const char *sender_name,
|
||||||
|
@ -705,8 +705,12 @@ proxy_g_signal_cb (GDBusProxy *proxy,
|
||||||
GVariant *parameters,
|
GVariant *parameters,
|
||||||
NotifyNotification *notification)
|
NotifyNotification *notification)
|
||||||
{
|
{
|
||||||
|
const char *interface;
|
||||||
|
|
||||||
g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification));
|
g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification));
|
||||||
|
|
||||||
|
interface = g_dbus_proxy_get_interface_name (proxy);
|
||||||
|
|
||||||
if (g_strcmp0 (signal_name, "NotificationClosed") == 0 &&
|
if (g_strcmp0 (signal_name, "NotificationClosed") == 0 &&
|
||||||
g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(uu)"))) {
|
g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(uu)"))) {
|
||||||
guint32 id, reason;
|
guint32 id, reason;
|
||||||
|
@ -715,36 +719,21 @@ proxy_g_signal_cb (GDBusProxy *proxy,
|
||||||
if (id != notification->priv->id)
|
if (id != notification->priv->id)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
g_object_ref (G_OBJECT (notification));
|
close_notification (notification, reason);
|
||||||
notification->priv->closed_reason = reason;
|
|
||||||
g_signal_emit (notification, signals[SIGNAL_CLOSED], 0);
|
|
||||||
notification->priv->id = 0;
|
|
||||||
g_object_unref (G_OBJECT (notification));
|
|
||||||
} else if (g_strcmp0 (signal_name, "ActionInvoked") == 0 &&
|
} 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)"))) {
|
g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(us)"))) {
|
||||||
guint32 id;
|
guint32 id;
|
||||||
const char *action;
|
const char *action;
|
||||||
CallbackPair *pair;
|
|
||||||
|
|
||||||
g_variant_get (parameters, "(u&s)", &id, &action);
|
g_variant_get (parameters, "(u&s)", &id, &action);
|
||||||
|
|
||||||
if (id != notification->priv->id)
|
if (id != notification->priv->id)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pair = (CallbackPair *) g_hash_table_lookup (notification->priv->action_map,
|
if (!activate_action (notification, action) &&
|
||||||
action);
|
g_ascii_strcasecmp (action, "default")) {
|
||||||
|
g_warning ("Received unknown action %s", action);
|
||||||
if (pair == NULL) {
|
|
||||||
if (g_ascii_strcasecmp (action, "default")) {
|
|
||||||
g_warning ("Received unknown action %s", action);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
} else if (g_strcmp0 (signal_name, "ActivationToken") == 0 &&
|
} else if (g_strcmp0 (signal_name, "ActivationToken") == 0 &&
|
||||||
g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(us)"))) {
|
g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(us)"))) {
|
||||||
|
@ -758,9 +747,306 @@ proxy_g_signal_cb (GDBusProxy *proxy,
|
||||||
|
|
||||||
g_free (notification->priv->activation_token);
|
g_free (notification->priv->activation_token);
|
||||||
notification->priv->activation_token = g_strdup (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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* notify_notification_show:
|
* notify_notification_show:
|
||||||
* @notification: The notification.
|
* @notification: The notification.
|
||||||
|
@ -782,9 +1068,7 @@ notify_notification_show (NotifyNotification *notification,
|
||||||
GHashTableIter iter;
|
GHashTableIter iter;
|
||||||
gpointer key, data;
|
gpointer key, data;
|
||||||
GVariant *result;
|
GVariant *result;
|
||||||
#ifdef GLIB_VERSION_2_32
|
|
||||||
GApplication *application = NULL;
|
GApplication *application = NULL;
|
||||||
#endif
|
|
||||||
|
|
||||||
g_return_val_if_fail (notification != NULL, FALSE);
|
g_return_val_if_fail (notification != NULL, FALSE);
|
||||||
g_return_val_if_fail (NOTIFY_IS_NOTIFICATION (notification), FALSE);
|
g_return_val_if_fail (NOTIFY_IS_NOTIFICATION (notification), FALSE);
|
||||||
|
@ -808,6 +1092,10 @@ notify_notification_show (NotifyNotification *notification,
|
||||||
notification);
|
notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_notify_uses_portal_notifications ()) {
|
||||||
|
return add_portal_notification (proxy, notification, error);
|
||||||
|
}
|
||||||
|
|
||||||
g_variant_builder_init (&actions_builder, G_VARIANT_TYPE ("as"));
|
g_variant_builder_init (&actions_builder, G_VARIANT_TYPE ("as"));
|
||||||
for (l = priv->actions; l != NULL; l = l->next) {
|
for (l = priv->actions; l != NULL; l = l->next) {
|
||||||
g_variant_builder_add (&actions_builder, "s", l->data);
|
g_variant_builder_add (&actions_builder, "s", l->data);
|
||||||
|
@ -819,13 +1107,18 @@ notify_notification_show (NotifyNotification *notification,
|
||||||
g_variant_builder_add (&hints_builder, "{sv}", key, data);
|
g_variant_builder_add (&hints_builder, "{sv}", key, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priv->snap_app &&
|
if (g_hash_table_lookup (priv->hints, "sender-pid") == NULL) {
|
||||||
|
g_variant_builder_add (&hints_builder, "{sv}", "sender-pid",
|
||||||
|
g_variant_new_int64 (getpid ()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_notify_get_snap_app () &&
|
||||||
g_hash_table_lookup (priv->hints, "desktop-entry") == NULL) {
|
g_hash_table_lookup (priv->hints, "desktop-entry") == NULL) {
|
||||||
gchar *snap_desktop;
|
gchar *snap_desktop;
|
||||||
|
|
||||||
snap_desktop = g_strdup_printf ("%s_%s",
|
snap_desktop = g_strdup_printf ("%s_%s",
|
||||||
priv->snap_name,
|
_notify_get_snap_name (),
|
||||||
priv->snap_app);
|
_notify_get_snap_app ());
|
||||||
|
|
||||||
g_debug ("Using desktop entry: %s", snap_desktop);
|
g_debug ("Using desktop entry: %s", snap_desktop);
|
||||||
g_variant_builder_add (&hints_builder, "{sv}",
|
g_variant_builder_add (&hints_builder, "{sv}",
|
||||||
|
@ -833,8 +1126,7 @@ notify_notification_show (NotifyNotification *notification,
|
||||||
g_variant_new_take_string (snap_desktop));
|
g_variant_new_take_string (snap_desktop));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef GLIB_VERSION_2_32
|
if (!_notify_get_snap_app ()) {
|
||||||
if (!priv->snap_app) {
|
|
||||||
application = g_application_get_default ();
|
application = g_application_get_default ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -849,7 +1141,6 @@ notify_notification_show (NotifyNotification *notification,
|
||||||
g_variant_new_string (application_id));
|
g_variant_new_string (application_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/* TODO: make this nonblocking */
|
/* TODO: make this nonblocking */
|
||||||
result = g_dbus_proxy_call_sync (proxy,
|
result = g_dbus_proxy_call_sync (proxy,
|
||||||
|
@ -928,6 +1219,10 @@ notify_notification_set_category (NotifyNotification *notification,
|
||||||
g_return_if_fail (notification != NULL);
|
g_return_if_fail (notification != NULL);
|
||||||
g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification));
|
g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification));
|
||||||
|
|
||||||
|
if (maybe_warn_portal_unsupported_feature ("Category")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (category != NULL && category[0] != '\0') {
|
if (category != NULL && category[0] != '\0') {
|
||||||
notify_notification_set_hint_string (notification,
|
notify_notification_set_hint_string (notification,
|
||||||
"category",
|
"category",
|
||||||
|
@ -1005,11 +1300,18 @@ notify_notification_set_image_from_pixbuf (NotifyNotification *notification,
|
||||||
hint_name = "icon_data";
|
hint_name = "icon_data";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_clear_object (¬ification->priv->icon_pixbuf);
|
||||||
|
|
||||||
if (pixbuf == NULL) {
|
if (pixbuf == NULL) {
|
||||||
notify_notification_set_hint (notification, hint_name, NULL);
|
notify_notification_set_hint (notification, hint_name, NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_notify_uses_portal_notifications ()) {
|
||||||
|
notification->priv->icon_pixbuf = g_object_ref (pixbuf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
g_object_get (pixbuf,
|
g_object_get (pixbuf,
|
||||||
"width", &width,
|
"width", &width,
|
||||||
"height", &height,
|
"height", &height,
|
||||||
|
@ -1066,7 +1368,7 @@ maybe_parse_snap_hint_value (NotifyNotification *notification,
|
||||||
{
|
{
|
||||||
StringParserFunc parse_func = NULL;
|
StringParserFunc parse_func = NULL;
|
||||||
|
|
||||||
if (!notification->priv->snap_path)
|
if (!_notify_get_snap_path ())
|
||||||
return value;
|
return value;
|
||||||
|
|
||||||
if (g_strcmp0 (key, "desktop-entry") == 0) {
|
if (g_strcmp0 (key, "desktop-entry") == 0) {
|
||||||
|
@ -1133,6 +1435,10 @@ notify_notification_set_app_name (NotifyNotification *notification,
|
||||||
{
|
{
|
||||||
g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification));
|
g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification));
|
||||||
|
|
||||||
|
if (maybe_warn_portal_unsupported_feature ("App Name")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
g_free (notification->priv->app_name);
|
g_free (notification->priv->app_name);
|
||||||
notification->priv->app_name = g_strdup (app_name);
|
notification->priv->app_name = g_strdup (app_name);
|
||||||
|
|
||||||
|
@ -1373,6 +1679,7 @@ notify_notification_add_action (NotifyNotification *notification,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* notify_notification_get_activation_token:
|
* notify_notification_get_activation_token:
|
||||||
|
* @notification: The notification.
|
||||||
*
|
*
|
||||||
* If an an action is currently being activated, return the activation token.
|
* If an an action is currently being activated, return the activation token.
|
||||||
* This function is intended to be used in a #NotifyActionCallback to get
|
* This function is intended to be used in a #NotifyActionCallback to get
|
||||||
|
@ -1387,11 +1694,9 @@ const char *
|
||||||
notify_notification_get_activation_token (NotifyNotification *notification)
|
notify_notification_get_activation_token (NotifyNotification *notification)
|
||||||
{
|
{
|
||||||
g_return_val_if_fail (NOTIFY_IS_NOTIFICATION (notification), NULL);
|
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 notification->priv->activation_token;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
|
@ -1430,6 +1735,12 @@ notify_notification_close (NotifyNotification *notification,
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_notify_uses_portal_notifications ()) {
|
||||||
|
return remove_portal_notification (proxy, notification,
|
||||||
|
NOTIFY_CLOSED_REASON_API_REQUEST,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
/* FIXME: make this nonblocking! */
|
/* FIXME: make this nonblocking! */
|
||||||
result = g_dbus_proxy_call_sync (proxy,
|
result = g_dbus_proxy_call_sync (proxy,
|
||||||
"CloseNotification",
|
"CloseNotification",
|
||||||
|
@ -1454,13 +1765,17 @@ notify_notification_close (NotifyNotification *notification,
|
||||||
* Returns the closed reason code for the notification. This is valid only
|
* Returns the closed reason code for the notification. This is valid only
|
||||||
* after the "closed" signal is emitted.
|
* 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
|
gint
|
||||||
notify_notification_get_closed_reason (const NotifyNotification *notification)
|
notify_notification_get_closed_reason (const NotifyNotification *notification)
|
||||||
{
|
{
|
||||||
g_return_val_if_fail (notification != NULL, -1);
|
g_return_val_if_fail (notification != NULL, NOTIFY_CLOSED_REASON_UNSET);
|
||||||
g_return_val_if_fail (NOTIFY_IS_NOTIFICATION (notification), -1);
|
g_return_val_if_fail (NOTIFY_IS_NOTIFICATION (notification),
|
||||||
|
NOTIFY_CLOSED_REASON_UNSET);
|
||||||
|
|
||||||
return notification->priv->closed_reason;
|
return notification->priv->closed_reason;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,11 @@ typedef struct _NotifyNotification NotifyNotification;
|
||||||
typedef struct _NotifyNotificationClass NotifyNotificationClass;
|
typedef struct _NotifyNotificationClass NotifyNotificationClass;
|
||||||
typedef struct _NotifyNotificationPrivate NotifyNotificationPrivate;
|
typedef struct _NotifyNotificationPrivate NotifyNotificationPrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NotifyNotification:
|
||||||
|
*
|
||||||
|
* A passive pop-up notification.
|
||||||
|
*/
|
||||||
struct _NotifyNotification
|
struct _NotifyNotification
|
||||||
{
|
{
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
|
@ -88,11 +93,34 @@ typedef enum
|
||||||
|
|
||||||
} NotifyUrgency;
|
} 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:
|
* NotifyActionCallback:
|
||||||
* @notification:
|
* @notification: a #NotifyActionCallback notification
|
||||||
* @action:
|
* @action: (transfer none): The activated action name
|
||||||
* @user_data:
|
* @user_data: (nullable) (transfer none): User provided data
|
||||||
*
|
*
|
||||||
* An action callback function.
|
* An action callback function.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -43,10 +43,14 @@
|
||||||
|
|
||||||
static gboolean _initted = FALSE;
|
static gboolean _initted = FALSE;
|
||||||
static char *_app_name = NULL;
|
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 GDBusProxy *_proxy = NULL;
|
||||||
static GList *_active_notifications = NULL;
|
static GList *_active_notifications = NULL;
|
||||||
static int _spec_version_major = 0;
|
static int _spec_version_major = 0;
|
||||||
static int _spec_version_minor = 0;
|
static int _spec_version_minor = 0;
|
||||||
|
static int _portal_version = 0;
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
_notify_check_spec_version (int major,
|
_notify_check_spec_version (int major,
|
||||||
|
@ -74,6 +78,26 @@ _notify_get_server_info (char **ret_name,
|
||||||
return FALSE;
|
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,
|
result = g_dbus_proxy_call_sync (proxy,
|
||||||
"GetServerInformation",
|
"GetServerInformation",
|
||||||
g_variant_new ("()"),
|
g_variant_new ("()"),
|
||||||
|
@ -119,6 +143,18 @@ _notify_update_spec_version (GError **error)
|
||||||
return TRUE;
|
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:
|
* notify_set_app_name:
|
||||||
|
@ -130,46 +166,267 @@ _notify_update_spec_version (GError **error)
|
||||||
void
|
void
|
||||||
notify_set_app_name (const char *app_name)
|
notify_set_app_name (const char *app_name)
|
||||||
{
|
{
|
||||||
g_free (_app_name);
|
set_app_name (app_name);
|
||||||
_app_name = g_strdup (app_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* notify_init:
|
* 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.
|
* 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.
|
* Returns: %TRUE if successful, or %FALSE on error.
|
||||||
*/
|
*/
|
||||||
gboolean
|
gboolean
|
||||||
notify_init (const char *app_name)
|
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)
|
if (_initted)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
#ifdef GLIB_VERSION_2_32
|
if (app_name == NULL) {
|
||||||
if (app_name == NULL && g_application_get_default ()) {
|
GApplication *application;
|
||||||
GApplication *application = g_application_get_default ();
|
|
||||||
|
|
||||||
app_name = g_application_get_application_id (application);
|
app_name = _notify_get_snap_app ();
|
||||||
|
if (app_name == NULL) {
|
||||||
|
app_name = _notify_get_flatpak_app ();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app_name == NULL &&
|
||||||
|
(application = g_application_get_default ())) {
|
||||||
|
app_name = g_application_get_application_id (application);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
notify_set_app_name (app_name);
|
if (!set_app_name (app_name)) {
|
||||||
|
return FALSE;
|
||||||
#ifndef GLIB_VERSION_2_36
|
}
|
||||||
g_type_init ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_initted = TRUE;
|
_initted = TRUE;
|
||||||
|
|
||||||
return 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:
|
* notify_get_app_name:
|
||||||
*
|
*
|
||||||
|
@ -219,6 +476,15 @@ notify_uninit (void)
|
||||||
_proxy = NULL;
|
_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;
|
_initted = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,6 +501,46 @@ notify_is_initted (void)
|
||||||
return _initted;
|
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:
|
* _notify_get_proxy:
|
||||||
* @error: (allow-none): a location to store a #GError, or %NULL
|
* @error: (allow-none): a location to store a #GError, or %NULL
|
||||||
|
@ -250,6 +556,14 @@ _notify_get_proxy (GError **error)
|
||||||
if (_proxy != NULL)
|
if (_proxy != NULL)
|
||||||
return _proxy;
|
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,
|
_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
|
||||||
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
|
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
|
||||||
NULL,
|
NULL,
|
||||||
|
@ -258,6 +572,8 @@ _notify_get_proxy (GError **error)
|
||||||
NOTIFY_DBUS_CORE_INTERFACE,
|
NOTIFY_DBUS_CORE_INTERFACE,
|
||||||
NULL,
|
NULL,
|
||||||
error);
|
error);
|
||||||
|
|
||||||
|
out:
|
||||||
if (_proxy == NULL) {
|
if (_proxy == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -295,6 +611,15 @@ notify_get_server_caps (void)
|
||||||
return NULL;
|
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,
|
result = g_dbus_proxy_call_sync (proxy,
|
||||||
"GetCapabilities",
|
"GetCapabilities",
|
||||||
g_variant_new ("()"),
|
g_variant_new ("()"),
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
|
|
||||||
<node name="/org/freedesktop/Notification">
|
|
||||||
|
|
||||||
<interface name="org.freedesktop.NotificationListener">
|
|
||||||
|
|
||||||
</interface>
|
|
||||||
</node>
|
|
|
@ -1,6 +1,6 @@
|
||||||
project('libnotify',
|
project('libnotify',
|
||||||
'c',
|
'c',
|
||||||
version: '0.7.11',
|
version: '0.8.1',
|
||||||
meson_version: '>= 0.47.0')
|
meson_version: '>= 0.47.0')
|
||||||
|
|
||||||
gnome = import('gnome')
|
gnome = import('gnome')
|
||||||
|
@ -22,8 +22,11 @@ LT_CURRENT=4
|
||||||
LT_REVISION=0
|
LT_REVISION=0
|
||||||
LT_AGE=0
|
LT_AGE=0
|
||||||
|
|
||||||
|
API_VERSION = 7
|
||||||
|
|
||||||
VERSION_ARRAY = meson.project_version().split('.')
|
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]
|
LIBNAME = meson.project_name().split('lib')[1]
|
||||||
|
|
||||||
default_includes = include_directories('.')
|
default_includes = include_directories('.')
|
||||||
|
@ -36,7 +39,7 @@ man1dir = join_paths(prefix, get_option('mandir'), 'man1')
|
||||||
libnotify_deps = []
|
libnotify_deps = []
|
||||||
extra_deps = []
|
extra_deps = []
|
||||||
|
|
||||||
glib_req_version = '>= 2.26.0'
|
glib_req_version = '>= 2.38.0'
|
||||||
|
|
||||||
gdk_pixbuf_dep = dependency('gdk-pixbuf-2.0')
|
gdk_pixbuf_dep = dependency('gdk-pixbuf-2.0')
|
||||||
glib_dep = dependency('glib-2.0', version: glib_req_version)
|
glib_dep = dependency('glib-2.0', version: glib_req_version)
|
||||||
|
|
|
@ -29,10 +29,6 @@ main ()
|
||||||
{
|
{
|
||||||
NotifyNotification *n;
|
NotifyNotification *n;
|
||||||
|
|
||||||
#ifndef GLIB_VERSION_2_36
|
|
||||||
g_type_init ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
notify_init ("Error Handling");
|
notify_init ("Error Handling");
|
||||||
|
|
||||||
n = notify_notification_new ("Summary", "Content", NULL);
|
n = notify_notification_new ("Summary", "Content", NULL);
|
||||||
|
|
|
@ -28,10 +28,6 @@ main ()
|
||||||
GError *error;
|
GError *error;
|
||||||
error = NULL;
|
error = NULL;
|
||||||
|
|
||||||
#ifndef GLIB_VERSION_2_36
|
|
||||||
g_type_init ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
notify_init ("Replace Test");
|
notify_init ("Replace Test");
|
||||||
|
|
||||||
n = notify_notification_new ("Summary",
|
n = notify_notification_new ("Summary",
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
#include <glib-unix.h>
|
||||||
#include <glib/gprintf.h>
|
#include <glib/gprintf.h>
|
||||||
|
|
||||||
#define N_(x) (x)
|
#define N_(x) (x)
|
||||||
|
@ -148,6 +149,19 @@ handle_closed (NotifyNotification *notify,
|
||||||
g_main_loop_quit (loop);
|
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
|
static void
|
||||||
handle_action (NotifyNotification *notify,
|
handle_action (NotifyNotification *notify,
|
||||||
char *action,
|
char *action,
|
||||||
|
@ -176,6 +190,22 @@ on_wait_timeout (gpointer data)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
server_has_capability (const gchar *capability)
|
||||||
|
{
|
||||||
|
GList *server_caps = notify_get_server_caps ();
|
||||||
|
gboolean supported;
|
||||||
|
|
||||||
|
supported = !!g_list_find_custom (server_caps,
|
||||||
|
capability,
|
||||||
|
(GCompareFunc) g_ascii_strcasecmp);
|
||||||
|
|
||||||
|
g_list_foreach (server_caps, (GFunc) g_free, NULL);
|
||||||
|
g_list_free (server_caps);
|
||||||
|
|
||||||
|
return supported;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main (int argc, char *argv[])
|
main (int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
@ -187,6 +217,10 @@ main (int argc, char *argv[])
|
||||||
static char **n_text = NULL;
|
static char **n_text = NULL;
|
||||||
static char **hints = NULL;
|
static char **hints = NULL;
|
||||||
static char **actions = 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 gboolean print_id = FALSE;
|
||||||
static gint notification_id = 0;
|
static gint notification_id = 0;
|
||||||
static gboolean do_version = FALSE;
|
static gboolean do_version = FALSE;
|
||||||
|
@ -249,10 +283,6 @@ main (int argc, char *argv[])
|
||||||
|
|
||||||
setlocale (LC_ALL, "");
|
setlocale (LC_ALL, "");
|
||||||
|
|
||||||
#ifndef GLIB_VERSION_2_36
|
|
||||||
g_type_init ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
g_set_prgname (argv[0]);
|
g_set_prgname (argv[0]);
|
||||||
g_log_set_always_fatal (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
|
g_log_set_always_fatal (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
|
||||||
|
|
||||||
|
@ -294,6 +324,18 @@ main (int argc, char *argv[])
|
||||||
if (!notify_init ("notify-send"))
|
if (!notify_init ("notify-send"))
|
||||||
exit (1);
|
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,
|
notify = g_object_new (NOTIFY_TYPE_NOTIFICATION,
|
||||||
"summary", summary,
|
"summary", summary,
|
||||||
"body", body,
|
"body", body,
|
||||||
|
@ -309,6 +351,12 @@ main (int argc, char *argv[])
|
||||||
if (transient) {
|
if (transient) {
|
||||||
notify_notification_set_hint (notify, "transient",
|
notify_notification_set_hint (notify, "transient",
|
||||||
g_variant_new_boolean (TRUE));
|
g_variant_new_boolean (TRUE));
|
||||||
|
|
||||||
|
if (!server_has_capability ("persistence")) {
|
||||||
|
g_debug ("Persistence is not supported by the "
|
||||||
|
"notifications server. "
|
||||||
|
"All notifications are transient.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free (body);
|
g_free (body);
|
||||||
|
@ -350,19 +398,12 @@ main (int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actions != NULL) {
|
if (actions != NULL) {
|
||||||
GList *server_caps = notify_get_server_caps ();
|
|
||||||
gint i = 0;
|
gint i = 0;
|
||||||
char *action = NULL;
|
char *action = NULL;
|
||||||
gchar **spl = NULL;
|
gchar **spl = NULL;
|
||||||
gboolean have_actions;
|
gboolean have_actions;
|
||||||
|
|
||||||
have_actions =
|
have_actions = server_has_capability ("actions");
|
||||||
!!g_list_find_custom (server_caps,
|
|
||||||
"actions",
|
|
||||||
(GCompareFunc) g_ascii_strcasecmp);
|
|
||||||
g_list_foreach (server_caps, (GFunc) g_free, NULL);
|
|
||||||
g_list_free (server_caps);
|
|
||||||
|
|
||||||
if (!have_actions) {
|
if (!have_actions) {
|
||||||
g_printerr (N_("Actions are not supported by this "
|
g_printerr (N_("Actions are not supported by this "
|
||||||
"notifications server. "
|
"notifications server. "
|
||||||
|
@ -427,6 +468,7 @@ main (int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wait) {
|
if (wait) {
|
||||||
|
g_unix_signal_add (SIGINT, on_sigint, notify);
|
||||||
loop = g_main_loop_new (NULL, FALSE);
|
loop = g_main_loop_new (NULL, FALSE);
|
||||||
g_main_loop_run (loop);
|
g_main_loop_run (loop);
|
||||||
g_main_loop_unref (loop);
|
g_main_loop_unref (loop);
|
||||||
|
|
Loading…
Reference in New Issue