From d3b7baa79d155131dd3e55ddffd881913378aa59 Mon Sep 17 00:00:00 2001 From: Rob Norris Date: Sat, 23 Apr 2016 12:18:50 +0100 Subject: [PATCH] Threading rewrite to remove use of unsupported cross platform threading update of the GUI. When Viking is run on a real Windows system (i.e. not just under wine) that has been built via the new cross build mechanism, it will lock up whenever a window is moved. Remove all use of gdk_threads_enter() / gdk_threads_leave() and always use the main thread to update the GUI. Thus make extensive changes to ensure updates are directed through gdk_threads_add_idle() or gtk_idle_add() and protection to prevent use of data that has gone out of scope. This should be more reliable. --- src/background.c | 144 +++++++++----- src/bingmapsource.c | 18 +- src/datasource_gps.c | 223 +++++++++++++--------- src/main.c | 4 - src/vikdemlayer.c | 8 +- src/vikgpslayer.c | 444 +++++++++++++++++++++++++++---------------- src/viktrwlayer.c | 4 - src/vikwindow.c | 2 - 8 files changed, 517 insertions(+), 330 deletions(-) diff --git a/src/background.c b/src/background.c index c49e517f..24c03576 100644 --- a/src/background.c +++ b/src/background.c @@ -38,10 +38,11 @@ static GThreadPool *thread_pool_local_mapnik = NULL; #endif static gboolean stop_all_threads = FALSE; -static GtkWidget *bgwindow = NULL; -static GtkWidget *bgtreeview = NULL; +// A single store of background items for all Windows +// Must always be accessed in the main thread static GtkListStore *bgstore = NULL; +G_LOCK_DEFINE_STATIC(window_list); // Still only actually updating the statusbar though static GSList *windows_to_update = NULL; @@ -64,15 +65,37 @@ void a_background_update_status ( VikWindow *vw, gpointer data ) vik_window_statusbar_update ( vw, buf, VIK_STATUSBAR_ITEMS ); } +// NB can be called from any thread static void background_thread_update () { + G_LOCK(window_list); g_slist_foreach ( windows_to_update, (GFunc) a_background_update_status, NULL ); + G_UNLOCK(window_list); +} + +typedef struct { + GtkTreeIter *iter; + gdouble percent; +} progress_t; + +// In main thread +static gboolean idle_progress_update ( gpointer user_data ) +{ + progress_t *progress = user_data; + if ( bgstore ) + gtk_list_store_set ( GTK_LIST_STORE(bgstore), progress->iter, PROGRESS_COLUMN, progress->percent, -1 ); + g_free ( progress ); + return FALSE; } /** * a_background_thread_progress: * @callbackdata: Thread data * @fraction: The value should be between 0.0 and 1.0 indicating percentage of the task complete + * + * Called from other threads + * + * Returns a non zero number if the thread should be terminated */ int a_background_thread_progress ( gpointer callbackdata, gdouble fraction ) { @@ -82,9 +105,10 @@ int a_background_thread_progress ( gpointer callbackdata, gdouble fraction ) gdouble myfraction = fabs(fraction); if ( myfraction > 1.0 ) myfraction = 1.0; - gdk_threads_enter(); - gtk_list_store_set( GTK_LIST_STORE(bgstore), (GtkTreeIter *) args[5], PROGRESS_COLUMN, myfraction*100, -1 ); - gdk_threads_leave(); + progress_t *progress = g_malloc0 ( sizeof(progress_t) ); + progress->percent = myfraction * 100; + progress->iter = (GtkTreeIter*)args[5]; + gdk_threads_add_idle ( idle_progress_update, progress ); } args[6] = GINT_TO_POINTER(GPOINTER_TO_INT(args[6])-1); @@ -106,10 +130,11 @@ static void thread_die ( gpointer args[VIK_BG_NUM_ARGS] ) background_thread_update (); } - g_free ( args[5] ); /* free iter */ g_free ( args ); } +// Called from other threads +// Returns a non zero number if the thread should be terminated int a_background_testcancel ( gpointer callbackdata ) { gpointer *args = (gpointer *) callbackdata; @@ -125,6 +150,21 @@ int a_background_testcancel ( gpointer callbackdata ) return 0; } +typedef struct { + GtkTreeIter *iter; +} remove_t; + +// Called from the main thread +static gboolean idle_remove ( gpointer user_data ) +{ + remove_t *remove = user_data; + if ( bgstore ) + gtk_list_store_remove ( bgstore, remove->iter ); + g_free ( remove->iter ); + return FALSE; +} + +// Called from other threads static void thread_helper ( gpointer args[VIK_BG_NUM_ARGS], gpointer user_data ) { /* unpack args */ @@ -135,11 +175,11 @@ static void thread_helper ( gpointer args[VIK_BG_NUM_ARGS], gpointer user_data ) func ( userdata, args ); - gdk_threads_enter(); - if ( ! args[0] ) - gtk_list_store_remove ( bgstore, (GtkTreeIter *) args[5] ); - gdk_threads_leave(); - + if ( ! args[0] ) { + remove_t *remove = g_malloc0 ( sizeof(remove_t) ); + remove->iter = args[5]; + gdk_threads_add_idle ( idle_remove, remove ); + } thread_die ( args ); } @@ -191,16 +231,7 @@ void a_background_thread ( Background_Pool_Type bp, GtkWindow *parent, const gch g_thread_pool_push( thread_pool_local, args, NULL ); } -/** - * a_background_show_window: - * - * Display the background window. - */ -void a_background_show_window () -{ - gtk_widget_show_all ( bgwindow ); -} - +// In main thread static void cancel_job_with_iter ( GtkTreeIter *piter ) { gpointer *args; @@ -214,35 +245,36 @@ static void cancel_job_with_iter ( GtkTreeIter *piter ) args[0] = GINT_TO_POINTER(1); /* set killswitch */ gtk_list_store_remove ( bgstore, piter ); + g_free ( piter ); args[5] = NULL; } -static void bgwindow_response (GtkDialog *dialog, gint arg1 ) +// In main thread +static void bgwindow_response (GtkDialog *dialog, gint response_id, GtkTreeView *bgtreeview ) { - /* note this function is a signal handler called back from the GTK main loop, - * so GDK is already locked. We need to release the lock before calling - * thread-safe routines - */ - if ( arg1 == 1 ) /* cancel */ + switch ( response_id ) { + case GTK_RESPONSE_DELETE_EVENT: + // Delibrate fall through + case GTK_RESPONSE_CLOSE: + gtk_widget_destroy ( GTK_WIDGET(dialog) ); + break; + case 1: // Delete / Cancel selected item { GtkTreeIter iter; - if ( gtk_tree_selection_get_selected ( gtk_tree_view_get_selection ( GTK_TREE_VIEW(bgtreeview) ), NULL, &iter ) ) - cancel_job_with_iter ( &iter ); - gdk_threads_leave(); + if ( gtk_tree_selection_get_selected ( gtk_tree_view_get_selection(bgtreeview), NULL, &iter ) ) + cancel_job_with_iter ( &iter ); background_thread_update(); - gdk_threads_enter(); } - else if ( arg1 == 2 ) /* clear */ + break; + case 2: // Clear All jobs { GtkTreeIter iter; while ( gtk_tree_model_get_iter_first ( GTK_TREE_MODEL(bgstore), &iter ) ) - cancel_job_with_iter ( &iter ); - gdk_threads_leave(); + cancel_job_with_iter ( &iter ); background_thread_update(); - gdk_threads_enter(); } - else /* OK */ - gtk_widget_hide ( bgwindow ); + default: break; + } } #define VIK_SETTINGS_BACKGROUND_MAX_THREADS "background_max_threads" @@ -302,14 +334,23 @@ void a_background_post_init() thread_pool_local_mapnik = g_thread_pool_new ( (GFunc) thread_helper, NULL, mapnik_threads, FALSE, NULL ); #endif + bgstore = gtk_list_store_new ( N_COLUMNS, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_POINTER ); +} + +/** + * a_background_show_window: + * + * Display the background window. + */ +void a_background_show_window () +{ + GtkWidget *bgwindow = NULL; + GtkWidget *bgtreeview = NULL; GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkWidget *scrolled_window; - g_debug(__FUNCTION__); - /* store & treeview */ - bgstore = gtk_list_store_new ( N_COLUMNS, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_POINTER ); bgtreeview = gtk_tree_view_new_with_model ( GTK_TREE_MODEL(bgstore) ); gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (bgtreeview), TRUE); gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (bgtreeview)), @@ -329,7 +370,7 @@ void a_background_post_init() gtk_container_add ( GTK_CONTAINER(scrolled_window), bgtreeview ); gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); - bgwindow = gtk_dialog_new_with_buttons ( "", NULL, 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_DELETE, 1, GTK_STOCK_CLEAR, 2, NULL ); + bgwindow = gtk_dialog_new_with_buttons ( _("Viking Background Jobs"), NULL, 0, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, GTK_STOCK_DELETE, 1, GTK_STOCK_CLEAR, 2, NULL ); gtk_dialog_set_default_response ( GTK_DIALOG(bgwindow), GTK_RESPONSE_ACCEPT ); GtkWidget *response_w = NULL; #if GTK_CHECK_VERSION (2, 20, 0) @@ -337,14 +378,14 @@ void a_background_post_init() #endif gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(bgwindow))), scrolled_window, TRUE, TRUE, 0 ); gtk_window_set_default_size ( GTK_WINDOW(bgwindow), 400, 400 ); - gtk_window_set_title ( GTK_WINDOW(bgwindow), _("Viking Background Jobs") ); if ( response_w ) gtk_widget_grab_focus ( response_w ); - /* don't destroy win */ - g_signal_connect ( G_OBJECT(bgwindow), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL ); - g_signal_connect ( G_OBJECT(bgwindow), "response", G_CALLBACK(bgwindow_response), 0 ); + g_signal_connect ( G_OBJECT(bgwindow), "response", G_CALLBACK(bgwindow_response), GTK_TREE_VIEW(bgtreeview) ); + + gtk_widget_show_all ( bgwindow ); + gtk_dialog_set_default_response ( GTK_DIALOG(bgwindow), GTK_RESPONSE_CLOSE ); } /** @@ -355,26 +396,27 @@ void a_background_post_init() void a_background_uninit() { stop_all_threads = TRUE; - // wait until these threads stop - g_thread_pool_free ( thread_pool_remote, TRUE, TRUE ); - // Don't wait for these + // Don't wait for these threads to complete - i.e. end now. + g_thread_pool_free ( thread_pool_remote, TRUE, FALSE ); g_thread_pool_free ( thread_pool_local, TRUE, FALSE ); #ifdef HAVE_LIBMAPNIK g_thread_pool_free ( thread_pool_local_mapnik, TRUE, FALSE ); #endif - gtk_list_store_clear ( bgstore ); g_object_unref ( bgstore ); - - gtk_widget_destroy ( bgwindow ); + bgstore = NULL; } void a_background_add_window (VikWindow *vw) { + G_LOCK(window_list); windows_to_update = g_slist_prepend(windows_to_update,vw); + G_UNLOCK(window_list); } void a_background_remove_window (VikWindow *vw) { + G_LOCK(window_list); windows_to_update = g_slist_remove(windows_to_update,vw); + G_UNLOCK(window_list); } diff --git a/src/bingmapsource.c b/src/bingmapsource.c index c999ac75..0c9fd995 100644 --- a/src/bingmapsource.c +++ b/src/bingmapsource.c @@ -482,15 +482,12 @@ done: return ret; } -static int +static gboolean _emit_update ( gpointer data ) { - gdk_threads_enter(); - /* TODO - vik_layers_panel_emit_update ( VIK_LAYERS_PANEL (data) ); - */ - gdk_threads_leave(); - return 0; + //if ( data ) + // vik_layers_panel_emit_update ( VIK_LAYERS_PANEL (data) ); + return FALSE; } static int @@ -502,10 +499,9 @@ _load_attributions_thread ( BingMapSource *self, gpointer threaddata ) return -1; /* Abort thread */ /* Emit update */ - /* As we are on a download thread, - * it's better to fire the update from the main loop. - */ - g_idle_add ( (GSourceFunc)_emit_update, NULL /* FIXME */ ); + // As we are on a download thread, must the update GUI from the main loop + // TODO: have reference to the window/viewport/layerspanel to update... + (void)gdk_threads_add_idle ( _emit_update, NULL ); return 0; } diff --git a/src/datasource_gps.c b/src/datasource_gps.c index 8e545d82..e051f133 100644 --- a/src/datasource_gps.c +++ b/src/datasource_gps.c @@ -99,19 +99,26 @@ typedef struct { /* progress dialog */ GtkWidget *gps_label; - GtkWidget *ver_label; - GtkWidget *id_label; - GtkWidget *wp_label; + GtkWidget *wpt_label; GtkWidget *trk_label; GtkWidget *rte_label; - GtkWidget *progress_label; vik_gps_xfer_type progress_type; - /* state */ - int total_count; - int count; + // Maintain separate counts for each type + int wpt_total_count; + int wpt_count; + int trk_total_count; + int trk_count; + int rte_total_count; + int rte_count; + gchar *info; // Know which way xfer is so xfer setting types are only stored for download vik_gps_dir direction; + // GUI Updates + gint id_status; + gint id_info; + gint id_total_count; + gint id_count; } gps_user_data_t; #define VIK_SETTINGS_GPS_GET_TRACKS "gps_download_tracks" @@ -123,7 +130,7 @@ typedef struct { static gpointer datasource_gps_init_func ( acq_vik_t *avt ) { - gps_user_data_t *gps_ud = g_malloc (sizeof(gps_user_data_t)); + gps_user_data_t *gps_ud = g_malloc0 (sizeof(gps_user_data_t)); gps_ud->direction = GPS_DOWN; return gps_ud; } @@ -308,7 +315,18 @@ static void datasource_gps_off ( gpointer user_data, gchar **babelargs, gchar ** static void datasource_gps_cleanup ( gpointer user_data ) { - g_free ( user_data ); + gps_user_data_t *gud = (gps_user_data_t *)user_data; + // Remove any outstanding GUI update requests + if ( gud->id_status ) + g_source_remove ( gud->id_status ); + if ( gud->id_info ) + g_source_remove ( gud->id_info ); + if ( gud->id_total_count ) + g_source_remove ( gud->id_total_count ); + if ( gud->id_count ) + g_source_remove ( gud->id_count ); + g_free ( gud->info ); + g_free ( gud ); gps_acquire_in_progress = FALSE; } @@ -322,69 +340,95 @@ void datasource_gps_clean_up ( gpointer user_data ) datasource_gps_cleanup ( user_data ); } -static void set_total_count(gint cnt, acq_dialog_widgets_t *w) +static gboolean show_total_count(acq_dialog_widgets_t *w) { - gchar *s = NULL; - gdk_threads_enter(); + gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data; if (w->running) { - gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data; + gint tmp_count; + GtkWidget *progress_label; const gchar *tmp_str; switch (gps_data->progress_type) { - case WPT: tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", cnt); gps_data->total_count = cnt; break; - case TRK: tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", cnt); gps_data->total_count = cnt; break; + case WPT: + progress_label = gps_data->wpt_label; + tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", gps_data->wpt_total_count); + tmp_count = gps_data->wpt_total_count; + break; + case TRK: + progress_label = gps_data->trk_label; + tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", gps_data->trk_total_count); + tmp_count = gps_data->trk_total_count; + break; default: - { - // 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 - gint mycnt = (cnt / 2) + 1; - tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", mycnt); - gps_data->total_count = mycnt; - break; - } + progress_label = gps_data->rte_label; + tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", gps_data->rte_total_count); + tmp_count = gps_data->rte_total_count; + break; } - s = g_strdup_printf(tmp_str, cnt); - gtk_label_set_text ( GTK_LABEL(gps_data->progress_label), s ); - gtk_widget_show ( gps_data->progress_label ); + gchar *s = g_strdup_printf(tmp_str, tmp_count); + gtk_label_set_text ( GTK_LABEL(progress_label), s ); + gtk_widget_show ( progress_label ); + g_free(s); } - g_free(s); s = NULL; - gdk_threads_leave(); + gps_data->id_total_count = 0; + return FALSE; } -static void set_current_count(gint cnt, acq_dialog_widgets_t *w) +static gboolean show_current_count(acq_dialog_widgets_t *w) { - gchar *s = NULL; - gdk_threads_enter(); + gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data; if (w->running) { - gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data; - - if (cnt < gps_data->total_count) { - switch (gps_data->progress_type) { - case WPT: s = g_strdup_printf(_("Downloaded %d out of %d %s..."), cnt, gps_data->total_count, "waypoints"); break; - case TRK: s = g_strdup_printf(_("Downloaded %d out of %d %s..."), cnt, gps_data->total_count, "trackpoints"); break; - default: s = g_strdup_printf(_("Downloaded %d out of %d %s..."), cnt, gps_data->total_count, "routepoints"); break; - } - } else { - switch (gps_data->progress_type) { - case WPT: s = g_strdup_printf(_("Downloaded %d %s."), cnt, "waypoints"); break; - case TRK: s = g_strdup_printf(_("Downloaded %d %s."), cnt, "trackpoints"); break; - default: s = g_strdup_printf(_("Downloaded %d %s."), cnt, "routepoints"); break; - } + GtkWidget *progress_label; + gchar *s = NULL; + switch (gps_data->progress_type) { + case WPT: + progress_label = gps_data->wpt_label; + if ( gps_data->wpt_count < gps_data->wpt_total_count ) + s = g_strdup_printf(_("Downloaded %d out of %d %s..."), gps_data->wpt_count, gps_data->wpt_total_count, "waypoints"); + else + s = g_strdup_printf(_("Downloaded %d %s."), gps_data->wpt_count, "waypoints"); + break; + case TRK: + progress_label = gps_data->trk_label; + if ( gps_data->trk_count < gps_data->trk_total_count ) + s = g_strdup_printf(_("Downloaded %d out of %d %s..."), gps_data->trk_count, gps_data->trk_total_count, "trackpoints"); + else + s = g_strdup_printf(_("Downloaded %d %s."), gps_data->trk_count, "trackpoints"); + break; + default: + progress_label = gps_data->rte_label; + if ( gps_data->rte_count < gps_data->rte_total_count ) + s = g_strdup_printf(_("Downloaded %d out of %d %s..."), gps_data->rte_count, gps_data->rte_total_count, "routepoints"); + else + s = g_strdup_printf(_("Downloaded %d %s."), gps_data->rte_count, "routepoints"); + break; } - gtk_label_set_text ( GTK_LABEL(gps_data->progress_label), s ); + gtk_label_set_text ( GTK_LABEL(progress_label), s ); + g_free(s); + } + gps_data->id_count = 0; + return FALSE; +} + +static gboolean show_gps_info(acq_dialog_widgets_t *w) +{ + gps_user_data_t *gps_data = (gps_user_data_t*)w->user_data; + if (w->running) { + gchar *s = g_strdup_printf ( _("GPS Device: %s"), gps_data->info ); + gtk_label_set_text ( GTK_LABEL(gps_data->gps_label), s ); + g_free(s); } - g_free(s); s = NULL; - gdk_threads_leave(); + gps_data->id_info = 0; + return FALSE; } -static void set_gps_info(const gchar *info, acq_dialog_widgets_t *w) +static gboolean show_gps_status(acq_dialog_widgets_t *w) { - gchar *s = NULL; - gdk_threads_enter(); + gps_user_data_t *gps_data = (gps_user_data_t*)w->user_data; if (w->running) { - s = g_strdup_printf(_("GPS Device: %s"), info); - gtk_label_set_text ( GTK_LABEL(((gps_user_data_t *)w->user_data)->gps_label), s ); + gtk_label_set_text ( GTK_LABEL(w->status), _("Status: Working...") ); } - g_free(s); s = NULL; - gdk_threads_leave(); + gps_data->id_status = 0; + return FALSE; } /* @@ -401,23 +445,16 @@ static void datasource_gps_progress ( BabelProgressCode c, gpointer data, acq_di case BABEL_DIAG_OUTPUT: line = (gchar *)data; - gdk_threads_enter(); - if (w->running) { - gtk_label_set_text ( GTK_LABEL(w->status), _("Status: Working...") ); - } - gdk_threads_leave(); + gps_data->id_status = gdk_threads_add_idle ( (GSourceFunc)show_gps_status, w ); /* tells us the type of items that will follow */ if (strstr(line, "Xfer Wpt")) { - gps_data->progress_label = gps_data->wp_label; gps_data->progress_type = WPT; } if (strstr(line, "Xfer Trk")) { - gps_data->progress_label = gps_data->trk_label; gps_data->progress_type = TRK; } if (strstr(line, "Xfer Rte")) { - gps_data->progress_label = gps_data->rte_label; gps_data->progress_type = RTE; } @@ -438,7 +475,8 @@ static void datasource_gps_progress ( BabelProgressCode c, gpointer data, acq_di info[ilen++] = ch; } info[ilen++] = 0; - set_gps_info(info, w); + gps_data->info = g_strdup (info); + gps_data->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, w ); } g_strfreev(tokens); } @@ -450,7 +488,8 @@ static void datasource_gps_progress ( BabelProgressCode c, gpointer data, acq_di n_tokens++; if (n_tokens > 1) { - set_gps_info(tokens[1], w); + gps_data->info = g_strdup (tokens[1]); + gps_data->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, w ); } g_strfreev(tokens); } @@ -462,13 +501,33 @@ static void datasource_gps_progress ( BabelProgressCode c, gpointer data, acq_di sscanf(line+17, "%x", &lsb); sscanf(line+20, "%x", &msb); cnt = lsb + msb * 256; - set_total_count(cnt, w); - gps_data->count = 0; + if ( gps_data->progress_type == RTE ) { + // 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 + gint mycnt = (cnt / 2) + 1; + gps_data->rte_total_count = mycnt; + gps_data->rte_count = 0; + } + else if ( gps_data->progress_type == TRK ) { + gps_data->trk_total_count = cnt; + gps_data->trk_count = 0; + } + else { + gps_data->wpt_total_count = cnt; + gps_data->wpt_count = 0; + } + + gps_data->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, w); } } if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") || strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) { - gps_data->count++; - set_current_count(gps_data->count, w); + if ( strstr(line, "WPTDAT") ) + gps_data->wpt_count++; + else if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) + gps_data->trk_count++; + else + // "RTEHDR" || "RTEWPT" + gps_data->rte_count++; + gps_data->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, w); } break; case BABEL_DONE: @@ -671,29 +730,17 @@ gpointer datasource_gps_setup ( GtkWidget *dialog, vik_gps_xfer_type xfer, gbool void datasource_gps_add_progress_widgets ( GtkWidget *dialog, gpointer user_data ) { - GtkWidget *gpslabel, *verlabel, *idlabel, *wplabel, *trklabel, *rtelabel; - gps_user_data_t *w_gps = (gps_user_data_t *)user_data; - gpslabel = gtk_label_new (_("GPS device: N/A")); - verlabel = gtk_label_new (""); - idlabel = gtk_label_new (""); - wplabel = gtk_label_new (""); - trklabel = gtk_label_new (""); - rtelabel = gtk_label_new (""); + w_gps->gps_label = gtk_label_new (_("GPS device: N/A")); + w_gps->wpt_label = gtk_label_new (""); + w_gps->trk_label = gtk_label_new (""); + w_gps->rte_label = gtk_label_new (""); - gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), gpslabel, FALSE, FALSE, 5 ); - gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), wplabel, FALSE, FALSE, 5 ); - gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), trklabel, FALSE, FALSE, 5 ); - gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), rtelabel, FALSE, FALSE, 5 ); + gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), w_gps->gps_label, FALSE, FALSE, 5 ); + gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), w_gps->wpt_label, FALSE, FALSE, 5 ); + gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), w_gps->trk_label, FALSE, FALSE, 5 ); + gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), w_gps->rte_label, FALSE, FALSE, 5 ); gtk_widget_show_all ( dialog ); - - w_gps->gps_label = gpslabel; - w_gps->id_label = idlabel; - w_gps->ver_label = verlabel; - w_gps->progress_label = w_gps->wp_label = wplabel; - w_gps->trk_label = trklabel; - w_gps->rte_label = rtelabel; - w_gps->total_count = -1; } diff --git a/src/main.c b/src/main.c index e5400f06..effecbe3 100644 --- a/src/main.c +++ b/src/main.c @@ -133,7 +133,6 @@ int main( int argc, char *argv[] ) #if ! GLIB_CHECK_VERSION (2, 32, 0) g_thread_init ( NULL ); #endif - gdk_threads_init (); gui_initialized = gtk_init_with_args (&argc, &argv, "files+", entries, NULL, &error); if (!gui_initialized) @@ -228,8 +227,6 @@ int main( int argc, char *argv[] ) main_icon = gdk_pixbuf_from_pixdata(&viking_pixbuf, FALSE, NULL); gtk_window_set_default_icon(main_icon); - gdk_threads_enter (); - // Ask for confirmation of default settings on first run vu_set_auto_features_on_first_run (); @@ -275,7 +272,6 @@ int main( int argc, char *argv[] ) vu_command_line ( first_window, latitude, longitude, zoom_level_osm, map_id ); gtk_main (); - gdk_threads_leave (); a_babel_uninit (); a_toolbar_uninit (); diff --git a/src/vikdemlayer.c b/src/vikdemlayer.c index 7df1a57a..12d52145 100644 --- a/src/vikdemlayer.c +++ b/src/vikdemlayer.c @@ -345,11 +345,9 @@ static int dem_layer_load_list_thread ( dem_load_thread_data *dltd, gpointer thr // ATM as each file is processed the screen is not updated (no mechanism exposed to a_dems_load_list) // Thus force draw only at the end, as loading is complete/aborted - //gdk_threads_enter(); // Test is helpful to prevent Gtk-CRITICAL warnings if the program is exitted whilst loading if ( IS_VIK_LAYER(dltd->vdl) ) - vik_layer_emit_update ( VIK_LAYER(dltd->vdl) ); // NB update from background thread - //gdk_threads_leave(); + vik_layer_emit_update ( VIK_LAYER(dltd->vdl) ); // NB update requested from background thread return result; } @@ -1181,16 +1179,14 @@ static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata ) else return; - //gdk_threads_enter(); g_mutex_lock ( p->mutex ); if ( p->vdl ) { g_object_weak_unref ( G_OBJECT(p->vdl), weak_ref_cb, p ); if ( dem_layer_add_file ( p->vdl, p->dest ) ) - vik_layer_emit_update ( VIK_LAYER(p->vdl) ); // NB update from background thread + vik_layer_emit_update ( VIK_LAYER(p->vdl) ); // NB update requested from background thread } g_mutex_unlock ( p->mutex ); - //gdk_threads_leave(); } diff --git a/src/vikgpslayer.c b/src/vikgpslayer.c index 4f3b78e0..f30e1a7f 100644 --- a/src/vikgpslayer.c +++ b/src/vikgpslayer.c @@ -3,6 +3,7 @@ * * Copyright (C) 2003-2005, Evan Battaglia * Copyright (C) 2006-2008, Quy Tonthat + * Copyright (C) 2016, Rob Norris * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -96,25 +97,34 @@ static gchar * old_params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", typedef struct { GMutex *mutex; + gboolean ok; + gboolean thread_complete; vik_gps_dir direction; gchar *port; - gboolean ok; - gint total_count; - gint count; + gint wpt_total_count; + gint wpt_count; + gint trk_total_count; + gint trk_count; + gint rte_total_count; + gint rte_count; VikTrwLayer *vtl; VikTrack *track; gchar *babelargs; - gchar *window_title; GtkWidget *dialog; GtkWidget *status_label; GtkWidget *gps_label; - GtkWidget *ver_label; - GtkWidget *id_label; - GtkWidget *wp_label; + GtkWidget *wpt_label; GtkWidget *trk_label; GtkWidget *rte_label; - GtkWidget *progress_label; vik_gps_xfer_type progress_type; + gboolean result; + gchar *info; + // GUI Updates + gint id_status_working; + gint id_status_end; + gint id_info; + gint id_total_count; + gint id_count; VikViewport *vvp; #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION) gboolean realtime_tracking; @@ -916,110 +926,208 @@ gboolean vik_gps_layer_is_empty ( VikGpsLayer *vgl ) static void gps_session_delete(GpsSession *sess) { vik_mutex_free(sess->mutex); + // Remove any outstanding GUI update requests + if ( sess->id_status_working ) + g_source_remove ( sess->id_status_working ); + if ( sess->id_status_end ) + g_source_remove ( sess->id_status_end ); + if ( sess->id_info ) + g_source_remove ( sess->id_info ); + if ( sess->id_total_count ) + g_source_remove ( sess->id_total_count ); + if ( sess->id_count ) + g_source_remove ( sess->id_count ); g_free(sess->babelargs); + g_free(sess->info); + g_free(sess->port); g_free(sess); } -static void set_total_count(gint cnt, GpsSession *sess) +static gboolean show_total_count(GpsSession *sess) { - gchar s[128]; - gdk_threads_enter(); g_mutex_lock(sess->mutex); if (sess->ok) { const gchar *tmp_str; + gint tc; + GtkWidget *progress_label; if (sess->direction == GPS_DOWN) { switch (sess->progress_type) { - case WPT: tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", cnt); sess->total_count = cnt; break; - case TRK: tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", cnt); sess->total_count = cnt; break; + case WPT: + tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", sess->wpt_total_count); + tc = sess->wpt_total_count; + progress_label = sess->wpt_label; + break; + case TRK: + tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", sess->trk_total_count); + tc = sess->trk_total_count; + progress_label = sess->trk_label; + break; default: + tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", sess->rte_total_count); + tc = sess->rte_total_count; + progress_label = sess->rte_label; + break; +/* { // 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 gint mycnt = (cnt / 2) + 1; tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", mycnt); - sess->total_count = mycnt; + tc = mycnt; break; } + */ } } else { switch (sess->progress_type) { - case WPT: tmp_str = ngettext("Uploading %d waypoint...", "Uploading %d waypoints...", cnt); break; - case TRK: tmp_str = ngettext("Uploading %d trackpoint...", "Uploading %d trackpoints...", cnt); break; - default: tmp_str = ngettext("Uploading %d routepoint...", "Uploading %d routepoints...", cnt); break; + case WPT: + tmp_str = ngettext("Uploading %d waypoint...", "Uploading %d waypoints...", sess->wpt_total_count); + tc = sess->wpt_total_count; + progress_label = sess->wpt_label; + break; + case TRK: + tmp_str = ngettext("Uploading %d trackpoint...", "Uploading %d trackpoints...", sess->trk_total_count); + tc = sess->trk_total_count; + progress_label = sess->trk_label; + break; + default: + tmp_str = ngettext("Uploading %d routepoint...", "Uploading %d routepoints...", sess->rte_total_count); + tc = sess->rte_total_count; + progress_label = sess->rte_label; + break; } } - g_snprintf(s, 128, tmp_str, cnt); - gtk_label_set_text ( GTK_LABEL(sess->progress_label), s ); - gtk_widget_show ( sess->progress_label ); - sess->total_count = cnt; + gchar s[128]; + g_snprintf(s, 128, tmp_str, tc); + gtk_label_set_text ( GTK_LABEL(progress_label), s ); + gtk_widget_show ( progress_label ); } + sess->id_total_count = 0; g_mutex_unlock(sess->mutex); - gdk_threads_leave(); + return FALSE; } -static void set_current_count(gint cnt, GpsSession *sess) +static gboolean show_current_count(GpsSession *sess) { - gchar s[128]; - const gchar *tmp_str; - - gdk_threads_enter(); g_mutex_lock(sess->mutex); if (sess->ok) { - if (cnt < sess->total_count) { - if (sess->direction == GPS_DOWN) - { + gchar s[128]; + gint count, total_count; + const gchar *tmp_str; + GtkWidget *progress_label; + if (sess->wpt_count < sess->wpt_total_count) { + if (sess->direction == GPS_DOWN) { switch (sess->progress_type) { - case WPT: tmp_str = ngettext("Downloaded %d out of %d waypoint...", "Downloaded %d out of %d waypoints...", sess->total_count); break; - case TRK: tmp_str = ngettext("Downloaded %d out of %d trackpoint...", "Downloaded %d out of %d trackpoints...", sess->total_count); break; - default: tmp_str = ngettext("Downloaded %d out of %d routepoint...", "Downloaded %d out of %d routepoints...", sess->total_count); break; + case WPT: + tmp_str = ngettext("Downloaded %d out of %d waypoint...", "Downloaded %d out of %d waypoints...", sess->wpt_total_count); + count = sess->wpt_count; + total_count = sess->wpt_total_count; + progress_label = sess->wpt_label; + break; + case TRK: + tmp_str = ngettext("Downloaded %d out of %d trackpoint...", "Downloaded %d out of %d trackpoints...", sess->trk_total_count); + count = sess->trk_count; + total_count = sess->trk_total_count; + progress_label = sess->trk_label; + break; + default: + tmp_str = ngettext("Downloaded %d out of %d routepoint...", "Downloaded %d out of %d routepoints...", sess->rte_total_count); + count = sess->rte_count; + total_count = sess->rte_total_count; + progress_label = sess->rte_label; + break; } } else { switch (sess->progress_type) { - case WPT: tmp_str = ngettext("Uploaded %d out of %d waypoint...", "Uploaded %d out of %d waypoints...", sess->total_count); break; - case TRK: tmp_str = ngettext("Uploaded %d out of %d trackpoint...", "Uploaded %d out of %d trackpoints...", sess->total_count); break; - default: tmp_str = ngettext("Uploaded %d out of %d routepoint...", "Uploaded %d out of %d routepoints...", sess->total_count); break; - } + case WPT: + tmp_str = ngettext("Uploaded %d out of %d waypoint...", "Uploaded %d out of %d waypoints...", sess->wpt_total_count); + count = sess->wpt_count; + total_count = sess->wpt_total_count; + progress_label = sess->wpt_label; + break; + case TRK: + tmp_str = ngettext("Uploaded %d out of %d trackpoint...", "Uploaded %d out of %d trackpoints...", sess->trk_total_count); + count = sess->trk_count; + total_count = sess->trk_total_count; + progress_label = sess->trk_label; + break; + default: + tmp_str = ngettext("Uploaded %d out of %d routepoint...", "Uploaded %d out of %d routepoints...", sess->rte_total_count); + count = sess->rte_count; + total_count = sess->rte_total_count; + progress_label = sess->rte_label; + break; + } } - g_snprintf(s, 128, tmp_str, cnt, sess->total_count); } else { - if (sess->direction == GPS_DOWN) - { + if (sess->direction == GPS_DOWN) { switch (sess->progress_type) { - case WPT: tmp_str = ngettext("Downloaded %d waypoint", "Downloaded %d waypoints", cnt); break; - case TRK: tmp_str = ngettext("Downloaded %d trackpoint", "Downloaded %d trackpoints", cnt); break; - default: tmp_str = ngettext("Downloaded %d routepoint", "Downloaded %d routepoints", cnt); break; - } + case WPT: + tmp_str = ngettext("Downloaded %d waypoint", "Downloaded %d waypoints", sess->wpt_count); + count = sess->wpt_count; + total_count = sess->wpt_total_count; + progress_label = sess->wpt_label; + break; + case TRK: + tmp_str = ngettext("Downloaded %d trackpoint", "Downloaded %d trackpoints", sess->trk_count); + count = sess->trk_count; + total_count = sess->trk_total_count; + progress_label = sess->trk_label; + break; + default: + tmp_str = ngettext("Downloaded %d routepoint", "Downloaded %d routepoints", sess->rte_count); + count = sess->rte_count; + total_count = sess->rte_total_count; + progress_label = sess->rte_label; + break; + } } else { switch (sess->progress_type) { - case WPT: tmp_str = ngettext("Uploaded %d waypoint", "Uploaded %d waypoints", cnt); break; - case TRK: tmp_str = ngettext("Uploaded %d trackpoint", "Uploaded %d trackpoints", cnt); break; - default: tmp_str = ngettext("Uploaded %d routepoint", "Uploaded %d routepoints", cnt); break; - } + case WPT: + tmp_str = ngettext("Uploaded %d waypoint", "Uploaded %d waypoints", sess->wpt_count); + count = sess->wpt_count; + total_count = sess->wpt_total_count; + progress_label = sess->wpt_label; + break; + case TRK: + tmp_str = ngettext("Uploaded %d trackpoint", "Uploaded %d trackpoints", sess->trk_count); + count = sess->trk_count; + total_count = sess->trk_total_count; + progress_label = sess->trk_label; + break; + default: + tmp_str = ngettext("Uploaded %d routepoint", "Uploaded %d routepoints", sess->rte_count); + count = sess->rte_count; + total_count = sess->rte_total_count; + progress_label = sess->rte_label; + break; + } } - g_snprintf(s, 128, tmp_str, cnt); - } - gtk_label_set_text ( GTK_LABEL(sess->progress_label), s ); + } + g_snprintf(s, 128, tmp_str, count, total_count); + gtk_label_set_text ( GTK_LABEL(progress_label), s ); } + sess->id_count = 0; g_mutex_unlock(sess->mutex); - gdk_threads_leave(); + return FALSE; } -static void set_gps_info(const gchar *info, GpsSession *sess) +static gboolean show_gps_info(GpsSession *sess) { - gchar s[256]; - gdk_threads_enter(); g_mutex_lock(sess->mutex); if (sess->ok) { - g_snprintf(s, 256, _("GPS Device: %s"), info); + gchar s[256]; + g_snprintf(s, 256, _("GPS Device: %s"), sess->info); gtk_label_set_text ( GTK_LABEL(sess->gps_label), s ); } + sess->id_info = 0; g_mutex_unlock(sess->mutex); - gdk_threads_leave(); + return FALSE; } /* @@ -1048,7 +1156,8 @@ static void process_line_for_gps_info ( const gchar *line, GpsSession *sess ) info[ilen++] = ch; } info[ilen++] = 0; - set_gps_info(info, sess); + sess->info = g_strdup (info); + sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess ); } g_strfreev(tokens); } @@ -1061,50 +1170,48 @@ static void process_line_for_gps_info ( const gchar *line, GpsSession *sess ) n_tokens++; if (n_tokens > 1) { - set_gps_info(tokens[1], sess); + sess->info = g_strdup (tokens[1]); + sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess ); } g_strfreev(tokens); } + +static gboolean show_gps_status_working ( GpsSession *sess ) +{ + g_mutex_lock(sess->mutex); + if ( sess->ok ) { + gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") ); + } + sess->id_status_working = 0; + g_mutex_unlock(sess->mutex); + return FALSE; } static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess ) { gchar *line; - gdk_threads_enter (); - g_mutex_lock(sess->mutex); - if (!sess->ok) { - g_mutex_unlock(sess->mutex); - gps_session_delete(sess); - gdk_threads_leave(); + if ( !sess->ok ) { + //gps_session_delete(sess); + sess->thread_complete = TRUE; + g_debug ("THREAD EXIT INTERUPPT"); g_thread_exit ( NULL ); } - g_mutex_unlock(sess->mutex); - gdk_threads_leave (); switch(c) { case BABEL_DIAG_OUTPUT: line = (gchar *)data; - gdk_threads_enter(); - g_mutex_lock(sess->mutex); - if (sess->ok) { - gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") ); - } - g_mutex_unlock(sess->mutex); - gdk_threads_leave(); + sess->id_status_working = gdk_threads_add_idle ( (GSourceFunc)show_gps_status_working, sess ); /* tells us the type of items that will follow */ if (strstr(line, "Xfer Wpt")) { - sess->progress_label = sess->wp_label; sess->progress_type = WPT; } if (strstr(line, "Xfer Trk")) { - sess->progress_label = sess->trk_label; sess->progress_type = TRK; } if (strstr(line, "Xfer Rte")) { - sess->progress_label = sess->rte_label; sess->progress_type = RTE; } @@ -1117,13 +1224,32 @@ static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSe sscanf(line+17, "%x", &lsb); sscanf(line+20, "%x", &msb); cnt = lsb + msb * 256; - set_total_count(cnt, sess); - sess->count = 0; + if ( sess->progress_type == RTE ) { + // 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 + gint mycnt = (cnt / 2) + 1; + sess->rte_total_count = mycnt; + sess->rte_count = 0; + } + else if ( sess->progress_type == WPT ) { + sess->wpt_total_count = cnt; + sess->wpt_count = 0; + } + else { + sess->trk_total_count = cnt; + sess->trk_count = 0; + } + sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess); } } if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") || strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) { - sess->count++; - set_current_count(sess->count, sess); + if ( strstr(line, "WPTDAT") ) + sess->wpt_count++; + else if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) + sess->trk_count++; + else + // "RTEHDR" || "RTEWPT" + sess->rte_count++; + sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess); } break; case BABEL_DONE: @@ -1131,7 +1257,6 @@ static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSe default: break; } - } static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess ) @@ -1139,28 +1264,17 @@ static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSess gchar *line; static int cnt = 0; - gdk_threads_enter (); - g_mutex_lock(sess->mutex); - if (!sess->ok) { - g_mutex_unlock(sess->mutex); - gps_session_delete(sess); - gdk_threads_leave(); + if ( !sess->ok ) { + //gps_session_delete(sess); + sess->thread_complete = TRUE; g_thread_exit ( NULL ); } - g_mutex_unlock(sess->mutex); - gdk_threads_leave (); switch(c) { case BABEL_DIAG_OUTPUT: line = (gchar *)data; - gdk_threads_enter(); - g_mutex_lock(sess->mutex); - if (sess->ok) { - gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") ); - } - g_mutex_unlock(sess->mutex); - gdk_threads_leave(); + sess->id_status_working = gdk_threads_add_idle ( (GSourceFunc)show_gps_status_working, sess ); process_line_for_gps_info ( line, sess ); @@ -1171,39 +1285,36 @@ static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSess sscanf(line+17, "%x", &lsb); sscanf(line+20, "%x", &msb); cnt = lsb + msb * 256; - /* set_total_count(cnt, sess); */ - sess->count = 0; + //sess->count = 0; ?? wpt, trk and/or rte?? or none } } if ( strstr(line, "WPTDAT")) { - if (sess->count == 0) { - sess->progress_label = sess->wp_label; - sess->progress_type = WPT; - set_total_count(cnt, sess); + sess->progress_type = WPT; + if (sess->wpt_count == 0) { + sess->wpt_total_count = cnt; + sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess ); } - sess->count++; - set_current_count(sess->count, sess); + sess->wpt_count++; + sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess ); } if ( strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) { - if (sess->count == 0) { - sess->progress_label = sess->rte_label; - sess->progress_type = RTE; - // 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 - // Anyway since we're uploading - we should know how many points we're going to put! - cnt = (cnt / 2) + 1; - set_total_count(cnt, sess); - } - sess->count++; - set_current_count(sess->count, sess); + sess->progress_type = RTE; + if (sess->rte_count == 0) { + // 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 + // Anyway since we're uploading - we should know how many points we're going to put! + cnt = (cnt / 2) + 1; + sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess); + } + sess->rte_count++; + sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess); } if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) { - if (sess->count == 0) { - sess->progress_label = sess->trk_label; - sess->progress_type = TRK; - set_total_count(cnt, sess); + sess->progress_type = TRK; + if (sess->trk_count == 0) { + sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess); } - sess->count++; - set_current_count(sess->count, sess); + sess->trk_count++; + sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess); } break; case BABEL_DONE: @@ -1211,32 +1322,45 @@ static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSess default: break; } +} +static gboolean show_gps_status_end ( GpsSession *sess ) +{ + g_mutex_lock(sess->mutex); + // (Download)Failure could be due to a number of reasons: such as no/wrong device attached or GPSBabel not installed + if (!sess->result) { + gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Error: No result.") ); + } + else { + if (sess->ok) { + gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Done.") ); + gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, TRUE ); + gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_REJECT, FALSE ); + } + } + sess->id_status_end = 0; + g_mutex_unlock(sess->mutex); + return FALSE; } +/** + * + */ static void gps_comm_thread(GpsSession *sess) { - gboolean result; - if (sess->direction == GPS_DOWN) { ProcessOptions po = { sess->babelargs, sess->port, NULL, NULL, NULL, NULL }; - result = a_babel_convert_from (sess->vtl, &po, (BabelStatusFunc) gps_download_progress_func, sess, NULL); + sess->result = a_babel_convert_from (sess->vtl, &po, (BabelStatusFunc) gps_download_progress_func, sess, NULL); } else { - result = a_babel_convert_to (sess->vtl, sess->track, sess->babelargs, sess->port, + sess->result = a_babel_convert_to (sess->vtl, sess->track, sess->babelargs, sess->port, (BabelStatusFunc) gps_upload_progress_func, sess); } - if (!result) { - gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Error: couldn't find gpsbabel.") ); - } - else { - g_mutex_lock(sess->mutex); + sess->id_status_end = gdk_threads_add_idle ( (GSourceFunc)show_gps_status_end, sess ); + + if (sess->result) { if (sess->ok) { - gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Done.") ); - gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, TRUE ); - gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_REJECT, FALSE ); - /* Do not change the view if we are following the current GPS position */ #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION) if (!sess->realtime_tracking) @@ -1246,24 +1370,20 @@ static void gps_comm_thread(GpsSession *sess) vik_layer_post_read ( VIK_LAYER(sess->vtl), sess->vvp, TRUE ); /* View the data available */ vik_trw_layer_auto_set_view ( sess->vtl, sess->vvp ) ; - vik_layer_emit_update ( VIK_LAYER(sess->vtl) ); // NB update from background thread + vik_layer_emit_update ( VIK_LAYER(sess->vtl) ); // NB update request from background thread } } } else { - /* canceled */ + /* cancelled */ } - g_mutex_unlock(sess->mutex); } - g_mutex_lock(sess->mutex); if (sess->ok) { + // Thread has completed successfully, but now set to false to avoid 'tell thread to stop' test after dialog run sess->ok = FALSE; - g_mutex_unlock(sess->mutex); - } - else { - g_mutex_unlock(sess->mutex); - gps_session_delete(sess); } + + sess->thread_complete = TRUE; g_thread_exit(NULL); } @@ -1296,7 +1416,7 @@ gint vik_gps_comm ( VikTrwLayer *vtl, gboolean do_waypoints, gboolean turn_off ) { - GpsSession *sess = g_malloc(sizeof(GpsSession)); + GpsSession *sess = g_malloc0(sizeof(GpsSession)); char *tracks = NULL; char *routes = NULL; char *waypoints = NULL; @@ -1307,7 +1427,6 @@ gint vik_gps_comm ( VikTrwLayer *vtl, sess->track = track; sess->port = g_strdup(port); sess->ok = TRUE; - sess->window_title = (dir == GPS_DOWN) ? _("GPS Download") : _("GPS Upload"); sess->vvp = vvp; // This must be done inside the main thread as the uniquify causes screen updates @@ -1343,34 +1462,32 @@ gint vik_gps_comm ( VikTrwLayer *vtl, tracks = NULL; waypoints = NULL; + GtkWidget *dialog = NULL; + // Only create dialog if we're going to do some transferring if ( do_tracks || do_waypoints || do_routes ) { - 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 ); + 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 ); + sess->dialog = dialog; gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, FALSE ); - gtk_window_set_title ( GTK_WINDOW(sess->dialog), sess->window_title ); + gtk_window_set_title ( GTK_WINDOW(sess->dialog), (dir == GPS_DOWN) ? _("GPS Download") : _("GPS Upload") ); sess->status_label = gtk_label_new (_("Status: detecting gpsbabel")); gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->status_label, FALSE, FALSE, 5 ); - gtk_widget_show_all(sess->status_label); sess->gps_label = gtk_label_new (_("GPS device: N/A")); - sess->ver_label = gtk_label_new (""); - sess->id_label = gtk_label_new (""); - sess->wp_label = gtk_label_new (""); + sess->wpt_label = gtk_label_new (""); sess->trk_label = gtk_label_new (""); sess->rte_label = gtk_label_new (""); gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->gps_label, FALSE, FALSE, 5 ); - gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->wp_label, FALSE, FALSE, 5 ); + gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->wpt_label, FALSE, FALSE, 5 ); gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->trk_label, FALSE, FALSE, 5 ); gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->rte_label, FALSE, FALSE, 5 ); gtk_widget_show_all(sess->dialog); - sess->progress_label = sess->wp_label; - sess->total_count = -1; - + gtk_dialog_set_default_response ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT ); // Starting gps read/write thread #if GLIB_CHECK_VERSION (2, 32, 0) g_thread_try_new ( "gps_comm_thread", (GThreadFunc)gps_comm_thread, sess, NULL ); @@ -1378,21 +1495,15 @@ gint vik_gps_comm ( VikTrwLayer *vtl, g_thread_create ( (GThreadFunc)gps_comm_thread, sess, FALSE, NULL ); #endif - gtk_dialog_set_default_response ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT ); - gtk_dialog_run(GTK_DIALOG(sess->dialog)); - - gtk_widget_destroy(sess->dialog); + gtk_dialog_run(GTK_DIALOG(dialog)); } else { if ( !turn_off ) a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No GPS items selected for transfer.") ); } - g_mutex_lock(sess->mutex); - if (sess->ok) { sess->ok = FALSE; /* tell thread to stop */ - g_mutex_unlock(sess->mutex); } else { if ( turn_off ) { @@ -1404,10 +1515,15 @@ gint vik_gps_comm ( VikTrwLayer *vtl, a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not turn off device.") ); g_free ( device_off ); } - g_mutex_unlock(sess->mutex); - gps_session_delete(sess); } + if ( dialog ) { + while ( !sess->thread_complete ) { + g_usleep (G_USEC_PER_SEC/10); + } + gtk_widget_destroy(dialog); + } + gps_session_delete(sess); return 0; } diff --git a/src/viktrwlayer.c b/src/viktrwlayer.c index 00055b1c..25bb2a98 100644 --- a/src/viktrwlayer.c +++ b/src/viktrwlayer.c @@ -9535,10 +9535,8 @@ static gboolean tool_sync_done = TRUE; static gboolean tool_sync(gpointer data) { VikViewport *vvp = data; - gdk_threads_enter(); vik_viewport_sync(vvp); tool_sync_done = TRUE; - gdk_threads_leave(); return FALSE; } @@ -9780,13 +9778,11 @@ static gboolean draw_sync ( gpointer data ) // normally because another update has taken precedent such as panning the display // which means this pixmap is no longer valid if ( ds->vtl->draw_sync_do ) { - gdk_threads_enter(); gdk_draw_drawable (ds->drawable, ds->gc, ds->pixmap, 0, 0, 0, 0, -1, -1); ds->vtl->draw_sync_done = TRUE; - gdk_threads_leave(); } g_free ( ds ); return FALSE; diff --git a/src/vikwindow.c b/src/vikwindow.c index f6ff4fbf..875b5285 100644 --- a/src/vikwindow.c +++ b/src/vikwindow.c @@ -1205,11 +1205,9 @@ gboolean draw_buf_done = TRUE; static gboolean draw_buf(gpointer data) { gpointer *pass_along = data; - gdk_threads_enter(); gdk_draw_drawable (pass_along[0], pass_along[1], pass_along[2], 0, 0, 0, 0, -1, -1); draw_buf_done = TRUE; - gdk_threads_leave(); return FALSE; } -- 2.39.5