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