diff options
author | Matthias Clasen <mclasen@redhat.com> | 2018-08-23 02:15:18 +0000 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2019-11-14 10:52:46 +0100 |
commit | c9350715e42f8bdf01ccd6a581f8560d7ba9e5dd (patch) | |
tree | 63f90c3762e336ca64692df439284493c6b5115d /libgeoclue | |
parent | 799489262253e8d622ce4c72572f0d48afeb238d (diff) | |
download | geoclue-c9350715e42f8bdf01ccd6a581f8560d7ba9e5dd.tar.gz |
gclue-simple: Add portal support
When running inside a flatpak sandbox, or when GTK_USE_PORTAL
is set in the environment, talk to the location portal instead
of GeoClue directly.
In this situation, gclue_simple_get_client will return NULL.
Diffstat (limited to 'libgeoclue')
-rw-r--r-- | libgeoclue/gclue-simple.c | 272 | ||||
-rw-r--r-- | libgeoclue/meson.build | 9 | ||||
-rw-r--r-- | libgeoclue/org.freedesktop.portal.Location.xml | 168 |
3 files changed, 441 insertions, 8 deletions
diff --git a/libgeoclue/gclue-simple.c b/libgeoclue/gclue-simple.c index 33958e0..039f30f 100644 --- a/libgeoclue/gclue-simple.c +++ b/libgeoclue/gclue-simple.c @@ -46,6 +46,7 @@ #include "gclue-simple.h" #include "gclue-helpers.h" +#include "xdp-location.h" #define BUS_NAME "org.freedesktop.GeoClue2" @@ -64,6 +65,12 @@ struct _GClueSimplePrivate GTask *task; GCancellable *cancellable; + + char *sender; + XdpLocation *portal; + gulong location_updated_id; + guint response_id; + char *session_id; }; G_DEFINE_TYPE_WITH_CODE (GClueSimple, @@ -85,6 +92,8 @@ enum static GParamSpec *gParamSpecs[LAST_PROP]; +static void clear_portal (GClueSimple *simple); + static void gclue_simple_finalize (GObject *object) { @@ -102,6 +111,8 @@ gclue_simple_finalize (GObject *object) g_clear_object (&priv->location); g_clear_object (&priv->task); + clear_portal (GCLUE_SIMPLE (object)); + /* Chain up to the parent class */ G_OBJECT_CLASS (gclue_simple_parent_class)->finalize (object); } @@ -329,6 +340,240 @@ on_client_created (GObject *source_object, task); } +/* We use the portal if we are inside a flatpak, + * or if GTK_USE_PORTAL is set in the environment. + */ +static gboolean +should_use_portal (void) +{ + static const char *use_portal = NULL; + + if (G_UNLIKELY (use_portal == NULL)) + { + if (g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS)) + use_portal = "1"; + else + { + use_portal = g_getenv ("GTK_USE_PORTAL"); + if (!use_portal) + use_portal = ""; + } + } + + return use_portal[0] == '1'; +} + +#define PORTAL_BUS_NAME "org.freedesktop.portal.Desktop" +#define PORTAL_OBJECT_PATH "/org/freedesktop/portal/desktop" +#define PORTAL_REQUEST_INTERFACE "org.freedesktop.portal.Request" +#define PORTAL_SESSION_INTERFACE "org.freedesktop.portal.Session" + +static void +clear_portal (GClueSimple *simple) +{ + GClueSimplePrivate *priv = simple->priv; + + if (priv->portal) { + GDBusConnection *bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (priv->portal)); + + if (priv->session_id) + g_dbus_connection_call (bus, + PORTAL_BUS_NAME, + priv->session_id, + PORTAL_SESSION_INTERFACE, + "Close", + NULL, NULL, 0, -1, NULL, NULL, NULL); + + if (priv->response_id) { + g_dbus_connection_signal_unsubscribe (bus, priv->response_id); + priv->response_id = 0; + } + } + + g_clear_object (&priv->portal); + priv->location_updated_id = 0; + g_clear_pointer (&priv->session_id, g_free); + g_clear_pointer (&priv->sender, g_free); +} + +static void +on_portal_location_updated (XdpLocation *portal, + const char *session_handle, + GVariant *data, + gpointer user_data) +{ + GClueSimple *simple = user_data; + GClueSimplePrivate *priv = simple->priv; + double latitude; + double longitude; + double altitude; + double accuracy; + double speed; + double heading; + const char *description; + GVariant *timestamp; + GClueLocation *location = gclue_location_skeleton_new (); + + g_variant_lookup (data, "Latitude", "d", &latitude); + g_variant_lookup (data, "Longitude", "d", &longitude); + g_variant_lookup (data, "Altitude", "d", &altitude); + g_variant_lookup (data, "Accuracy", "d", &accuracy); + g_variant_lookup (data, "Speed", "d", &speed); + g_variant_lookup (data, "Heading", "d", &heading); + g_variant_lookup (data, "Description", "&s", &description); + g_variant_lookup (data, "Timestamp", "@(tt)", ×tamp); + + gclue_location_set_latitude (location, latitude); + gclue_location_set_longitude (location, longitude); + gclue_location_set_altitude (location, altitude); + gclue_location_set_accuracy (location, accuracy); + gclue_location_set_speed (location, speed); + gclue_location_set_heading (location, heading); + gclue_location_set_description (location, description); + gclue_location_set_timestamp (location, timestamp); + + g_set_object (&priv->location, location); + + if (priv->task) { + g_task_return_boolean (priv->task, TRUE); + g_clear_object (&priv->task); + } + else { + g_object_notify (G_OBJECT (simple), "location"); + } + + g_object_unref (location); +} + +static void +on_started (GDBusConnection *bus, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + GClueSimple *simple = user_data; + GClueSimplePrivate *priv = simple->priv; + guint32 response; + g_autoptr(GVariant) ret = NULL; + + g_dbus_connection_signal_unsubscribe (bus, priv->response_id); + priv->response_id = 0; + + g_variant_get (parameters, "(u@a{sv})", &response, &ret); + + if (response != 0) { + clear_portal (simple); + g_task_return_new_error (priv->task, G_IO_ERROR, G_IO_ERROR_FAILED, "Start failed"); + g_clear_object (&priv->task); + } +} + +static void +on_session_created (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GTask *task = G_TASK (user_data); + GClueSimple *simple = g_task_get_source_object (task); + GClueSimplePrivate *priv = simple->priv; + GDBusConnection *bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (priv->portal)); + GError *error = NULL; + g_autofree char *handle = NULL; + g_autofree char *token = NULL; + g_autofree char *request_path = NULL; + GVariantBuilder options; + + if (!xdp_location_call_create_session_finish (priv->portal, &handle, result, &error)) { + clear_portal (simple); + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + if (!g_str_equal (handle, priv->session_id)) { + clear_portal (simple); + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Unexpected session id"); + g_object_unref (task); + return; + } + + priv->task = task; + + token = g_strdup_printf ("geoclue%d", g_random_int_range (0, G_MAXINT)); + request_path = g_strconcat (PORTAL_OBJECT_PATH, "/request/", priv->sender, "/", token, NULL); + priv->response_id = g_dbus_connection_signal_subscribe (bus, + PORTAL_BUS_NAME, + PORTAL_REQUEST_INTERFACE, + "Response", + request_path, + NULL, + G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, + on_started, + simple, + NULL); + + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&options, "{sv}", "handle_token", g_variant_new_string (token)); + xdp_location_call_start (priv->portal, + priv->session_id, + "", /* FIXME parent window */ + g_variant_builder_end (&options), + NULL, NULL, NULL); +} + +static void +on_portal_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; + GDBusConnection *bus; + GError *error = NULL; + int i; + g_autofree char *session_token = NULL; + GVariantBuilder options; + + priv->portal = xdp_location_proxy_new_for_bus_finish (res, &error); + + if (error != NULL) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (priv->portal)); + + priv->location_updated_id = g_signal_connect (priv->portal, + "location-updated", + G_CALLBACK (on_portal_location_updated), + simple); + + priv->sender = g_strdup (g_dbus_connection_get_unique_name (bus) + 1); + for (i = 0; priv->sender[i]; i++) + if (priv->sender[i] == '.') + priv->sender[i] = '_'; + + session_token = g_strdup_printf ("geoclue%d", g_random_int_range (0, G_MAXINT)); + priv->session_id = g_strconcat (PORTAL_OBJECT_PATH, "/session/", priv->sender, "/", + session_token, NULL); + + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&options, "{sv}", + "session_handle_token", g_variant_new_string (session_token)); + g_variant_builder_add (&options, "{sv}", "distance-threshold", g_variant_new_uint32 (0)); + g_variant_builder_add (&options, "{sv}", "time-threshold", g_variant_new_uint32 (0)); + g_variant_builder_add (&options, "{sv}", "accuracy", g_variant_new_uint32 (0)); + + xdp_location_call_create_session (priv->portal, + g_variant_builder_end (&options), + NULL, on_session_created, task); +} + static void gclue_simple_init_async (GAsyncInitable *initable, int io_priority, @@ -341,12 +586,23 @@ gclue_simple_init_async (GAsyncInitable *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); + if (should_use_portal ()) { + xdp_location_proxy_new_for_bus (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + PORTAL_BUS_NAME, + PORTAL_OBJECT_PATH, + cancellable, + on_portal_created, + task); + } + else { + 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 @@ -503,7 +759,7 @@ gclue_simple_new_sync (const char *desktop_id, * gclue_simple_get_client: * @simple: A #GClueSimple object. * - * Gets the client proxy. + * Gets the client proxy, or %NULL if @simple is not using a client proxy. * * Returns: (transfer none) (type GClueClientProxy): The client object. */ @@ -521,7 +777,7 @@ gclue_simple_get_client (GClueSimple *simple) * * Gets the current location. * - * Returns: (transfer none) (type GClueLocationProxy): The last known location + * Returns: (transfer none) (type GClueLocation): The last known location * as #GClueLocation. */ GClueLocation * diff --git a/libgeoclue/meson.build b/libgeoclue/meson.build index e228ec3..b9645ee 100644 --- a/libgeoclue/meson.build +++ b/libgeoclue/meson.build @@ -25,11 +25,20 @@ gclue_manager = gnome.gdbus_codegen( install_header: true, install_dir: include_subdir) +# Location portal interface +location_portal = gnome.gdbus_codegen('xdp-location', + 'org.freedesktop.portal.Location.xml', + interface_prefix: 'org.freedesktop.portal.', + namespace: 'Xdp', + install_header: false) + libgeoclue_sources = files('gclue-helpers.c', 'gclue-simple.c') libgeoclue_sources += gclue_client[0] libgeoclue_sources += gclue_location[0] libgeoclue_sources += gclue_manager[0] libgeoclue_sources += libgeoclue_public_api_gen_sources[1] +libgeoclue_sources += location_portal[0] +libgeoclue_sources += location_portal[1] libgeoclue_headers = files('geoclue.h', 'gclue-helpers.h', 'gclue-simple.h') diff --git a/libgeoclue/org.freedesktop.portal.Location.xml b/libgeoclue/org.freedesktop.portal.Location.xml new file mode 100644 index 0000000..0695f4b --- /dev/null +++ b/libgeoclue/org.freedesktop.portal.Location.xml @@ -0,0 +1,168 @@ +<?xml version="1.0"?> +<!-- + Copyright (C) 2018 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 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, see <http://www.gnu.org/licenses/>. + + Author: Matthias Clasen <mclasen@redhat.com> +--> + +<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <!-- + org.freedesktop.portal.Location: + @short_description: Portal for obtaining information about the location + + This simple interface lets sandboxed applications query basic + information about the location. + + This documentation describes version 1 of this interface. + --> + <interface name="org.freedesktop.portal.Location"> + <!-- + CreateSession: + @options: Vardict with optional further information + @handle: Object path for the created #org.freedesktop.portal.Session object + + Create a location session. A successfully created session can at + any time be closed using org.freedesktop.portal.Session::Close, or may + at any time be closed by the portal implementation, which will be + signalled via org.freedesktop.portal.Session::Closed. +` + Supported keys in the @options vardict include: + <variablelist> + <varlistentry> + <term>session_handle_token s</term> + <listitem><para> + A string that will be used as the last element of the session handle. Must be a valid + object path element. See the #org.freedesktop.portal.Session documentation for + more information about the session handle. + </para></listitem> + </varlistentry> + <varlistentry> + <term>distance-threshold u</term> + <listitem><para> + Distance threshold in meters. Default is 0. + </para></listitem> + </varlistentry> + <varlistentry> + <term>time-threshold u</term> + <listitem><para> + Time threshold in seconds. Default is 0. + </para></listitem> + </varlistentry> + <varlistentry> + <term>accuracy u</term> + <listitem><para> + Requested accuracy. Default is EXACT. + Values: NONE 0, COUNTRY 1, CITY 2, NEIGHBORHOOD 3, STREET 4, EXACT 5 + </para></listitem> + </varlistentry> + </variablelist> + --> + <method name="CreateSession"> + <arg type="a{sv}" name="options" direction="in"/> + <arg type="o" name="handle" direction="out"/> + </method> + + <!-- + Start: + @session_handle: Object path for the #org.freedesktop.portal.Session object + @parent_window: Identifier for the application window, see <link linkend="parent_window">Common Conventions</link> + @options: Vardict with optional further information + @handle: Object path for the #org.freedesktop.portal.Request object representing this call + + Start the location session. + An application can only attempt start a session once. + + Supported keys in the @options vardict include: + <variablelist> + <varlistentry> + <term>handle_token s</term> + <listitem><para> + A string that will be used as the last element of the @handle. Must be a valid + object path element. See the #org.freedesktop.portal.Request documentation for + more information about the @handle. + </para></listitem> + </varlistentry> + </variablelist> + --> + <method name="Start"> + <arg type="o" name="session_handle" direction="in"/> + <arg type="s" name="parent_window" direction="in"/> + <arg type="a{sv}" name="options" direction="in"/> + <arg type="o" name="handle" direction="out"/> + </method> + + <!-- + LocationUpdated: + @session_handle: Object path for the #org.freedesktop.portal.Session object + @location: Vardict with the current location data + + The LocationUpdated signal is emitted when the location has changed, as well + as when the initial location has been determined. + + The following results may get returned via the @location: + <variablelist> + <varlistentry> + <term>latitude d</term> + <listitem><para> + The latitude, in degrees. + </para></listitem> + </varlistentry> + <varlistentry> + <term>longitude d</term> + <listitem><para> + The longitude, in degrees. + </para></listitem> + </varlistentry> + <varlistentry> + <term>altitude d</term> + <listitem><para> + The altitude, in meters. + </para></listitem> + </varlistentry> + <varlistentry> + <term>accuracy d</term> + <listitem><para> + The accuracy, in meters. + </para></listitem> + </varlistentry> + <varlistentry> + <term>speed d</term> + <listitem><para> + The speed, in meters per second. + </para></listitem> + </varlistentry> + <varlistentry> + <term>heading d</term> + <listitem><para> + The heading, in degrees, going clockwise. North 0, East 90, South 180, West 270. + </para></listitem> + </varlistentry> + <varlistentry> + <term>timestamp (tt)</term> + <listitem><para> + The timestamp, as seconds and microsections since the Unix epoch. + </para></listitem> + </varlistentry> + </variablelist> + --> + <signal name="LocationUpdated"> + <arg type="o" name="session_handle" direction="out"/> + <arg type="a{sv}" name="location" direction="out"/> + </signal> + + <property name="version" type="u" access="read"/> + </interface> +</node> |