]> git.street.me.uk Git - andy/viking.git/blob - src/vikgpslayer.c
Added gpslayer
[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  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "viking.h"
23 #include "vikgpslayer_pixmap.h"
24 #include "babel.h"
25
26 #include <string.h>
27 #include <glib.h>
28 #include <glib/gprintf.h>
29
30 #define DISCONNECT_UPDATE_SIGNAL(vl, val) g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, val)
31
32 static VikGpsLayer *gps_layer_copy ( VikGpsLayer *val, gpointer vp );
33 static void gps_layer_marshall( VikGpsLayer *val, guint8 **data, gint *len );
34 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
35 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp );
36 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id );
37
38 static void gps_layer_change_coord_mode ( VikGpsLayer *val, VikCoordMode mode );
39 static void gps_layer_add_menu_items( VikGpsLayer *vtl, GtkMenu *menu, gpointer vlp );
40 static void gps_layer_drag_drop_request ( VikGpsLayer *val_src, VikGpsLayer *val_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
41
42
43 static void gps_upload_cb( gpointer layer_and_vlp[2] );
44 static void gps_download_cb( gpointer layer_and_vlp[2] );
45
46 typedef enum {GARMIN_P = 0, MAGELLAN_P, NUM_PROTOCOLS} vik_gps_proto;
47 static gchar * params_protocols[] = {"Garmin", "Magellan", NULL};
48 static gchar * protocols_args[]   = {"garmin", "magellan"};
49 /*#define NUM_PROTOCOLS (sizeof(params_protocols)/sizeof(params_protocols[0]) - 1) */
50 static gchar * params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1", NULL};
51 #define NUM_PORTS (sizeof(params_ports)/sizeof(params_ports[0]) - 1)
52 typedef enum {GPS_DOWN=0, GPS_UP} gps_dir;
53
54 typedef struct {
55   GMutex *mutex;
56   gps_dir direction;
57   gchar *port;
58   gboolean ok;
59   gint total_count;
60   gint count;
61   VikTrwLayer *vtl;
62   gchar *cmd_args;
63   gchar * window_title;
64   GtkWidget *dialog;
65   GtkWidget *status_label;
66   GtkWidget *gps_label;
67   GtkWidget *ver_label;
68   GtkWidget *id_label;
69   GtkWidget *wp_label;
70   GtkWidget *progress_label;
71   GtkWidget *trk_label;
72 } GpsSession;
73 static void gps_session_delete(GpsSession *sess);
74
75 static VikLayerParam gps_layer_params[] = {
76   { "gps_protocol", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "GPS Protocol:", VIK_LAYER_WIDGET_COMBOBOX, params_protocols, NULL},
77   { "gps_port", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Serial Port:", VIK_LAYER_WIDGET_COMBOBOX, params_ports, NULL},
78 };
79 static enum {PARAM_PROTOCOL=0, PARAM_PORT, NUM_PARAMS};
80
81 VikLayerInterface vik_gps_layer_interface = {
82   "GPS",
83   &gpslayer_pixbuf,
84
85   NULL,
86   0,
87
88   gps_layer_params,
89   NUM_PARAMS,
90   NULL,
91   0,
92
93   (VikLayerFuncCreate)                  vik_gps_layer_create,
94   (VikLayerFuncRealize)                 vik_gps_layer_realize,
95   (VikLayerFuncPostRead)                NULL,
96   (VikLayerFuncFree)                    vik_gps_layer_free,
97
98   (VikLayerFuncProperties)              NULL,
99   (VikLayerFuncDraw)                    vik_gps_layer_draw,
100   (VikLayerFuncChangeCoordMode)         gps_layer_change_coord_mode,
101
102   (VikLayerFuncAddMenuItems)            gps_layer_add_menu_items,
103   (VikLayerFuncSublayerAddMenuItems)    NULL,
104
105   (VikLayerFuncSublayerRenameRequest)   NULL,
106   (VikLayerFuncSublayerToggleVisible)   NULL,
107
108   (VikLayerFuncCopy)                    gps_layer_copy,
109   (VikLayerFuncMarshall)                gps_layer_marshall,
110   (VikLayerFuncUnmarshall)              gps_layer_unmarshall,
111
112   (VikLayerFuncSetParam)                gps_layer_set_param,
113   (VikLayerFuncGetParam)                gps_layer_get_param,
114
115   (VikLayerFuncReadFileData)            NULL,
116   (VikLayerFuncWriteFileData)           NULL,
117
118   (VikLayerFuncDeleteItem)              NULL,
119   (VikLayerFuncCopyItem)                NULL,
120   (VikLayerFuncPasteItem)               NULL,
121   (VikLayerFuncFreeCopiedItem)          NULL,
122   (VikLayerFuncDragDropRequest)         gps_layer_drag_drop_request,
123 };
124
125 static enum {TRW_DOWNLOAD, TRW_UPLOAD, NUM_TRW};
126 static gchar * trw_names[] = {"GPS Download", "GPS Upload"};
127 struct _VikGpsLayer {
128   VikLayer vl;
129   VikTrwLayer * trw_children[NUM_TRW];
130   /* params */
131   guint protocol_id;
132   guint serial_port_id;
133 };
134
135 GType vik_gps_layer_get_type ()
136 {
137   static GType val_type = 0;
138
139   if (!val_type)
140   {
141     static const GTypeInfo val_info =
142     {
143       sizeof (VikGpsLayerClass),
144       NULL, /* base_init */
145       NULL, /* base_finalize */
146       NULL, /* class init */
147       NULL, /* class_finalize */
148       NULL, /* class_data */
149       sizeof (VikGpsLayer),
150       0,
151       NULL /* instance init */
152     };
153     val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikGpsLayer", &val_info, 0 );
154   }
155
156   return val_type;
157 }
158
159 VikGpsLayer *vik_gps_layer_create (VikViewport *vp)
160 {
161   VikGpsLayer *rv = vik_gps_layer_new ();
162   vik_layer_rename ( VIK_LAYER(rv), vik_gps_layer_interface.name );
163   rv->trw_children[TRW_UPLOAD] = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vp, NULL, FALSE ));
164   rv->trw_children[TRW_DOWNLOAD] = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vp, NULL, FALSE ));
165   return rv;
166 }
167
168 static VikGpsLayer *gps_layer_copy ( VikGpsLayer *vgl, gpointer vp )
169 {
170   VikGpsLayer *rv = vik_gps_layer_new ();
171   int i;
172
173   for (i = 0; i < NUM_TRW; i++) {
174     rv->trw_children[i] = vik_layer_copy(VIK_LAYER(vgl->trw_children[i]), vp);
175     g_signal_connect_swapped ( G_OBJECT(rv->trw_children[i]), "update", G_CALLBACK(vik_layer_emit_update), rv );
176   }
177
178   return rv;
179 }
180
181 /* "Copy" */
182 static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *datalen )
183 {
184   VikLayer *child_layer;
185   guint8 *ld; 
186   gint ll;
187   GByteArray* b = g_byte_array_new ();
188   gint len;
189   gint i;
190
191 #define alm_append(obj, sz)     \
192   len = (sz);                   \
193   g_byte_array_append ( b, (guint8 *)&len, sizeof(len) );       \
194   g_byte_array_append ( b, (guint8 *)(obj), len );
195
196   vik_layer_marshall_params(VIK_LAYER(vgl), &ld, &ll);
197   alm_append(ld, ll);
198   g_free(ld);
199
200   for (i = 0; i < NUM_TRW; i++) {
201     child_layer = VIK_LAYER(vgl->trw_children[i]);
202     vik_layer_marshall(child_layer, &ld, &ll);
203     if (ld) {
204       alm_append(ld, ll);
205       g_free(ld);
206     }
207   }
208   *data = b->data;
209   *datalen = b->len;
210   g_byte_array_free(b, FALSE);
211 #undef alm_append
212 }
213
214 /* "Paste" */
215 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
216 {
217 #define alm_size (*(gint *)data)
218 #define alm_next \
219   len -= sizeof(gint) + alm_size; \
220   data += sizeof(gint) + alm_size;
221   
222   VikGpsLayer *rv = vik_gps_layer_new();
223   VikLayer *child_layer;
224   gint i;
225
226   vik_layer_unmarshall_params ( VIK_LAYER(rv), data+sizeof(gint), alm_size, vvp );
227   alm_next;
228
229   i = 0;
230   while (len>0 && i < NUM_TRW) {
231     child_layer = vik_layer_unmarshall ( data + sizeof(gint), alm_size, vvp );
232     if (child_layer) {
233       rv->trw_children[i++] = child_layer;
234       g_signal_connect_swapped ( G_OBJECT(child_layer), "update", G_CALLBACK(vik_layer_emit_update), rv );
235     }
236     alm_next;
237   }
238   //  g_print("gps_layer_unmarshall ended with len=%d\n", len);
239   g_assert(len == 0);
240   return rv;
241 #undef alm_size
242 #undef alm_next
243 }
244
245 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp )
246 {
247   switch ( id )
248   {
249     case PARAM_PROTOCOL:
250       if (data.u < NUM_PROTOCOLS)
251         vgl->protocol_id = data.u;
252       else
253         g_warning("Unknown GPS Protocol");
254       break;
255     case PARAM_PORT:
256       if (data.u < NUM_PORTS)
257         vgl->serial_port_id = data.u;
258       else
259         g_warning("Unknown serial port device");
260       break;
261   }
262
263   return TRUE;
264 }
265
266 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id )
267 {
268   VikLayerParamData rv;
269   switch ( id )
270   {
271     case PARAM_PROTOCOL:
272       rv.u = vgl->protocol_id;
273       break;
274     case PARAM_PORT:
275       rv.u = vgl->serial_port_id;
276       break;
277     default:
278       g_warning("gps_layer_get_param(): unknown parameter");
279   }
280
281   /* fprintf(stderr, "gps_layer_get_param() called\n"); */
282
283   return rv;
284 }
285
286 VikGpsLayer *vik_gps_layer_new ()
287 {
288   gint i;
289   VikGpsLayer *vgl = VIK_GPS_LAYER ( g_object_new ( VIK_GPS_LAYER_TYPE, NULL ) );
290   vik_layer_init ( VIK_LAYER(vgl), VIK_LAYER_GPS );
291   for (i = 0; i < NUM_TRW; i++) {
292     vgl->trw_children[i] = NULL;
293   }
294
295   /* Setting params here */
296   vgl->protocol_id = 0;
297   vgl->serial_port_id = 0;
298
299   return vgl;
300 }
301
302 void vik_gps_layer_draw ( VikGpsLayer *vgl, gpointer data )
303 {
304   gint i;
305
306   for (i = 0; i < NUM_TRW; i++) {
307     vik_layer_draw(vgl->trw_children[i], data);
308   }
309 }
310
311 static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode )
312 {
313   gint i;
314   for (i = 0; i < NUM_TRW; i++) {
315     vik_layer_change_coord_mode(vgl->trw_children[i], mode);
316   }
317 }
318
319 static void gps_layer_add_menu_items( VikGpsLayer *vgl, GtkMenu *menu, gpointer vlp )
320 {
321   static gpointer pass_along[2];
322   GtkWidget *item;
323   pass_along[0] = vgl;
324   pass_along[1] = vlp;
325
326   item = gtk_menu_item_new();
327   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
328   gtk_widget_show ( item );
329
330   item = gtk_menu_item_new_with_label ( "Upload to GPS" );
331   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_upload_cb), pass_along );
332   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
333   gtk_widget_show ( item );
334
335   item = gtk_menu_item_new_with_label ( "Download from GPS" );
336   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_download_cb), pass_along );
337   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
338   gtk_widget_show ( item );
339
340 }
341
342 static void disconnect_layer_signal ( VikLayer *vl, VikGpsLayer *vgl )
343 {
344 #if 0
345   guint num_signals = DISCONNECT_UPDATE_SIGNAL(vl,vgl);
346   fprintf(stderr, "disconnect_layer_signal(): num_signals = %d\n", num_signals);
347 #endif
348
349   g_assert(DISCONNECT_UPDATE_SIGNAL(vl,vgl)==1);
350 }
351
352 void vik_gps_layer_free ( VikGpsLayer *vgl )
353 {
354 #ifdef XXXXXXXXXXXXXXX
355   g_list_foreach ( val->children, (GFunc)(disconnect_layer_signal), val );
356   g_list_foreach ( val->children, (GFunc)(g_object_unref), NULL );
357   g_list_free ( val->children );
358 #endif /* XXXXXXXXXXXXXXX */
359   gint i;
360   for (i = 0; i < NUM_TRW; i++) {
361     disconnect_layer_signal(vgl->trw_children[i], vgl);
362     g_object_unref(vgl->trw_children[i]);
363   }
364 }
365
366 static void delete_layer_iter ( VikLayer *vl )
367 {
368   if ( vl->realized )
369     vik_treeview_item_delete ( vl->vt, &(vl->iter) );
370 }
371
372 gboolean vik_gps_layer_delete ( VikGpsLayer *vgl, GtkTreeIter *iter )
373 {
374   gint i;
375   VikLayer *l = VIK_LAYER( vik_treeview_item_get_pointer ( VIK_LAYER(vgl)->vt, iter ) );
376   gboolean was_visible = l->visible;
377
378   vik_treeview_item_delete ( VIK_LAYER(vgl)->vt, iter );
379   for (i = 0; i < NUM_TRW; i++) {
380     if (vgl->trw_children[i] == l)
381       vgl->trw_children[i] = NULL;
382   }
383   g_assert(DISCONNECT_UPDATE_SIGNAL(l,vgl)==1);
384   g_object_unref ( l );
385
386   return was_visible;
387 }
388
389 #if 0
390 /* returns 0 == we're good, 1 == didn't find any layers, 2 == got rejected */
391 guint vik_gps_layer_tool ( VikGpsLayer *val, guint16 layer_type, VikToolInterfaceFunc tool_func, GdkEventButton *event, VikViewport *vvp )
392 {
393   GList *iter = val->children;
394   gboolean found_rej = FALSE;
395   if (!iter)
396     return FALSE;
397   while (iter->next)
398     iter = iter->next;
399
400   while ( iter )
401   {
402     /* if this layer "accepts" the tool call */
403     if ( VIK_LAYER(iter->data)->visible && VIK_LAYER(iter->data)->type == layer_type )
404     {
405       if ( tool_func ( VIK_LAYER(iter->data), event, vvp ) )
406         return 0;
407       else
408         found_rej = TRUE;
409     }
410
411     /* recursive -- try the same for the child gps layer. */
412     else if ( VIK_LAYER(iter->data)->visible && VIK_LAYER(iter->data)->type == VIK_LAYER_GPS )
413     {
414       gint rv = vik_gps_layer_tool(VIK_GPS_LAYER(iter->data), layer_type, tool_func, event, vvp);
415       if ( rv == 0 )
416         return 0;
417       else if ( rv == 2 )
418         found_rej = TRUE;
419     }
420     iter = iter->prev;
421   }
422   return found_rej ? 2 : 1; /* no one wanted to accept the tool call in this layer */
423 }
424 #endif 
425
426 void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIter *layer_iter )
427 {
428   GtkTreeIter iter;
429   int ix;
430
431   for (ix = 0; ix < NUM_TRW; ix++) {
432     VikLayer * trw = vgl->trw_children[ix];
433     vik_treeview_add_layer ( VIK_LAYER(vgl)->vt, layer_iter, &iter,
434         trw_names[ix], vgl, 
435         trw, trw->type, trw->type );
436     if ( ! trw->visible )
437       vik_treeview_item_set_visible ( VIK_LAYER(vgl)->vt, &iter, FALSE );
438     vik_layer_realize ( trw, VIK_LAYER(vgl)->vt, &iter );
439     g_signal_connect_swapped ( G_OBJECT(trw), "update", G_CALLBACK(vik_layer_emit_update), vgl );
440   }
441 }
442
443 static void gps_layer_drag_drop_request ( VikGpsLayer *val_src, VikGpsLayer *val_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
444 {
445   VikTreeview *vt = VIK_LAYER(val_src)->vt;
446   VikLayer *vl = vik_treeview_item_get_pointer(vt, src_item_iter);
447   GtkTreeIter dest_iter;
448   gchar *dp;
449   gboolean target_exists;
450
451   /* DEBUG */
452   fprintf(stderr, "gps_layer_drag_drop_request() called\n");
453
454   dp = gtk_tree_path_to_string(dest_path);
455   target_exists = vik_treeview_get_iter_from_path_str(vt, &dest_iter, dp);
456
457   /* vik_gps_layer_delete unrefs, but we don't want that here.
458    * we're still using the layer. */
459   g_object_ref ( vl );
460   vik_gps_layer_delete(val_src, src_item_iter);
461
462 #ifdef XXXXXXXXXXXXXXXXXXXXXXXXX
463   TODO:
464   if (target_exists) {
465     vik_gps_layer_insert_layer(val_dest, vl, &dest_iter);
466   } else {
467     vik_gps_layer_insert_layer(val_dest, vl, NULL); /* append */
468   }
469 #endif /* XXXXXXXXXXXXXXXXXXXXXXXXX */
470   g_free(dp);
471 }
472
473 static void gps_session_delete(GpsSession *sess)
474 {
475   /* TODO */
476   g_mutex_free(sess->mutex);
477   g_free(sess->cmd_args);
478
479   g_free(sess);
480
481 }
482
483 static void set_total_count(gint cnt, GpsSession *sess)
484 {
485   gchar s[128];
486   gdk_threads_enter();
487   g_mutex_lock(sess->mutex);
488   if (sess->ok) {
489     g_sprintf(s, "%s %d %s...",
490         (sess->direction == GPS_DOWN) ? "Downloading" : "Uploading", cnt,
491         (sess->progress_label == sess->wp_label) ? "waypoints" : "trackpoints");
492     gtk_label_set_text ( GTK_LABEL(sess->progress_label), s );
493     gtk_widget_show ( sess->progress_label );
494     sess->total_count = cnt;
495   }
496   g_mutex_unlock(sess->mutex);
497   gdk_threads_leave();
498 }
499
500 static void set_current_count(gint cnt, GpsSession *sess)
501 {
502   gchar s[128];
503   gchar *dir_str = (sess->direction == GPS_DOWN) ? "Downloaded" : "Uploaded";
504
505   gdk_threads_enter();
506   g_mutex_lock(sess->mutex);
507   if (sess->ok) {
508     if (cnt < sess->total_count) {
509       g_sprintf(s, "%s %d out of %d %s...", dir_str, cnt, sess->total_count, (sess->progress_label == sess->wp_label) ? "waypoints" : "trackpoints");
510     } else {
511       g_sprintf(s, "%s %d %s.", dir_str, cnt, (sess->progress_label == sess->wp_label) ? "waypoints" : "trackpoints");
512     }     
513     gtk_label_set_text ( GTK_LABEL(sess->progress_label), s );
514   }
515   g_mutex_unlock(sess->mutex);
516   gdk_threads_leave();
517 }
518
519 static void set_gps_info(const gchar *info, GpsSession *sess)
520 {
521   gchar s[256];
522   gdk_threads_enter();
523   g_mutex_lock(sess->mutex);
524   if (sess->ok) {
525     g_sprintf(s, "GPS Device: %s", info);
526     gtk_label_set_text ( GTK_LABEL(sess->gps_label), s );
527   }
528   g_mutex_unlock(sess->mutex);
529   gdk_threads_leave();
530 }
531
532 static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
533 {
534   gchar *line;
535
536   gdk_threads_enter ();
537   g_mutex_lock(sess->mutex);
538   if (!sess->ok) {
539     g_mutex_unlock(sess->mutex);
540     gps_session_delete(sess);
541     gdk_threads_leave();
542     g_thread_exit ( NULL );
543   }
544   g_mutex_unlock(sess->mutex);
545   gdk_threads_leave ();
546
547   switch(c) {
548   case BABEL_DIAG_OUTPUT:
549     line = (gchar *)data;
550
551     /* tells us how many items there will be */
552     if (strstr(line, "Xfer Wpt")) { 
553       sess->progress_label = sess->wp_label;
554     }
555     if (strstr(line, "Xfer Trk")) { 
556       sess->progress_label = sess->trk_label;
557     }
558     if (strstr(line, "PRDDAT")) {
559       gchar **tokens = g_strsplit(line, " ", 0);
560       gchar info[128];
561       int ilen = 0;
562       int i;
563
564       for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
565         guint ch;
566         sscanf(tokens[i], "%x", &ch);
567         info[ilen++] = ch;
568       }
569       info[ilen++] = 0;
570       set_gps_info(info, sess);
571     }
572     if (strstr(line, "RECORD")) { 
573       int lsb, msb, cnt;
574
575       sscanf(line+17, "%x", &lsb); 
576       sscanf(line+20, "%x", &msb);
577       cnt = lsb + msb * 256;
578       set_total_count(cnt, sess);
579       sess->count = 0;
580     }
581     if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) {
582       sess->count++;
583       set_current_count(sess->count, sess);
584     }
585     break;
586   case BABEL_DONE:
587     break;
588   default:
589     break;
590   }
591
592 }
593
594 static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
595 {
596   gchar *line;
597   static int cnt = 0;
598
599   gdk_threads_enter ();
600   g_mutex_lock(sess->mutex);
601   if (!sess->ok) {
602     g_mutex_unlock(sess->mutex);
603     gps_session_delete(sess);
604     gdk_threads_leave();
605     g_thread_exit ( NULL );
606   }
607   g_mutex_unlock(sess->mutex);
608   gdk_threads_leave ();
609
610   switch(c) {
611   case BABEL_DIAG_OUTPUT:
612     line = (gchar *)data;
613
614     if (strstr(line, "PRDDAT")) {
615       gchar **tokens = g_strsplit(line, " ", 0);
616       gchar info[128];
617       int ilen = 0;
618       int i;
619
620       for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
621         guint ch;
622         sscanf(tokens[i], "%x", &ch);
623         info[ilen++] = ch;
624       }
625       info[ilen++] = 0;
626       set_gps_info(info, sess);
627     }
628     if (strstr(line, "RECORD")) { 
629       int lsb, msb;
630
631       sscanf(line+17, "%x", &lsb); 
632       sscanf(line+20, "%x", &msb);
633       cnt = lsb + msb * 256;
634       /* set_total_count(cnt, sess); */
635       sess->count = 0;
636     }
637     if ( strstr(line, "WPTDAT")) {
638       if (sess->count == 0) {
639         sess->progress_label = sess->wp_label;
640         set_total_count(cnt, sess);
641       }
642       sess->count++;
643       set_current_count(sess->count, sess);
644
645     }
646     if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) {
647       if (sess->count == 0) {
648         sess->progress_label = sess->trk_label;
649         set_total_count(cnt, sess);
650       }
651       sess->count++;
652       set_current_count(sess->count, sess);
653     }
654     break;
655   case BABEL_DONE:
656     break;
657   default:
658     break;
659   }
660
661 }
662
663 static void gps_comm_thread(GpsSession *sess)
664 {
665   gboolean result;
666
667   if (sess->direction == GPS_DOWN)
668     result = a_babel_convert_from (sess->vtl, sess->cmd_args,
669         (BabelStatusFunc) gps_download_progress_func, sess->port, sess);
670   else
671     result = a_babel_convert_to (sess->vtl, sess->cmd_args,
672         (BabelStatusFunc) gps_upload_progress_func, sess->port, sess);
673
674   gdk_threads_enter();
675   if (!result) {
676     gtk_label_set_text ( GTK_LABEL(sess->status_label), "Error: couldn't find gpsbabel." );
677   } 
678   else {
679     g_mutex_lock(sess->mutex);
680     if (sess->ok) {
681       gtk_label_set_text ( GTK_LABEL(sess->status_label), "Done." );
682       gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, TRUE );
683       gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_REJECT, FALSE );
684     } else {
685       /* canceled */
686     }
687     g_mutex_unlock(sess->mutex);
688   }
689
690   g_mutex_lock(sess->mutex);
691   if (sess->ok) {
692     sess->ok = FALSE;
693     g_mutex_unlock(sess->mutex);
694   }
695   else {
696     g_mutex_unlock(sess->mutex);
697     gps_session_delete(sess);
698   }
699   gdk_threads_leave();
700   g_thread_exit(NULL);
701 }
702
703 static gint gps_comm(VikTrwLayer *vtl, gps_dir dir, vik_gps_proto proto, gchar *port) {
704   GpsSession *sess = g_malloc(sizeof(GpsSession));
705
706   sess->mutex = g_mutex_new();
707   sess->direction = dir;
708   sess->vtl = vtl;
709   sess->port = g_strdup(port);
710   sess->ok = TRUE;
711   sess->cmd_args = g_strdup_printf("-D 9 -t -w -%c %s",
712       (dir == GPS_DOWN) ? 'i' : 'o', protocols_args[proto]);
713   sess->window_title = (dir == GPS_DOWN) ? "GPS Download" : "GPS Upload";
714
715   sess->dialog = gtk_dialog_new_with_buttons ( "", NULL, 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL );
716   gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog),
717       GTK_RESPONSE_ACCEPT, FALSE );
718   gtk_window_set_title ( GTK_WINDOW(sess->dialog), sess->window_title );
719
720   sess->status_label = gtk_label_new ("Status: detecting gpsbabel");
721   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(sess->dialog)->vbox),
722       sess->status_label, FALSE, FALSE, 5 );
723   gtk_widget_show_all(sess->status_label);
724
725   sess->gps_label = gtk_label_new ("GPS device: N/A");
726   sess->ver_label = gtk_label_new ("");
727   sess->id_label = gtk_label_new ("");
728   sess->wp_label = gtk_label_new ("");
729   sess->trk_label = gtk_label_new ("");
730
731   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(sess->dialog)->vbox), sess->gps_label, FALSE, FALSE, 5 );
732   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(sess->dialog)->vbox), sess->wp_label, FALSE, FALSE, 5 );
733   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(sess->dialog)->vbox), sess->trk_label, FALSE, FALSE, 5 );
734
735   gtk_widget_show_all(sess->dialog);
736
737   sess->progress_label = sess->wp_label;
738   sess->total_count = -1;
739
740   /* TODO: starting gps read/write thread here */
741   g_thread_create((GThreadFunc)gps_comm_thread, sess, FALSE, NULL );
742
743   gtk_dialog_run(GTK_DIALOG(sess->dialog));
744
745   gtk_widget_destroy(sess->dialog);
746
747   g_mutex_lock(sess->mutex);
748   if (sess->ok) {
749     sess->ok = FALSE;   /* tell thread to stop */
750     g_mutex_unlock(sess->mutex);
751   }
752   else {
753     g_mutex_unlock(sess->mutex);
754     gps_session_delete(sess);
755   }
756
757   // fprintf(stderr, "\"gps_comm: cmd_args=%s\" port=%s\n", sess->cmd_args, sess->port);
758   return 0;
759 }
760
761 static void gps_upload_cb( gpointer layer_and_vlp[2] )
762 {
763   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
764   VikTrwLayer *vtl = vgl->trw_children[TRW_UPLOAD];
765   gps_comm(vtl, GPS_UP, vgl->protocol_id, params_ports[vgl->serial_port_id]);
766 }
767
768 static void gps_download_cb( gpointer layer_and_vlp[2] )
769 {
770   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
771   VikTrwLayer *vtl = vgl->trw_children[TRW_DOWNLOAD];
772   gps_comm(vtl, GPS_DOWN, vgl->protocol_id, params_ports[vgl->serial_port_id]);
773 }