1 /* vim: set et ts=8 sw=8: */
2 /*
3 * Geoclue convenience library.
4 *
5 * Copyright 2015 Red Hat, Inc.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 *
21 * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
22 */
23
24 /**
25 * SECTION: gclue-simple
26 * @title: GClueSimple
27 * @short_description: Simplified convenience API
28 *
29 * #GClueSimple make it very simple to get latest location and monitoring
30 * location updates. It takes care of the boring tasks of creating a
31 * #GClueClientProxy instance, starting it, waiting till we have a location fix
32 * and then creating a #GClueLocationProxy instance for it.
33 *
34 * Use #gclue_simple_new() or #gclue_simple_new_sync() to create a new
35 * #GClueSimple instance. Once you have a #GClueSimple instance, you can get the
36 * latest location using #gclue_simple_get_location() or reading the
37 * #GClueSimple:location property. To monitor location updates, connect to
38 * notify signal for this property.
39 *
40 * While most applications will find this API very useful, it is most
41 * useful for applications that simply want to get the current location as
42 * quickly as possible and do not care about accuracy (much).
43 */
44
45 #include <glib/gi18n.h>
46
47 #include "gclue-simple.h"
48 #include "gclue-helpers.h"
49
50 #define BUS_NAME "org.freedesktop.GeoClue2"
51
52 static void
53 gclue_simple_async_initable_init (GAsyncInitableIface *iface);
54
55 struct _GClueSimplePrivate
56 {
57 char *desktop_id;
58 GClueAccuracyLevel accuracy_level;
59
60 GClueClient *client;
61 GClueLocation *location;
62
63 gulong update_id;
64
65 GTask *task;
66 GCancellable *cancellable;
67 };
68
69 G_DEFINE_TYPE_WITH_CODE (GClueSimple,
70 gclue_simple,
71 G_TYPE_OBJECT,
72 G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
73 gclue_simple_async_initable_init)
74 G_ADD_PRIVATE (GClueSimple));
75
76 enum
77 {
78 PROP_0,
79 PROP_DESKTOP_ID,
80 PROP_ACCURACY_LEVEL,
81 PROP_CLIENT,
82 PROP_LOCATION,
83 LAST_PROP
84 };
85
86 static GParamSpec *gParamSpecs[LAST_PROP];
87
88 static void
gclue_simple_finalize(GObject * object)89 gclue_simple_finalize (GObject *object)
90 {
91 GClueSimplePrivate *priv = GCLUE_SIMPLE (object)->priv;
92
93 g_clear_pointer (&priv->desktop_id, g_free);
94 if (priv->update_id != 0) {
95 g_signal_handler_disconnect (priv->client, priv->update_id);
96 priv->update_id = 0;
97 }
98 if (priv->cancellable != NULL)
99 g_cancellable_cancel (priv->cancellable);
100 g_clear_object (&priv->cancellable);
101 g_clear_object (&priv->client);
102 g_clear_object (&priv->location);
103 g_clear_object (&priv->task);
104
105 /* Chain up to the parent class */
106 G_OBJECT_CLASS (gclue_simple_parent_class)->finalize (object);
107 }
108
109 static void
gclue_simple_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)110 gclue_simple_get_property (GObject *object,
111 guint prop_id,
112 GValue *value,
113 GParamSpec *pspec)
114 {
115 GClueSimple *simple = GCLUE_SIMPLE (object);
116
117 switch (prop_id) {
118 case PROP_CLIENT:
119 g_value_set_object (value, simple->priv->client);
120 break;
121
122 case PROP_LOCATION:
123 g_value_set_object (value, simple->priv->location);
124 break;
125
126 default:
127 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
128 }
129 }
130
131 static void
gclue_simple_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)132 gclue_simple_set_property (GObject *object,
133 guint prop_id,
134 const GValue *value,
135 GParamSpec *pspec)
136 {
137 GClueSimple *simple = GCLUE_SIMPLE (object);
138
139 switch (prop_id) {
140 case PROP_DESKTOP_ID:
141 simple->priv->desktop_id = g_value_dup_string (value);
142 break;
143
144 case PROP_ACCURACY_LEVEL:
145 simple->priv->accuracy_level = g_value_get_enum (value);
146 break;
147
148 default:
149 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
150 }
151 }
152
153 static void
gclue_simple_class_init(GClueSimpleClass * klass)154 gclue_simple_class_init (GClueSimpleClass *klass)
155 {
156 GObjectClass *object_class;
157
158 object_class = G_OBJECT_CLASS (klass);
159 object_class->finalize = gclue_simple_finalize;
160 object_class->get_property = gclue_simple_get_property;
161 object_class->set_property = gclue_simple_set_property;
162
163 /**
164 * GClueSimple:desktop-id:
165 *
166 * The Desktop ID of the application.
167 */
168 gParamSpecs[PROP_DESKTOP_ID] = g_param_spec_string ("desktop-id",
169 "DesktopID",
170 "Desktop ID",
171 NULL,
172 G_PARAM_WRITABLE |
173 G_PARAM_CONSTRUCT_ONLY);
174 g_object_class_install_property (object_class,
175 PROP_DESKTOP_ID,
176 gParamSpecs[PROP_DESKTOP_ID]);
177
178 /**
179 * GClueSimple:accuracy-level:
180 *
181 * The requested maximum accuracy level.
182 */
183 gParamSpecs[PROP_ACCURACY_LEVEL] = g_param_spec_enum ("accuracy-level",
184 "AccuracyLevel",
185 "Requested accuracy level",
186 GCLUE_TYPE_ACCURACY_LEVEL,
187 GCLUE_ACCURACY_LEVEL_NONE,
188 G_PARAM_WRITABLE |
189 G_PARAM_CONSTRUCT_ONLY);
190 g_object_class_install_property (object_class,
191 PROP_ACCURACY_LEVEL,
192 gParamSpecs[PROP_ACCURACY_LEVEL]);
193
194 /**
195 * GClueSimple:client:
196 *
197 * The client proxy.
198 */
199 gParamSpecs[PROP_CLIENT] = g_param_spec_object ("client",
200 "Client",
201 "Client proxy",
202 GCLUE_TYPE_CLIENT_PROXY,
203 G_PARAM_READABLE);
204 g_object_class_install_property (object_class,
205 PROP_CLIENT,
206 gParamSpecs[PROP_CLIENT]);
207
208 /**
209 * GClueSimple:location:
210 *
211 * The current location.
212 */
213 gParamSpecs[PROP_LOCATION] = g_param_spec_object ("location",
214 "Location",
215 "Location proxy",
216 GCLUE_TYPE_LOCATION_PROXY,
217 G_PARAM_READABLE);
218 g_object_class_install_property (object_class,
219 PROP_LOCATION,
220 gParamSpecs[PROP_LOCATION]);
221 }
222
223 static void
on_location_proxy_ready(GObject * source_object,GAsyncResult * res,gpointer user_data)224 on_location_proxy_ready (GObject *source_object,
225 GAsyncResult *res,
226 gpointer user_data)
227 {
228 GClueSimplePrivate *priv = GCLUE_SIMPLE (user_data)->priv;
229 GClueLocation *location;
230 GError *error = NULL;
231
232 location = gclue_location_proxy_new_for_bus_finish (res, &error);
233 if (error != NULL) {
234 if (priv->task != NULL) {
235 g_task_return_error (priv->task, error);
236 g_clear_object (&priv->task);
237 } else {
238 g_warning ("Failed to create location proxy: %s",
239 error->message);
240 }
241
242 return;
243 }
244 g_clear_object (&priv->location);
245 priv->location = location;
246
247 if (priv->task != NULL) {
248 g_task_return_boolean (priv->task, TRUE);
249 g_clear_object (&priv->task);
250 } else {
251 g_object_notify (G_OBJECT (user_data), "location");
252 }
253 }
254
255 static void
on_location_updated(GClueClient * client,const char * old_location,const char * new_location,gpointer user_data)256 on_location_updated (GClueClient *client,
257 const char *old_location,
258 const char *new_location,
259 gpointer user_data)
260 {
261 GClueSimplePrivate *priv = GCLUE_SIMPLE (user_data)->priv;
262
263 if (new_location == NULL || g_strcmp0 (new_location, "/") == 0)
264 return;
265
266 gclue_location_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
267 G_DBUS_PROXY_FLAGS_NONE,
268 BUS_NAME,
269 new_location,
270 priv->cancellable,
271 on_location_proxy_ready,
272 user_data);
273 }
274
275 static void
on_client_started(GObject * source_object,GAsyncResult * res,gpointer user_data)276 on_client_started (GObject *source_object,
277 GAsyncResult *res,
278 gpointer user_data)
279 {
280 GTask *task = G_TASK (user_data);
281 GClueClient *client = GCLUE_CLIENT (source_object);
282 GClueSimple *simple;
283 const char *location;
284 GError *error = NULL;
285
286 simple = g_task_get_source_object (task);
287
288 gclue_client_call_start_finish (client, res, &error);
289 if (error != NULL) {
290 g_task_return_error (task, error);
291 g_clear_object (&simple->priv->task);
292
293 return;
294 }
295
296 location = gclue_client_get_location (client);
297
298 on_location_updated (client, NULL, location, simple);
299 }
300
301 static void
on_client_created(GObject * source_object,GAsyncResult * res,gpointer user_data)302 on_client_created (GObject *source_object,
303 GAsyncResult *res,
304 gpointer user_data)
305 {
306 GTask *task = G_TASK (user_data);
307 GClueSimple *simple = g_task_get_source_object (task);
308 GClueSimplePrivate *priv = simple->priv;
309 GError *error = NULL;
310
311 priv->client = gclue_client_proxy_create_full_finish (res, &error);
312 if (error != NULL) {
313 g_task_return_error (task, error);
314 g_clear_object (&priv->task);
315
316 return;
317 }
318
319 priv->task = task;
320 priv->update_id =
321 g_signal_connect (priv->client,
322 "location-updated",
323 G_CALLBACK (on_location_updated),
324 simple);
325
326 gclue_client_call_start (priv->client,
327 g_task_get_cancellable (task),
328 on_client_started,
329 task);
330 }
331
332 static void
gclue_simple_init_async(GAsyncInitable * initable,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)333 gclue_simple_init_async (GAsyncInitable *initable,
334 int io_priority,
335 GCancellable *cancellable,
336 GAsyncReadyCallback callback,
337 gpointer user_data)
338 {
339 GTask *task;
340 GClueSimple *simple = GCLUE_SIMPLE (initable);
341
342 task = g_task_new (initable, cancellable, callback, user_data);
343
344 gclue_client_proxy_create_full (simple->priv->desktop_id,
345 simple->priv->accuracy_level,
346 GCLUE_CLIENT_PROXY_CREATE_AUTO_DELETE,
347 cancellable,
348 on_client_created,
349 task);
350 }
351
352 static gboolean
gclue_simple_init_finish(GAsyncInitable * initable,GAsyncResult * result,GError ** error)353 gclue_simple_init_finish (GAsyncInitable *initable,
354 GAsyncResult *result,
355 GError **error)
356 {
357 return g_task_propagate_boolean (G_TASK (result), error);
358 }
359
360 static void
gclue_simple_async_initable_init(GAsyncInitableIface * iface)361 gclue_simple_async_initable_init (GAsyncInitableIface *iface)
362 {
363 iface->init_async = gclue_simple_init_async;
364 iface->init_finish = gclue_simple_init_finish;
365 }
366
367 static void
gclue_simple_init(GClueSimple * simple)368 gclue_simple_init (GClueSimple *simple)
369 {
370 simple->priv = G_TYPE_INSTANCE_GET_PRIVATE (simple,
371 GCLUE_TYPE_SIMPLE,
372 GClueSimplePrivate);
373 simple->priv->cancellable = g_cancellable_new ();
374 }
375
376 /**
377 * gclue_simple_new:
378 * @desktop_id: The desktop file id (the basename of the desktop file).
379 * @accuracy_level: The requested accuracy level as #GClueAccuracyLevel.
380 * @cancellable: (allow-none): A #GCancellable or %NULL.
381 * @callback: A #GAsyncReadyCallback to call when the results are ready.
382 * @user_data: User data to pass to @callback.
383 *
384 * Asynchronously creates a #GClueSimple instance. Use
385 * #gclue_simple_new_finish() to get the created #GClueSimple instance.
386 *
387 * See #gclue_simple_new_sync() for the synchronous, blocking version
388 * of this function.
389 */
390 void
gclue_simple_new(const char * desktop_id,GClueAccuracyLevel accuracy_level,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)391 gclue_simple_new (const char *desktop_id,
392 GClueAccuracyLevel accuracy_level,
393 GCancellable *cancellable,
394 GAsyncReadyCallback callback,
395 gpointer user_data)
396 {
397 g_async_initable_new_async (GCLUE_TYPE_SIMPLE,
398 G_PRIORITY_DEFAULT,
399 cancellable,
400 callback,
401 user_data,
402 "desktop-id", desktop_id,
403 "accuracy-level", accuracy_level,
404 NULL);
405 }
406
407 /**
408 * gclue_simple_new_finish:
409 * @result: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
410 * #gclue_simple_new().
411 * @error: Return location for error or %NULL.
412 *
413 * Finishes an operation started with #gclue_simple_new().
414 *
415 * Returns: (transfer full) (type GClueSimple): The constructed proxy
416 * object or %NULL if @error is set.
417 */
418 GClueSimple *
gclue_simple_new_finish(GAsyncResult * result,GError ** error)419 gclue_simple_new_finish (GAsyncResult *result,
420 GError **error)
421 {
422 GObject *object;
423 GObject *source_object;
424
425 source_object = g_async_result_get_source_object (result);
426 object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
427 result,
428 error);
429 g_object_unref (source_object);
430 if (object != NULL)
431 return GCLUE_SIMPLE (object);
432 else
433 return NULL;
434 }
435
436 static void
on_simple_ready(GObject * source_object,GAsyncResult * res,gpointer user_data)437 on_simple_ready (GObject *source_object,
438 GAsyncResult *res,
439 gpointer user_data)
440 {
441 GClueSimple *simple;
442 GTask *task = G_TASK (user_data);
443 GMainLoop *main_loop;
444 GError *error = NULL;
445
446 simple = gclue_simple_new_finish (res, &error);
447 if (error != NULL) {
448 g_task_return_error (task, error);
449
450 goto out;
451 }
452
453 g_task_return_pointer (task, simple, g_object_unref);
454
455 out:
456 main_loop = g_task_get_task_data (task);
457 g_main_loop_quit (main_loop);
458 }
459
460 /**
461 * gclue_simple_new_sync:
462 * @desktop_id: The desktop file id (the basename of the desktop file).
463 * @accuracy_level: The requested accuracy level as #GClueAccuracyLevel.
464 * @cancellable: (allow-none): A #GCancellable or %NULL.
465 * @error: Return location for error or %NULL.
466 *
467 * The synchronous and blocking version of #gclue_simple_new().
468 *
469 * Returns: (transfer full) (type GClueSimple): The new #GClueSimple object or
470 * %NULL if @error is set.
471 */
472 GClueSimple *
gclue_simple_new_sync(const char * desktop_id,GClueAccuracyLevel accuracy_level,GCancellable * cancellable,GError ** error)473 gclue_simple_new_sync (const char *desktop_id,
474 GClueAccuracyLevel accuracy_level,
475 GCancellable *cancellable,
476 GError **error)
477 {
478 GClueSimple *simple;
479 GMainLoop *main_loop;
480 GTask *task;
481
482 task = g_task_new (NULL, cancellable, NULL, NULL);
483 main_loop = g_main_loop_new (NULL, FALSE);
484 g_task_set_task_data (task,
485 main_loop,
486 (GDestroyNotify) g_main_loop_unref);
487
488 gclue_simple_new (desktop_id,
489 accuracy_level,
490 cancellable,
491 on_simple_ready,
492 task);
493
494 g_main_loop_run (main_loop);
495
496 simple = g_task_propagate_pointer (task, error);
497
498 g_object_unref (task);
499
500 return simple;
501 }
502 /**
503 * gclue_simple_get_client:
504 * @simple: A #GClueSimple object.
505 *
506 * Gets the client proxy.
507 *
508 * Returns: (transfer none) (type GClueClientProxy): The client object.
509 */
510 GClueClient *
gclue_simple_get_client(GClueSimple * simple)511 gclue_simple_get_client (GClueSimple *simple)
512 {
513 g_return_val_if_fail (GCLUE_IS_SIMPLE(simple), NULL);
514
515 return simple->priv->client;
516 }
517
518 /**
519 * gclue_simple_get_location:
520 * @simple: A #GClueSimple object.
521 *
522 * Gets the current location.
523 *
524 * Returns: (transfer none) (type GClueLocationProxy): The last known location
525 * as #GClueLocation.
526 */
527 GClueLocation *
gclue_simple_get_location(GClueSimple * simple)528 gclue_simple_get_location (GClueSimple *simple)
529 {
530 g_return_val_if_fail (GCLUE_IS_SIMPLE(simple), NULL);
531
532 return simple->priv->location;
533 }
534