From 4b1367d2fee7be0789744e1db35d6f9200b29163 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Thu, 27 Dec 2018 21:44:31 +0100
Subject: Updated the API in order to allow Python plugins to rely on native
 plugins.

---
 plugins/pychrysalide/plugin.c   | 180 ++++++++++++++++++++++++++++++++++++++--
 plugins/pychrysalide/pychrysa.c |  42 +++++++---
 plugins/pychrysalide/pychrysa.h |   3 +
 src/plugins/pglist.c            |   2 +
 src/plugins/pglist.h            |   8 +-
 src/plugins/plugin-def.h        |  12 ++-
 src/plugins/plugin-int.h        |   5 ++
 src/plugins/plugin.c            |  42 ++++++++++
 src/plugins/plugin.h            |   3 +
 9 files changed, 272 insertions(+), 25 deletions(-)

diff --git a/plugins/pychrysalide/plugin.c b/plugins/pychrysalide/plugin.c
index 44f518e..f232548 100644
--- a/plugins/pychrysalide/plugin.c
+++ b/plugins/pychrysalide/plugin.c
@@ -87,6 +87,15 @@ static bool g_python_plugin_do_exit(GPythonPlugin *);
 /* Procède à une opération liée à un contenu binaire. */
 static void g_python_plugin_handle_binary_content(const GPythonPlugin *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *);
 
+/* Procède à une opération liée à l'analyse d'un format. */
+static bool g_python_plugin_handle_binary_format_analysis(const GPythonPlugin *, PluginAction, GBinFormat *, wgroup_id_t, GtkStatusStack *);
+
+/* Procède à un préchargement de format de fichier. */
+static bool g_python_plugin_preload_binary_format(const GPythonPlugin *, PluginAction, GBinFormat *, GPreloadInfo *, GtkStatusStack *);
+
+/* Procède au rattachement d'éventuelles infos de débogage. */
+static void g_python_plugin_attach_debug_format(const GPythonPlugin *, PluginAction, GExeFormat *);
+
 /* Exécute une action pendant un désassemblage de binaire. */
 static void g_python_plugin_process_disass(const GPythonPlugin *, PluginAction, GLoadedBinary *, GtkStatusStack *, GProcContext *);
 
@@ -327,14 +336,14 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename)
 
     /* Localisation des différents points d'entrée déclarés */
 
-#define register_python_binding(inst, sym, binding)                                 \
+#define register_python_binding(inst, pysym, sym, binding)                          \
     ({                                                                              \
         bool __result;                                                              \
-        if (!has_python_method(inst, #sym))                                         \
+        if (!has_python_method(inst, #pysym))                                       \
         {                                                                           \
             log_variadic_message(LMT_ERROR,                                         \
                                  _("No '%s' entry in plugin candidate '%s'"),       \
-                                 #sym, G_PLUGIN_MODULE(result)->filename);          \
+                                 #pysym, G_PLUGIN_MODULE(result)->filename);        \
             __result = false;                                                       \
         }                                                                           \
         else                                                                        \
@@ -361,12 +370,14 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename)
                         break;
 
                     case PGA_PLUGIN_INIT:
-                        if (!register_python_binding(instance, init, (pg_management_fc)g_python_plugin_do_init))
+                        if (!register_python_binding(instance, init, init,
+                                                     (pg_management_fc)g_python_plugin_do_init))
                             goto gppn_bad_plugin;
                         break;
 
                     case PGA_PLUGIN_EXIT:
-                        if (!register_python_binding(instance, exit, (pg_management_fc)g_python_plugin_do_exit))
+                        if (!register_python_binding(instance, exit, exit,
+                                                     (pg_management_fc)g_python_plugin_do_exit))
                             goto gppn_bad_plugin;
                         break;
 
@@ -389,7 +400,7 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename)
                         {
                             case PGA_CONTENT_EXPLORER:
                             case PGA_CONTENT_RESOLVER:
-                                if (!register_python_binding(instance, handle_content, \
+                                if (!register_python_binding(instance, handle_content, handle_content,
                                                              (pg_handle_content_fc)g_python_plugin_handle_binary_content))
                                     goto gppn_bad_plugin;
                                 break;
@@ -408,6 +419,27 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename)
 
                         switch (action)
                         {
+                            case PGA_FORMAT_ANALYSIS_STARTED:
+                            case PGA_FORMAT_ANALYSIS_ENDED:
+                            case PGA_FORMAT_POST_ANALYSIS_STARTED:
+                            case PGA_FORMAT_POST_ANALYSIS_ENDED:
+                                if (!register_python_binding(instance, handle_format_analysis, handle_fmt_analysis,
+                                                             (pg_handle_format_analysis_fc)g_python_plugin_handle_binary_format_analysis))
+                                    goto gppn_bad_plugin;
+                                break;
+
+                            case PGA_FORMAT_PRELOAD:
+                                if (!register_python_binding(instance, preload_format, preload_format,
+                                                             (pg_preload_format_fc)g_python_plugin_preload_binary_format))
+                                    goto gppn_bad_plugin;
+                                break;
+
+                            case PGA_FORMAT_ATTACH_DEBUG:
+                                if (!register_python_binding(instance, attach_debug_format, attach_debug,
+                                                             (pg_attach_debug)g_python_plugin_attach_debug_format))
+                                    goto gppn_bad_plugin;
+                                break;
+
                             default:
                                 log_variadic_message(LMT_WARNING,
                                                      _("Unknown action '0x%02x' in plugin '%s'..."),
@@ -419,7 +451,7 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename)
                         break;
 
                     case DPS_DISASSEMBLY:
-                        if (!register_python_binding(instance, process_disass,
+                        if (!register_python_binding(instance, process_disassembly, process_disass,
                                                      (pg_process_disassembly_fc)g_python_plugin_process_disass))
                             goto gppn_bad_plugin;
                         break;
@@ -668,9 +700,9 @@ static bool g_python_plugin_do_exit(GPythonPlugin *plugin)
 
 static void g_python_plugin_handle_binary_content(const GPythonPlugin *plugin, PluginAction action, GBinContent *content, wgroup_id_t wid, GtkStatusStack *status)
 {
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *args;                         /* Arguments pour l'appel      */
     PyObject *value;                        /* Valeurs obtenues            */
-    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
 
     gstate = PyGILState_Ensure();
 
@@ -693,6 +725,129 @@ static void g_python_plugin_handle_binary_content(const GPythonPlugin *plugin, P
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : plugin = greffon à manipuler.                                *
+*                action = type d'action attendue.                             *
+*                format = format de binaire à manipuler pendant l'opération.  *
+*                gid    = groupe de travail dédié.                            *
+*                status = barre de statut à tenir informée.                   *
+*                                                                             *
+*  Description : Procède à une opération liée à l'analyse d'un format.        *
+*                                                                             *
+*  Retour      : Bilan de l'exécution du traitement.                          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_python_plugin_handle_binary_format_analysis(const GPythonPlugin *plugin, PluginAction action, GBinFormat *format, wgroup_id_t gid, GtkStatusStack *status)
+{
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+    PyObject *args;                         /* Arguments pour l'appel      */
+    PyObject *value;                        /* Valeurs obtenues            */
+
+    gstate = PyGILState_Ensure();
+
+    args = PyTuple_New(4);
+
+    PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action));
+    PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(format)));
+    PyTuple_SetItem(args, 2, PyLong_FromUnsignedLong(gid));
+    PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status)));
+
+    value = run_python_method(plugin->instance, "handle_format_analysis", args);
+
+    Py_XDECREF(value);
+    Py_DECREF(args);
+
+    PyGILState_Release(gstate);
+
+    return true;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : plugin = greffon à manipuler.                                *
+*                action = type d'action attendue.                             *
+*                format = format de binaire à manipuler pendant l'opération.  *
+*                info   = informations à constituer en avance de phase.       *
+*                status = barre de statut à tenir informée.                   *
+*                                                                             *
+*  Description : Procède à un préchargement de format de fichier.             *
+*                                                                             *
+*  Retour      : Bilan de l'exécution du traitement.                          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_python_plugin_preload_binary_format(const GPythonPlugin *plugin, PluginAction action, GBinFormat *format, GPreloadInfo *info, GtkStatusStack *status)
+{
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+    PyObject *args;                         /* Arguments pour l'appel      */
+    PyObject *value;                        /* Valeurs obtenues            */
+
+    gstate = PyGILState_Ensure();
+
+    args = PyTuple_New(4);
+
+    PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action));
+    PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(format)));
+    PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(info)));
+    PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status)));
+
+    value = run_python_method(plugin->instance, "preload_format", args);
+
+    Py_XDECREF(value);
+    Py_DECREF(args);
+
+    PyGILState_Release(gstate);
+
+    return true;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : plugin = greffon à manipuler.                                *
+*                action = type d'action attendue.                             *
+*                format = format de binaire à manipuler pendant l'opération.  *
+*                                                                             *
+*  Description : Procède au rattachement d'éventuelles infos de débogage.     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_python_plugin_attach_debug_format(const GPythonPlugin *plugin, PluginAction action, GExeFormat *format)
+{
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+    PyObject *args;                         /* Arguments pour l'appel      */
+    PyObject *value;                        /* Valeurs obtenues            */
+
+    gstate = PyGILState_Ensure();
+
+    args = PyTuple_New(2);
+
+    PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action));
+    PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(format)));
+
+    value = run_python_method(plugin->instance, "attach_debug_format", args);
+
+    Py_XDECREF(value);
+    Py_DECREF(args);
+
+    PyGILState_Release(gstate);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : plugin  = greffon à manipuler.                               *
 *                action  = type d'action attendue.                            *
 *                binary  = binaire dont le contenu est en cours de traitement.*
@@ -709,9 +864,12 @@ static void g_python_plugin_handle_binary_content(const GPythonPlugin *plugin, P
 
 static void g_python_plugin_process_disass(const GPythonPlugin *plugin, PluginAction action, GLoadedBinary *binary, GtkStatusStack *status, GProcContext *context)
 {
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *args;                         /* Arguments pour l'appel      */
     PyObject *value;                        /* Valeurs obtenues            */
 
+    gstate = PyGILState_Ensure();
+
     args = PyTuple_New(4);
 
     PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action));
@@ -719,11 +877,13 @@ static void g_python_plugin_process_disass(const GPythonPlugin *plugin, PluginAc
     PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(status)));
     PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(context)));
 
-    value = run_python_method(plugin->instance, "process_binary_disassembly", args);
+    value = run_python_method(plugin->instance, "process_disassembly", args);
 
     Py_XDECREF(value);
     Py_DECREF(args);
 
+    PyGILState_Release(gstate);
+
 }
 
 
@@ -756,6 +916,8 @@ static bool py_plugin_module_define_constants(PyTypeObject *obj_type)
     result &= PyDict_AddULongMacro(obj_type, PGA_PLUGIN_INIT);
     result &= PyDict_AddULongMacro(obj_type, PGA_PLUGIN_EXIT);
 
+    result &= PyDict_AddULongMacro(obj_type, PGA_NATIVE_LOADED);
+
     result &= PyDict_AddULongMacro(obj_type, PGA_CONTENT_EXPLORER);
     result &= PyDict_AddULongMacro(obj_type, PGA_CONTENT_RESOLVER);
     result &= PyDict_AddULongMacro(obj_type, PGA_CONTENT_ANALYZED);
diff --git a/plugins/pychrysalide/pychrysa.c b/plugins/pychrysalide/pychrysa.c
index 7772592..ff36dab 100644
--- a/plugins/pychrysalide/pychrysa.c
+++ b/plugins/pychrysalide/pychrysa.c
@@ -64,7 +64,7 @@
 
 
 DEFINE_CHRYSALIDE_CONTAINER_PLUGIN("PyChrysalide", "Provides bindings to Python", "0.1.0",
-                                   EMPTY_PG_LIST(.required), AL(PGA_PLUGIN_INIT, PGA_PLUGIN_EXIT));
+                                   EMPTY_PG_LIST(.required), AL(PGA_PLUGIN_INIT, PGA_PLUGIN_EXIT, PGA_NATIVE_LOADED));
 
 
 /* Note la nature du chargement */
@@ -93,7 +93,7 @@ static bool is_current_abi_suitable(void);
 static bool set_version_for_gtk_namespace(const char *);
 
 /* Charge autant de greffons composés en Python que possible. */
-static bool load_python_plugins(GPluginModule *);
+static void load_python_plugins(GPluginModule *);
 
 
 
@@ -452,13 +452,13 @@ PyMODINIT_FUNC PyInit_pychrysalide(void)
 *                                                                             *
 *  Description : Charge autant de greffons composés en Python que possible.   *
 *                                                                             *
-*  Retour      : true.                                                        *
+*  Retour      : -                                                            *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static bool load_python_plugins(GPluginModule *plugin)
+static void load_python_plugins(GPluginModule *plugin)
 {
     char *paths;                            /* Emplacements de greffons    */
     char *save;                             /* Sauvegarde pour ré-entrance */
@@ -545,8 +545,6 @@ static bool load_python_plugins(GPluginModule *plugin)
 
     }
 
-    return true;
-
 }
 
 
@@ -618,11 +616,7 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
      * En mode autonome, le shell Python remonte bien l'erreur par contre.
      */
 
-    if (_chrysalide_module == NULL)
-        result = false;
-
-    else
-        result = load_python_plugins(plugin);
+    result = (_chrysalide_module != NULL);
 
     _main_tstate = PyThreadState_Get();
 
@@ -656,6 +650,32 @@ G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *plugin)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : plugin = greffon à manipuler.                                *
+*                action = type d'action attendue.                             *
+*                                                                             *
+*  Description : Accompagne la fin du chargement des modules natifs.          *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+G_MODULE_EXPORT void chrysalide_plugin_on_native_loaded(GPluginModule *plugin, PluginAction action)
+{
+    if (!_standalone)
+        PyEval_AcquireLock();
+
+    load_python_plugins(plugin);
+
+    if (!_standalone)
+        PyEval_ReleaseLock();
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : -                                                            *
 *                                                                             *
 *  Description : Fournit les informations du thread principal.                *
diff --git a/plugins/pychrysalide/pychrysa.h b/plugins/pychrysalide/pychrysa.h
index da9913b..0f844e6 100644
--- a/plugins/pychrysalide/pychrysa.h
+++ b/plugins/pychrysalide/pychrysa.h
@@ -48,6 +48,9 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *);
 /* Prend acte du déchargement du greffon. */
 G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *);
 
+/* Accompagne la fin du chargement des modules natifs. */
+G_MODULE_EXPORT void chrysalide_plugin_on_native_loaded(GPluginModule *, PluginAction);
+
 /* Fournit les informations du thread principal. */
 PyThreadState *get_pychrysalide_main_tstate(void);
 
diff --git a/src/plugins/pglist.c b/src/plugins/pglist.c
index 57f7538..b7c8d1d 100644
--- a/src/plugins/pglist.c
+++ b/src/plugins/pglist.c
@@ -459,6 +459,8 @@ void load_remaning_plugins(void)
 
     g_rw_lock_reader_unlock(&_pg_lock);
 
+    notify_native_loaded;
+
 }
 
 
diff --git a/src/plugins/pglist.h b/src/plugins/pglist.h
index 0c8f6af..1306571 100644
--- a/src/plugins/pglist.h
+++ b/src/plugins/pglist.h
@@ -85,6 +85,11 @@ GPluginModule **get_all_plugins_for_action(PluginAction, size_t *);
     while (0)
 
 
+/* DPS_PG_MANAGEMENT */
+
+#define notify_native_loaded \
+    process_all_plugins_for(PGA_NATIVE_LOADED, g_plugin_module_notify_native_loaded, NULL)
+
 /* DPS_SETUP */
 
 #define include_plugin_theme(r, c) \
@@ -103,9 +108,6 @@ GPluginModule **get_all_plugins_for_action(PluginAction, size_t *);
 #define handle_binary_format_analysis(a, f, g, s) \
     process_all_plugins_for(a, g_plugin_module_handle_binary_format_analysis, f, g, s)
 
-#define handle_binary_format(a, f, s) \
-    process_all_plugins_for(a, g_plugin_module_handle_binary_format, f, s)
-
 #define preload_binary_format(a, f, i, s) \
     process_all_plugins_for(a, g_plugin_module_preload_binary_format, f, i, s)
 
diff --git a/src/plugins/plugin-def.h b/src/plugins/plugin-def.h
index 96b04b1..4c59606 100644
--- a/src/plugins/plugin-def.h
+++ b/src/plugins/plugin-def.h
@@ -76,6 +76,7 @@ typedef uint32_t plugin_action_t;
 
 #define DPS_NONE                DEFINE_PLUGIN_SUB_CATEGORY(0)
 #define DPS_PG_MANAGEMENT       DEFINE_PLUGIN_SUB_CATEGORY(1)
+#define DPS_CORE_MANAGEMENT     DEFINE_PLUGIN_SUB_CATEGORY(2)
 
 /* DPC_GUI */
 
@@ -111,10 +112,17 @@ typedef enum _PluginAction
      */
 
     /* Chargement */
-    PGA_PLUGIN_INIT = DPC_BASIC | DPS_PG_MANAGEMENT | DEFINE_PLUGIN_ACTION(0),
+    PGA_PLUGIN_INIT   = DPC_BASIC | DPS_PG_MANAGEMENT | DEFINE_PLUGIN_ACTION(0),
 
     /* Déchargement */
-    PGA_PLUGIN_EXIT = DPC_BASIC | DPS_PG_MANAGEMENT | DEFINE_PLUGIN_ACTION(1),
+    PGA_PLUGIN_EXIT   = DPC_BASIC | DPS_PG_MANAGEMENT | DEFINE_PLUGIN_ACTION(1),
+
+    /**
+     * DPC_BASIC | DPS_CORE_MANAGEMENT
+     */
+
+    /* Fin du chargement des greffons natifs */
+    PGA_NATIVE_LOADED = DPC_BASIC | DPS_CORE_MANAGEMENT | DEFINE_PLUGIN_ACTION(0),
 
     /**
      * DPC_GUI | DPS_SETUP
diff --git a/src/plugins/plugin-int.h b/src/plugins/plugin-int.h
index 8df3527..2d98217 100644
--- a/src/plugins/plugin-int.h
+++ b/src/plugins/plugin-int.h
@@ -41,6 +41,9 @@
 /* Prend acte du [dé]chargement du greffon. */
 typedef bool (* pg_management_fc) (GPluginModule *);
 
+/* Accompagne la fin du chargement des modules natifs. */
+typedef void (* pg_native_loaded_fc) (GPluginModule *, PluginAction);
+
 /* Procède à une opération liée à un contenu binaire. */
 typedef void (* pg_handle_content_fc) (const GPluginModule *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *);
 
@@ -83,6 +86,8 @@ struct _GPluginModule
     pg_management_fc init;                  /* Procédure d'initialisation  */
     pg_management_fc exit;                  /* Procédure d'extinction      */
 
+    pg_native_loaded_fc native_loaded;      /* Fin des chargements natifs  */
+
     pg_include_theme_fc include_theme;      /* Extension d'un thème        */
 
     pg_handle_content_fc handle_content;    /* Explorations ou résolutions */
diff --git a/src/plugins/plugin.c b/src/plugins/plugin.c
index 1608032..a9bd9da 100644
--- a/src/plugins/plugin.c
+++ b/src/plugins/plugin.c
@@ -288,6 +288,27 @@ GPluginModule *g_plugin_module_new(const gchar *filename)
 
                         break;
 
+                    case DPS_CORE_MANAGEMENT:
+
+                        switch (action)
+                        {
+                            case PGA_NATIVE_LOADED:
+                                if (!load_plugin_symbol(result->module,
+                                                        "chrysalide_plugin_on_native_loaded",
+                                                        &result->native_loaded))
+                                    goto bad_plugin;
+                                break;
+
+                            default:
+                                log_variadic_message(LMT_WARNING,
+                                                     _("Unknown action '0x%02x' in plugin '%s'..."),
+                                                     result->interface->actions[i], filename);
+                                break;
+
+                        }
+
+                        break;
+
                     default:
                         log_variadic_message(LMT_WARNING,
                                              _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename);
@@ -775,6 +796,27 @@ void g_plugin_module_log_variadic_message(const GPluginModule *plugin, LogMessag
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : plugin = greffon à manipuler.                                *
+*                action = type d'action attendue.                             *
+*                unused = variable non utilisé pour l'usage de __VA_ARGS__.   *
+*                                                                             *
+*  Description : Accompagne la fin du chargement des modules natifs.          *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_plugin_module_notify_native_loaded(GPluginModule *plugin, PluginAction action, void *unused)
+{
+    plugin->native_loaded(plugin, action);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : plugin    = greffon à manipuler.                             *
 *                action    = type d'action attendue.                          *
 *                resources = liste de ressources à constituer. [OUT]          *
diff --git a/src/plugins/plugin.h b/src/plugins/plugin.h
index 86dba25..4b3b306 100644
--- a/src/plugins/plugin.h
+++ b/src/plugins/plugin.h
@@ -91,6 +91,9 @@ bool g_plugin_module_resolve_dependencies(GPluginModule *, GPluginModule **, siz
 /* Termine le chargement du greffon préparé. */
 bool g_plugin_module_load(GPluginModule *, GPluginModule **, size_t);
 
+/* Accompagne la fin du chargement des modules natifs. */
+void g_plugin_module_notify_native_loaded(GPluginModule *, PluginAction, void *);
+
 /* Complète une liste de resources pour thème. */
 void g_plugin_module_include_theme(const GPluginModule *, PluginAction, char ***, size_t *);
 
-- 
cgit v0.11.2-87-g4458