summaryrefslogtreecommitdiff
path: root/libgeoclue
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2018-08-23 02:15:18 +0000
committerMatthias Clasen <mclasen@redhat.com>2019-11-14 10:52:46 +0100
commitc9350715e42f8bdf01ccd6a581f8560d7ba9e5dd (patch)
tree63f90c3762e336ca64692df439284493c6b5115d /libgeoclue
parent799489262253e8d622ce4c72572f0d48afeb238d (diff)
downloadgeoclue-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.c272
-rw-r--r--libgeoclue/meson.build9
-rw-r--r--libgeoclue/org.freedesktop.portal.Location.xml168
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)", &timestamp);
+
+ 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>