1 /* This file is part of Maliit framework
2  *
3  * Copyright (C) 2012 One Laptop per Child Association
4  *
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 licence, 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 Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
22 #include "maliitattributeextensionregistry.h"
23 #include "maliitbus.h"
24 
25 struct _MaliitAttributeExtensionRegistryPrivate
26 {
27     GHashTable *extensions;
28 };
29 
30 static MaliitAttributeExtensionRegistry *global_singleton;
31 
G_DEFINE_TYPE(MaliitAttributeExtensionRegistry,maliit_attribute_extension_registry,G_TYPE_OBJECT)32 G_DEFINE_TYPE (MaliitAttributeExtensionRegistry, maliit_attribute_extension_registry, G_TYPE_OBJECT)
33 
34 static void
35 maliit_attribute_extension_registry_finalize (GObject *object)
36 {
37     global_singleton = NULL;
38 
39     G_OBJECT_CLASS (maliit_attribute_extension_registry_parent_class)->finalize (object);
40 }
41 
42 static void
extension_notify(gpointer data,GObject * where_the_object_was)43 extension_notify (gpointer data,
44                   GObject *where_the_object_was)
45 {
46     MaliitAttributeExtensionRegistry *registry = MALIIT_ATTRIBUTE_EXTENSION_REGISTRY (data);
47     MaliitAttributeExtensionRegistryPrivate *priv = registry->priv;
48     GHashTableIter iter;
49     MaliitAttributeExtension *extension;
50 
51     g_hash_table_iter_init (&iter, priv->extensions);
52     while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&extension)) {
53         if ((gpointer)extension == (gpointer)where_the_object_was) {
54             g_hash_table_iter_steal (&iter);
55             break;
56         }
57     }
58 }
59 
60 static void
maliit_attribute_extension_registry_dispose(GObject * object)61 maliit_attribute_extension_registry_dispose (GObject *object)
62 {
63     MaliitAttributeExtensionRegistry *registry = MALIIT_ATTRIBUTE_EXTENSION_REGISTRY (object);
64     MaliitAttributeExtensionRegistryPrivate *priv = registry->priv;
65 
66     if (priv->extensions) {
67         GHashTable *extensions = priv->extensions;
68 
69         priv->extensions = NULL;
70         g_hash_table_unref (extensions);
71     }
72 
73     G_OBJECT_CLASS (maliit_attribute_extension_registry_parent_class)->dispose (object);
74 }
75 
76 static GObject*
maliit_attribute_extension_registry_constructor(GType type,guint n_params,GObjectConstructParam * params)77 maliit_attribute_extension_registry_constructor (GType type,
78                                                  guint n_params,
79                                                  GObjectConstructParam *params)
80 {
81     GObject *object;
82 
83     if (global_singleton) {
84         object = g_object_ref (G_OBJECT (global_singleton));
85     } else {
86         object = G_OBJECT_CLASS (maliit_attribute_extension_registry_parent_class)->constructor (type,
87                                                                                                  n_params,
88                                                                                                  params);
89         /* We are doing an additional reference here, so object will not
90          * be destroyed when last owner removes its reference. This is a
91          * leak, but for now it ensures that singleton have a lifetime of
92          * application.  This needs to be fixed, when object lifetimes are
93          * fixed in gtk-input-context. */
94         global_singleton = MALIIT_ATTRIBUTE_EXTENSION_REGISTRY (g_object_ref (object));
95     }
96 
97     return object;
98 }
99 
100 static void
maliit_attribute_extension_registry_class_init(MaliitAttributeExtensionRegistryClass * registry_class)101 maliit_attribute_extension_registry_class_init (MaliitAttributeExtensionRegistryClass *registry_class)
102 {
103     GObjectClass *g_object_class = G_OBJECT_CLASS (registry_class);
104 
105     g_object_class->finalize = maliit_attribute_extension_registry_finalize;
106     g_object_class->dispose = maliit_attribute_extension_registry_dispose;
107     g_object_class->constructor = maliit_attribute_extension_registry_constructor;
108 
109     g_type_class_add_private (registry_class, sizeof (MaliitAttributeExtensionRegistryPrivate));
110 }
111 
112 static void
extension_weak_unref(MaliitAttributeExtension * extension,MaliitAttributeExtensionRegistry * registry)113 extension_weak_unref (MaliitAttributeExtension *extension,
114                       MaliitAttributeExtensionRegistry *registry)
115 {
116     g_object_weak_unref (G_OBJECT (extension),
117                          extension_notify,
118                          registry);
119 }
120 
121 static void
extension_weak_unref_global(gpointer data)122 extension_weak_unref_global (gpointer data)
123 {
124     MaliitAttributeExtension *extension = MALIIT_ATTRIBUTE_EXTENSION (data);
125 
126     extension_weak_unref (extension,
127                           global_singleton);
128 }
129 
130 static void
register_all_extensions(MaliitServer * server,gpointer user_data)131 register_all_extensions (MaliitServer *server, gpointer user_data)
132 {
133     MaliitAttributeExtensionRegistry *registry = user_data;
134     GList *extensions = maliit_attribute_extension_registry_get_extensions (registry);
135     GList *iter;
136     GError *error = NULL;
137 
138     for (iter = extensions; iter; iter = iter->next) {
139         MaliitAttributeExtension *extension = MALIIT_ATTRIBUTE_EXTENSION (iter->data);
140 
141         if (maliit_server_call_register_attribute_extension_sync (server,
142                                                                   maliit_attribute_extension_get_id (extension),
143                                                                   maliit_attribute_extension_get_filename (extension),
144                                                                   NULL,
145                                                                   &error)) {
146             GHashTable *attributes = maliit_attribute_extension_get_attributes (extension);
147             GHashTableIter attributes_iter;
148             gpointer key;
149             gpointer value;
150 
151             g_hash_table_iter_init (&attributes_iter, attributes);
152 
153             while (g_hash_table_iter_next (&attributes_iter, &key, &value)) {
154                 maliit_attribute_extension_registry_extension_changed(registry, extension, key, value);
155             }
156         } else {
157             g_warning ("Could not register an extension in mass registerer: %s", error->message);
158             g_clear_error (&error);
159         }
160     }
161 
162     g_list_free (extensions);
163 }
164 
165 static void
connection_established(GObject * source_object G_GNUC_UNUSED,GAsyncResult * res,gpointer user_data)166 connection_established (GObject      *source_object G_GNUC_UNUSED,
167                         GAsyncResult *res,
168                         gpointer      user_data)
169 {
170     GError *error = NULL;
171     MaliitServer *server = maliit_get_server_finish (res, &error);
172 
173     if (server) {
174         register_all_extensions (server, user_data);
175     } else {
176         g_warning ("Unable to connect to server: %s", error->message);
177         g_clear_error (&error);
178     }
179 }
180 
181 static void
maliit_attribute_extension_registry_init(MaliitAttributeExtensionRegistry * registry)182 maliit_attribute_extension_registry_init (MaliitAttributeExtensionRegistry *registry)
183 {
184     MaliitAttributeExtensionRegistryPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (registry,
185                                                                                  MALIIT_TYPE_ATTRIBUTE_EXTENSION_REGISTRY,
186                                                                                  MaliitAttributeExtensionRegistryPrivate);
187 
188     priv->extensions = g_hash_table_new_full (g_direct_hash,
189                                               g_direct_equal,
190                                               NULL,
191                                               extension_weak_unref_global);
192 
193     registry->priv = priv;
194 
195     maliit_get_server (NULL, connection_established, registry);
196 }
197 
198 MaliitAttributeExtensionRegistry *
maliit_attribute_extension_registry_get_instance(void)199 maliit_attribute_extension_registry_get_instance (void)
200 {
201     return MALIIT_ATTRIBUTE_EXTENSION_REGISTRY (g_object_new (MALIIT_TYPE_ATTRIBUTE_EXTENSION_REGISTRY,
202                                                               NULL));
203 }
204 
205 void
maliit_attribute_extension_registry_add_extension(MaliitAttributeExtensionRegistry * registry,MaliitAttributeExtension * extension)206 maliit_attribute_extension_registry_add_extension (MaliitAttributeExtensionRegistry *registry,
207                                                    MaliitAttributeExtension *extension)
208 {
209     MaliitServer *server;
210     GHashTable *extensions;
211     gint id;
212     GError *error = NULL;
213 
214     g_return_if_fail (MALIIT_IS_ATTRIBUTE_EXTENSION_REGISTRY (registry));
215     g_return_if_fail (MALIIT_IS_ATTRIBUTE_EXTENSION (extension));
216 
217     extensions = registry->priv->extensions;
218     id = maliit_attribute_extension_get_id (extension);
219 
220     if (!g_hash_table_lookup_extended (extensions, GINT_TO_POINTER (id), NULL, NULL)) {
221         g_object_weak_ref (G_OBJECT (extension),
222                            extension_notify,
223                            registry);
224 
225         g_hash_table_insert (extensions,
226                              GINT_TO_POINTER (id),
227                              extension);
228 
229         server = maliit_get_server_sync (NULL, &error);
230 
231         if (server) {
232             if (!maliit_server_call_register_attribute_extension_sync (server,
233                                                                        id,
234                                                                        maliit_attribute_extension_get_filename (extension),
235                                                                        NULL,
236                                                                        &error)) {
237                 g_warning ("Unable to register extension: %s", error->message);
238                 g_clear_error (&error);
239             }
240         } else {
241             g_warning ("Unable to connect to server: %s", error->message);
242             g_clear_error (&error);
243         }
244     }
245 }
246 
247 void
maliit_attribute_extension_registry_remove_extension(MaliitAttributeExtensionRegistry * registry,MaliitAttributeExtension * extension)248 maliit_attribute_extension_registry_remove_extension (MaliitAttributeExtensionRegistry *registry,
249                                                       MaliitAttributeExtension *extension)
250 {
251     MaliitServer *server;
252     GHashTable *extensions;
253     gint id;
254     GError *error = NULL;
255 
256     g_return_if_fail (MALIIT_IS_ATTRIBUTE_EXTENSION_REGISTRY (registry));
257     g_return_if_fail (MALIIT_IS_ATTRIBUTE_EXTENSION (extension));
258 
259     extensions = registry->priv->extensions;
260     id = maliit_attribute_extension_get_id (extension);
261 
262     if (g_hash_table_lookup_extended (extensions, GINT_TO_POINTER (id), NULL, NULL)) {
263         g_hash_table_remove (extensions,
264                              GINT_TO_POINTER (id));
265 
266         server = maliit_get_server_sync (NULL, &error);
267 
268         if (server) {
269             if (!maliit_server_call_unregister_attribute_extension_sync (server,
270                                                                          id,
271                                                                          NULL,
272                                                                          &error)) {
273                 g_warning ("Unable to unregister extension: %s", error->message);
274                 g_clear_error (&error);
275             }
276         } else {
277             g_warning ("Unable to connect to server: %s", error->message);
278             g_clear_error (&error);
279         }
280     }
281 }
282 
283 /* For glib < 2.30 */
284 #ifndef G_VALUE_INIT
285 #define G_VALUE_INIT { 0, { { 0 } } }
286 #endif
287 
288 void
maliit_attribute_extension_registry_extension_changed(MaliitAttributeExtensionRegistry * registry,MaliitAttributeExtension * extension,const gchar * key,GVariant * value)289 maliit_attribute_extension_registry_extension_changed (MaliitAttributeExtensionRegistry *registry,
290                                                        MaliitAttributeExtension *extension,
291                                                        const gchar *key,
292                                                        GVariant *value)
293 {
294     MaliitServer *server;
295     gchar **parts;
296     GError *error = NULL;
297 
298     g_return_if_fail (MALIIT_IS_ATTRIBUTE_EXTENSION_REGISTRY (registry));
299     g_return_if_fail (MALIIT_IS_ATTRIBUTE_EXTENSION (extension));
300     g_return_if_fail (key != NULL);
301     g_return_if_fail (value != NULL);
302 
303     parts = g_strsplit (key + 1, "/", 3);
304 
305     if (!parts)
306         return;
307 
308     if (g_strv_length (parts) == 3) {
309         gchar *target = g_strdup_printf ("/%s", parts[0]);
310 
311         server = maliit_get_server_sync (NULL, &error);
312 
313         if (server) {
314             if (!maliit_server_call_set_extended_attribute_sync (server,
315                                                                  maliit_attribute_extension_get_id (extension),
316                                                                  target,
317                                                                  parts[1],
318                                                                  parts[2],
319                                                                  value,
320                                                                  NULL,
321                                                                  &error)) {
322                 g_warning ("Unable to set extended attribute: %s", error->message);
323                 g_clear_error (&error);
324             }
325         } else {
326             g_warning ("Unable to connect to server: %s", error->message);
327             g_clear_error (&error);
328         }
329 
330         g_free (target);
331     } else {
332         g_warning("Key `%s' is not valid. It needs to be `/target/item/key'", key);
333     }
334     g_strfreev (parts);
335 }
336 
337 static void
fill_list_with_extensions(gpointer key G_GNUC_UNUSED,gpointer value,gpointer user_data)338 fill_list_with_extensions (gpointer key G_GNUC_UNUSED,
339                            gpointer value,
340                            gpointer user_data)
341 {
342     MaliitAttributeExtension *extension = MALIIT_ATTRIBUTE_EXTENSION (value);
343     GList **list = (GList **)user_data;
344 
345     *list = g_list_prepend (*list, extension);
346 }
347 
348 GList *
maliit_attribute_extension_registry_get_extensions(MaliitAttributeExtensionRegistry * registry)349 maliit_attribute_extension_registry_get_extensions (MaliitAttributeExtensionRegistry *registry)
350 {
351     GList *list;
352 
353     g_return_val_if_fail (MALIIT_IS_ATTRIBUTE_EXTENSION_REGISTRY (registry), NULL);
354 
355     list = NULL;
356     g_hash_table_foreach (registry->priv->extensions,
357                           fill_list_with_extensions,
358                           &list);
359 
360     return list;
361 }
362 
363 void
maliit_attribute_extension_registry_update_attribute(MaliitAttributeExtensionRegistry * registry,gint id,const gchar * target,const gchar * target_item,const gchar * attribute,GVariant * value)364 maliit_attribute_extension_registry_update_attribute (MaliitAttributeExtensionRegistry *registry,
365                                                       gint id,
366                                                       const gchar *target,
367                                                       const gchar *target_item,
368                                                       const gchar *attribute,
369                                                       GVariant *value)
370 {
371     MaliitAttributeExtension *extension;
372 
373     g_return_if_fail (MALIIT_IS_ATTRIBUTE_EXTENSION_REGISTRY (registry));
374     g_return_if_fail (id >= 0);
375     g_return_if_fail (target != NULL);
376     g_return_if_fail (target_item != NULL);
377     g_return_if_fail (attribute != NULL);
378     g_return_if_fail (value != NULL);
379 
380     if (g_hash_table_lookup_extended (registry->priv->extensions,
381                                       GINT_TO_POINTER (id),
382                                       NULL,
383                                       (gpointer *)&extension)) {
384         gchar *key = g_strdup_printf ("%s/%s/%s", target, target_item, attribute);
385 
386         maliit_attribute_extension_update_attribute (extension,
387                                                      key,
388                                                      value);
389         g_free (key);
390     } else {
391         g_warning ("Extension %d was not found.", id);
392     }
393 }
394