Branch data Line data Source code
1 : : /*
2 : : * Copyright © 2013 Lars Uebernickel
3 : : *
4 : : * SPDX-License-Identifier: LGPL-2.1-or-later
5 : : *
6 : : * This library is free software; you can redistribute it and/or
7 : : * modify it under the terms of the GNU Lesser General Public
8 : : * License as published by the Free Software Foundation; either
9 : : * version 2.1 of the License, or (at your option) any later version.
10 : : *
11 : : * This library is distributed in the hope that it will be useful,
12 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : * Lesser General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU Lesser General
17 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 : : *
19 : : * Authors: Lars Uebernickel <lars@uebernic.de>
20 : : */
21 : :
22 : : #include "gnotification-server.h"
23 : :
24 : : #include <gio/gio.h>
25 : :
26 : : typedef GObjectClass GNotificationServerClass;
27 : :
28 : : struct _GNotificationServer
29 : : {
30 : : GObject parent;
31 : :
32 : : GDBusConnection *connection;
33 : : guint name_owner_id;
34 : : guint object_id;
35 : :
36 : : guint is_running;
37 : :
38 : : /* app_ids -> hashtables of notification ids -> a{sv} */
39 : : GHashTable *applications;
40 : : };
41 : :
42 : 12 : G_DEFINE_TYPE (GNotificationServer, g_notification_server, G_TYPE_OBJECT)
43 : :
44 : : enum
45 : : {
46 : : PROP_0,
47 : : PROP_IS_RUNNING
48 : : };
49 : :
50 : : static GDBusInterfaceInfo *
51 : 1 : org_gtk_Notifications_get_interface (void)
52 : : {
53 : : static GDBusInterfaceInfo *iface_info;
54 : :
55 : 1 : if (iface_info == NULL)
56 : : {
57 : : GDBusNodeInfo *info;
58 : 1 : GError *error = NULL;
59 : :
60 : 1 : info = g_dbus_node_info_new_for_xml (
61 : : "<node>"
62 : : " <interface name='org.gtk.Notifications'>"
63 : : " <method name='AddNotification'>"
64 : : " <arg type='s' direction='in' />"
65 : : " <arg type='s' direction='in' />"
66 : : " <arg type='a{sv}' direction='in' />"
67 : : " </method>"
68 : : " <method name='RemoveNotification'>"
69 : : " <arg type='s' direction='in' />"
70 : : " <arg type='s' direction='in' />"
71 : : " </method>"
72 : : " </interface>"
73 : : "</node>", &error);
74 : :
75 : 1 : if (info == NULL)
76 : 0 : g_error ("%s", error->message);
77 : :
78 : 1 : iface_info = g_dbus_node_info_lookup_interface (info, "org.gtk.Notifications");
79 : 1 : g_assert (iface_info);
80 : :
81 : 1 : g_dbus_interface_info_ref (iface_info);
82 : 1 : g_dbus_node_info_unref (info);
83 : : }
84 : :
85 : 1 : return iface_info;
86 : : }
87 : :
88 : : static void
89 : 5 : g_notification_server_notification_added (GNotificationServer *server,
90 : : const gchar *app_id,
91 : : const gchar *notification_id,
92 : : GVariant *notification)
93 : : {
94 : : GHashTable *notifications;
95 : :
96 : 5 : notifications = g_hash_table_lookup (server->applications, app_id);
97 : 5 : if (notifications == NULL)
98 : : {
99 : 1 : notifications = g_hash_table_new_full (g_str_hash, g_str_equal,
100 : : g_free, (GDestroyNotify) g_variant_unref);
101 : 1 : g_hash_table_insert (server->applications, g_strdup (app_id), notifications);
102 : : }
103 : :
104 : 10 : g_hash_table_replace (notifications, g_strdup (notification_id), g_variant_ref (notification));
105 : :
106 : 5 : g_signal_emit_by_name (server, "notification-received", app_id, notification_id, notification);
107 : 5 : }
108 : :
109 : : static void
110 : 1 : g_notification_server_notification_removed (GNotificationServer *server,
111 : : const gchar *app_id,
112 : : const gchar *notification_id)
113 : : {
114 : : GHashTable *notifications;
115 : :
116 : 1 : notifications = g_hash_table_lookup (server->applications, app_id);
117 : 1 : if (notifications)
118 : : {
119 : 1 : g_hash_table_remove (notifications, notification_id);
120 : 1 : if (g_hash_table_size (notifications) == 0)
121 : 0 : g_hash_table_remove (server->applications, app_id);
122 : : }
123 : :
124 : 1 : g_signal_emit_by_name (server, "notification-removed", app_id, notification_id);
125 : 1 : }
126 : :
127 : : static void
128 : 6 : org_gtk_Notifications_method_call (GDBusConnection *connection,
129 : : const gchar *sender,
130 : : const gchar *object_path,
131 : : const gchar *interface_name,
132 : : const gchar *method_name,
133 : : GVariant *parameters,
134 : : GDBusMethodInvocation *invocation,
135 : : gpointer user_data)
136 : : {
137 : 6 : GNotificationServer *server = user_data;
138 : :
139 : 6 : if (g_str_equal (method_name, "AddNotification"))
140 : : {
141 : : const gchar *app_id;
142 : : const gchar *notification_id;
143 : : GVariant *notification;
144 : :
145 : 5 : g_variant_get (parameters, "(&s&s@a{sv})", &app_id, ¬ification_id, ¬ification);
146 : 5 : g_notification_server_notification_added (server, app_id, notification_id, notification);
147 : 5 : g_dbus_method_invocation_return_value (invocation, NULL);
148 : :
149 : 5 : g_variant_unref (notification);
150 : : }
151 : 1 : else if (g_str_equal (method_name, "RemoveNotification"))
152 : : {
153 : : const gchar *app_id;
154 : : const gchar *notification_id;
155 : :
156 : 1 : g_variant_get (parameters, "(&s&s)", &app_id, ¬ification_id);
157 : 1 : g_notification_server_notification_removed (server, app_id, notification_id);
158 : 1 : g_dbus_method_invocation_return_value (invocation, NULL);
159 : : }
160 : : else
161 : : {
162 : 0 : g_dbus_method_invocation_return_dbus_error (invocation, "UnknownMethod", "No such method");
163 : : }
164 : 6 : }
165 : :
166 : : static void
167 : 1 : g_notification_server_dispose (GObject *object)
168 : : {
169 : 1 : GNotificationServer *server = G_NOTIFICATION_SERVER (object);
170 : :
171 : 1 : g_notification_server_stop (server);
172 : :
173 : 1 : g_clear_pointer (&server->applications, g_hash_table_unref);
174 : 1 : g_clear_object (&server->connection);
175 : :
176 : 1 : G_OBJECT_CLASS (g_notification_server_parent_class)->dispose (object);
177 : 1 : }
178 : :
179 : : static void
180 : 0 : g_notification_server_get_property (GObject *object,
181 : : guint property_id,
182 : : GValue *value,
183 : : GParamSpec *pspec)
184 : : {
185 : 0 : GNotificationServer *server = G_NOTIFICATION_SERVER (object);
186 : :
187 : 0 : switch (property_id)
188 : : {
189 : 0 : case PROP_IS_RUNNING:
190 : 0 : g_value_set_boolean (value, server->is_running);
191 : 0 : break;
192 : :
193 : 0 : default:
194 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
195 : : }
196 : 0 : }
197 : :
198 : : static void
199 : 1 : g_notification_server_class_init (GNotificationServerClass *class)
200 : : {
201 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (class);
202 : :
203 : 1 : object_class->get_property = g_notification_server_get_property;
204 : 1 : object_class->dispose = g_notification_server_dispose;
205 : :
206 : 1 : g_object_class_install_property (object_class, PROP_IS_RUNNING,
207 : : g_param_spec_boolean ("is-running", "", "", FALSE,
208 : : G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
209 : :
210 : 1 : g_signal_new ("notification-received", G_TYPE_NOTIFICATION_SERVER, G_SIGNAL_RUN_FIRST,
211 : : 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 3,
212 : : G_TYPE_STRING, G_TYPE_STRING, G_TYPE_VARIANT);
213 : :
214 : 1 : g_signal_new ("notification-removed", G_TYPE_NOTIFICATION_SERVER, G_SIGNAL_RUN_FIRST,
215 : : 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2,
216 : : G_TYPE_STRING, G_TYPE_STRING);
217 : 1 : }
218 : :
219 : : static void
220 : 1 : g_notification_server_bus_acquired (GDBusConnection *connection,
221 : : const gchar *name,
222 : : gpointer user_data)
223 : : {
224 : 1 : const GDBusInterfaceVTable vtable = {
225 : : org_gtk_Notifications_method_call, NULL, NULL, { 0 }
226 : : };
227 : 1 : GNotificationServer *server = user_data;
228 : :
229 : 1 : server->object_id = g_dbus_connection_register_object (connection, "/org/gtk/Notifications",
230 : : org_gtk_Notifications_get_interface (),
231 : : &vtable, server, NULL, NULL);
232 : :
233 : : /* register_object only fails if the same object is exported more than once */
234 : 1 : g_assert (server->object_id > 0);
235 : :
236 : 1 : server->connection = g_object_ref (connection);
237 : 1 : }
238 : :
239 : : static void
240 : 1 : g_notification_server_name_acquired (GDBusConnection *connection,
241 : : const gchar *name,
242 : : gpointer user_data)
243 : : {
244 : 1 : GNotificationServer *server = user_data;
245 : :
246 : 1 : server->is_running = TRUE;
247 : 1 : g_object_notify (G_OBJECT (server), "is-running");
248 : 1 : }
249 : :
250 : : static void
251 : 0 : g_notification_server_name_lost (GDBusConnection *connection,
252 : : const gchar *name,
253 : : gpointer user_data)
254 : : {
255 : 0 : GNotificationServer *server = user_data;
256 : :
257 : 0 : g_notification_server_stop (server);
258 : :
259 : 0 : if (connection == NULL && server->connection)
260 : 0 : g_clear_object (&server->connection);
261 : 0 : }
262 : :
263 : : static void
264 : 1 : g_notification_server_init (GNotificationServer *server)
265 : : {
266 : 1 : server->applications = g_hash_table_new_full (g_str_hash, g_str_equal,
267 : : g_free, (GDestroyNotify) g_hash_table_unref);
268 : :
269 : 1 : server->name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
270 : : "org.gtk.Notifications",
271 : : G_BUS_NAME_OWNER_FLAGS_NONE,
272 : : g_notification_server_bus_acquired,
273 : : g_notification_server_name_acquired,
274 : : g_notification_server_name_lost,
275 : : server, NULL);
276 : 1 : }
277 : :
278 : : GNotificationServer *
279 : 1 : g_notification_server_new (void)
280 : : {
281 : 1 : return g_object_new (G_TYPE_NOTIFICATION_SERVER, NULL);
282 : : }
283 : :
284 : : void
285 : 2 : g_notification_server_stop (GNotificationServer *server)
286 : : {
287 : 2 : g_return_if_fail (G_IS_NOTIFICATION_SERVER (server));
288 : :
289 : 2 : if (server->name_owner_id)
290 : : {
291 : 1 : g_bus_unown_name (server->name_owner_id);
292 : 1 : server->name_owner_id = 0;
293 : : }
294 : :
295 : 2 : if (server->object_id && server->connection)
296 : : {
297 : 1 : g_dbus_connection_unregister_object (server->connection, server->object_id);
298 : 1 : server->object_id = 0;
299 : : }
300 : :
301 : 2 : if (server->is_running)
302 : : {
303 : 1 : server->is_running = FALSE;
304 : 1 : g_object_notify (G_OBJECT (server), "is-running");
305 : : }
306 : : }
307 : :
308 : : gboolean
309 : 2 : g_notification_server_get_is_running (GNotificationServer *server)
310 : : {
311 : 2 : g_return_val_if_fail (G_IS_NOTIFICATION_SERVER (server), FALSE);
312 : :
313 : 2 : return server->is_running;
314 : : }
315 : :
316 : : gchar **
317 : 0 : g_notification_server_list_applications (GNotificationServer *server)
318 : : {
319 : 0 : g_return_val_if_fail (G_IS_NOTIFICATION_SERVER (server), NULL);
320 : :
321 : 0 : return (gchar **) g_hash_table_get_keys_as_array (server->applications, NULL);
322 : : }
323 : :
324 : : gchar **
325 : 0 : g_notification_server_list_notifications (GNotificationServer *server,
326 : : const gchar *app_id)
327 : : {
328 : : GHashTable *notifications;
329 : :
330 : 0 : g_return_val_if_fail (G_IS_NOTIFICATION_SERVER (server), NULL);
331 : 0 : g_return_val_if_fail (app_id != NULL, NULL);
332 : :
333 : 0 : notifications = g_hash_table_lookup (server->applications, app_id);
334 : :
335 : 0 : if (notifications == NULL)
336 : 0 : return NULL;
337 : :
338 : 0 : return (gchar **) g_hash_table_get_keys_as_array (notifications, NULL);
339 : : }
|