gio/tests/giomodule.c: Use G_MODULE_SUFFIX
[platform/upstream/glib.git] / gio / tests / gnotification-server.c
1 /*
2  * Copyright © 2013 Lars Uebernickel
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published
6  * by the Free Software Foundation; either version 2 of the licence or (at
7  * your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but 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
15  * Public License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17  * Boston, MA 02111-1307, USA.
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 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 org_gtk_Notifications_get_interface (void)
52 {
53   static GDBusInterfaceInfo *iface_info;
54
55   if (iface_info == NULL)
56     {
57       GDBusNodeInfo *info;
58       GError *error = NULL;
59
60       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       if (info == NULL)
76         g_error ("%s", error->message);
77
78       iface_info = g_dbus_node_info_lookup_interface (info, "org.gtk.Notifications");
79       g_assert (iface_info);
80
81       g_dbus_interface_info_ref (iface_info);
82       g_dbus_node_info_unref (info);
83     }
84
85   return iface_info;
86 }
87
88 static void
89 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   notifications = g_hash_table_lookup (server->applications, app_id);
97   if (notifications == NULL)
98     {
99       notifications = g_hash_table_new_full (g_str_hash, g_str_equal,
100                                              g_free, (GDestroyNotify) g_variant_unref);
101       g_hash_table_insert (server->applications, g_strdup (app_id), notifications);
102     }
103
104   g_hash_table_replace (notifications, g_strdup (notification_id), g_variant_ref (notification));
105
106   g_signal_emit_by_name (server, "notification-received", app_id, notification_id, notification);
107 }
108
109 static void
110 g_notification_server_notification_removed (GNotificationServer *server,
111                                             const gchar         *app_id,
112                                             const gchar         *notification_id)
113 {
114   GHashTable *notifications;
115
116   notifications = g_hash_table_lookup (server->applications, app_id);
117   if (notifications)
118     {
119       g_hash_table_remove (notifications, notification_id);
120       if (g_hash_table_size (notifications) == 0)
121         g_hash_table_remove (server->applications, app_id);
122     }
123
124   g_signal_emit_by_name (server, "notification-removed", app_id, notification_id);
125 }
126
127 static void
128 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   GNotificationServer *server = user_data;
138
139   if (g_str_equal (method_name, "AddNotification"))
140     {
141       const gchar *app_id;
142       const gchar *notification_id;
143       GVariant *notification;
144
145       g_variant_get (parameters, "(&s&s@a{sv})", &app_id, &notification_id, &notification);
146       g_notification_server_notification_added (server, app_id, notification_id, notification);
147       g_dbus_method_invocation_return_value (invocation, NULL);
148
149       g_variant_unref (notification);
150     }
151   else if (g_str_equal (method_name, "RemoveNotification"))
152     {
153       const gchar *app_id;
154       const gchar *notification_id;
155
156       g_variant_get (parameters, "(&s&s)", &app_id, &notification_id);
157       g_notification_server_notification_removed (server, app_id, notification_id);
158       g_dbus_method_invocation_return_value (invocation, NULL);
159     }
160   else
161     {
162       g_dbus_method_invocation_return_dbus_error (invocation, "UnknownMethod", "No such method");
163     }
164 }
165
166 static void
167 g_notification_server_dispose (GObject *object)
168 {
169   GNotificationServer *server = G_NOTIFICATION_SERVER (object);
170
171   g_notification_server_stop (server);
172
173   g_clear_pointer (&server->applications, g_hash_table_unref);
174   g_clear_object (&server->connection);
175
176   G_OBJECT_CLASS (g_notification_server_parent_class)->dispose (object);
177 }
178
179 static void
180 g_notification_server_get_property (GObject    *object,
181                                     guint       property_id,
182                                     GValue     *value,
183                                     GParamSpec *pspec)
184 {
185   GNotificationServer *server = G_NOTIFICATION_SERVER (object);
186
187   switch (property_id)
188     {
189     case PROP_IS_RUNNING:
190       g_value_set_boolean (value, server->is_running);
191       break;
192
193     default:
194       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
195     }
196 }
197
198 static void
199 g_notification_server_class_init (GNotificationServerClass *class)
200 {
201   GObjectClass *object_class = G_OBJECT_CLASS (class);
202
203   object_class->get_property = g_notification_server_get_property;
204   object_class->dispose = g_notification_server_dispose;
205
206   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   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   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 }
218
219 static void
220 g_notification_server_bus_acquired (GDBusConnection *connection,
221                                     const gchar     *name,
222                                     gpointer         user_data)
223 {
224   const GDBusInterfaceVTable vtable = {
225     org_gtk_Notifications_method_call, NULL, NULL
226   };
227   GNotificationServer *server = user_data;
228
229   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   g_assert (server->object_id > 0);
235
236   server->connection = g_object_ref (connection);
237 }
238
239 static void
240 g_notification_server_name_acquired (GDBusConnection *connection,
241                                      const gchar     *name,
242                                      gpointer         user_data)
243 {
244   GNotificationServer *server = user_data;
245
246   server->is_running = TRUE;
247   g_object_notify (G_OBJECT (server), "is-running");
248 }
249
250 static void
251 g_notification_server_name_lost (GDBusConnection *connection,
252                                  const gchar     *name,
253                                  gpointer         user_data)
254 {
255   GNotificationServer *server = user_data;
256
257   g_notification_server_stop (server);
258
259   if (connection == NULL && server->connection)
260     g_clear_object (&server->connection);
261 }
262
263 static void
264 g_notification_server_init (GNotificationServer *server)
265 {
266   server->applications = g_hash_table_new_full (g_str_hash, g_str_equal,
267                                                 g_free, (GDestroyNotify) g_hash_table_unref);
268
269   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 }
277
278 GNotificationServer *
279 g_notification_server_new (void)
280 {
281   return g_object_new (G_TYPE_NOTIFICATION_SERVER, NULL);
282 }
283
284 void
285 g_notification_server_stop (GNotificationServer *server)
286 {
287   g_return_if_fail (G_IS_NOTIFICATION_SERVER (server));
288
289   if (server->name_owner_id)
290     {
291       g_bus_unown_name (server->name_owner_id);
292       server->name_owner_id = 0;
293     }
294
295   if (server->object_id && server->connection)
296     {
297       g_dbus_connection_unregister_object (server->connection, server->object_id);
298       server->object_id = 0;
299     }
300
301   if (server->is_running)
302     {
303       server->is_running = FALSE;
304       g_object_notify (G_OBJECT (server), "is-running");
305     }
306 }
307
308 gboolean
309 g_notification_server_get_is_running (GNotificationServer *server)
310 {
311   g_return_val_if_fail (G_IS_NOTIFICATION_SERVER (server), FALSE);
312
313   return server->is_running;
314 }
315
316 gchar **
317 g_notification_server_list_applications (GNotificationServer *server)
318 {
319   g_return_val_if_fail (G_IS_NOTIFICATION_SERVER (server), NULL);
320
321   return (gchar **) g_hash_table_get_keys_as_array (server->applications, NULL);
322 }
323
324 gchar **
325 g_notification_server_list_notifications (GNotificationServer *server,
326                                           const gchar         *app_id)
327 {
328   GHashTable *notifications;
329
330   g_return_val_if_fail (G_IS_NOTIFICATION_SERVER (server), NULL);
331   g_return_val_if_fail (app_id != NULL, NULL);
332
333   notifications = g_hash_table_lookup (server->applications, app_id);
334
335   if (notifications == NULL)
336     return NULL;
337
338   return (gchar **) g_hash_table_get_keys_as_array (notifications, NULL);
339 }