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