/* Chrysalide - Outil d'analyse de fichiers binaires * statusstack.c - empilement d'informations de statut * * Copyright (C) 2015-2024 Cyrille Bagard * * This file is part of Chrysalide. * * Chrysalide is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Chrysalide is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Chrysalide. If not, see . */ #include "statusstack.h" #include #include #include #include #include "helpers.h" #include "statusstack-int.h" #include "../core/global.h" /* -------------------------- GESTION GENERALE DES STATUTS -------------------------- */ /* Initialise la classe des barres de statut améliorées. */ static void gtk_status_stack_class_init(GtkStatusStackClass *); /* Initialise une instance de barre de statut améliorée. */ static void gtk_status_stack_init(GtkStatusStack *); /* Supprime toutes les références externes. */ static void gtk_status_stack_dispose(GtkStatusStack *); /* Procède à la libération totale de la mémoire. */ static void gtk_status_stack_finalize(GtkStatusStack *); /* Met à jour dans la barre les débits réseau observés. */ static gboolean gtk_status_stack_update_network_stats(GtkStatusStack *); /* -------------------- STATUT DES INFORMATIONS DE DESASSEMBLAGE -------------------- */ /* Navigation au sein d'assemblage */ struct _navigation_info_t { char *segment; /* Segment d'appartenance */ mrange_t current; /* Emplacement correspondant */ VMPA_BUFFER(phys); /* Localisation physique */ VMPA_BUFFER(virt); /* Localisation virtuelle */ char *symbol; /* Eventuel symbole concerné */ char *format; /* Architecture et format */ char *details; /* Encodage de l'instruction */ }; /* Met en place le suivi d'informations de navigation. */ static void init_navigation_info(navigation_info_t *); /* Supprime l'empreinte mémoire d'informations de navigation. */ static void fini_navigation_info(navigation_info_t *); /* S'assure de l'affichage à jour de la partie "navigation". */ static gboolean gtk_status_stack_show_current_location(GtkStatusStack *); /* Réagit à un clic sur l'icône de zoom. */ static void gtk_status_stack_on_zoom_icon_press(GtkEntry *, GtkEntryIconPosition, GtkStatusStack *); /* -------------------------- STATUT DES SUIVIS D'ACTIVITE -------------------------- */ /* Informations de progression */ typedef struct _activity_status_t { activity_id_t id; /* Identifiant unique */ char *message; /* Indication à faire valoir */ unsigned long current; /* Position courante */ unsigned long max; /* Couverture à parcourir */ double last_updated; /* Dernière valeur poussée */ } activity_status_t; /* Mémorisation des progressions au sein d'activités */ struct _activity_info_t { GMutex access; /* Accès à la pile */ activity_id_t generator; /* Générateur de séquence */ activity_status_t *statuses; /* Statuts de progression */ size_t count; /* Nombre de ces statuts */ guint tag; /* Identifiant de mise à jour */ }; /* Met en place le suivi d'informations d'activité. */ static void init_activity_info(activity_info_t *); /* Supprime l'empreinte mémoire d'informations d'activité. */ static void fini_activity_info(activity_info_t *); /* Recherche les indications de statut d'une activité donnée. */ static activity_status_t *find_activity_status_by_id(activity_info_t *, activity_id_t); /* S'assure de l'affichage à jour de la partie "activité". */ static gboolean gtk_status_stack_show_current_activity(GtkStatusStack *); /* ---------------------------------------------------------------------------------- */ /* GESTION GENERALE DES STATUTS */ /* ---------------------------------------------------------------------------------- */ /* Détermine le type du composant d'affichage générique. */ G_DEFINE_TYPE(GtkStatusStack, gtk_status_stack, GTK_TYPE_BOX); /****************************************************************************** * * * Paramètres : class = classe GTK à initialiser. * * * * Description : Initialise la classe des barres de statut améliorées. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_status_stack_class_init(GtkStatusStackClass *class) { GObjectClass *object; /* Plus haut niveau équivalent */ GtkWidgetClass *widget; /* Classe de haut niveau */ object = G_OBJECT_CLASS(class); object->dispose = (GObjectFinalizeFunc/* ! */)gtk_status_stack_dispose; object->finalize = (GObjectFinalizeFunc)gtk_status_stack_finalize; widget = GTK_WIDGET_CLASS(class); gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gtkext/statusstack.ui"); gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_status_stack_on_zoom_icon_press)); gtk_widget_class_bind_template_child(widget, GtkStatusStack, main); gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_segment); gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_phys); gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_virt); gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_offset); gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_format); gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_details); gtk_widget_class_bind_template_child(widget, GtkStatusStack, zoom); gtk_widget_class_bind_template_child(widget, GtkStatusStack, activity_message); gtk_widget_class_bind_template_child(widget, GtkStatusStack, activity_progress); gtk_widget_class_bind_template_child(widget, GtkStatusStack, net_recv_speed); gtk_widget_class_bind_template_child(widget, GtkStatusStack, net_send_speed); } /****************************************************************************** * * * Paramètres : stack = composant GTK à initialiser. * * * * Description : Initialise une instance de barre de statut améliorée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_status_stack_init(GtkStatusStack *stack) { gtk_widget_init_template(GTK_WIDGET(stack)); stack->def_source = NULL; stack->nav_info = calloc(1, sizeof(navigation_info_t)); init_navigation_info(stack->nav_info); stack->activity_info = calloc(1, sizeof(activity_info_t)); init_activity_info(stack->activity_info); stack->next_index = 0; stack->network_update_tag = g_timeout_add(NETWORK_UPDATE_INTERVAL, G_SOURCE_FUNC(gtk_status_stack_update_network_stats), stack); } /****************************************************************************** * * * Paramètres : stack = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_status_stack_dispose(GtkStatusStack *stack) { g_source_remove(stack->network_update_tag); gtk_widget_dispose_template(GTK_WIDGET(stack), GTK_TYPE_STATUS_STACK); G_OBJECT_CLASS(gtk_status_stack_parent_class)->dispose(G_OBJECT(stack)); } /****************************************************************************** * * * Paramètres : stack = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_status_stack_finalize(GtkStatusStack *stack) { fini_navigation_info(stack->nav_info); free(stack->nav_info); fini_activity_info(stack->activity_info); free(stack->activity_info); G_OBJECT_CLASS(gtk_status_stack_parent_class)->finalize(G_OBJECT(stack)); } /****************************************************************************** * * * Paramètres : - * * * * Description : Crée une nouvelle instance de barre de statut. * * * * Retour : Composant GTK mis en place. * * * * Remarques : - * * * ******************************************************************************/ GtkStatusStack *gtk_status_stack_new(void) { GtkStatusStack *result; /* Instance à retourner */ result = g_object_new(GTK_TYPE_STATUS_STACK, NULL); return result; } /****************************************************************************** * * * Paramètres : stack = barre de statut à actualiser. * * * * Description : Réinitialise la barre de statut à son stade par défaut. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_status_stack_reset(GtkStatusStack *stack) { gtk_stack_set_visible_child_name(stack->main, "default"); stack->def_source = NULL; } /****************************************************************************** * * * Paramètres : stack = barre de statut à actualiser. * * * * Description : Met à jour dans la barre les débits réseau observés. * * * * Retour : G_SOURCE_CONTINUE pour poursuivre les mises à jour. * * * * Remarques : - * * * ******************************************************************************/ static gboolean gtk_status_stack_update_network_stats(GtkStatusStack *stack) { gboolean result; /* Indication à retourner */ gint64 timestamp; /* Position temporelle */ size_t received; /* Quantité d'octets reçus */ size_t sent; /* Quantité d'octets émis */ gint64 diff_time; /* Différentiel de temps */ size_t diff_bytes; /* Différentiel de volume */ double speed; /* Débit de transfert constaté */ size_t i; /* Boucle de parcours */ char *value; /* Valeur à afficher */ const char *units[] = { _("b/s"), _("kb/s"), _("Mb/s"), _("Gb/s"), _("Tb/s") }; result = G_SOURCE_CONTINUE; /* Mémorisation des données */ timestamp = g_get_monotonic_time(); get_network_stats(&received, &sent); if (stack->next_index < NETWORK_UPDATE_COUNT) { stack->last_bytes_received[stack->next_index] = received; stack->last_bytes_sent[stack->next_index] = sent; stack->last_timestamps[stack->next_index] = timestamp; stack->next_index++; } else { memcpy(stack->last_bytes_received, stack->last_bytes_received + 1, (NETWORK_UPDATE_COUNT - 1) * sizeof(size_t)); memcpy(stack->last_bytes_sent, stack->last_bytes_sent + 1, (NETWORK_UPDATE_COUNT - 1) * sizeof(size_t)); memcpy(stack->last_timestamps, stack->last_timestamps + 1, (NETWORK_UPDATE_COUNT - 1) * sizeof(gint64)); stack->last_bytes_received[NETWORK_UPDATE_COUNT - 1] = received; stack->last_bytes_sent[NETWORK_UPDATE_COUNT - 1] = sent; stack->last_timestamps[NETWORK_UPDATE_COUNT - 1] = timestamp; } if (stack->next_index < NETWORK_UPDATE_COUNT) goto done; diff_time = stack->last_timestamps[NETWORK_UPDATE_COUNT - 1] - stack->last_timestamps[0]; /* Débit de réception */ diff_bytes = stack->last_bytes_received[NETWORK_UPDATE_COUNT - 1] - stack->last_bytes_received[0]; speed = (diff_bytes * 1000000) / diff_time; for (i = 0; i < G_N_ELEMENTS(units); i++) { if (speed < 1024) break; speed /= 1024; } if (i == 0) asprintf(&value, "%d %s", (int)speed, units[i]); else asprintf(&value, "%.1f %s", speed, units[i]); gtk_label_set_label(stack->net_recv_speed, value); free(value); /* Débit de émission */ diff_bytes = stack->last_bytes_sent[NETWORK_UPDATE_COUNT - 1] - stack->last_bytes_sent[0]; speed = (diff_bytes * 1000000) / diff_time; for (i = 0; i < G_N_ELEMENTS(units); i++) { if (speed < 1024) break; speed /= 1024; } if (i == 0) asprintf(&value, "%d %s", (int)speed, units[i]); else asprintf(&value, "%.1f %s", speed, units[i]); gtk_label_set_label(stack->net_send_speed, value); free(value); done: return result; } /* ---------------------------------------------------------------------------------- */ /* STATUT DES INFORMATIONS DE DESASSEMBLAGE */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : info = informations à initialiser. * * * * Description : Met en place le suivi d'informations de navigation. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void init_navigation_info(navigation_info_t *info) { info->segment = NULL; info->symbol = NULL; info->format = NULL; info->details = NULL; } /****************************************************************************** * * * Paramètres : info = informations à libérer de la mémoire. * * * * Description : Supprime l'empreinte mémoire d'informations de navigation. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void fini_navigation_info(navigation_info_t *info) { if (info->segment != NULL) free(info->segment); if (info->symbol != NULL) free(info->symbol); if (info->format != NULL) free(info->format); if (info->details != NULL) free(info->details); } /****************************************************************************** * * * Paramètres : stack = barre de statut à actualiser. * * range = emplacement à mettre en valeur. * * segment = zone de binaire d'appartenance. * * symbol = éventuelle position par rapport à un symbole. * * format = format du binaire manipulé * * details = détails supplémentaires (liés à l'encodage ?) * * * * Description : Actualise les informations liées une position d'assemblage. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_status_stack_update_current_location(GtkStatusStack *stack, const mrange_t *range, const char *segment, const char *symbol, const char *format, const char *details) { navigation_info_t *info; /* Informations à constituer */ const vmpa2t *addr; /* Localisation de départ */ info = stack->nav_info; if (cmp_mrange(&info->current, range) == 0) goto useless; /* Zone d'appartenance */ if (segment != NULL) info->segment = strdup(segment); else info->segment = NULL; /* Adresses de base */ copy_mrange(&info->current, range); addr = get_mrange_addr(range); vmpa2_phys_to_string(addr, MDS_UNDEFINED, info->phys, NULL); vmpa2_virt_to_string(addr, MDS_UNDEFINED, info->virt, NULL); /* Symbole concerné */ if (symbol != NULL) info->symbol = strdup(symbol); else info->symbol = NULL; /* Architecture & format */ info->format = strdup(format); if (details != NULL) info->details = strdup(details); else info->details = NULL; /* Nettoyage et conclusion */ gtk_status_stack_show_current_location(stack); useless: ; } /****************************************************************************** * * * Paramètres : stack = pile de statuts à manipuler. * * * * Description : S'assure de l'affichage à jour de la partie "navigation". * * * * Retour : G_SOURCE_REMOVE pour une exécution unique. * * * * Remarques : - * * * ******************************************************************************/ static gboolean gtk_status_stack_show_current_location(GtkStatusStack *stack) { navigation_info_t *info; /* Informations à constituer */ char raw_pos[6 + VMPA_MAX_LEN + 1]; /* Formatage final en direct */ stack->def_source = (GSourceFunc)gtk_status_stack_show_current_location; gtk_stack_set_visible_child_name(stack->main, "navigation"); info = stack->nav_info; /* Première partie : navigation */ gtk_label_set_text(stack->nav_segment, info->segment != NULL ? info->segment : ""); snprintf(raw_pos, sizeof(raw_pos), "phys: %s", info->phys); gtk_label_set_text(stack->nav_phys, raw_pos); snprintf(raw_pos, sizeof(raw_pos), "virt: %s", info->virt); gtk_label_set_text(stack->nav_virt, raw_pos); gtk_label_set_text(stack->nav_offset, info->symbol != NULL ? info->symbol : ""); /* Seconde partie : format & architecture */ gtk_label_set_text(stack->nav_format, info->format); gtk_label_set_text(stack->nav_details, info->details != NULL ? info->details : ""); return G_SOURCE_REMOVE; } /****************************************************************************** * * * Paramètres : entry = zone de texte visée par la procédure. * * icon_pos = position de l'image associée à l'entrée. * * stack = composant graphique de gestion des statuts. * * * * Description : Réagit à un clic sur l'icône de zoom. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_status_stack_on_zoom_icon_press(GtkEntry *entry, GtkEntryIconPosition icon_pos, GtkStatusStack *stack) { #if 0 GtkWidget *popup; /* Popup à faire surgir */ GdkRectangle rect; /* Zone précise à cibler */ popup = gtk_popover_new(); gtk_entry_get_icon_area(entry, GTK_ENTRY_ICON_SECONDARY, &rect); gtk_popover_set_pointing_to(GTK_POPOVER(popup), &rect); gtk_widget_show(popup); #endif } /* ---------------------------------------------------------------------------------- */ /* STATUT DES SUIVIS D'ACTIVITE */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : info = informations à initialiser. * * * * Description : Met en place le suivi d'informations d'activité. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void init_activity_info(activity_info_t *info) { g_mutex_init(&info->access); info->generator = NO_ACTIVITY_ID; info->statuses = NULL; info->count = 0; info->tag = 0; } /****************************************************************************** * * * Paramètres : info = informations à libérer de la mémoire. * * * * Description : Supprime l'empreinte mémoire d'informations d'activité. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void fini_activity_info(activity_info_t *info) { size_t i; /* Boucle de parcours */ if (info->tag != 0) g_source_remove(info->tag); info->tag = 0; for (i = 0; i < info->count; i++) { if (info->statuses[i].message != NULL) free(info->statuses[i].message); } if (info->statuses != NULL) { free(info->statuses); info->statuses = NULL; } info->count = 0; g_mutex_clear(&info->access); } /****************************************************************************** * * * Paramètres : info = informations relatives aux activités à consulter. * * id = identifiant de l'activité à cibler. * * * * Description : Recherche les indications de statut d'une activité donnée. * * * * Retour : Structure d'encadrement trouvée ou NULL. * * * * Remarques : - * * * ******************************************************************************/ static activity_status_t *find_activity_status_by_id(activity_info_t *info, activity_id_t id) { activity_status_t *result; /* Statut trouvé à renvoyer */ size_t i; /* Boucle de parcours */ result = NULL; assert(!g_mutex_trylock(&info->access)); for (i = 0; i < info->count; i++) if (info->statuses[i].id == id) { result = info->statuses + i; break; } return result; } /****************************************************************************** * * * Paramètres : stack = barre de statut à actualiser. * * msg = nouveau message de statut à copier. * * max = taille de la plage à parcourir. * * * * Description : Démarre le suivi d'une nouvelle activité. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ activity_id_t gtk_status_stack_add_activity(GtkStatusStack *stack, const char *msg, unsigned long max) { activity_id_t result; /* Numéro unique à renvoyer */ activity_info_t *info; /* Informations à consulter */ activity_status_t *new; /* Nouveau suivi d'activité */ info = stack->activity_info; g_mutex_lock(&info->access); while (1) { result = ++info->generator; if (find_activity_status_by_id(info, result) == NULL) break; } while (0); info->statuses = realloc(info->statuses, ++info->count * sizeof(activity_status_t)); new = info->statuses + info->count - 1; /* Identifiant */ new->id = result; /* Intitulé */ if (msg == NULL) new->message = NULL; else new->message = strdup(msg); /* Valeur */ new->current = 0; new->max = max; new->last_updated = 0; /* Actualisation */ if (info->tag != 0) g_source_remove(info->tag); info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack); g_mutex_unlock(&info->access); return result; } /****************************************************************************** * * * Paramètres : stack = barre de statut à actualiser. * * id = identifiant de l'activité à cibler. * * msg = nouveau message de statut à copier. * * * * Description : Actualise les informations concernant une activité. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_status_stack_update_activity_message(GtkStatusStack *stack, activity_id_t id, const char *msg) { activity_info_t *info; /* Informations à consulter */ activity_status_t *status; /* Suivi d'activité à traiter */ bool msg_changed; /* Changement d'intitulé */ info = stack->activity_info; g_mutex_lock(&info->access); status = find_activity_status_by_id(info, id); assert(status != NULL); if (status == NULL) goto exit; /* Intitulé */ if (status->message != NULL) { if (msg == NULL) msg_changed = true; else msg_changed = (strcmp(status->message, msg) != 0); free(status->message); } else msg_changed = (msg != NULL); if (msg == NULL) status->message = NULL; else status->message = strdup(msg); /* On n'actualise que le sommet de la pile */ if ((status - info->statuses + 1) == info->count && msg_changed) { if (info->tag != 0) g_source_remove(info->tag); info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack); } exit: g_mutex_unlock(&info->access); } /****************************************************************************** * * * Paramètres : stack = barre de statut à actualiser. * * id = identifiant de l'activité à cibler. * * inc = nouvelle valeur pour une progression donnée. * * * * Description : Actualise la progression d'une activité. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_status_stack_update_activity_value(GtkStatusStack *stack, activity_id_t id, unsigned long inc) { activity_info_t *info; /* Informations à consulter */ activity_status_t *status; /* Suivi d'activité à traiter */ double new; /* Nouvelle progression */ info = stack->activity_info; g_mutex_lock(&info->access); status = find_activity_status_by_id(info, id); assert(status != NULL); if (status == NULL) goto exit; /* Valeur */ status->current += inc; new = (status->current * 1.0) / status->max; /* On n'actualise que le sommet de la pile */ if ((status - info->statuses + 1) == info->count && (new - status->last_updated) > 0.1) { status->last_updated = new; if (info->tag != 0) g_source_remove(info->tag); info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack); } exit: g_mutex_unlock(&info->access); } /****************************************************************************** * * * Paramètres : stack = barre de statut à actualiser. * * id = identifiant de l'activité à cibler. * * extra = nouvelle échéance supplémentaire des traitements. * * * * Description : Etend la portée des travaux d'une nouvelle activité. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_status_stack_extend_activity_max(GtkStatusStack *stack, activity_id_t id, unsigned long extra) { activity_info_t *info; /* Informations à consulter */ activity_status_t *status; /* Suivi d'activité à traiter */ info = stack->activity_info; g_mutex_lock(&info->access); status = find_activity_status_by_id(info, id); assert(status != NULL); if (status == NULL) goto exit; /* Valeur */ status->max += extra; /* On n'actualise que le sommet de la pile */ if ((status - info->statuses + 1) == info->count) { if (info->tag != 0) g_source_remove(info->tag); info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack); } exit: g_mutex_unlock(&info->access); } /****************************************************************************** * * * Paramètres : stack = barre de statut à actualiser. * * * * Description : Met fin au suivi d'une activité donnée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_status_stack_remove_activity(GtkStatusStack *stack, activity_id_t id) { activity_info_t *info; /* Informations à consulter */ activity_status_t *status; /* Suivi d'activité à traiter */ bool is_last; /* Dernière position ? */ info = stack->activity_info; g_mutex_lock(&info->access); status = find_activity_status_by_id(info, id); assert(status != NULL); if (status == NULL) goto exit; is_last = ((status - info->statuses + 1) == info->count); /* Suppression des données */ if (is_last) { if (info->tag != 0) g_source_remove(info->tag); } if (status->message != NULL) free(status->message); /* Réajustement des enregistrements */ if (!is_last) memmove(status, status + 1, (((info->statuses + info->count) - status) - 1) * sizeof(activity_status_t)); info->statuses = realloc(info->statuses, --info->count * sizeof(activity_status_t)); /* Bascule vers un autre affichage ou actualisation ? */ if (info->count == 0) { info->tag = 0; g_idle_add(stack->def_source, stack); } else if (is_last) info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack); exit: g_mutex_unlock(&info->access); } /****************************************************************************** * * * Paramètres : stack = pile de statuts à manipuler. * * * * Description : S'assure de l'affichage à jour de la partie "activité". * * * * Retour : G_SOURCE_REMOVE pour une exécution unique. * * * * Remarques : - * * * ******************************************************************************/ static gboolean gtk_status_stack_show_current_activity(GtkStatusStack *stack) { activity_info_t *info; /* Informations à consulter */ activity_status_t *last; /* Dernier statut à traiter */ info = stack->activity_info; g_mutex_lock(&info->access); if (!g_source_is_destroyed(g_main_current_source())) { if (info->count > 0) { gtk_stack_set_visible_child_name(stack->main, "activity"); last = &info->statuses[info->count - 1]; gtk_label_set_text(stack->activity_message, last->message); gtk_progress_bar_set_fraction(stack->activity_progress, (last->current * 1.0) / last->max); } info->tag = 0; } g_mutex_unlock(&info->access); return G_SOURCE_REMOVE; }