f986dcfeddc5da8ad73ad57ef7dc221cb4622ace
[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 /* private structure */
36 typedef struct _PositionPublisherPrivate PositionPublisherPrivate;
37
38 struct _PositionPublisherPrivate
39 {
40   ConnectionWatcher *watcher;
41   LocationGPSDevice *gps_device;
42   GSList *connections;
43   GHashTable *location;
44
45   gboolean dispose_has_run;
46 };
47
48 #define POSITION_PUBLISHER_GET_PRIVATE(o)     (G_TYPE_INSTANCE_GET_PRIVATE ((o), POSITION_PUBLISHER_TYPE, PositionPublisherPrivate))
49
50 static void conn_invalidated_cb (TpProxy *conn,
51     guint domain,
52     gint code,
53     gchar *message,
54     PositionPublisher *self);
55
56 static void
57 remove_connection (PositionPublisher *self,
58     TpConnection *conn)
59 {
60   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
61
62   g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (conn_invalidated_cb),
63       self);
64   priv->connections = g_slist_remove (priv->connections, conn);
65   g_object_unref (conn);
66 }
67
68 static void
69 set_location_cb (TpConnection *conn,
70     const GError *error,
71     gpointer user_data,
72     GObject *weak_object)
73 {
74   PositionPublisher *self = POSITION_PUBLISHER (weak_object);
75
76   if (error != NULL)
77     {
78       g_print ("SetLocation failed: %s\n", error->message);
79
80       if (error->code == TP_ERROR_NOT_IMPLEMENTED)
81         {
82           g_print ("remove connection\n");
83           remove_connection (self, conn);
84         }
85
86       return;
87     }
88
89   g_print ("SetLocation succeed\n");
90 }
91
92 static void
93 publish_to_conn (PositionPublisher *self,
94     TpConnection *conn)
95 {
96   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
97
98   if (priv->location == NULL)
99     return;
100
101   tp_cli_connection_interface_location_call_set_location (conn, -1,
102       priv->location, set_location_cb, NULL, NULL, G_OBJECT (self));
103 }
104
105 static void
106 publish_to_all (PositionPublisher *self)
107 {
108   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
109   GSList *l;
110
111   for (l = priv->connections; l != NULL; l = g_slist_next (l))
112     {
113       TpConnection *conn = l->data;
114
115       publish_to_conn (self, conn);
116     }
117 }
118
119 static void
120 update_position (PositionPublisher *self,
121     gdouble lat,
122     gdouble lon,
123     gdouble alt,
124     gdouble accuracy)
125 {
126   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
127
128   g_print ("update position: lat: %f  lon:  %f  alt: %f accuracy: %f\n",
129       lat, lon, alt, accuracy);
130
131   if (priv->location != NULL)
132     g_hash_table_unref (priv->location);
133
134   priv->location = tp_asv_new (
135       "timestamp", G_TYPE_INT64, (gint64) time (NULL),
136       "lat", G_TYPE_DOUBLE, lat,
137       "lon", G_TYPE_DOUBLE, lon,
138       "alt", G_TYPE_DOUBLE, alt,
139       "accuracy", G_TYPE_DOUBLE, accuracy,
140       NULL);
141
142   publish_to_all (self);
143 }
144
145 static void
146 location_changed_cb (LocationGPSDevice *device,
147     PositionPublisher *self)
148 {
149   if (device == NULL)
150     return;
151
152   if (device->fix == NULL)
153     return;
154
155   if (!(device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET))
156     return;
157
158   update_position (self, device->fix->latitude, device->fix->longitude,
159       device->fix->altitude, device->fix->eph / 100.0);
160 }
161
162 static void
163 conn_invalidated_cb (TpProxy *conn,
164     guint domain,
165     gint code,
166     gchar *message,
167     PositionPublisher *self)
168 {
169   g_print ("connection %s invalidated; removing\n", tp_proxy_get_object_path (
170         conn));
171
172   remove_connection (self, TP_CONNECTION (conn));
173 }
174
175 static void
176 connection_added_cb (ConnectionWatcher *watcher,
177     TpConnection *conn,
178     PositionPublisher *self)
179 {
180   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
181
182   if (g_slist_find (priv->connections, conn) != NULL)
183     return;
184
185   if (!tp_proxy_has_interface_by_id (conn,
186         TP_IFACE_QUARK_CONNECTION_INTERFACE_LOCATION))
187     return;
188
189   priv->connections = g_slist_prepend (priv->connections, g_object_ref (conn));
190
191   g_signal_connect (conn, "invalidated",
192       G_CALLBACK (conn_invalidated_cb), self);
193
194   publish_to_conn (self, conn);
195 }
196
197 static void
198 position_publisher_init (PositionPublisher *obj)
199 {
200   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (obj);
201
202   priv->watcher = connection_watcher_new ();
203
204   g_signal_connect (priv->watcher, "connection-added",
205       G_CALLBACK (connection_added_cb), obj);
206
207   priv->gps_device = g_object_new (LOCATION_TYPE_GPS_DEVICE, NULL);
208
209   g_signal_connect (priv->gps_device, "changed",
210       G_CALLBACK (location_changed_cb), obj);
211
212   priv->connections = NULL;
213 }
214
215 static void
216 position_publisher_constructed (GObject *object)
217 {
218   PositionPublisher *self = POSITION_PUBLISHER (object);
219   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
220
221   connection_watcher_start (priv->watcher);
222
223   if (G_OBJECT_CLASS (position_publisher_parent_class)->constructed)
224     G_OBJECT_CLASS (position_publisher_parent_class)->constructed (object);
225 }
226
227 static void position_publisher_dispose (GObject *object);
228 static void position_publisher_finalize (GObject *object);
229
230 static void
231 position_publisher_class_init (PositionPublisherClass *position_publisher_class)
232 {
233   GObjectClass *object_class = G_OBJECT_CLASS (position_publisher_class);
234
235   g_type_class_add_private (position_publisher_class, sizeof (PositionPublisherPrivate));
236
237   object_class->dispose = position_publisher_dispose;
238   object_class->finalize = position_publisher_finalize;
239
240   object_class->constructed = position_publisher_constructed;
241 }
242
243 void
244 position_publisher_dispose (GObject *object)
245 {
246   PositionPublisher *self = POSITION_PUBLISHER (object);
247   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
248   GSList *l;
249
250   if (priv->dispose_has_run)
251     return;
252
253   priv->dispose_has_run = TRUE;
254
255   g_object_unref (priv->watcher);
256   g_object_unref (priv->gps_device);
257
258   for (l = priv->connections; l != NULL; l = g_slist_next (l))
259     {
260       g_object_unref (l->data);
261     }
262
263   g_hash_table_unref (priv->location);
264
265   if (G_OBJECT_CLASS (position_publisher_parent_class)->dispose)
266     G_OBJECT_CLASS (position_publisher_parent_class)->dispose (object);
267 }
268
269 void
270 position_publisher_finalize (GObject *object)
271 {
272   PositionPublisher *self = POSITION_PUBLISHER (object);
273   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
274
275   g_slist_free (priv->connections);
276
277   G_OBJECT_CLASS (position_publisher_parent_class)->finalize (object);
278 }
279
280 PositionPublisher *
281 position_publisher_new (void)
282 {
283   return g_object_new (POSITION_PUBLISHER_TYPE,
284       NULL);
285 }