display connection path when SetLocation fails
[azimuth] / src / position-publisher.c
1 /*
2  * position-publisher.c - Source for PositionPublisher
3  * Copyright (C) 2010 Guillaume Desmottes
4  * @author Guillaume Desmottes <gdesmott@gnome.org>
5  *
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.
10  *
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.
15  *
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
19  */
20
21
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <telepathy-glib/dbus.h>
26 #include <telepathy-glib/interfaces.h>
27
28 #include <location/location-gps-device.h>
29
30 #include "connection-watcher.h"
31 #include "position-publisher.h"
32
33 G_DEFINE_TYPE(PositionPublisher, position_publisher, G_TYPE_OBJECT)
34
35 /* Minimum time before 2 publishing (in seconds) */
36 #define PUBLISH_THROTTLE 10
37
38 /* private structure */
39 typedef struct _PositionPublisherPrivate PositionPublisherPrivate;
40
41 struct _PositionPublisherPrivate
42 {
43   ConnectionWatcher *watcher;
44   LocationGPSDevice *gps_device;
45   GSList *connections;
46   GHashTable *location;
47   /* If not 0, we are waiting before publishing again */
48   guint throttle_timeout;
49   /* TRUE if location has been modified while we were waiting */
50   gboolean modified;
51
52   gboolean dispose_has_run;
53 };
54
55 #define POSITION_PUBLISHER_GET_PRIVATE(o)     (G_TYPE_INSTANCE_GET_PRIVATE ((o), POSITION_PUBLISHER_TYPE, PositionPublisherPrivate))
56
57 static void conn_invalidated_cb (TpProxy *conn,
58     guint domain,
59     gint code,
60     gchar *message,
61     PositionPublisher *self);
62
63 static void publish_to_all (PositionPublisher *self);
64
65 static void
66 remove_connection (PositionPublisher *self,
67     TpConnection *conn)
68 {
69   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
70
71   g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (conn_invalidated_cb),
72       self);
73   priv->connections = g_slist_remove (priv->connections, conn);
74   g_object_unref (conn);
75 }
76
77 static void
78 set_location_cb (TpConnection *conn,
79     const GError *error,
80     gpointer user_data,
81     GObject *weak_object)
82 {
83   PositionPublisher *self = POSITION_PUBLISHER (weak_object);
84
85   if (error != NULL)
86     {
87       g_print ("SetLocation failed (%s): %s\n", tp_proxy_get_object_path (conn),
88           error->message);
89
90       if (error->code == TP_ERROR_NOT_IMPLEMENTED)
91         {
92           g_print ("remove connection\n");
93           remove_connection (self, conn);
94         }
95
96       return;
97     }
98
99   g_print ("SetLocation succeed\n");
100 }
101
102 static gboolean
103 publish_throttle_timeout_cb (gpointer data)
104 {
105   PositionPublisher *self = data;
106   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
107
108   priv->throttle_timeout = 0;
109
110   if (priv->modified)
111     {
112       publish_to_all (self);
113       priv->modified = FALSE;
114     }
115
116   return FALSE;
117 }
118
119 static void
120 publish_to_conn (PositionPublisher *self,
121     TpConnection *conn)
122 {
123   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
124
125   if (priv->location == NULL)
126     return;
127
128   if (priv->throttle_timeout != 0)
129     /* We are waiting */
130     return;
131
132   tp_cli_connection_interface_location_call_set_location (conn, -1,
133       priv->location, set_location_cb, NULL, NULL, G_OBJECT (self));
134
135   /* We won't publish during the next PUBLISH_THROTTLE seconds */
136   priv->throttle_timeout = g_timeout_add_seconds (PUBLISH_THROTTLE,
137       publish_throttle_timeout_cb, self);
138 }
139
140 static void
141 publish_to_all (PositionPublisher *self)
142 {
143   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
144   GSList *l;
145
146   for (l = priv->connections; l != NULL; l = g_slist_next (l))
147     {
148       TpConnection *conn = l->data;
149
150       publish_to_conn (self, conn);
151     }
152 }
153
154 static void
155 update_position (PositionPublisher *self,
156     gdouble lat,
157     gdouble lon,
158     gdouble alt,
159     gdouble accuracy)
160 {
161   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
162
163   g_print ("update position: lat: %f  lon:  %f  alt: %f accuracy: %f\n",
164       lat, lon, alt, accuracy);
165
166   if (priv->location != NULL)
167     g_hash_table_unref (priv->location);
168
169   priv->location = tp_asv_new (
170       "timestamp", G_TYPE_INT64, (gint64) time (NULL),
171       "lat", G_TYPE_DOUBLE, lat,
172       "lon", G_TYPE_DOUBLE, lon,
173       "alt", G_TYPE_DOUBLE, alt,
174       "accuracy", G_TYPE_DOUBLE, accuracy,
175       NULL);
176
177   priv->modified = TRUE;
178
179   publish_to_all (self);
180 }
181
182 static void
183 location_changed_cb (LocationGPSDevice *device,
184     PositionPublisher *self)
185 {
186   if (device == NULL)
187     return;
188
189   if (device->fix == NULL)
190     return;
191
192   if (!(device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET))
193     return;
194
195   update_position (self, device->fix->latitude, device->fix->longitude,
196       device->fix->altitude, device->fix->eph / 100.0);
197 }
198
199 static void
200 conn_invalidated_cb (TpProxy *conn,
201     guint domain,
202     gint code,
203     gchar *message,
204     PositionPublisher *self)
205 {
206   g_print ("connection %s invalidated; removing\n", tp_proxy_get_object_path (
207         conn));
208
209   remove_connection (self, TP_CONNECTION (conn));
210 }
211
212 static void
213 connection_added_cb (ConnectionWatcher *watcher,
214     TpConnection *conn,
215     PositionPublisher *self)
216 {
217   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
218
219   if (g_slist_find (priv->connections, conn) != NULL)
220     return;
221
222   if (!tp_proxy_has_interface_by_id (conn,
223         TP_IFACE_QUARK_CONNECTION_INTERFACE_LOCATION))
224     return;
225
226   priv->connections = g_slist_prepend (priv->connections, g_object_ref (conn));
227
228   g_signal_connect (conn, "invalidated",
229       G_CALLBACK (conn_invalidated_cb), self);
230
231   publish_to_conn (self, conn);
232 }
233
234 static void
235 position_publisher_init (PositionPublisher *obj)
236 {
237   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (obj);
238
239   priv->watcher = connection_watcher_new ();
240
241   g_signal_connect (priv->watcher, "connection-added",
242       G_CALLBACK (connection_added_cb), obj);
243
244   priv->gps_device = g_object_new (LOCATION_TYPE_GPS_DEVICE, NULL);
245
246   g_signal_connect (priv->gps_device, "changed",
247       G_CALLBACK (location_changed_cb), obj);
248
249   priv->connections = NULL;
250 }
251
252 static void
253 position_publisher_constructed (GObject *object)
254 {
255   PositionPublisher *self = POSITION_PUBLISHER (object);
256   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
257
258   connection_watcher_start (priv->watcher);
259
260   if (G_OBJECT_CLASS (position_publisher_parent_class)->constructed)
261     G_OBJECT_CLASS (position_publisher_parent_class)->constructed (object);
262 }
263
264 static void position_publisher_dispose (GObject *object);
265 static void position_publisher_finalize (GObject *object);
266
267 static void
268 position_publisher_class_init (PositionPublisherClass *position_publisher_class)
269 {
270   GObjectClass *object_class = G_OBJECT_CLASS (position_publisher_class);
271
272   g_type_class_add_private (position_publisher_class, sizeof (PositionPublisherPrivate));
273
274   object_class->dispose = position_publisher_dispose;
275   object_class->finalize = position_publisher_finalize;
276
277   object_class->constructed = position_publisher_constructed;
278 }
279
280 void
281 position_publisher_dispose (GObject *object)
282 {
283   PositionPublisher *self = POSITION_PUBLISHER (object);
284   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
285   GSList *l;
286
287   if (priv->dispose_has_run)
288     return;
289
290   priv->dispose_has_run = TRUE;
291
292   g_object_unref (priv->watcher);
293   g_object_unref (priv->gps_device);
294
295   for (l = priv->connections; l != NULL; l = g_slist_next (l))
296     {
297       g_object_unref (l->data);
298     }
299
300   g_hash_table_unref (priv->location);
301
302   if (priv->throttle_timeout != 0)
303     g_source_remove (priv->throttle_timeout);
304
305   if (G_OBJECT_CLASS (position_publisher_parent_class)->dispose)
306     G_OBJECT_CLASS (position_publisher_parent_class)->dispose (object);
307 }
308
309 void
310 position_publisher_finalize (GObject *object)
311 {
312   PositionPublisher *self = POSITION_PUBLISHER (object);
313   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
314
315   g_slist_free (priv->connections);
316
317   G_OBJECT_CLASS (position_publisher_parent_class)->finalize (object);
318 }
319
320 PositionPublisher *
321 position_publisher_new (void)
322 {
323   return g_object_new (POSITION_PUBLISHER_TYPE,
324       NULL);
325 }