/* vim: set et ts=8 sw=8: */ /* * Geoclue convenience library. * * Copyright 2015 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Author: Zeeshan Ali (Khattak) */ /** * SECTION: gclue-simple * @title: GClueSimple * @short_description: Simplified convenience API * * #GClueSimple make it very simple to get latest location and monitoring * location updates. It takes care of the boring tasks of creating a * #GClueClientProxy instance, starting it, waiting till we have a location fix * and then creating a #GClueLocationProxy instance for it. * * Use #gclue_simple_new() or #gclue_simple_new_sync() to create a new * #GClueSimple instance. Once you have a #GClueSimple instance, you can get the * latest location using #gclue_simple_get_location() or reading the * #GClueSimple:location property. To monitor location updates, connect to * notify signal for this property. * * While most applications will find this API very useful, it is most * useful for applications that simply want to get the current location as * quickly as possible and do not care about accuracy (much). */ #include #include "gclue-simple.h" #include "gclue-helpers.h" #define BUS_NAME "org.freedesktop.GeoClue2" static void gclue_simple_async_initable_init (GAsyncInitableIface *iface); struct _GClueSimplePrivate { char *desktop_id; GClueAccuracyLevel accuracy_level; GClueClient *client; GClueLocation *location; gulong update_id; GTask *task; GCancellable *cancellable; }; G_DEFINE_TYPE_WITH_CODE (GClueSimple, gclue_simple, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, gclue_simple_async_initable_init) G_ADD_PRIVATE (GClueSimple)); enum { PROP_0, PROP_DESKTOP_ID, PROP_ACCURACY_LEVEL, PROP_CLIENT, PROP_LOCATION, LAST_PROP }; static GParamSpec *gParamSpecs[LAST_PROP]; static void gclue_simple_finalize (GObject *object) { GClueSimplePrivate *priv = GCLUE_SIMPLE (object)->priv; g_clear_pointer (&priv->desktop_id, g_free); if (priv->update_id != 0) { g_signal_handler_disconnect (priv->client, priv->update_id); priv->update_id = 0; } if (priv->cancellable != NULL) g_cancellable_cancel (priv->cancellable); g_clear_object (&priv->cancellable); g_clear_object (&priv->client); g_clear_object (&priv->location); g_clear_object (&priv->task); /* Chain up to the parent class */ G_OBJECT_CLASS (gclue_simple_parent_class)->finalize (object); } static void gclue_simple_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GClueSimple *simple = GCLUE_SIMPLE (object); switch (prop_id) { case PROP_CLIENT: g_value_set_object (value, simple->priv->client); break; case PROP_LOCATION: g_value_set_object (value, simple->priv->location); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void gclue_simple_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GClueSimple *simple = GCLUE_SIMPLE (object); switch (prop_id) { case PROP_DESKTOP_ID: simple->priv->desktop_id = g_value_dup_string (value); break; case PROP_ACCURACY_LEVEL: simple->priv->accuracy_level = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void gclue_simple_class_init (GClueSimpleClass *klass) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); object_class->finalize = gclue_simple_finalize; object_class->get_property = gclue_simple_get_property; object_class->set_property = gclue_simple_set_property; /** * GClueSimple:desktop-id: * * The Desktop ID of the application. */ gParamSpecs[PROP_DESKTOP_ID] = g_param_spec_string ("desktop-id", "DesktopID", "Desktop ID", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_DESKTOP_ID, gParamSpecs[PROP_DESKTOP_ID]); /** * GClueSimple:accuracy-level: * * The requested maximum accuracy level. */ gParamSpecs[PROP_ACCURACY_LEVEL] = g_param_spec_enum ("accuracy-level", "AccuracyLevel", "Requested accuracy level", GCLUE_TYPE_ACCURACY_LEVEL, GCLUE_ACCURACY_LEVEL_NONE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_ACCURACY_LEVEL, gParamSpecs[PROP_ACCURACY_LEVEL]); /** * GClueSimple:client: * * The client proxy. */ gParamSpecs[PROP_CLIENT] = g_param_spec_object ("client", "Client", "Client proxy", GCLUE_TYPE_CLIENT_PROXY, G_PARAM_READABLE); g_object_class_install_property (object_class, PROP_CLIENT, gParamSpecs[PROP_CLIENT]); /** * GClueSimple:location: * * The current location. */ gParamSpecs[PROP_LOCATION] = g_param_spec_object ("location", "Location", "Location proxy", GCLUE_TYPE_LOCATION_PROXY, G_PARAM_READABLE); g_object_class_install_property (object_class, PROP_LOCATION, gParamSpecs[PROP_LOCATION]); } static void on_location_proxy_ready (GObject *source_object, GAsyncResult *res, gpointer user_data) { GClueSimplePrivate *priv = GCLUE_SIMPLE (user_data)->priv; GClueLocation *location; GError *error = NULL; location = gclue_location_proxy_new_for_bus_finish (res, &error); if (error != NULL) { if (priv->task != NULL) { g_task_return_error (priv->task, error); g_clear_object (&priv->task); } else { g_warning ("Failed to create location proxy: %s", error->message); } return; } g_clear_object (&priv->location); priv->location = location; if (priv->task != NULL) { g_task_return_boolean (priv->task, TRUE); g_clear_object (&priv->task); } else { g_object_notify (G_OBJECT (user_data), "location"); } } static void on_location_updated (GClueClient *client, const char *old_location, const char *new_location, gpointer user_data) { GClueSimplePrivate *priv = GCLUE_SIMPLE (user_data)->priv; if (new_location == NULL || g_strcmp0 (new_location, "/") == 0) return; gclue_location_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, BUS_NAME, new_location, priv->cancellable, on_location_proxy_ready, user_data); } static void on_client_started (GObject *source_object, GAsyncResult *res, gpointer user_data) { GTask *task = G_TASK (user_data); GClueClient *client = GCLUE_CLIENT (source_object); GClueSimple *simple; const char *location; GError *error = NULL; simple = g_task_get_source_object (task); gclue_client_call_start_finish (client, res, &error); if (error != NULL) { g_task_return_error (task, error); g_clear_object (&simple->priv->task); return; } location = gclue_client_get_location (client); on_location_updated (client, NULL, location, simple); } static void on_client_created (GObject *source_object, GAsyncResult *res, gpointer user_data) { GTask *task = G_TASK (user_data); GClueSimple *simple = g_task_get_source_object (task); GClueSimplePrivate *priv = simple->priv; GError *error = NULL; priv->client = gclue_client_proxy_create_full_finish (res, &error); if (error != NULL) { g_task_return_error (task, error); g_clear_object (&priv->task); return; } priv->task = task; priv->update_id = g_signal_connect (priv->client, "location-updated", G_CALLBACK (on_location_updated), simple); gclue_client_call_start (priv->client, g_task_get_cancellable (task), on_client_started, task); } static void gclue_simple_init_async (GAsyncInitable *initable, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; GClueSimple *simple = GCLUE_SIMPLE (initable); task = g_task_new (initable, cancellable, callback, user_data); gclue_client_proxy_create_full (simple->priv->desktop_id, simple->priv->accuracy_level, GCLUE_CLIENT_PROXY_CREATE_AUTO_DELETE, cancellable, on_client_created, task); } static gboolean gclue_simple_init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) { return g_task_propagate_boolean (G_TASK (result), error); } static void gclue_simple_async_initable_init (GAsyncInitableIface *iface) { iface->init_async = gclue_simple_init_async; iface->init_finish = gclue_simple_init_finish; } static void gclue_simple_init (GClueSimple *simple) { simple->priv = G_TYPE_INSTANCE_GET_PRIVATE (simple, GCLUE_TYPE_SIMPLE, GClueSimplePrivate); simple->priv->cancellable = g_cancellable_new (); } /** * gclue_simple_new: * @desktop_id: The desktop file id (the basename of the desktop file). * @accuracy_level: The requested accuracy level as #GClueAccuracyLevel. * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the results are ready. * @user_data: User data to pass to @callback. * * Asynchronously creates a #GClueSimple instance. Use * #gclue_simple_new_finish() to get the created #GClueSimple instance. * * See #gclue_simple_new_sync() for the synchronous, blocking version * of this function. */ void gclue_simple_new (const char *desktop_id, GClueAccuracyLevel accuracy_level, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_async_initable_new_async (GCLUE_TYPE_SIMPLE, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "desktop-id", desktop_id, "accuracy-level", accuracy_level, NULL); } /** * gclue_simple_new_finish: * @result: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to * #gclue_simple_new(). * @error: Return location for error or %NULL. * * Finishes an operation started with #gclue_simple_new(). * * Returns: (transfer full) (type GClueSimple): The constructed proxy * object or %NULL if @error is set. */ GClueSimple * gclue_simple_new_finish (GAsyncResult *result, GError **error) { GObject *object; GObject *source_object; source_object = g_async_result_get_source_object (result); object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), result, error); g_object_unref (source_object); if (object != NULL) return GCLUE_SIMPLE (object); else return NULL; } static void on_simple_ready (GObject *source_object, GAsyncResult *res, gpointer user_data) { GClueSimple *simple; GTask *task = G_TASK (user_data); GMainLoop *main_loop; GError *error = NULL; simple = gclue_simple_new_finish (res, &error); if (error != NULL) { g_task_return_error (task, error); goto out; } g_task_return_pointer (task, simple, g_object_unref); out: main_loop = g_task_get_task_data (task); g_main_loop_quit (main_loop); } /** * gclue_simple_new_sync: * @desktop_id: The desktop file id (the basename of the desktop file). * @accuracy_level: The requested accuracy level as #GClueAccuracyLevel. * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * The synchronous and blocking version of #gclue_simple_new(). * * Returns: (transfer full) (type GClueSimple): The new #GClueSimple object or * %NULL if @error is set. */ GClueSimple * gclue_simple_new_sync (const char *desktop_id, GClueAccuracyLevel accuracy_level, GCancellable *cancellable, GError **error) { GClueSimple *simple; GMainLoop *main_loop; GTask *task; task = g_task_new (NULL, cancellable, NULL, NULL); main_loop = g_main_loop_new (NULL, FALSE); g_task_set_task_data (task, main_loop, (GDestroyNotify) g_main_loop_unref); gclue_simple_new (desktop_id, accuracy_level, cancellable, on_simple_ready, task); g_main_loop_run (main_loop); simple = g_task_propagate_pointer (task, error); g_object_unref (task); return simple; } /** * gclue_simple_get_client: * @simple: A #GClueSimple object. * * Gets the client proxy. * * Returns: (transfer none) (type GClueClientProxy): The client object. */ GClueClient * gclue_simple_get_client (GClueSimple *simple) { g_return_val_if_fail (GCLUE_IS_SIMPLE(simple), NULL); return simple->priv->client; } /** * gclue_simple_get_location: * @simple: A #GClueSimple object. * * Gets the current location. * * Returns: (transfer none) (type GClueLocationProxy): The last known location * as #GClueLocation. */ GClueLocation * gclue_simple_get_location (GClueSimple *simple) { g_return_val_if_fail (GCLUE_IS_SIMPLE(simple), NULL); return simple->priv->location; }