]> git.street.me.uk Git - andy/viking.git/blob - src/vikgpslayer.c
Some spelling fixes in a comment
[andy/viking.git] / src / vikgpslayer.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5  * Copyright (C) 2006-2008, Quy Tonthat <qtonthat@gmail.com>
6  * Copyright (C) 2016, Rob Norris <rw_norris@hotmail.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <stdlib.h>
29 #ifdef HAVE_MATH_H
30 #include <math.h>
31 #endif
32 #include "viking.h"
33 #include "icons/icons.h"
34 #include "babel.h"
35 #include "viktrwlayer.h"
36
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40 #ifdef HAVE_STRING_H
41 #include <string.h>
42 #endif
43 #include <glib/gstdio.h>
44 #include <glib/gprintf.h>
45 #include <glib/gi18n.h>
46 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
47 #include <gps.h>
48 #endif
49
50 static VikGpsLayer *vik_gps_layer_create (VikViewport *vp);
51 static void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIter *layer_iter );
52 static void vik_gps_layer_free ( VikGpsLayer *vgl );
53 static void vik_gps_layer_draw ( VikGpsLayer *vgl, VikViewport *vp );
54 static VikGpsLayer *vik_gps_layer_new ( VikViewport *vp );
55 static void vik_gps_layer_post_read ( VikGpsLayer *vgl, VikViewport *vp, gboolean from_file );
56
57 static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *len );
58 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
59 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, VikLayerSetParam *vlsp );
60 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation );
61
62 static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl );
63
64 static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode );
65 static void gps_layer_add_menu_items( VikGpsLayer *vtl, GtkMenu *menu, gpointer vlp );
66
67 static void gps_upload_cb( gpointer layer_and_vlp[2] );
68 static void gps_download_cb( gpointer layer_and_vlp[2] );
69 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] );
70 static void gps_empty_download_cb( gpointer layer_and_vlp[2] );
71 static void gps_empty_all_cb( gpointer layer_and_vlp[2] );
72 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
73 static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] );
74 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2] );
75 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp);
76 static void rt_gpsd_disconnect(VikGpsLayer *vgl);
77 static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed);
78 #endif
79
80 // Shouldn't need to use these much any more as the protocol is now saved as a string.
81 // They are kept for compatibility loading old .vik files
82 typedef enum {GARMIN_P = 0, MAGELLAN_P, DELORME_P, NAVILINK_P, OLD_NUM_PROTOCOLS} vik_gps_proto;
83 static gchar * protocols_args[]   = {"garmin", "magellan", "delbin", "navilink", NULL};
84 #ifdef WINDOWS
85 static gchar * params_ports[] = {"com1", "usb:", NULL};
86 #else
87 static gchar * params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1", "usb:", NULL};
88 #endif
89 /* NUM_PORTS not actually used */
90 /* #define NUM_PORTS (sizeof(params_ports)/sizeof(params_ports[0]) - 1) */
91 /* Compatibility with previous versions */
92 #ifdef WINDOWS
93 static gchar * old_params_ports[] = {"com1", "usb:", NULL};
94 #else
95 static gchar * old_params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1", "usb:", NULL};
96 #endif
97 #define OLD_NUM_PORTS (sizeof(old_params_ports)/sizeof(old_params_ports[0]) - 1)
98
99 typedef struct {
100   GMutex *mutex;
101   gboolean ok;
102   gboolean thread_complete;
103   vik_gps_dir direction;
104   gchar *port;
105   gint wpt_total_count;
106   gint wpt_count;
107   gint trk_total_count;
108   gint trk_count;
109   gint rte_total_count;
110   gint rte_count;
111   VikTrwLayer *vtl;
112   VikTrack *track;
113   gchar *babelargs;
114   GtkWidget *dialog;
115   GtkWidget *status_label;
116   GtkWidget *gps_label;
117   GtkWidget *wpt_label;
118   GtkWidget *trk_label;
119   GtkWidget *rte_label;
120   vik_gps_xfer_type progress_type;
121   gboolean result;
122   gchar *info;
123   // GUI Updates
124   gint id_status_working;
125   gint id_status_end;
126   gint id_info;
127   gint id_total_count;
128   gint id_count;
129   VikViewport *vvp;
130 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
131   gboolean realtime_tracking;
132 #endif
133 } GpsSession;
134 static void gps_session_delete(GpsSession *sess);
135
136 static gchar *params_groups[] = {
137   N_("Data Mode"),
138 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
139   N_("Realtime Tracking Mode"),
140 #endif
141 };
142
143 enum {GROUP_DATA_MODE, GROUP_REALTIME_MODE};
144
145
146 static VikLayerParamData gps_protocol_default ( void )
147 {
148   VikLayerParamData data;
149   data.s = g_strdup ( "garmin" );
150   return data;
151 }
152
153 static VikLayerParamData gps_port_default ( void )
154 {
155   VikLayerParamData data;
156   data.s = g_strdup ( "usb:" );
157 #ifndef WINDOWS
158   /* Attempt to auto set default USB serial port entry */
159   /* Ordered to make lowest device favourite if available */
160   if (g_access ("/dev/ttyUSB1", R_OK) == 0) {
161         if ( data.s )
162           g_free ( (gchar *)data.s );
163     data.s = g_strdup ("/dev/ttyUSB1");
164   }
165   if (g_access ("/dev/ttyUSB0", R_OK) == 0) {
166         if ( data.s )
167           g_free ( (gchar *)data.s );
168     data.s = g_strdup ("/dev/ttyUSB0");
169   }
170 #endif
171   return data;
172 }
173
174 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
175 static gchar *params_vehicle_position[] = {
176   N_("Keep vehicle at center"),
177   N_("Keep vehicle on screen"),
178   N_("Disable"),
179   NULL
180 };
181 enum {
182   VEHICLE_POSITION_CENTERED = 0,
183   VEHICLE_POSITION_ON_SCREEN,
184   VEHICLE_POSITION_NONE,
185 };
186
187 static VikLayerParamData moving_map_method_default ( void ) { return VIK_LPD_UINT ( VEHICLE_POSITION_ON_SCREEN ); }
188
189 static VikLayerParamData gpsd_host_default ( void )
190 {
191   VikLayerParamData data;
192   data.s = g_strdup ( "localhost" );
193   return data;
194 }
195
196 static VikLayerParamData gpsd_port_default ( void )
197 {
198   VikLayerParamData data;
199   data.s = g_strdup ( DEFAULT_GPSD_PORT );
200   return data;
201 }
202
203 static VikLayerParamData gpsd_retry_interval_default ( void )
204 {
205   VikLayerParamData data;
206   data.s = g_strdup ( "10" );
207   return data;
208 }
209
210 #endif
211
212 static VikLayerParam gps_layer_params[] = {
213  //     NB gps_layer_inst_init() is performed after parameter registeration
214  //  thus to give the protocols some potential values use the old static list
215  // TODO: find another way to use gps_layer_inst_init()?
216   { VIK_LAYER_GPS, "gps_protocol", VIK_LAYER_PARAM_STRING, GROUP_DATA_MODE, N_("GPS Protocol:"), VIK_LAYER_WIDGET_COMBOBOX, protocols_args, NULL, NULL, gps_protocol_default, NULL, NULL }, // List reassigned at runtime
217   { VIK_LAYER_GPS, "gps_port", VIK_LAYER_PARAM_STRING, GROUP_DATA_MODE, N_("Serial Port:"), VIK_LAYER_WIDGET_COMBOBOX, params_ports, NULL, NULL, gps_port_default, NULL, NULL },
218   { VIK_LAYER_GPS, "gps_download_tracks", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Download Tracks:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
219   { VIK_LAYER_GPS, "gps_upload_tracks", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Upload Tracks:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
220   { VIK_LAYER_GPS, "gps_download_routes", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Download Routes:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
221   { VIK_LAYER_GPS, "gps_upload_routes", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Upload Routes:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
222   { VIK_LAYER_GPS, "gps_download_waypoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Download Waypoints:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
223   { VIK_LAYER_GPS, "gps_upload_waypoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Upload Waypoints:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
224 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
225   { VIK_LAYER_GPS, "record_tracking", VIK_LAYER_PARAM_BOOLEAN, GROUP_REALTIME_MODE, N_("Recording tracks"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
226   { VIK_LAYER_GPS, "center_start_tracking", VIK_LAYER_PARAM_BOOLEAN, GROUP_REALTIME_MODE, N_("Jump to current position on start"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
227   { VIK_LAYER_GPS, "moving_map_method", VIK_LAYER_PARAM_UINT, GROUP_REALTIME_MODE, N_("Moving Map Method:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_vehicle_position, NULL, NULL, moving_map_method_default, NULL, NULL },
228   { VIK_LAYER_GPS, "realtime_update_statusbar", VIK_LAYER_PARAM_BOOLEAN, GROUP_REALTIME_MODE, N_("Update Statusbar:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, N_("Display information in the statusbar on GPS updates"), vik_lpd_true_default, NULL, NULL },
229   { VIK_LAYER_GPS, "auto_connect", VIK_LAYER_PARAM_BOOLEAN, GROUP_REALTIME_MODE, N_("Auto Connect"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, N_("Automatically connect to GPSD"), vik_lpd_false_default, NULL, NULL },
230   { VIK_LAYER_GPS, "gpsd_host", VIK_LAYER_PARAM_STRING, GROUP_REALTIME_MODE, N_("Gpsd Host:"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, gpsd_host_default, NULL, NULL },
231   { VIK_LAYER_GPS, "gpsd_port", VIK_LAYER_PARAM_STRING, GROUP_REALTIME_MODE, N_("Gpsd Port:"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, gpsd_port_default, NULL, NULL },
232   { VIK_LAYER_GPS, "gpsd_retry_interval", VIK_LAYER_PARAM_STRING, GROUP_REALTIME_MODE, N_("Gpsd Retry Interval (seconds):"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, gpsd_retry_interval_default, NULL, NULL },
233 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
234 };
235 enum {
236   PARAM_PROTOCOL=0, PARAM_PORT,
237   PARAM_DOWNLOAD_TRACKS, PARAM_UPLOAD_TRACKS,
238   PARAM_DOWNLOAD_ROUTES, PARAM_UPLOAD_ROUTES,
239   PARAM_DOWNLOAD_WAYPOINTS, PARAM_UPLOAD_WAYPOINTS,
240 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
241   PARAM_REALTIME_REC,
242   PARAM_REALTIME_CENTER_START,
243   PARAM_VEHICLE_POSITION,
244   PARAM_REALTIME_UPDATE_STATUSBAR,
245   PARAM_GPSD_CONNECT,
246   PARAM_GPSD_HOST,
247   PARAM_GPSD_PORT,
248   PARAM_GPSD_RETRY_INTERVAL,
249 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
250   NUM_PARAMS};
251
252 VikLayerInterface vik_gps_layer_interface = {
253   "GPS",
254   N_("GPS"),
255   NULL,
256   &vikgpslayer_pixbuf,
257
258   NULL,
259   0,
260
261   gps_layer_params,
262   NUM_PARAMS,
263   params_groups,
264   sizeof(params_groups)/sizeof(params_groups[0]),
265
266   VIK_MENU_ITEM_ALL,
267
268   (VikLayerFuncCreate)                  vik_gps_layer_create,
269   (VikLayerFuncRealize)                 vik_gps_layer_realize,
270   (VikLayerFuncPostRead)                vik_gps_layer_post_read,
271   (VikLayerFuncFree)                    vik_gps_layer_free,
272
273   (VikLayerFuncProperties)              NULL,
274   (VikLayerFuncDraw)                    vik_gps_layer_draw,
275   (VikLayerFuncChangeCoordMode)         gps_layer_change_coord_mode,
276
277   (VikLayerFuncGetTimestamp)            NULL,
278
279   (VikLayerFuncSetMenuItemsSelection)   NULL,
280   (VikLayerFuncGetMenuItemsSelection)   NULL,
281
282   (VikLayerFuncAddMenuItems)            gps_layer_add_menu_items,
283   (VikLayerFuncSublayerAddMenuItems)    NULL,
284
285   (VikLayerFuncSublayerRenameRequest)   NULL,
286   (VikLayerFuncSublayerToggleVisible)   NULL,
287   (VikLayerFuncSublayerTooltip)         NULL,
288   (VikLayerFuncLayerTooltip)            gps_layer_tooltip,
289   (VikLayerFuncLayerSelected)           NULL,
290
291   (VikLayerFuncMarshall)                gps_layer_marshall,
292   (VikLayerFuncUnmarshall)              gps_layer_unmarshall,
293
294   (VikLayerFuncSetParam)                gps_layer_set_param,
295   (VikLayerFuncGetParam)                gps_layer_get_param,
296   (VikLayerFuncChangeParam)             NULL,
297
298   (VikLayerFuncReadFileData)            NULL,
299   (VikLayerFuncWriteFileData)           NULL,
300
301   (VikLayerFuncDeleteItem)              NULL,
302   (VikLayerFuncCutItem)                 NULL,
303   (VikLayerFuncCopyItem)                NULL,
304   (VikLayerFuncPasteItem)               NULL,
305   (VikLayerFuncFreeCopiedItem)          NULL,
306   (VikLayerFuncDragDropRequest)         NULL,
307
308   (VikLayerFuncSelectClick)             NULL,
309   (VikLayerFuncSelectMove)              NULL,
310   (VikLayerFuncSelectRelease)           NULL,
311   (VikLayerFuncSelectedViewportMenu)    NULL,
312 };
313
314 enum {TRW_DOWNLOAD=0, TRW_UPLOAD,
315 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
316   TRW_REALTIME,
317 #endif
318   NUM_TRW};
319 static gchar * trw_names[] = {
320   N_("GPS Download"), N_("GPS Upload"),
321 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
322   N_("GPS Realtime Tracking"),
323 #endif
324 };
325
326 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
327 typedef struct {
328   struct gps_data_t gpsd;
329   VikGpsLayer *vgl;
330   int gpsd_open;
331 } VglGpsd;
332
333 typedef struct {
334   struct gps_fix_t fix;
335   gint satellites_used;
336   gboolean dirty;   /* needs to be saved */
337 } GpsFix;
338 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
339
340 struct _VikGpsLayer {
341   VikLayer vl;
342   VikTrwLayer * trw_children[NUM_TRW];
343   GList * children;     /* used only for writing file */
344   int cur_read_child;   /* used only for reading file */
345 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
346   VglGpsd *vgpsd;
347   gboolean connected_to_gpsd;
348   gboolean realtime_tracking;
349   gboolean first_realtime_trackpoint;
350   GpsFix realtime_fix;
351   GpsFix last_fix;
352
353   VikTrack *realtime_track;
354
355   GIOChannel *realtime_io_channel;
356   guint realtime_io_watch_id;
357   guint realtime_retry_timer;
358   GdkGC *realtime_track_gc;
359   GdkGC *realtime_track_bg_gc;
360   GdkGC *realtime_track_pt_gc;
361   GdkGC *realtime_track_pt1_gc;
362   GdkGC *realtime_track_pt2_gc;
363
364   /* params */
365   gboolean auto_connect_to_gpsd;
366   gchar *gpsd_host;
367   gchar *gpsd_port;
368   gint gpsd_retry_interval;
369   gboolean realtime_record;
370   gboolean realtime_jump_to_start;
371   guint vehicle_position;
372   gboolean realtime_update_statusbar;
373   VikTrackpoint *trkpt;
374   VikTrackpoint *trkpt_prev;
375 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
376   gchar *protocol;
377   gchar *serial_port;
378   gboolean download_tracks;
379   gboolean download_routes;
380   gboolean download_waypoints;
381   gboolean upload_tracks;
382   gboolean upload_routes;
383   gboolean upload_waypoints;
384 };
385
386 /**
387  * Overwrite the static setup with dynamically generated GPS Babel device list
388  */
389 static void gps_layer_inst_init ( VikGpsLayer *self )
390 {
391   gint new_proto = 0;
392   // +1 for luck (i.e the NULL terminator)
393   gchar **new_protocols = g_malloc_n(1 + g_list_length(a_babel_device_list), sizeof(gpointer));
394
395   GList *gl = g_list_first ( a_babel_device_list );
396   while ( gl ) {
397     // should be using label property but use name for now
398     //  thus don't need to mess around converting label to name later on
399     new_protocols[new_proto++] = ((BabelDevice*)gl->data)->name;
400     gl = g_list_next ( gl );
401   }
402   new_protocols[new_proto] = NULL;
403
404   vik_gps_layer_interface.params[PARAM_PROTOCOL].widget_data = new_protocols;
405 }
406
407 GType vik_gps_layer_get_type ()
408 {
409   static GType val_type = 0;
410
411   if (!val_type)
412   {
413     static const GTypeInfo val_info =
414     {
415       sizeof (VikGpsLayerClass),
416       NULL, /* base_init */
417       NULL, /* base_finalize */
418       NULL, /* class init */
419       NULL, /* class_finalize */
420       NULL, /* class_data */
421       sizeof (VikGpsLayer),
422       0,
423       (GInstanceInitFunc) gps_layer_inst_init,
424     };
425     val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikGpsLayer", &val_info, 0 );
426   }
427
428   return val_type;
429 }
430
431 static VikGpsLayer *vik_gps_layer_create (VikViewport *vp)
432 {
433   int i;
434
435   VikGpsLayer *rv = vik_gps_layer_new (vp);
436   vik_layer_rename ( VIK_LAYER(rv), vik_gps_layer_interface.name );
437
438   for (i = 0; i < NUM_TRW; i++) {
439     rv->trw_children[i] = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vp, FALSE ));
440     vik_layer_set_menu_items_selection(VIK_LAYER(rv->trw_children[i]), VIK_MENU_ITEM_ALL & ~(VIK_MENU_ITEM_CUT|VIK_MENU_ITEM_DELETE));
441   }
442   return rv;
443 }
444
445 static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl )
446 {
447   static gchar buf1[256];
448   buf1[0] = '\0';
449   static gchar rbuf[512];
450   rbuf[0] = '\0';
451
452   g_snprintf (buf1, sizeof(buf1), "%s:%s", vgl->protocol, vgl->serial_port);
453
454 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
455   static gchar buf2[256];
456   buf2[0] = '\0';
457   if ( vgl->connected_to_gpsd )
458     g_snprintf (buf2, sizeof(buf2), "GPSD:%s:%s %s", vgl->gpsd_host, vgl->gpsd_port, _("Connected"));
459   else
460     g_snprintf (buf2, sizeof(buf2), "GPSD:%s:%s %s", vgl->gpsd_host, vgl->gpsd_port, _("Disconnected"));
461
462   g_snprintf (rbuf, sizeof(rbuf), "%s\n%s", buf1, buf2);
463 #else
464   g_snprintf (rbuf, sizeof(rbuf), "%s", buf1);
465 #endif
466   return rbuf;
467 }
468
469 /* "Copy" */
470 static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *datalen )
471 {
472   VikLayer *child_layer;
473   guint8 *ld; 
474   gint ll;
475   GByteArray* b = g_byte_array_new ();
476   gint len;
477   gint i;
478
479 #define alm_append(obj, sz)     \
480   len = (sz);                   \
481   g_byte_array_append ( b, (guint8 *)&len, sizeof(len) );       \
482   g_byte_array_append ( b, (guint8 *)(obj), len );
483
484   vik_layer_marshall_params(VIK_LAYER(vgl), &ld, &ll);
485   alm_append(ld, ll);
486   g_free(ld);
487
488   for (i = 0; i < NUM_TRW; i++) {
489     child_layer = VIK_LAYER(vgl->trw_children[i]);
490     vik_layer_marshall(child_layer, &ld, &ll);
491     if (ld) {
492       alm_append(ld, ll);
493       g_free(ld);
494     }
495   }
496   *data = b->data;
497   *datalen = b->len;
498   g_byte_array_free(b, FALSE);
499 #undef alm_append
500 }
501
502 /* "Paste" */
503 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
504 {
505 #define alm_size (*(gint *)data)
506 #define alm_next \
507   len -= sizeof(gint) + alm_size; \
508   data += sizeof(gint) + alm_size;
509   
510   VikGpsLayer *rv = vik_gps_layer_new(vvp);
511   VikLayer *child_layer;
512   gint i;
513
514   vik_layer_unmarshall_params ( VIK_LAYER(rv), data+sizeof(gint), alm_size, vvp );
515   alm_next;
516
517   i = 0;
518   while (len>0 && i < NUM_TRW) {
519     child_layer = vik_layer_unmarshall ( data + sizeof(gint), alm_size, vvp );
520     if (child_layer) {
521       rv->trw_children[i++] = (VikTrwLayer *)child_layer;
522       // NB no need to attach signal update handler here
523       //  as this will always be performed later on in vik_gps_layer_realize()
524     }
525     alm_next;
526   }
527   //  g_print("gps_layer_unmarshall ended with len=%d\n", len);
528   g_assert(len == 0);
529   return rv;
530 #undef alm_size
531 #undef alm_next
532 }
533
534 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, VikLayerSetParam *vlsp )
535 {
536   switch ( vlsp->id )
537   {
538     case PARAM_PROTOCOL:
539       if (vlsp->data.s) {
540         g_free(vgl->protocol);
541         // Backwards Compatibility: previous versions <v1.4 stored protocol as an array index
542         int index = vlsp->data.s[0] - '0';
543         if (vlsp->data.s[0] != '\0' &&
544             g_ascii_isdigit (vlsp->data.s[0]) &&
545             vlsp->data.s[1] == '\0' &&
546             index < OLD_NUM_PROTOCOLS)
547           // It is a single digit: activate compatibility
548           vgl->protocol = g_strdup(protocols_args[index]);
549         else
550           vgl->protocol = g_strdup(vlsp->data.s);
551         g_debug("%s: %s", __FUNCTION__, vgl->protocol);
552       }
553       else
554         g_warning(_("Unknown GPS Protocol"));
555       break;
556     case PARAM_PORT:
557       if (vlsp->data.s) {
558         g_free(vgl->serial_port);
559         // Backwards Compatibility: previous versions <v0.9.91 stored serial_port as an array index
560         int index = vlsp->data.s[0] - '0';
561         if (vlsp->data.s[0] != '\0' &&
562             g_ascii_isdigit (vlsp->data.s[0]) &&
563             vlsp->data.s[1] == '\0' &&
564             index < OLD_NUM_PORTS)
565           /* It is a single digit: activate compatibility */
566           vgl->serial_port = g_strdup(old_params_ports[index]);
567         else
568           vgl->serial_port = g_strdup(vlsp->data.s);
569         g_debug("%s: %s", __FUNCTION__, vgl->serial_port);
570       }
571       else
572         g_warning(_("Unknown serial port device"));
573       break;
574     case PARAM_DOWNLOAD_TRACKS:
575       vgl->download_tracks = vlsp->data.b;
576       break;
577     case PARAM_UPLOAD_TRACKS:
578       vgl->upload_tracks = vlsp->data.b;
579       break;
580     case PARAM_DOWNLOAD_ROUTES:
581       vgl->download_routes = vlsp->data.b;
582       break;
583     case PARAM_UPLOAD_ROUTES:
584       vgl->upload_routes = vlsp->data.b;
585       break;
586     case PARAM_DOWNLOAD_WAYPOINTS:
587       vgl->download_waypoints = vlsp->data.b;
588       break;
589     case PARAM_UPLOAD_WAYPOINTS:
590       vgl->upload_waypoints = vlsp->data.b;
591       break;
592 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
593     case PARAM_GPSD_CONNECT:
594       vgl->auto_connect_to_gpsd = vlsp->data.b;
595       break;
596     case PARAM_GPSD_HOST:
597       if (vlsp->data.s) {
598         if (vgl->gpsd_host)
599           g_free(vgl->gpsd_host);
600         vgl->gpsd_host = g_strdup(vlsp->data.s);
601       }
602       break;
603     case PARAM_GPSD_PORT:
604       if (vlsp->data.s) {
605         if (vgl->gpsd_port)
606           g_free(vgl->gpsd_port);
607         vgl->gpsd_port = g_strdup(vlsp->data.s);
608       }
609       break;
610     case PARAM_GPSD_RETRY_INTERVAL:
611       vgl->gpsd_retry_interval = strtol(vlsp->data.s, NULL, 10);
612       break;
613     case PARAM_REALTIME_REC:
614       vgl->realtime_record = vlsp->data.b;
615       break;
616     case PARAM_REALTIME_CENTER_START:
617       vgl->realtime_jump_to_start = vlsp->data.b;
618       break;
619     case PARAM_VEHICLE_POSITION:
620       vgl->vehicle_position = vlsp->data.u;
621       break;
622     case PARAM_REALTIME_UPDATE_STATUSBAR:
623       vgl->realtime_update_statusbar = vlsp->data.b;
624       break;
625 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
626     default:
627       g_warning("gps_layer_set_param(): unknown parameter");
628   }
629
630   return TRUE;
631 }
632
633 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation )
634 {
635   VikLayerParamData rv;
636   switch ( id )
637   {
638     case PARAM_PROTOCOL:
639       rv.s = vgl->protocol;
640       g_debug("%s: %s", __FUNCTION__, rv.s);
641       break;
642     case PARAM_PORT:
643       rv.s = vgl->serial_port;
644       g_debug("%s: %s", __FUNCTION__, rv.s);
645       break;
646     case PARAM_DOWNLOAD_TRACKS:
647       rv.b = vgl->download_tracks;
648       break;
649     case PARAM_UPLOAD_TRACKS:
650       rv.b = vgl->upload_tracks;
651       break;
652     case PARAM_DOWNLOAD_ROUTES:
653       rv.b = vgl->download_routes;
654       break;
655     case PARAM_UPLOAD_ROUTES:
656       rv.b = vgl->upload_routes;
657       break;
658     case PARAM_DOWNLOAD_WAYPOINTS:
659       rv.b = vgl->download_waypoints;
660       break;
661     case PARAM_UPLOAD_WAYPOINTS:
662       rv.b = vgl->upload_waypoints;
663       break;
664 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
665     case PARAM_GPSD_CONNECT:
666       rv.b = vgl->auto_connect_to_gpsd;
667       break;
668     case PARAM_GPSD_HOST:
669       rv.s = vgl->gpsd_host ? vgl->gpsd_host : "";
670       break;
671     case PARAM_GPSD_PORT:
672       rv.s = vgl->gpsd_port ? vgl->gpsd_port : g_strdup(DEFAULT_GPSD_PORT);
673       break;
674     case PARAM_GPSD_RETRY_INTERVAL:
675       rv.s = g_strdup_printf("%d", vgl->gpsd_retry_interval);
676       break;
677     case PARAM_REALTIME_REC:
678       rv.b = vgl->realtime_record;
679       break;
680     case PARAM_REALTIME_CENTER_START:
681       rv.b = vgl->realtime_jump_to_start;
682       break;
683     case PARAM_VEHICLE_POSITION:
684       rv.u = vgl->vehicle_position;
685       break;
686     case PARAM_REALTIME_UPDATE_STATUSBAR:
687       rv.u = vgl->realtime_update_statusbar;
688       break;
689 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
690     default:
691       g_warning(_("%s: unknown parameter"), __FUNCTION__);
692   }
693
694   return rv;
695 }
696
697 VikGpsLayer *vik_gps_layer_new (VikViewport *vp)
698 {
699   gint i;
700   VikGpsLayer *vgl = VIK_GPS_LAYER ( g_object_new ( VIK_GPS_LAYER_TYPE, NULL ) );
701   vik_layer_set_type ( VIK_LAYER(vgl), VIK_LAYER_GPS );
702   for (i = 0; i < NUM_TRW; i++) {
703     vgl->trw_children[i] = NULL;
704   }
705   vgl->children = NULL;
706   vgl->cur_read_child = 0;
707
708 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
709   vgl->first_realtime_trackpoint = FALSE;
710   vgl->trkpt = NULL;
711   vgl->trkpt_prev = NULL;
712   vgl->vgpsd = NULL;
713   vgl->realtime_io_channel = NULL;
714   vgl->realtime_io_watch_id = 0;
715   vgl->realtime_retry_timer = 0;
716   if ( vp ) {
717     vgl->realtime_track_gc = vik_viewport_new_gc ( vp, "#203070", 2 );
718     vgl->realtime_track_bg_gc = vik_viewport_new_gc ( vp, "grey", 2 );
719     vgl->realtime_track_pt1_gc = vik_viewport_new_gc ( vp, "red", 2 );
720     vgl->realtime_track_pt2_gc = vik_viewport_new_gc ( vp, "green", 2 );
721     vgl->realtime_track_pt_gc = vgl->realtime_track_pt1_gc;
722   }
723   vgl->realtime_track = NULL;
724 #endif // VIK_CONFIG_REALTIME_GPS_TRACKING
725
726   vik_layer_set_defaults ( VIK_LAYER(vgl), vp );
727
728   return vgl;
729 }
730
731 static void vik_gps_layer_post_read ( VikGpsLayer *vgl, VikViewport *vvp, gboolean from_file )
732 {
733   for (guint i = 0; i < NUM_TRW; i++) {
734     trw_layer_calculate_bounds_waypoints ( vgl->trw_children[i] );
735     trw_layer_calculate_bounds_tracks ( vgl->trw_children[i] );
736   }
737 }
738
739 static void vik_gps_layer_draw ( VikGpsLayer *vgl, VikViewport *vp )
740 {
741   gint i;
742   VikLayer *vl;
743   VikLayer *trigger = VIK_LAYER(vik_viewport_get_trigger( vp ));
744
745   for (i = 0; i < NUM_TRW; i++) {
746     vl = VIK_LAYER(vgl->trw_children[i]);
747     if (vl == trigger) {
748       if ( vik_viewport_get_half_drawn ( vp ) ) {
749         vik_viewport_set_half_drawn ( vp, FALSE );
750         vik_viewport_snapshot_load( vp );
751       } else {
752         vik_viewport_snapshot_save( vp );
753       }
754     }
755     if (!vik_viewport_get_half_drawn(vp))
756       vik_layer_draw ( vl, vp );
757   }
758 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
759   if (vgl->realtime_tracking) {
760     if (VIK_LAYER(vgl) == trigger) {
761       if ( vik_viewport_get_half_drawn ( vp ) ) {
762         vik_viewport_set_half_drawn ( vp, FALSE );
763         vik_viewport_snapshot_load( vp );
764       } else {
765         vik_viewport_snapshot_save( vp );
766       }
767     }
768     if (!vik_viewport_get_half_drawn(vp))
769       realtime_tracking_draw(vgl, vp);
770   }
771 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
772 }
773
774 static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode )
775 {
776   gint i;
777   for (i = 0; i < NUM_TRW; i++) {
778     vik_layer_change_coord_mode(VIK_LAYER(vgl->trw_children[i]), mode);
779   }
780 }
781
782 static void gps_layer_add_menu_items( VikGpsLayer *vgl, GtkMenu *menu, gpointer vlp )
783 {
784   static gpointer pass_along[2];
785   GtkWidget *item;
786   pass_along[0] = vgl;
787   pass_along[1] = vlp;
788
789   item = gtk_menu_item_new();
790   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
791   gtk_widget_show ( item );
792
793   /* Now with icons */
794   item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS") );
795   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
796   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_upload_cb), pass_along );
797   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
798   gtk_widget_show ( item );
799
800   item = gtk_image_menu_item_new_with_mnemonic ( _("Download from _GPS") );
801   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
802   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_download_cb), pass_along );
803   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
804   gtk_widget_show ( item );
805
806 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
807   item = gtk_image_menu_item_new_with_mnemonic ( vgl->realtime_tracking  ?
808                                                  "_Stop Realtime Tracking" :
809                                                  "_Start Realtime Tracking" );
810   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, vgl->realtime_tracking ?
811                                   gtk_image_new_from_stock (GTK_STOCK_MEDIA_STOP, GTK_ICON_SIZE_MENU) :
812                                   gtk_image_new_from_stock (GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_MENU) );
813   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_start_stop_tracking_cb), pass_along );
814   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
815   gtk_widget_show ( item );
816
817   item = gtk_menu_item_new();
818   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
819   gtk_widget_show ( item );
820
821   item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _Realtime") );
822   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
823   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_realtime_cb), pass_along );
824   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
825   gtk_widget_show ( item );
826 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
827
828   item = gtk_image_menu_item_new_with_mnemonic ( _("E_mpty Upload") );
829   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
830   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_upload_cb), pass_along );
831   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
832   gtk_widget_show ( item );
833
834   item = gtk_image_menu_item_new_with_mnemonic ( _("_Empty Download") );
835   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
836   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_download_cb), pass_along );
837   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
838   gtk_widget_show ( item );
839
840   item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _All") );
841   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
842   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_all_cb), pass_along );
843   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
844   gtk_widget_show ( item );
845
846 }
847
848 static void disconnect_layer_signal ( VikLayer *vl, VikGpsLayer *vgl )
849 {
850   guint number_handlers = g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, vgl);
851   if ( number_handlers != 1 ) {
852     g_critical(_("Unexpected number of disconnected handlers: %d"), number_handlers);
853   }
854 }
855
856 static void vik_gps_layer_free ( VikGpsLayer *vgl )
857 {
858   gint i;
859   for (i = 0; i < NUM_TRW; i++) {
860     if (vgl->vl.realized)
861       disconnect_layer_signal(VIK_LAYER(vgl->trw_children[i]), vgl);
862     g_object_unref(vgl->trw_children[i]);
863   }
864 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
865   rt_gpsd_disconnect(vgl);
866   if (vgl->realtime_track_gc != NULL)
867     g_object_unref(vgl->realtime_track_gc);
868   if (vgl->realtime_track_bg_gc != NULL)
869     g_object_unref(vgl->realtime_track_bg_gc);
870   if (vgl->realtime_track_pt1_gc != NULL)
871     g_object_unref(vgl->realtime_track_pt1_gc);
872   if (vgl->realtime_track_pt2_gc != NULL)
873     g_object_unref(vgl->realtime_track_pt2_gc);
874 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
875 }
876
877 static void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIter *layer_iter )
878 {
879   GtkTreeIter iter;
880   int ix;
881
882   // TODO set to garmin by default
883   //if (a_babel_device_list)
884   // device = ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
885   // Need to access uibuild widgets somehow....
886
887   for (ix = 0; ix < NUM_TRW; ix++) {
888     VikLayer * trw = VIK_LAYER(vgl->trw_children[ix]);
889     vik_treeview_add_layer ( VIK_LAYER(vgl)->vt, layer_iter, &iter,
890         _(trw_names[ix]), vgl, TRUE,
891         trw, trw->type, trw->type, vik_layer_get_timestamp(trw) );
892     if ( ! trw->visible )
893       vik_treeview_item_set_visible ( VIK_LAYER(vgl)->vt, &iter, FALSE );
894     vik_layer_realize ( trw, VIK_LAYER(vgl)->vt, &iter );
895     g_signal_connect_swapped ( G_OBJECT(trw), "update", G_CALLBACK(vik_layer_emit_update_secondary), vgl );
896   }
897
898 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
899   if ( vgl->auto_connect_to_gpsd ) {
900     vgl->realtime_tracking = TRUE;
901     vgl->first_realtime_trackpoint = TRUE;
902     (void)rt_gpsd_connect ( vgl, FALSE );
903   }
904 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
905 }
906
907 const GList *vik_gps_layer_get_children ( VikGpsLayer *vgl )
908 {
909   int i;
910
911   if (vgl->children == NULL) {
912     for (i = NUM_TRW - 1; i >= 0; i--)
913       vgl->children = g_list_prepend(vgl->children, vgl->trw_children[i]);
914   }
915   return vgl->children;
916 }
917
918 VikTrwLayer * vik_gps_layer_get_a_child(VikGpsLayer *vgl)
919 {
920   g_assert ((vgl->cur_read_child >= 0) && (vgl->cur_read_child < NUM_TRW));
921
922   VikTrwLayer * vtl = vgl->trw_children[vgl->cur_read_child];
923   if (++(vgl->cur_read_child) >= NUM_TRW)
924     vgl->cur_read_child = 0;
925   return(vtl);
926 }
927
928 gboolean vik_gps_layer_is_empty ( VikGpsLayer *vgl )
929 {
930   if ( vgl->trw_children[0] )
931     return FALSE;
932   return TRUE;
933 }
934
935 static void gps_session_delete(GpsSession *sess)
936 {
937   vik_mutex_free(sess->mutex);
938   // Remove any outstanding GUI update requests
939   if ( sess->id_status_working )
940     g_source_remove ( sess->id_status_working );
941   if ( sess->id_status_end )
942     g_source_remove ( sess->id_status_end );
943   if ( sess->id_info )
944     g_source_remove ( sess->id_info );
945   if ( sess->id_total_count )
946     g_source_remove ( sess->id_total_count );
947   if ( sess->id_count )
948     g_source_remove ( sess->id_count );
949   g_free(sess->babelargs);
950   g_free(sess->info);
951   g_free(sess->port);
952   g_free(sess);
953 }
954
955 static gboolean show_total_count(GpsSession *sess)
956 {
957   g_mutex_lock(sess->mutex);
958   if (sess->ok) {
959     const gchar *tmp_str;
960     gint tc;
961     GtkWidget *progress_label;
962     if (sess->direction == GPS_DOWN)
963     {
964       switch (sess->progress_type) {
965       case WPT:
966         tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", sess->wpt_total_count);
967         tc = sess->wpt_total_count;
968         progress_label = sess->wpt_label;
969         break;
970       case TRK:
971         tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", sess->trk_total_count);
972         tc = sess->trk_total_count;
973         progress_label = sess->trk_label;
974         break;
975       default:
976         tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", sess->rte_total_count);
977         tc = sess->rte_total_count;
978         progress_label = sess->rte_label;
979         break;
980 /*
981         {
982           // Maybe a gpsbabel bug/feature (upto at least v1.4.3 or maybe my Garmin device) but the count always seems x2 too many for routepoints
983           gint mycnt = (cnt / 2) + 1;
984           tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", mycnt);
985           tc = mycnt;
986           break;
987         }
988         */
989       }
990     }
991     else
992     {
993       switch (sess->progress_type) {
994       case WPT:
995         tmp_str = ngettext("Uploading %d waypoint...", "Uploading %d waypoints...", sess->wpt_total_count);
996         tc = sess->wpt_total_count;
997         progress_label = sess->wpt_label;
998         break;
999       case TRK:
1000         tmp_str = ngettext("Uploading %d trackpoint...", "Uploading %d trackpoints...", sess->trk_total_count);
1001         tc = sess->trk_total_count;
1002         progress_label = sess->trk_label;
1003         break;
1004       default:
1005         tmp_str = ngettext("Uploading %d routepoint...", "Uploading %d routepoints...", sess->rte_total_count);
1006         tc = sess->rte_total_count;
1007         progress_label = sess->rte_label;
1008         break;
1009       }
1010     }
1011
1012     gchar s[128];
1013     g_snprintf(s, 128, tmp_str, tc);
1014     gtk_label_set_text ( GTK_LABEL(progress_label), s );
1015     gtk_widget_show ( progress_label );
1016   }
1017   sess->id_total_count = 0;
1018   g_mutex_unlock(sess->mutex);
1019   return FALSE;
1020 }
1021
1022 static gboolean show_current_count(GpsSession *sess)
1023 {
1024   g_mutex_lock(sess->mutex);
1025   if (sess->ok) {
1026     gchar s[128];
1027     gint count, total_count;
1028     const gchar *tmp_str;
1029     GtkWidget *progress_label;
1030     if (sess->wpt_count < sess->wpt_total_count) {
1031       if (sess->direction == GPS_DOWN) {
1032         switch (sess->progress_type) {
1033         case WPT:
1034           tmp_str = ngettext("Downloaded %d out of %d waypoint...", "Downloaded %d out of %d waypoints...", sess->wpt_total_count);
1035           count = sess->wpt_count;
1036           total_count = sess->wpt_total_count;
1037           progress_label = sess->wpt_label;
1038           break;
1039         case TRK:
1040           tmp_str = ngettext("Downloaded %d out of %d trackpoint...", "Downloaded %d out of %d trackpoints...", sess->trk_total_count);
1041           count = sess->trk_count;
1042           total_count = sess->trk_total_count;
1043           progress_label = sess->trk_label;
1044           break;
1045         default:
1046           tmp_str = ngettext("Downloaded %d out of %d routepoint...", "Downloaded %d out of %d routepoints...", sess->rte_total_count);
1047           count = sess->rte_count;
1048           total_count = sess->rte_total_count;
1049           progress_label = sess->rte_label;
1050           break;
1051         }
1052       }
1053       else {
1054         switch (sess->progress_type) {
1055         case WPT:
1056           tmp_str = ngettext("Uploaded %d out of %d waypoint...", "Uploaded %d out of %d waypoints...", sess->wpt_total_count);
1057           count = sess->wpt_count;
1058           total_count = sess->wpt_total_count;
1059           progress_label = sess->wpt_label;
1060           break;
1061         case TRK:
1062           tmp_str = ngettext("Uploaded %d out of %d trackpoint...", "Uploaded %d out of %d trackpoints...", sess->trk_total_count);
1063           count = sess->trk_count;
1064           total_count = sess->trk_total_count;
1065           progress_label = sess->trk_label;
1066           break;
1067         default:
1068           tmp_str = ngettext("Uploaded %d out of %d routepoint...", "Uploaded %d out of %d routepoints...", sess->rte_total_count);
1069           count = sess->rte_count;
1070           total_count = sess->rte_total_count;
1071           progress_label = sess->rte_label;
1072           break;
1073         }
1074       }
1075     } else {
1076       if (sess->direction == GPS_DOWN) {
1077         switch (sess->progress_type) {
1078         case WPT:
1079           tmp_str = ngettext("Downloaded %d waypoint", "Downloaded %d waypoints", sess->wpt_count);
1080           count = sess->wpt_count;
1081           total_count = sess->wpt_total_count;
1082           progress_label = sess->wpt_label;
1083           break;
1084         case TRK:
1085           tmp_str = ngettext("Downloaded %d trackpoint", "Downloaded %d trackpoints", sess->trk_count);
1086           count = sess->trk_count;
1087           total_count = sess->trk_total_count;
1088           progress_label = sess->trk_label;
1089           break;
1090         default:
1091           tmp_str = ngettext("Downloaded %d routepoint", "Downloaded %d routepoints", sess->rte_count);
1092           count = sess->rte_count;
1093           total_count = sess->rte_total_count;
1094           progress_label = sess->rte_label;
1095           break;
1096         }
1097       }
1098       else {
1099         switch (sess->progress_type) {
1100         case WPT:
1101           tmp_str = ngettext("Uploaded %d waypoint", "Uploaded %d waypoints", sess->wpt_count);
1102           count = sess->wpt_count;
1103           total_count = sess->wpt_total_count;
1104           progress_label = sess->wpt_label;
1105           break;
1106         case TRK:
1107           tmp_str = ngettext("Uploaded %d trackpoint", "Uploaded %d trackpoints", sess->trk_count);
1108           count = sess->trk_count;
1109           total_count = sess->trk_total_count;
1110           progress_label = sess->trk_label;
1111           break;
1112         default:
1113           tmp_str = ngettext("Uploaded %d routepoint", "Uploaded %d routepoints", sess->rte_count);
1114           count = sess->rte_count;
1115           total_count = sess->rte_total_count;
1116           progress_label = sess->rte_label;
1117           break;
1118         }
1119       }
1120     }
1121     g_snprintf(s, 128, tmp_str, count, total_count);
1122     gtk_label_set_text ( GTK_LABEL(progress_label), s );
1123   }
1124   sess->id_count = 0;
1125   g_mutex_unlock(sess->mutex);
1126   return FALSE;
1127 }
1128
1129 static gboolean show_gps_info(GpsSession *sess)
1130 {
1131   g_mutex_lock(sess->mutex);
1132   if (sess->ok) {
1133     gchar s[256];
1134     g_snprintf(s, 256, _("GPS Device: %s"), sess->info);
1135     gtk_label_set_text ( GTK_LABEL(sess->gps_label), s );
1136   }
1137   sess->id_info = 0;
1138   g_mutex_unlock(sess->mutex);
1139   return FALSE;
1140 }
1141
1142 /*
1143  * Common processing for GPS Device information
1144  * It doesn't matter whether we're uploading or downloading
1145  */
1146 static void process_line_for_gps_info ( const gchar *line, GpsSession *sess )
1147 {
1148   if (strstr(line, "PRDDAT")) {
1149     gchar **tokens = g_strsplit(line, " ", 0);
1150     gchar info[128];
1151     int ilen = 0;
1152     int i;
1153     int n_tokens = 0;
1154
1155     while (tokens[n_tokens])
1156       n_tokens++;
1157
1158     // I'm not entirely clear what information this is trying to get...
1159     //  Obviously trying to decipher some kind of text/naming scheme
1160     //  Anyway this will be superceded if there is 'Unit:' information
1161     if (n_tokens > 8) {
1162       for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
1163         guint ch;
1164         sscanf(tokens[i], "%x", &ch);
1165         info[ilen++] = ch;
1166       }
1167       info[ilen++] = 0;
1168       sess->info = g_strdup (info);
1169       sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1170     }
1171     g_strfreev(tokens);
1172   }
1173
1174   /* eg: "Unit:\teTrex Legend HCx Software Version 2.90\n" */
1175   if (strstr(line, "Unit:")) {
1176     gchar **tokens = g_strsplit(line, "\t", 0);
1177     int n_tokens = 0;
1178     while (tokens[n_tokens])
1179       n_tokens++;
1180
1181     if (n_tokens > 1) {
1182       sess->info = g_strdup (tokens[1]);
1183       sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1184     }
1185     g_strfreev(tokens);
1186   }
1187
1188   if (strstr(line, "[ERROR] GPS")) {
1189     gchar **tokens = g_strsplit(line, "\n", 0);
1190     sess->info = g_strdup(tokens[0]);
1191     sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1192     g_strfreev(tokens);
1193   }
1194
1195   if (strstr(line, "an't in")) {
1196     gchar **tokens = g_strsplit(line, "\n", 0);
1197     sess->info = g_strdup(tokens[0]);
1198     sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1199     g_strfreev(tokens);
1200   }
1201
1202   if (strstr(line, "Can't get waypoint")) {
1203     gchar **tokens = g_strsplit(line, "\n", 0);
1204     sess->info = g_strdup(tokens[0]);
1205     sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1206     g_strfreev(tokens);
1207   }
1208 }
1209
1210 static gboolean show_gps_status_working ( GpsSession *sess )
1211 {
1212   g_mutex_lock(sess->mutex);
1213   if ( sess->ok ) {
1214     gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") );
1215   }
1216   sess->id_status_working = 0;
1217   g_mutex_unlock(sess->mutex);
1218   return FALSE;
1219 }
1220
1221 static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1222 {
1223   gchar *line;
1224
1225   if ( !sess->ok ) {
1226     //gps_session_delete(sess);
1227     sess->thread_complete = TRUE;
1228     g_debug ("THREAD EXIT INTERUPPT");
1229     g_thread_exit ( NULL );
1230   }
1231
1232   switch(c) {
1233   case BABEL_DIAG_OUTPUT:
1234     line = (gchar *)data;
1235
1236     sess->id_status_working = gdk_threads_add_idle ( (GSourceFunc)show_gps_status_working, sess );
1237
1238     /* tells us the type of items that will follow */
1239     if (strstr(line, "Xfer Wpt")) {
1240       sess->progress_type = WPT;
1241     }
1242     if (strstr(line, "Xfer Trk")) {
1243       sess->progress_type = TRK;
1244     }
1245     if (strstr(line, "Xfer Rte")) {
1246       sess->progress_type = RTE;
1247     }
1248
1249     process_line_for_gps_info ( line, sess );
1250
1251     if (strstr(line, "RECORD")) {
1252       int lsb, msb, cnt;
1253
1254       if (strlen(line) > 20) {
1255         sscanf(line+17, "%x", &lsb); 
1256         sscanf(line+20, "%x", &msb);
1257         cnt = lsb + msb * 256;
1258         if ( sess->progress_type == RTE ) {
1259           // Maybe a gpsbabel bug/feature (upto at least v1.4.3 or maybe my Garmin device) but the count always seems x2 too many for routepoints
1260           gint mycnt = (cnt / 2) + 1;
1261           sess->rte_total_count = mycnt;
1262           sess->rte_count = 0;
1263         }
1264         else if ( sess->progress_type == WPT ) {
1265           sess->wpt_total_count = cnt;
1266           sess->wpt_count = 0;
1267         }
1268         else {
1269           sess->trk_total_count = cnt;
1270           sess->trk_count = 0;
1271         }
1272         sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess);
1273       }
1274     }
1275     if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") || strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1276       if ( strstr(line, "WPTDAT") )
1277         sess->wpt_count++;
1278       else if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") )
1279         sess->trk_count++;
1280       else
1281         // "RTEHDR" || "RTEWPT"
1282         sess->rte_count++;
1283       sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess);
1284     }
1285     break;
1286   case BABEL_DONE:
1287     break;
1288   default:
1289     break;
1290   }
1291 }
1292
1293 static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1294 {
1295   gchar *line;
1296   static int cnt = 0;
1297
1298   if ( !sess->ok ) {
1299     //gps_session_delete(sess);
1300     sess->thread_complete = TRUE;
1301     g_thread_exit ( NULL );
1302   }
1303
1304   switch(c) {
1305   case BABEL_DIAG_OUTPUT:
1306     line = (gchar *)data;
1307
1308     sess->id_status_working = gdk_threads_add_idle ( (GSourceFunc)show_gps_status_working, sess );
1309
1310     process_line_for_gps_info ( line, sess );
1311
1312     if (strstr(line, "RECORD")) { 
1313       int lsb, msb;
1314
1315       if (strlen(line) > 20) {
1316         sscanf(line+17, "%x", &lsb); 
1317         sscanf(line+20, "%x", &msb);
1318         cnt = lsb + msb * 256;
1319         //sess->count = 0; ?? wpt, trk and/or rte?? or none
1320       }
1321     }
1322     if ( strstr(line, "WPTDAT")) {
1323       sess->progress_type = WPT;
1324       if (sess->wpt_count == 0) {
1325         sess->wpt_total_count = cnt;
1326         sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess );
1327       }
1328       sess->wpt_count++;
1329       sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess );
1330     }
1331     if ( strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1332       sess->progress_type = RTE;
1333       if (sess->rte_count == 0) {
1334         // Maybe a gpsbabel bug/feature (upto at least v1.4.3 or maybe my Garmin device) but the count always seems x2 too many for routepoints
1335         // Anyway since we're uploading - we should know how many points we're going to put!
1336         cnt = (cnt / 2) + 1;
1337         sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess);
1338       }
1339       sess->rte_count++;
1340       sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess);
1341     }
1342     if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) {
1343       sess->progress_type = TRK;
1344       if (sess->trk_count == 0) {
1345         sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess);
1346       }
1347       sess->trk_count++;
1348       sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess);
1349     }
1350     break;
1351   case BABEL_DONE:
1352     break;
1353   default:
1354     break;
1355   }
1356 }
1357
1358 static gboolean show_gps_status_end ( GpsSession *sess )
1359 {
1360   g_mutex_lock(sess->mutex);
1361   // (Download)Failure could be due to a number of reasons: such as no/wrong device attached or GPSBabel not installed
1362   if (!sess->result) {
1363     gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Error: No result.") );
1364   }
1365   else {
1366     if (sess->ok) {
1367       gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Done.") );
1368       gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, TRUE );
1369       gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_REJECT, FALSE );
1370     }
1371   }
1372   sess->id_status_end = 0;
1373   g_mutex_unlock(sess->mutex);
1374   return FALSE;
1375 }
1376
1377 /**
1378  *
1379  */
1380 static void gps_comm_thread(GpsSession *sess)
1381 {
1382   if (sess->direction == GPS_DOWN) {
1383     ProcessOptions po = { sess->babelargs, sess->port, NULL, NULL, NULL, NULL };
1384     sess->result = a_babel_convert_from (sess->vtl, &po, (BabelStatusFunc) gps_download_progress_func, sess, NULL);
1385   }
1386   else {
1387     sess->result = a_babel_convert_to (sess->vtl, sess->track, sess->babelargs, sess->port,
1388         (BabelStatusFunc) gps_upload_progress_func, sess);
1389   }
1390
1391   sess->id_status_end = gdk_threads_add_idle ( (GSourceFunc)show_gps_status_end, sess );
1392   
1393   if (sess->result) {
1394     if (sess->ok) {
1395       /* Do not change the view if we are following the current GPS position */
1396 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1397       if (!sess->realtime_tracking)
1398 #endif
1399       {
1400         if ( sess->vvp && sess->direction == GPS_DOWN ) {
1401           vik_layer_post_read ( VIK_LAYER(sess->vtl), sess->vvp, TRUE );
1402           /* View the data available */
1403           vik_trw_layer_auto_set_view ( sess->vtl, sess->vvp ) ;
1404           vik_layer_emit_update ( VIK_LAYER(sess->vtl) ); // NB update request from background thread
1405         }
1406       }
1407     } else {
1408       /* cancelled */
1409     }
1410   }
1411
1412   if (sess->ok) {
1413     // Thread has completed successfully, but now set to false to avoid 'tell thread to stop' test after dialog run
1414     sess->ok = FALSE;
1415   }
1416
1417   sess->thread_complete = TRUE;
1418   g_thread_exit(NULL);
1419 }
1420
1421 /**
1422  * vik_gps_comm:
1423  * @vtl: The TrackWaypoint layer to operate on
1424  * @track: Operate on a particular track when specified
1425  * @dir: The direction of the transfer
1426  * @protocol: The GPS device communication protocol
1427  * @port: The GPS serial port
1428  * @tracking: If tracking then viewport display update will be skipped
1429  * @vvp: A viewport is required as the display may get updated
1430  * @vlp: A layers panel is needed for uploading as the items maybe modified
1431  * @do_tracks: Whether tracks shoud be processed
1432  * @do_waypoints: Whether waypoints shoud be processed
1433  * @turn_off: Whether we should attempt to turn off the GPS device after the transfer (only some devices support this)
1434  *
1435  * Talk to a GPS Device using a thread which updates a dialog with the progress
1436  */
1437 gint vik_gps_comm ( VikTrwLayer *vtl,
1438                     VikTrack *track,
1439                     vik_gps_dir dir,
1440                     gchar *protocol,
1441                     gchar *port,
1442                     gboolean tracking,
1443                     VikViewport *vvp,
1444                     VikLayersPanel *vlp,
1445                     gboolean do_tracks,
1446                     gboolean do_routes,
1447                     gboolean do_waypoints,
1448                     gboolean turn_off )
1449 {
1450   GpsSession *sess = g_malloc0(sizeof(GpsSession));
1451   char *tracks = NULL;
1452   char *routes = NULL;
1453   char *waypoints = NULL;
1454
1455   sess->mutex = vik_mutex_new();
1456   sess->direction = dir;
1457   sess->vtl = vtl;
1458   sess->track = track;
1459   sess->port = g_strdup(port);
1460   sess->ok = TRUE;
1461   sess->vvp = vvp;
1462
1463   // This must be done inside the main thread as the uniquify causes screen updates
1464   //  (originally performed this nearer the point of upload in the thread)
1465   if ( dir == GPS_UP ) {
1466     // Enforce unique names in the layer upload to the GPS device
1467     // NB this may only be a Garmin device restriction (and may be not every Garmin device either...)
1468     // Thus this maintains the older code in built restriction
1469     if ( ! vik_trw_layer_uniquify ( sess->vtl, vlp ) )
1470       vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(sess->vtl))), VIK_STATUSBAR_INFO,
1471                                   _("Warning - GPS Upload items may overwrite each other") );
1472   }
1473
1474 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1475   sess->realtime_tracking = tracking;
1476 #endif
1477
1478   if (do_tracks)
1479     tracks = "-t";
1480   else
1481     tracks = "";
1482   if (do_routes)
1483     routes = "-r";
1484   else
1485     routes = "";
1486   if (do_waypoints)
1487     waypoints = "-w";
1488   else
1489     waypoints = "";
1490
1491   sess->babelargs = g_strdup_printf("-D 9 %s %s %s -%c %s",
1492                                    tracks, routes, waypoints, (dir == GPS_DOWN) ? 'i' : 'o', protocol);
1493   tracks = NULL;
1494   waypoints = NULL;
1495
1496   GtkWidget *dialog = NULL;
1497
1498   // Only create dialog if we're going to do some transferring
1499   if ( do_tracks || do_waypoints || do_routes ) {
1500     dialog = gtk_dialog_new_with_buttons ( "", VIK_GTK_WINDOW_FROM_LAYER(vtl), 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL );
1501     sess->dialog = dialog;
1502     gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog),
1503                                         GTK_RESPONSE_ACCEPT, FALSE );
1504     gtk_window_set_title ( GTK_WINDOW(sess->dialog), (dir == GPS_DOWN) ? _("GPS Download") : _("GPS Upload") );
1505
1506     sess->status_label = gtk_label_new (_("Status: detecting gpsbabel"));
1507     gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->status_label, FALSE, FALSE, 5 );
1508
1509     sess->gps_label = gtk_label_new (_("GPS device: N/A"));
1510     sess->wpt_label = gtk_label_new ("");
1511     sess->trk_label = gtk_label_new ("");
1512     sess->rte_label = gtk_label_new ("");
1513
1514     gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->gps_label, FALSE, FALSE, 5 );
1515     gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->wpt_label, FALSE, FALSE, 5 );
1516     gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->trk_label, FALSE, FALSE, 5 );
1517     gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->rte_label, FALSE, FALSE, 5 );
1518
1519     gtk_widget_show_all(sess->dialog);
1520
1521     gtk_dialog_set_default_response ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT );
1522     // Starting gps read/write thread
1523 #if GLIB_CHECK_VERSION (2, 32, 0)
1524     g_thread_try_new ( "gps_comm_thread", (GThreadFunc)gps_comm_thread, sess, NULL );
1525 #else
1526     g_thread_create ( (GThreadFunc)gps_comm_thread, sess, FALSE, NULL );
1527 #endif
1528
1529     gtk_dialog_run(GTK_DIALOG(dialog));
1530   }
1531   else {
1532     if ( !turn_off )
1533       a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No GPS items selected for transfer.") );
1534   }
1535
1536   if (sess->ok) {
1537     sess->ok = FALSE;   /* tell thread to stop */
1538   }
1539   else {
1540     if ( turn_off ) {
1541       // No need for thread for powering off device (should be quick operation...) - so use babel command directly:
1542       gchar *device_off = g_strdup_printf("-i %s,%s", protocol, "power_off");
1543       ProcessOptions po = { device_off, port, NULL, NULL, NULL, NULL };
1544       gboolean result = a_babel_convert_from (NULL, &po, NULL, NULL, NULL);
1545       if ( !result )
1546         a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not turn off device.") );
1547       g_free ( device_off );
1548     }
1549   }
1550
1551   if ( dialog ) {
1552     while ( !sess->thread_complete ) {
1553       g_usleep (G_USEC_PER_SEC/10);
1554     }
1555     gtk_widget_destroy(dialog);
1556   }
1557   gps_session_delete(sess);
1558   return 0;
1559 }
1560
1561 static void gps_upload_cb( gpointer layer_and_vlp[2] )
1562 {
1563   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1564   VikLayersPanel *vlp = VIK_LAYERS_PANEL(layer_and_vlp[1]);
1565   VikTrwLayer *vtl = vgl->trw_children[TRW_UPLOAD];
1566   VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1567   VikViewport *vvp = vik_window_viewport(vw);
1568   vik_gps_comm(vtl, NULL, GPS_UP, vgl->protocol, vgl->serial_port, FALSE, vvp, vlp, vgl->upload_tracks, vgl->upload_routes, vgl->upload_waypoints, FALSE);
1569 }
1570
1571 static void gps_download_cb( gpointer layer_and_vlp[2] )
1572 {
1573   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1574   VikTrwLayer *vtl = vgl->trw_children[TRW_DOWNLOAD];
1575   VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1576   VikViewport *vvp = vik_window_viewport(vw);
1577 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1578   vik_gps_comm(vtl, NULL, GPS_DOWN, vgl->protocol, vgl->serial_port, vgl->realtime_tracking, vvp, NULL, vgl->download_tracks, vgl->download_routes, vgl->download_waypoints, FALSE);
1579 #else
1580   vik_gps_comm(vtl, NULL, GPS_DOWN, vgl->protocol, vgl->serial_port, FALSE, vvp, NULL, vgl->download_tracks, vgl->download_routes, vgl->download_waypoints, FALSE);
1581 #endif
1582 }
1583
1584 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] )
1585 {
1586   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1587   // Get confirmation from the user
1588   if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1589                               _("Are you sure you want to delete GPS Upload data?"),
1590                               NULL ) )
1591     return;
1592   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1593   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1594   vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1595 }
1596
1597 static void gps_empty_download_cb( gpointer layer_and_vlp[2] )
1598 {
1599   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1600   // Get confirmation from the user
1601   if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1602                               _("Are you sure you want to delete GPS Download data?"),
1603                               NULL ) )
1604     return;
1605   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1606   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1607   vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1608 }
1609
1610 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1611 static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] )
1612 {
1613   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1614   // Get confirmation from the user
1615   if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1616                               _("Are you sure you want to delete GPS Realtime data?"),
1617                               NULL ) )
1618     return;
1619   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1620   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1621   vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_REALTIME]);
1622 }
1623 #endif
1624
1625 static void gps_empty_all_cb( gpointer layer_and_vlp[2] )
1626 {
1627   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1628   // Get confirmation from the user
1629   if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1630                               _("Are you sure you want to delete All GPS data?"),
1631                               NULL ) )
1632     return;
1633   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1634   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1635   vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1636   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1637   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1638   vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1639 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1640   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1641   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1642   vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_REALTIME]);
1643 #endif
1644 }
1645
1646 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1647 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp)
1648 {
1649   struct LatLon ll;
1650   VikCoord nw, se;
1651   struct LatLon lnw, lse;
1652   vik_viewport_screen_to_coord ( vp, -20, -20, &nw );
1653   vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp)+20, vik_viewport_get_width(vp)+20, &se );
1654   vik_coord_to_latlon ( &nw, &lnw );
1655   vik_coord_to_latlon ( &se, &lse );
1656   if ( vgl->realtime_fix.fix.latitude > lse.lat &&
1657        vgl->realtime_fix.fix.latitude < lnw.lat &&
1658        vgl->realtime_fix.fix.longitude > lnw.lon &&
1659        vgl->realtime_fix.fix.longitude < lse.lon &&
1660        !isnan (vgl->realtime_fix.fix.track) ) {
1661     VikCoord gps;
1662     gint x, y;
1663     gint half_back_x, half_back_y;
1664     gint half_back_bg_x, half_back_bg_y;
1665     gint pt_x, pt_y;
1666     gint ptbg_x;
1667     gint side1_x, side1_y, side2_x, side2_y;
1668     gint side1bg_x, side1bg_y, side2bg_x, side2bg_y;
1669
1670     ll.lat = vgl->realtime_fix.fix.latitude;
1671     ll.lon = vgl->realtime_fix.fix.longitude;
1672     vik_coord_load_from_latlon ( &gps, vik_viewport_get_coord_mode(vp), &ll);
1673     vik_viewport_coord_to_screen ( vp, &gps, &x, &y );
1674
1675     gdouble heading_cos = cos(DEG2RAD(vgl->realtime_fix.fix.track));
1676     gdouble heading_sin = sin(DEG2RAD(vgl->realtime_fix.fix.track));
1677
1678     half_back_y = y+8*heading_cos;
1679     half_back_x = x-8*heading_sin;
1680     half_back_bg_y = y+10*heading_cos;
1681     half_back_bg_x = x-10*heading_sin;
1682
1683     pt_y = half_back_y-24*heading_cos;
1684     pt_x = half_back_x+24*heading_sin;
1685     //ptbg_y = half_back_bg_y-28*heading_cos;
1686     ptbg_x = half_back_bg_x+28*heading_sin;
1687
1688     side1_y = half_back_y+9*heading_sin;
1689     side1_x = half_back_x+9*heading_cos;
1690     side1bg_y = half_back_bg_y+11*heading_sin;
1691     side1bg_x = half_back_bg_x+11*heading_cos;
1692
1693     side2_y = half_back_y-9*heading_sin;
1694     side2_x = half_back_x-9*heading_cos;
1695     side2bg_y = half_back_bg_y-11*heading_sin;
1696     side2bg_x = half_back_bg_x-11*heading_cos;
1697
1698      GdkPoint trian[3] = { { pt_x, pt_y }, {side1_x, side1_y}, {side2_x, side2_y} };
1699      GdkPoint trian_bg[3] = { { ptbg_x, pt_y }, {side1bg_x, side1bg_y}, {side2bg_x, side2bg_y} };
1700
1701      vik_viewport_draw_polygon ( vp, vgl->realtime_track_bg_gc, TRUE, trian_bg, 3 );
1702      vik_viewport_draw_polygon ( vp, vgl->realtime_track_gc, TRUE, trian, 3 );
1703      vik_viewport_draw_rectangle ( vp,
1704          (vgl->realtime_fix.fix.mode > MODE_2D) ? vgl->realtime_track_pt2_gc : vgl->realtime_track_pt1_gc,
1705          TRUE, x-2, y-2, 4, 4 );
1706      //vgl->realtime_track_pt_gc = (vgl->realtime_track_pt_gc == vgl->realtime_track_pt1_gc) ? vgl->realtime_track_pt2_gc : vgl->realtime_track_pt1_gc;
1707   }
1708 }
1709
1710 static VikTrackpoint* create_realtime_trackpoint(VikGpsLayer *vgl, gboolean forced)
1711 {
1712     struct LatLon ll;
1713     GList *last_tp;
1714
1715     /* Note that fix.time is a double, but it should not affect the precision
1716        for most GPS */
1717     time_t cur_timestamp = vgl->realtime_fix.fix.time;
1718     time_t last_timestamp = vgl->last_fix.fix.time;
1719
1720     if (cur_timestamp < last_timestamp) {
1721       return NULL;
1722     }
1723
1724     if (vgl->realtime_record && vgl->realtime_fix.dirty) {
1725       gboolean replace = FALSE;
1726       int heading = isnan(vgl->realtime_fix.fix.track) ? 0 : (int)floor(vgl->realtime_fix.fix.track);
1727       int last_heading = isnan(vgl->last_fix.fix.track) ? 0 : (int)floor(vgl->last_fix.fix.track);
1728       int alt = isnan(vgl->realtime_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->realtime_fix.fix.altitude);
1729       int last_alt = isnan(vgl->last_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->last_fix.fix.altitude);
1730       if (((last_tp = g_list_last(vgl->realtime_track->trackpoints)) != NULL) &&
1731           (vgl->realtime_fix.fix.mode > MODE_2D) &&
1732           (vgl->last_fix.fix.mode <= MODE_2D) &&
1733           ((cur_timestamp - last_timestamp) < 2)) {
1734         g_free(last_tp->data);
1735         vgl->realtime_track->trackpoints = g_list_delete_link(vgl->realtime_track->trackpoints, last_tp);
1736         replace = TRUE;
1737       }
1738       if (replace ||
1739           ((cur_timestamp != last_timestamp) &&
1740           ((forced || 
1741             ((heading < last_heading) && (heading < (last_heading - 3))) || 
1742             ((heading > last_heading) && (heading > (last_heading + 3))) ||
1743             ((alt != VIK_DEFAULT_ALTITUDE) && (alt != last_alt)))))) {
1744         /* TODO: check for new segments */
1745         VikTrackpoint *tp = vik_trackpoint_new();
1746         tp->newsegment = FALSE;
1747         tp->has_timestamp = TRUE;
1748         tp->timestamp = vgl->realtime_fix.fix.time;
1749         tp->altitude = alt;
1750         /* speed only available for 3D fix. Check for NAN when use this speed */
1751         tp->speed = vgl->realtime_fix.fix.speed;  
1752         tp->course = vgl->realtime_fix.fix.track;
1753         tp->nsats = vgl->realtime_fix.satellites_used;
1754         tp->fix_mode = vgl->realtime_fix.fix.mode;
1755
1756         ll.lat = vgl->realtime_fix.fix.latitude;
1757         ll.lon = vgl->realtime_fix.fix.longitude;
1758         vik_coord_load_from_latlon(&tp->coord,
1759              vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1760
1761         vik_track_add_trackpoint ( vgl->realtime_track, tp, TRUE ); // Ensure bounds is recalculated
1762         vgl->realtime_fix.dirty = FALSE;
1763         vgl->realtime_fix.satellites_used = 0;
1764         vgl->last_fix = vgl->realtime_fix;
1765         return tp;
1766       }
1767     }
1768     return NULL;
1769 }
1770
1771 #define VIK_SETTINGS_GPS_STATUSBAR_FORMAT "gps_statusbar_format"
1772
1773 static void update_statusbar ( VikGpsLayer *vgl, VikWindow *vw )
1774 {
1775   gchar *statusbar_format_code = NULL;
1776   gboolean need2free = FALSE;
1777   if ( !a_settings_get_string ( VIK_SETTINGS_GPS_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
1778     // Otherwise use default
1779     statusbar_format_code = g_strdup ( "GSA" );
1780     need2free = TRUE;
1781   }
1782
1783   gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, vgl->trkpt, vgl->trkpt_prev, vgl->realtime_track, vgl->last_fix.fix.climb );
1784   vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
1785   g_free ( msg );
1786
1787   if ( need2free )
1788     g_free ( statusbar_format_code );
1789
1790 }
1791
1792 static void gpsd_raw_hook(VglGpsd *vgpsd, gchar *data)
1793 {
1794   gboolean update_all = FALSE;
1795   VikGpsLayer *vgl = vgpsd->vgl;
1796
1797   if (!vgl->realtime_tracking) {
1798     g_warning("%s: receiving GPS data while not in realtime mode", __PRETTY_FUNCTION__);
1799     return;
1800   }
1801
1802   if ((vgpsd->gpsd.fix.mode >= MODE_2D) &&
1803       !isnan(vgpsd->gpsd.fix.latitude) &&
1804       !isnan(vgpsd->gpsd.fix.longitude)) {
1805
1806     VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1807     VikViewport *vvp = vik_window_viewport(vw);
1808     vgl->realtime_fix.fix = vgpsd->gpsd.fix;
1809     vgl->realtime_fix.satellites_used = vgpsd->gpsd.satellites_used;
1810     vgl->realtime_fix.dirty = TRUE;
1811
1812     struct LatLon ll;
1813     VikCoord vehicle_coord;
1814
1815     ll.lat = vgl->realtime_fix.fix.latitude;
1816     ll.lon = vgl->realtime_fix.fix.longitude;
1817     vik_coord_load_from_latlon(&vehicle_coord,
1818            vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1819
1820     if ((vgl->vehicle_position == VEHICLE_POSITION_CENTERED) ||
1821         (vgl->realtime_jump_to_start && vgl->first_realtime_trackpoint)) {
1822       vik_viewport_set_center_coord(vvp, &vehicle_coord, FALSE);
1823       update_all = TRUE;
1824     }
1825     else if (vgl->vehicle_position == VEHICLE_POSITION_ON_SCREEN) {
1826       const int hdiv = 6;
1827       const int vdiv = 6;
1828       const int px = 20; /* adjust ment in pixels to make sure vehicle is inside the box */
1829       gint width = vik_viewport_get_width(vvp);
1830       gint height = vik_viewport_get_height(vvp);
1831       gint vx, vy;
1832
1833       vik_viewport_coord_to_screen(vvp, &vehicle_coord, &vx, &vy);
1834       update_all = TRUE;
1835       if (vx < (width/hdiv))
1836         vik_viewport_set_center_screen(vvp, vx - width/2 + width/hdiv + px, vy);
1837       else if (vx > (width - width/hdiv))
1838         vik_viewport_set_center_screen(vvp, vx + width/2 - width/hdiv - px, vy);
1839       else if (vy < (height/vdiv))
1840         vik_viewport_set_center_screen(vvp, vx, vy - height/2 + height/vdiv + px);
1841       else if (vy > (height - height/vdiv))
1842         vik_viewport_set_center_screen(vvp, vx, vy + height/2 - height/vdiv - px);
1843       else
1844         update_all = FALSE;
1845     }
1846
1847     vgl->first_realtime_trackpoint = FALSE;
1848
1849     vgl->trkpt = create_realtime_trackpoint ( vgl, FALSE );
1850
1851     if ( vgl->trkpt ) {
1852       if ( vgl->realtime_update_statusbar )
1853         update_statusbar ( vgl, vw );
1854       vgl->trkpt_prev = vgl->trkpt;
1855     }
1856
1857     vik_layer_emit_update ( update_all ? VIK_LAYER(vgl) : VIK_LAYER(vgl->trw_children[TRW_REALTIME]) ); // NB update from background thread
1858   }
1859 }
1860
1861 static gboolean gpsd_data_available(GIOChannel *source, GIOCondition condition, gpointer data)
1862 {
1863   VikGpsLayer *vgl = data;
1864   if (condition == G_IO_IN) {
1865 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1866     if (!gps_poll(&vgl->vgpsd->gpsd)) {
1867 #elif GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1868     if (gps_read(&vgl->vgpsd->gpsd) > -1) {
1869       // Reuse old function to perform operations on the new GPS data
1870       gpsd_raw_hook(vgl->vgpsd, NULL);
1871 #else
1872       // Broken compile
1873 #endif
1874       return TRUE;
1875     }
1876     else {
1877       g_warning("Disconnected from gpsd. Trying to reconnect");
1878       rt_gpsd_disconnect(vgl);
1879       (void)rt_gpsd_connect(vgl, FALSE);
1880     }
1881   }
1882   return FALSE; /* no further calling */
1883 }
1884
1885 /**
1886  * make_track_name:
1887  *
1888  * returns allocated string for a new realtime track name
1889  * NB no i18n ATM
1890  * free string after use
1891  */
1892 static gchar *make_track_name(VikTrwLayer *vtl)
1893 {
1894   const gchar basename[] = "REALTIME";
1895   const gint bufsize = sizeof(basename) + 5;
1896   gchar *name = g_malloc(bufsize);
1897   strcpy(name, basename);
1898   gint i = 2;
1899
1900   while (vik_trw_layer_get_track(vtl, name) != NULL) {
1901     g_snprintf(name, bufsize, "%s#%d", basename, i);
1902     i++;
1903   }
1904   return(name);
1905 }
1906
1907 static gboolean rt_gpsd_try_connect(gpointer *data)
1908 {
1909   VikGpsLayer *vgl = (VikGpsLayer *)data;
1910 #if GPSD_API_MAJOR_VERSION == 3
1911   struct gps_data_t *gpsd = gps_open(vgl->gpsd_host, vgl->gpsd_port);
1912
1913   if (gpsd == NULL) {
1914 #elif GPSD_API_MAJOR_VERSION == 4
1915   vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1916
1917   if (gps_open_r(vgl->gpsd_host, vgl->gpsd_port, /*(struct gps_data_t *)*/vgl->vgpsd) != 0) {
1918 #elif GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1919   vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1920   vgl->vgpsd->gpsd_open = gps_open ( vgl->gpsd_host, vgl->gpsd_port, &vgl->vgpsd->gpsd );
1921   if ( vgl->vgpsd->gpsd_open != 0 ) {
1922 #else
1923 // Delibrately break compilation...
1924 #endif
1925     g_debug("Failed to connect to gpsd at %s (port %s). Will retry in %d seconds",
1926             vgl->gpsd_host, vgl->gpsd_port, vgl->gpsd_retry_interval);
1927     return TRUE;   /* keep timer running */
1928   }
1929
1930 #if GPSD_API_MAJOR_VERSION == 3
1931   vgl->vgpsd = realloc(gpsd, sizeof(VglGpsd));
1932 #endif
1933   vgl->vgpsd->vgl = vgl;
1934
1935   vgl->realtime_fix.dirty = vgl->last_fix.dirty = FALSE;
1936   /* track alt/time graph uses VIK_DEFAULT_ALTITUDE (0.0) as invalid */
1937   vgl->realtime_fix.fix.altitude = vgl->last_fix.fix.altitude = VIK_DEFAULT_ALTITUDE;
1938   vgl->realtime_fix.fix.speed = vgl->last_fix.fix.speed = NAN;
1939
1940   if (vgl->realtime_record) {
1941     VikTrwLayer *vtl = vgl->trw_children[TRW_REALTIME];
1942     vgl->realtime_track = vik_track_new();
1943     vgl->realtime_track->visible = TRUE;
1944     gchar *name = make_track_name(vtl);
1945     vik_trw_layer_add_track(vtl, name, vgl->realtime_track);
1946     g_free(name);
1947   }
1948
1949   vgl->connected_to_gpsd = TRUE;
1950
1951 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1952   gps_set_raw_hook(&vgl->vgpsd->gpsd, gpsd_raw_hook);
1953 #endif
1954
1955   vgl->realtime_io_channel = g_io_channel_unix_new(vgl->vgpsd->gpsd.gps_fd);
1956   vgl->realtime_io_watch_id = g_io_add_watch( vgl->realtime_io_channel,
1957                     G_IO_IN | G_IO_ERR | G_IO_HUP, gpsd_data_available, vgl);
1958
1959 #if GPSD_API_MAJOR_VERSION == 3
1960   gps_query(&vgl->vgpsd->gpsd, "w+x");
1961 #endif
1962 #if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1963    if ( gps_stream(&vgl->vgpsd->gpsd, WATCH_ENABLE, NULL) == -1 )
1964      g_critical ( "gps_stream error" );
1965 #endif
1966
1967   return FALSE;  /* no longer called by timeout */
1968 }
1969
1970 static gboolean rt_ask_retry(VikGpsLayer *vgl)
1971 {
1972   gchar *msg = g_strdup_printf ( _("Failed to connect to gpsd at %s (port %s)\n"
1973                                    "Should Viking keep trying (every %d seconds)?"),
1974                                    vgl->gpsd_host, vgl->gpsd_port, vgl->gpsd_retry_interval );
1975   gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vgl), msg, NULL );
1976   g_free ( msg );
1977   return ans;
1978 }
1979
1980 static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed)
1981 {
1982   vgl->realtime_retry_timer = 0;
1983   if (rt_gpsd_try_connect((gpointer *)vgl)) {
1984     if (vgl->gpsd_retry_interval <= 0) {
1985       g_warning("Failed to connect to gpsd but will not retry because retry interval was set to %d (which is 0 or negative)", vgl->gpsd_retry_interval);
1986       return FALSE;
1987     }
1988     else if (ask_if_failed && !rt_ask_retry(vgl))
1989       return FALSE;
1990     else
1991       vgl->realtime_retry_timer = g_timeout_add_seconds(vgl->gpsd_retry_interval,
1992         (GSourceFunc)rt_gpsd_try_connect, (gpointer *)vgl);
1993   }
1994   return TRUE;
1995 }
1996
1997 static void rt_gpsd_disconnect(VikGpsLayer *vgl)
1998 {
1999   if (vgl->realtime_retry_timer) {
2000     g_source_remove(vgl->realtime_retry_timer);
2001     vgl->realtime_retry_timer = 0;
2002   }
2003   if (vgl->realtime_io_watch_id) {
2004     g_source_remove(vgl->realtime_io_watch_id);
2005     vgl->realtime_io_watch_id = 0;
2006   }
2007   if (vgl->realtime_io_channel) {
2008     GError *error = NULL;
2009     g_io_channel_shutdown (vgl->realtime_io_channel, FALSE, &error);
2010     vgl->realtime_io_channel = NULL;
2011   }
2012   if (vgl->vgpsd) {
2013 #if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
2014     gps_stream(&vgl->vgpsd->gpsd, WATCH_DISABLE, NULL);
2015 #endif
2016     if ( vgl->vgpsd->gpsd_open == 0 )
2017       (void)gps_close(&vgl->vgpsd->gpsd);
2018 #if GPSD_API_MAJOR_VERSION == 3
2019     free(vgl->vgpsd);
2020 #elif GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
2021     g_free(vgl->vgpsd);
2022 #endif
2023     vgl->vgpsd = NULL;
2024   }
2025
2026   if (vgl->realtime_record && vgl->realtime_track) {
2027     if ((vgl->realtime_track->trackpoints == NULL) || (vgl->realtime_track->trackpoints->next == NULL))
2028       vik_trw_layer_delete_track(vgl->trw_children[TRW_REALTIME], vgl->realtime_track);
2029     vgl->realtime_track = NULL;
2030   }
2031   vgl->connected_to_gpsd = FALSE;
2032 }
2033
2034 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2])
2035 {
2036   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
2037   vgl->realtime_tracking = (vgl->realtime_tracking == FALSE);
2038
2039   /* Make sure we are still in the boat with libgps */
2040   g_assert((VIK_GPS_MODE_2D == MODE_2D) && (VIK_GPS_MODE_3D == MODE_3D));
2041
2042   if (vgl->realtime_tracking) {
2043     vgl->first_realtime_trackpoint = TRUE;
2044     if (!rt_gpsd_connect(vgl, TRUE)) {
2045       vgl->first_realtime_trackpoint = FALSE;
2046       vgl->realtime_tracking = FALSE;
2047       vgl->trkpt = NULL;
2048     }
2049   }
2050   else {  /* stop realtime tracking */
2051     vgl->first_realtime_trackpoint = FALSE;
2052     vgl->trkpt = NULL;
2053     rt_gpsd_disconnect(vgl);
2054   }
2055 }
2056 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */