/* Chrysalide - Outil d'analyse de fichiers binaires * sigredir.c - encadrement des signaux supplémentaire par rapport à celui de la GLib * * Copyright (C) 2014-2025 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 "sigredir.h" #include #include #include #include #include #include /* Informations utiles à l'appel final */ typedef struct _gsignal_wrapper_params_t { GClosure *closure; /* Glue pour les appels */ guint n_params; /* Nombre de paramètres */ GValue instance_and_params[0]; /* Instance & paramètres */ } gsignal_wrapper_params_t; /* Transmet un signal dans le contexte principal. */ static gboolean to_main_wrapper(gpointer); /* Supprime de la mémoire le transporteur d'informations. */ static void destroy_wrapper_params(gpointer); /* Informations concernant une diffusion de signal */ typedef struct _gsignal_wrapper_info_t { gpointer instance; /* Instance GLib initiatrice */ gulong id; /* Identifiant de connexion */ GClosure *closure; /* Glue pour les appels */ guint n_params; /* Nombre de paramètres */ GType param_types[0]; /* Type des paramètres associés*/ } gsignal_wrapper_info_t; /* Réceptionne un signal et redirige son exécution. */ static void carry_signal_to_main_thread(gsignal_wrapper_info_t *, ...); /****************************************************************************** * * * Paramètres : data = collecteur d'informations sur la diffusion. * * * * Description : Transmet un signal dans le contexte principal. * * * * Retour : FALSE / G_SOURCE_REMOVE pour arrêter la transmission. * * * * Remarques : - * * * ******************************************************************************/ static gboolean to_main_wrapper(gpointer data) { gsignal_wrapper_params_t *params; /* Informations d'appel */ params = (gsignal_wrapper_params_t *)data; g_closure_invoke(params->closure, NULL /* return_value */, params->n_params + 1, params->instance_and_params, NULL /* invocation_hint */); return G_SOURCE_REMOVE; } /****************************************************************************** * * * Paramètres : data = collecteur d'informations à supprimer. * * * * Description : Supprime de la mémoire le transporteur d'informations. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void destroy_wrapper_params(gpointer data) { gsignal_wrapper_params_t *params; /* Informations d'appel */ guint i; /* Boucle de parcours */ params = (gsignal_wrapper_params_t *)data; g_closure_unref(params->closure); for (i = 0; i < (params->n_params + 1); i++) g_value_unset(params->instance_and_params + i); free(params); } /****************************************************************************** * * * Paramètres : info = collecteur d'informations sur la diffusion. * * ... = arguments poussés par la GLib sur la pile. * * * * Description : Réceptionne un signal et redirige son exécution. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void carry_signal_to_main_thread(gsignal_wrapper_info_t *info, ...) { gsignal_wrapper_params_t *params; /* Informations d'appel */ GValue *param_values; /* Paramètres d'appel */ va_list ap; /* Liste d'arguments sur pile */ guint i; /* Boucle de parcours */ bool static_scope; /* Portée des arguments */ gchar *error; /* Eventuelle erreur inattendue*/ params = calloc(1, sizeof(gsignal_wrapper_params_t) + sizeof(GValue) * (info->n_params + 1)); params->closure = info->closure; g_closure_ref(info->closure); params->n_params = info->n_params; g_value_init(params->instance_and_params, G_TYPE_FROM_INSTANCE(info->instance)); g_value_set_instance(params->instance_and_params, info->instance); param_values = params->instance_and_params + 1; va_start(ap, info); error = NULL; for (i = 0; i < info->n_params; i++) { static_scope = info->param_types[i] & G_SIGNAL_TYPE_STATIC_SCOPE; G_VALUE_COLLECT_INIT(param_values + i, info->param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE, ap, static_scope ? G_VALUE_NOCOPY_CONTENTS : 0, &error); if (error != NULL) { fprintf(stderr, "%s: %s", G_STRLOC, error); g_free(error); break; } } va_end(ap); if (error == NULL) g_idle_add_full(G_PRIORITY_HIGH_IDLE, to_main_wrapper, params, destroy_wrapper_params); } /****************************************************************************** * * * Paramètres : info = collecteur d'informations à supprimer. * * * * Description : Déconnecte un signal redirigé vers le contexte principal. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_signal_disconnect_from_main(gsignal_wrapper_info_t *info) { g_signal_handler_disconnect(info->instance, info->id); free(info); } /****************************************************************************** * * * Paramètres : instance = object GLib à l'origine de l'émission. * * signal = identification du signal à réceptionner. * * handler = fonction C servant de réceptacle. * * data = éventuelle donnée de l'utilisateur à ajouter. * * marshal = précise la fonction de transfert des arguments. * * flags = fournit quelques indications supplémentaires. * * * * Description : Reproduit le comportement de la fonction g_signal_connect(). * * * * Retour : Identifiant supérieur zéro en cas de succès. * * * * Remarques : - * * * ******************************************************************************/ gsignal_wrapper_info_t *_g_signal_connect_to_main(gpointer instance, const gchar *signal, GCallback handler, gpointer data, GClosureMarshal marshal, GConnectFlags flags) { gsignal_wrapper_info_t *result; /* Structure à renvoyer */ guint signal_id; /* Identifiant du signal visé */ GSignalQuery query; /* Information sur le signal */ /* Collection d'informations */ signal_id = g_signal_lookup(signal, G_TYPE_FROM_INSTANCE(instance)); g_signal_query(signal_id, &query); assert(query.signal_id != 0); assert(query.return_type == G_TYPE_NONE); /* Allocation adaptée */ result = malloc(sizeof(gsignal_wrapper_info_t) + sizeof(GType) * query.n_params); result->instance = instance; if (flags & G_CONNECT_SWAPPED) result->closure = g_cclosure_new_swap(handler, data, NULL); else result->closure = g_cclosure_new(handler, data, NULL); g_closure_ref(result->closure); g_closure_sink(result->closure); g_closure_set_marshal(result->closure, marshal); result->n_params = query.n_params; memcpy(result->param_types, query.param_types, sizeof(GType) * query.n_params); /* Connexion au signal */ result->id = g_signal_connect_swapped(instance, signal, G_CALLBACK(carry_signal_to_main_thread), result); return result; }