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