Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

notification v2 specs: Allow passing GFileIcon, sound and add additional properties #147

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libportal/email.c
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ compose_email (EmailCall *call)
for (i = 0; call->attachments[i]; i++)
{
g_autoptr(GError) error = NULL;
int fd;
portal_autofd int fd = -1;
int fd_in;

fd = g_open (call->attachments[i], O_PATH | O_CLOEXEC);
Expand Down
174 changes: 158 additions & 16 deletions libportal/notification.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,84 @@

#include "config.h"

#define _GNU_SOURCE
#include <sys/mman.h>
#include <fcntl.h>

#include <glib/gstdio.h>
#include <gio/gunixfdlist.h>
#include <gio/gunixoutputstream.h>

#include "notification.h"
#include "portal-private.h"

static void
prepare_media (GVariantBuilder *builder,
GVariant *serialized_media,
GUnixFDList *fd_list)
{
g_autoptr(GVariant) value = NULL;
g_autoptr(GFile) file = NULL;
g_autoptr(GInputStream) stream_in = NULL;
g_autoptr(GOutputStream) stream_out = NULL;
const char *key;
g_autofree char *path = NULL;
portal_autofd int fd = -1;
int fd_in = 0;

if (g_variant_is_of_type (serialized_media, G_VARIANT_TYPE("(sv)")))
g_variant_get (serialized_media, "(&sv)", &key, &value);

if (key && value && strcmp (key, "file") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
file = g_file_new_for_commandline_arg (g_variant_get_string (value, NULL));

/* Pass through everything that isn't a GFile */
if (!G_IS_FILE (file))
{
g_variant_builder_add_value (builder, serialized_media);
return;
}

stream_in = G_INPUT_STREAM (g_file_read (file, NULL, NULL));
fd = memfd_create ("notification-media", MFD_ALLOW_SEALING);
stream_out = g_unix_output_stream_new (fd, FALSE);

g_output_stream_splice (stream_out, stream_in, G_OUTPUT_STREAM_SPLICE_NONE, NULL, NULL);

fd_in = g_unix_fd_list_append (fd_list, fd, NULL);

g_variant_builder_add (builder, "(sv)", "file-descriptor", g_variant_new_handle (fd_in));
}

static void
prepare_notification (GVariantBuilder *builder,
GVariant *notification,
GUnixFDList *fd_list)
{
GVariantIter iter;
const char *key;
GVariant *value;

g_variant_iter_init (&iter, notification);

while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
{
g_variant_builder_open (builder, G_VARIANT_TYPE ("{sv}"));
g_variant_builder_add (builder, "s", key);
g_variant_builder_open (builder, G_VARIANT_TYPE_VARIANT);

if (strcmp (key, "icon") == 0)
prepare_media (builder, value, fd_list);
else if (strcmp (key, "sound") == 0)
prepare_media (builder, value, fd_list);
else
g_variant_builder_add_value (builder, value);

g_variant_builder_close (builder);
g_variant_builder_close (builder);
}
}

static void
action_invoked (GDBusConnection *bus,
const char *sender_name,
Expand Down Expand Up @@ -94,19 +169,34 @@ call_done (GObject *source,
*
* - title `s`: a user-visible string to display as title
* - body `s`: a user-visible string to display as body
* - icon `v`: a serialized icon (in the format produced by [method@Gio.Icon.serialize])
* - markup-body `s`: a user-visible string to display as body with support for markup
* - icon `v`: a serialized icon (in the format produced by [method@Gio.Icon.serialize]
* for class@Gio.ThemedIcon, class@Gio.FileIcon and class@Gio.BytesIcon)
* - sound `v`: a serialized sound
* - priority `s`: "low", "normal", "high" or "urgent"
* - default-action `s`: name of an action that
* will be activated when the user clicks on the notification
* - default-action-target `v`: target parameter to send along when
* activating the default action.
* - buttons `aa{sv}`: array of serialized buttons
* - desktop-file-id `s`: A desktop file id containing the .desktop suffix.
* - display-hint `as`: An array of display hints.
* - category `s`: A category for this notification. [See the spec for supported categories](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Notification.html#org-freedesktop-portal-notification-addnotification)
*
* The serialized sound consists of a `s` or `sv`:
* - default : Play the default sound for the notification.
* - silent : Don't ever play a sound for the notification.
* - file `s`: A path to a sound file.
* - bytes `ay`: An array of bytes.
*
* The supported sound formats are ogg/opus, ogg/vorbis and wav/pcm.
*
* Each serialized button is a dictionary with the following supported keys:
*
* - label `s`: user-visible lable for the button. Mandatory
* - label `s`: user-visible lable for the button. Mandatory without a purpose.
* - action `s`: name of an action that will be activated when
* the user clicks on the button. Mandatory
* - purpose `s`: information used by the server to style the button specially.
* - target `v`: target parameter to send along when activating
* the button
*
Expand All @@ -131,33 +221,44 @@ xdp_portal_add_notification (XdpPortal *portal,
{
GAsyncReadyCallback call_done_cb = NULL;
CallDoneData *call_done_data = NULL;
g_autoptr(GUnixFDList) fd_list = NULL;
GVariantBuilder builder;

g_return_if_fail (XDP_IS_PORTAL (portal));
g_return_if_fail (flags == XDP_NOTIFICATION_FLAG_NONE);

fd_list = g_unix_fd_list_new ();
ensure_action_invoked_connection (portal);

g_variant_builder_init (&builder, G_VARIANT_TYPE ("(sa{sv})"));
g_variant_builder_add (&builder, "s", id);

g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}"));
prepare_notification (&builder, notification, fd_list);
g_variant_builder_close (&builder);

if (callback)
{
call_done_cb = call_done;
call_done_cb = call_done;
call_done_data = g_new (CallDoneData, 1);
call_done_data->portal = g_object_ref (portal);
call_done_data->callback = callback,
call_done_data->data = data;
}

g_dbus_connection_call (portal->bus,
PORTAL_BUS_NAME,
PORTAL_OBJECT_PATH,
"org.freedesktop.portal.Notification",
"AddNotification",
g_variant_new ("(s@a{sv})", id, notification),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
call_done_cb,
call_done_data);
g_dbus_connection_call_with_unix_fd_list (portal->bus,
PORTAL_BUS_NAME,
PORTAL_OBJECT_PATH,
"org.freedesktop.portal.Notification",
"AddNotification",
g_variant_builder_end (&builder),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
fd_list,
jsparber marked this conversation as resolved.
Show resolved Hide resolved
cancellable,
call_done_cb,
call_done_data);
}

/**
Expand All @@ -179,7 +280,7 @@ xdp_portal_add_notification_finish (XdpPortal *portal,
{
g_autoptr(GVariant) res = NULL;

res = g_dbus_connection_call_finish (portal->bus, result, error);
res = g_dbus_connection_call_with_unix_fd_list_finish (portal->bus, NULL, result, error);

return !!res;
}
Expand Down Expand Up @@ -210,3 +311,44 @@ xdp_portal_remove_notification (XdpPortal *portal,
NULL,
NULL);
}

/**
* xdp_portal_get_supported_options:
* @portal: a [class@Portal]
*
* Returns: a vardict of supported options for properties that have options.
*
* Since: 0.7.2
*/
GVariant *
xdp_portal_get_supported_options (XdpPortal *portal)
{
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) ret = NULL;
g_autoptr(GVariant) options = NULL;

g_return_val_if_fail (XDP_IS_PORTAL (portal), FALSE);

ret = g_dbus_connection_call_sync (portal->bus,
PORTAL_BUS_NAME,
PORTAL_OBJECT_PATH,
"org.freedesktop.DBus.Properties",
"Get",
g_variant_new ("(ss)",
"org.freedesktop.portal.Notification",
"SupportedOptions"),
G_VARIANT_TYPE ("(v)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if (!ret)
{
g_warning ("Failed to get SupportedOptions property: %s", error->message);
return FALSE;
}

g_variant_get (ret, "(v)", &options);

return g_steal_pointer (&options);
}
3 changes: 3 additions & 0 deletions libportal/notification.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,7 @@ XDP_PUBLIC
void xdp_portal_remove_notification (XdpPortal *portal,
const char *id);

XDP_PUBLIC
GVariant *xdp_portal_get_supported_options (XdpPortal *portal);

G_END_DECLS
29 changes: 29 additions & 0 deletions libportal/portal-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

#pragma once

#include <errno.h>

#include "parent-private.h"
#include "portal-helpers.h"

Expand Down Expand Up @@ -71,3 +73,30 @@ const char * portal_get_bus_name (void);
#define FLATPAK_PORTAL_BUS_NAME "org.freedesktop.portal.Flatpak"
#define FLATPAK_PORTAL_OBJECT_PATH "/org/freedesktop/portal/Flatpak"
#define FLATPAK_PORTAL_INTERFACE "org.freedesktop.portal.Flatpak"

static inline int
portal_steal_fd (int *fdp)
{
int fd = *fdp;
*fdp = -1;
return fd;
}

static inline void
portal_close_fd (int *fdp)
{
int errsv;

g_assert (fdp);

int fd = portal_steal_fd (fdp);
if (fd >= 0)
{
errsv = errno;
if (close (fd) < 0)
g_assert (errno != EBADF);
errno = errsv;
}
}

#define portal_autofd __attribute__((cleanup(portal_close_fd)))
Loading