2 * position-publisher.c - Source for PositionPublisher
3 * Copyright (C) 2010 Guillaume Desmottes
4 * @author Guillaume Desmottes <gdesmott@gnome.org>
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 License, or (at your option) any later version.
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.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 #include <telepathy-glib/dbus.h>
26 #include <telepathy-glib/interfaces.h>
28 #include <location/location-gps-device.h>
29 #include <location/location-gpsd-control.h>
31 #include "connection-watcher.h"
32 #include "position-publisher.h"
34 G_DEFINE_TYPE(PositionPublisher, position_publisher, G_TYPE_OBJECT)
44 /* Minimum time before 2 publishing (in seconds) */
45 #define PUBLISH_THROTTLE 10
47 /* private structure */
48 typedef struct _PositionPublisherPrivate PositionPublisherPrivate;
50 struct _PositionPublisherPrivate
52 ConnectionWatcher *watcher;
53 LocationGPSDControl *gps_control;
54 LocationGPSDevice *gps_device;
57 /* If not 0, we are waiting before publishing again */
58 guint throttle_timeout;
59 /* TRUE if location has been modified while we were waiting */
64 gboolean dispose_has_run;
67 #define POSITION_PUBLISHER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), POSITION_PUBLISHER_TYPE, PositionPublisherPrivate))
69 static void conn_invalidated_cb (TpProxy *conn,
73 PositionPublisher *self);
75 static void publish_to_all (PositionPublisher *self);
78 remove_connection (PositionPublisher *self,
81 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
83 g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (conn_invalidated_cb),
85 priv->connections = g_slist_remove (priv->connections, conn);
86 g_object_unref (conn);
90 set_location_cb (TpConnection *conn,
95 PositionPublisher *self = POSITION_PUBLISHER (weak_object);
99 g_print ("SetLocation failed (%s): %s\n", tp_proxy_get_object_path (conn),
102 if (error->code == TP_ERROR_NOT_IMPLEMENTED)
104 g_print ("remove connection\n");
105 remove_connection (self, conn);
111 g_print ("SetLocation succeed (%s)\n", tp_proxy_get_object_path (conn));
115 publish_throttle_timeout_cb (gpointer data)
117 PositionPublisher *self = data;
118 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
120 priv->throttle_timeout = 0;
124 publish_to_all (self);
125 priv->modified = FALSE;
132 publish_to_conn (PositionPublisher *self,
135 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
137 if (priv->location == NULL)
140 if (priv->throttle_timeout != 0)
144 tp_cli_connection_interface_location_call_set_location (conn, -1,
145 priv->location, set_location_cb, NULL, NULL, G_OBJECT (self));
147 /* We won't publish during the next PUBLISH_THROTTLE seconds */
148 priv->throttle_timeout = g_timeout_add_seconds (PUBLISH_THROTTLE,
149 publish_throttle_timeout_cb, self);
153 publish_to_all (PositionPublisher *self)
155 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
158 for (l = priv->connections; l != NULL; l = g_slist_next (l))
160 TpConnection *conn = l->data;
162 publish_to_conn (self, conn);
167 update_position (PositionPublisher *self,
173 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
175 g_print ("update position: lat: %f lon: %f alt: %f accuracy: %f\n",
176 lat, lon, alt, accuracy);
178 if (priv->location != NULL)
179 g_hash_table_unref (priv->location);
181 priv->location = tp_asv_new (
182 "timestamp", G_TYPE_INT64, (gint64) time (NULL),
183 "lat", G_TYPE_DOUBLE, lat,
184 "lon", G_TYPE_DOUBLE, lon,
185 "alt", G_TYPE_DOUBLE, alt,
186 "accuracy", G_TYPE_DOUBLE, accuracy,
189 priv->modified = TRUE;
191 publish_to_all (self);
195 location_changed_cb (LocationGPSDevice *device,
196 PositionPublisher *self)
201 if (device->fix == NULL)
204 if (!(device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET))
207 update_position (self, device->fix->latitude, device->fix->longitude,
208 device->fix->altitude, device->fix->eph / 100.0);
212 conn_invalidated_cb (TpProxy *conn,
216 PositionPublisher *self)
218 g_print ("connection %s invalidated; removing\n", tp_proxy_get_object_path (
221 remove_connection (self, TP_CONNECTION (conn));
225 connection_added_cb (ConnectionWatcher *watcher,
227 PositionPublisher *self)
229 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
231 if (g_slist_find (priv->connections, conn) != NULL)
234 if (!tp_proxy_has_interface_by_id (conn,
235 TP_IFACE_QUARK_CONNECTION_INTERFACE_LOCATION))
238 priv->connections = g_slist_prepend (priv->connections, g_object_ref (conn));
240 g_signal_connect (conn, "invalidated",
241 G_CALLBACK (conn_invalidated_cb), self);
243 publish_to_conn (self, conn);
247 position_publisher_init (PositionPublisher *obj)
249 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (obj);
251 priv->watcher = connection_watcher_new ();
253 g_signal_connect (priv->watcher, "connection-added",
254 G_CALLBACK (connection_added_cb), obj);
256 priv->gps_control = location_gpsd_control_get_default();
258 g_object_set (G_OBJECT(priv->gps_control),
259 "preferred-method", LOCATION_METHOD_USER_SELECTED,
260 "preferred-interval", LOCATION_INTERVAL_120S,
263 priv->gps_device = g_object_new (LOCATION_TYPE_GPS_DEVICE, NULL);
265 g_signal_connect (priv->gps_device, "changed",
266 G_CALLBACK (location_changed_cb), obj);
268 priv->connections = NULL;
272 position_publisher_constructed (GObject *object)
274 PositionPublisher *self = POSITION_PUBLISHER (object);
275 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
277 connection_watcher_start (priv->watcher);
279 if (G_OBJECT_CLASS (position_publisher_parent_class)->constructed)
280 G_OBJECT_CLASS (position_publisher_parent_class)->constructed (object);
283 static void position_publisher_dispose (GObject *object);
284 static void position_publisher_finalize (GObject *object);
285 static void position_publisher_get_property (GObject *object,
286 guint property_id, GValue *value, GParamSpec *pspec);
287 static void position_publisher_set_property (GObject *object,
288 guint property_id, const GValue *value, GParamSpec *pspec);
291 position_publisher_class_init (PositionPublisherClass *position_publisher_class)
293 GObjectClass *object_class = G_OBJECT_CLASS (position_publisher_class);
294 GParamSpec *param_spec;
296 g_type_class_add_private (position_publisher_class, sizeof (PositionPublisherPrivate));
298 object_class->dispose = position_publisher_dispose;
299 object_class->finalize = position_publisher_finalize;
300 object_class->get_property = position_publisher_get_property;
301 object_class->set_property = position_publisher_set_property;
303 object_class->constructed = position_publisher_constructed;
305 param_spec = g_param_spec_boolean ("blur", "Blur?",
306 "Whether the real GPS position is truncated for privacy concerns.", TRUE,
307 G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE
308 | G_PARAM_STATIC_STRINGS);
309 g_object_class_install_property (object_class, PROP_BLUR, param_spec);
311 param_spec = g_param_spec_boolean ("start-gps", "Start GPS?",
312 "Whether the publisher starts the GPS.", FALSE,
313 G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE
314 | G_PARAM_STATIC_STRINGS);
315 g_object_class_install_property (object_class, PROP_START_GPS, param_spec);
319 position_publisher_dispose (GObject *object)
321 PositionPublisher *self = POSITION_PUBLISHER (object);
322 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
325 if (priv->dispose_has_run)
328 priv->dispose_has_run = TRUE;
332 location_gpsd_control_stop (priv->gps_control);
333 priv->start_gps = FALSE;
336 g_object_unref (priv->watcher);
337 g_object_unref (priv->gps_control);
338 g_object_unref (priv->gps_device);
340 for (l = priv->connections; l != NULL; l = g_slist_next (l))
342 g_object_unref (l->data);
345 g_hash_table_unref (priv->location);
347 if (priv->throttle_timeout != 0)
348 g_source_remove (priv->throttle_timeout);
350 if (G_OBJECT_CLASS (position_publisher_parent_class)->dispose)
351 G_OBJECT_CLASS (position_publisher_parent_class)->dispose (object);
355 position_publisher_finalize (GObject *object)
357 PositionPublisher *self = POSITION_PUBLISHER (object);
358 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
360 g_slist_free (priv->connections);
362 G_OBJECT_CLASS (position_publisher_parent_class)->finalize (object);
366 position_publisher_get_property (GObject *object,
371 PositionPublisher *self = POSITION_PUBLISHER (object);
372 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
374 switch (property_id) {
376 g_value_set_boolean (value, priv->blur);
379 g_value_set_boolean (value, priv->start_gps);
382 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
388 position_publisher_set_property (GObject *object,
393 PositionPublisher *self = POSITION_PUBLISHER (object);
394 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
396 switch (property_id) {
398 priv->blur = g_value_get_boolean (value);
401 priv->start_gps = g_value_get_boolean (value);
403 location_gpsd_control_start (priv->gps_control);
405 location_gpsd_control_stop (priv->gps_control);
409 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
415 position_publisher_new (gboolean blur, gboolean start_gps)
417 return g_object_new (POSITION_PUBLISHER_TYPE,
419 "start-gps", start_gps,