1 /*
2 * Copyright (C) 2018, Matthias Clasen
3 *
4 * This file is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as
6 * published by the Free Software Foundation, version 3.0 of the
7 * License.
8 *
9 * This file is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * SPDX-License-Identifier: LGPL-3.0-only
18 */
19
20 #include "config.h"
21
22 #include "notification.h"
23 #include "portal-private.h"
24
25 static void
action_invoked(GDBusConnection * bus,const char * sender_name,const char * object_path,const char * interface_name,const char * signal_name,GVariant * parameters,gpointer data)26 action_invoked (GDBusConnection *bus,
27 const char *sender_name,
28 const char *object_path,
29 const char *interface_name,
30 const char *signal_name,
31 GVariant *parameters,
32 gpointer data)
33 {
34 XdpPortal *portal = data;
35 g_autoptr(GVariant) info = NULL;
36 const char *id;
37 const char *action;
38 g_autoptr(GVariant) parameter = NULL;
39
40 g_variant_get (parameters, "(&s&s@av)", &id, &action, ¶meter);
41
42 g_signal_emit_by_name (portal, "notification-action-invoked",
43 id, action, parameter);
44 }
45
46 static void
ensure_action_invoked_connection(XdpPortal * portal)47 ensure_action_invoked_connection (XdpPortal *portal)
48 {
49 if (portal->action_invoked_signal == 0)
50 portal->action_invoked_signal =
51 g_dbus_connection_signal_subscribe (portal->bus,
52 PORTAL_BUS_NAME,
53 "org.freedesktop.portal.Notification",
54 "ActionInvoked",
55 PORTAL_OBJECT_PATH,
56 NULL,
57 G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
58 action_invoked,
59 portal,
60 NULL);
61 }
62
63 typedef struct {
64 XdpPortal *portal;
65 GAsyncReadyCallback callback;
66 gpointer data;
67 } CallDoneData;
68
69 static void
call_done(GObject * source,GAsyncResult * result,gpointer data)70 call_done (GObject *source,
71 GAsyncResult *result,
72 gpointer data)
73 {
74 CallDoneData *call_done_data = data;
75
76 call_done_data->callback (G_OBJECT (call_done_data->portal), result, call_done_data->data);
77
78 g_object_unref (call_done_data->portal);
79 g_free (call_done_data);
80 }
81
82 /**
83 * xdp_portal_add_notification:
84 * @portal: a [class@Portal]
85 * @id: unique ID for the notification
86 * @notification: a [struct@GLib.Variant] dictionary with the content of the notification
87 * @flags: options for this call
88 * @cancellable: (nullable): optional [class@Gio.Cancellable]
89 * @callback: (scope async): a callback to call when the request is done
90 * @data: (closure): data to pass to @callback
91 *
92 * Sends a desktop notification.
93 *
94 * The following keys may be present in @notification:
95 *
96 * - title `s`: a user-visible string to display as title
97 * - body `s`: a user-visible string to display as body
98 * - icon `v`: a serialized icon (in the format produced by [method@Gio.Icon.serialize])
99 * - priority `s`: "low", "normal", "high" or "urgent"
100 * - default-action `s`: name of an action that
101 * will be activated when the user clicks on the notification
102 * - default-action-target `v`: target parameter to send along when
103 * activating the default action.
104 * - buttons `aa{sv}`: array of serialized buttons
105 *
106 * Each serialized button is a dictionary with the following supported keys:
107 *
108 * - label `s`: user-visible lable for the button. Mandatory
109 * - action `s`: name of an action that will be activated when
110 * the user clicks on the button. Mandatory
111 * - target `v`: target parameter to send along when activating
112 * the button
113 *
114 * Actions with a prefix of "app." are assumed to be exported by the
115 * application and will be activated via the org.freedesktop.Application
116 * interface, others are activated by emitting the
117 * [signal@Portal::notification-action-invoked] signal.
118 *
119 * It is the callers responsibility to ensure that the ID is unique
120 * among all notifications.
121 *
122 * To withdraw a notification, use [method@Portal.remove_notification].
123 */
124 void
xdp_portal_add_notification(XdpPortal * portal,const char * id,GVariant * notification,XdpNotificationFlags flags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer data)125 xdp_portal_add_notification (XdpPortal *portal,
126 const char *id,
127 GVariant *notification,
128 XdpNotificationFlags flags,
129 GCancellable *cancellable,
130 GAsyncReadyCallback callback,
131 gpointer data)
132 {
133 GAsyncReadyCallback call_done_cb = NULL;
134 CallDoneData *call_done_data = NULL;
135
136 g_return_if_fail (XDP_IS_PORTAL (portal));
137 g_return_if_fail (flags == XDP_NOTIFICATION_FLAG_NONE);
138
139 ensure_action_invoked_connection (portal);
140
141 if (callback)
142 {
143 call_done_cb = call_done;
144 call_done_data = g_new (CallDoneData, 1);
145 call_done_data->portal = g_object_ref (portal);
146 call_done_data->callback = callback,
147 call_done_data->data = data;
148 }
149
150 g_dbus_connection_call (portal->bus,
151 PORTAL_BUS_NAME,
152 PORTAL_OBJECT_PATH,
153 "org.freedesktop.portal.Notification",
154 "AddNotification",
155 g_variant_new ("(s@a{sv})", id, notification),
156 NULL,
157 G_DBUS_CALL_FLAGS_NONE,
158 -1,
159 cancellable,
160 call_done_cb,
161 call_done_data);
162 }
163
164 /**
165 * xdp_portal_add_notification_finish:
166 * @portal: a [class@Portal]
167 * @result: a [iface@Gio.AsyncResult]
168 * @error: return location for an error
169 *
170 * Finishes the notification request, and returns the result
171 * as a boolean.
172 *
173 * Returns: `TRUE` if the notification was added
174 */
175 gboolean
xdp_portal_add_notification_finish(XdpPortal * portal,GAsyncResult * result,GError ** error)176 xdp_portal_add_notification_finish (XdpPortal *portal,
177 GAsyncResult *result,
178 GError **error)
179 {
180 g_autoptr(GVariant) res = NULL;
181
182 res = g_dbus_connection_call_finish (portal->bus, result, error);
183
184 return !!res;
185 }
186
187 /**
188 * xdp_portal_remove_notification:
189 * @portal: a [class@Portal]
190 * @id: the ID of an notification
191 *
192 * Withdraws a desktop notification.
193 */
194 void
xdp_portal_remove_notification(XdpPortal * portal,const char * id)195 xdp_portal_remove_notification (XdpPortal *portal,
196 const char *id)
197 {
198 g_return_if_fail (XDP_IS_PORTAL (portal));
199
200 g_dbus_connection_call (portal->bus,
201 PORTAL_BUS_NAME,
202 PORTAL_OBJECT_PATH,
203 "org.freedesktop.portal.Notification",
204 "RemoveNotification",
205 g_variant_new ("(s)", id),
206 NULL,
207 G_DBUS_CALL_FLAGS_NONE,
208 -1,
209 NULL,
210 NULL,
211 NULL);
212 }
213