feat: notify application after tool initialization

This commit is contained in:
grayTerminal-sh 2026-07-18 15:42:48 +02:00
parent 6f2338e72e
commit 349ff157ba
6 changed files with 1779 additions and 16 deletions

View file

@ -31,6 +31,25 @@ typedef struct
gsize version_failed_count;
} ToolInitializationSummary;
/**
* @brief Fonction appelée après la fin dune initialisation.
*
* Le callback est exécuté sur le contexte principal GLib.
*
* summary est non NULL uniquement après une réussite.
* error est non NULL uniquement lorsquune erreur est disponible.
*
* Les pointeurs summary et error sont empruntés et restent valides
* uniquement pendant lappel du callback.
*/
typedef void (*ToolInitializerCompletedCallback)(
ToolInitializer *tool_initializer,
BackgroundTaskState final_state,
const ToolInitializationSummary *summary,
const GError *error,
gpointer user_data
);
/**
* @brief Codes derreur de ToolInitializer.
*/
@ -150,6 +169,35 @@ ToolRegistry *tool_initializer_get_registry(
ToolInitializer *tool_initializer
);
/**
* @brief Enregistre le callback appelé après chaque initialisation.
*
* Le même callback est conservé pour les lancements suivants.
*
* En passant callback == NULL, lenregistrement actuel est supprimé.
*
* Lorsque destroy_notify est non NULL, ToolInitializer devient
* responsable de user_data. La fonction de destruction sera appelée
* exactement une fois lorsque :
*
* - le callback est remplacé ;
* - le callback est supprimé ;
* - linitialiseur est détruit.
*
* Le callback est exécuté sur le contexte principal GLib.
*
* @param tool_initializer Initialiseur concerné.
* @param callback Fonction appelée après la fin, ou NULL.
* @param user_data Données transmises au callback.
* @param destroy_notify Fonction libérant user_data, ou NULL.
*/
void tool_initializer_set_completed_callback(
ToolInitializer *tool_initializer,
ToolInitializerCompletedCallback callback,
gpointer user_data,
GDestroyNotify destroy_notify
);
/**
* @brief Démarre linitialisation en arrière-plan.
*

Binary file not shown.

View file

@ -77,6 +77,107 @@ static void application_present_error(
const char *message
);
/**
* @brief Traite la fin de linitialisation des outils externes.
*
* Ce callback est exécuté sur le contexte principal GLib.
*
* @param tool_initializer Initialiseur ayant terminé.
* @param final_state État final de la tâche.
* @param summary Résumé emprunté en cas de réussite.
* @param error Erreur empruntée en cas déchec.
* @param user_data Pointeur emprunté vers Application.
*/
static void application_on_tool_initialization_completed(
ToolInitializer *tool_initializer,
BackgroundTaskState final_state,
const ToolInitializationSummary *summary,
const GError *error,
gpointer user_data
)
{
Application *application = user_data;
if (application == NULL ||
tool_initializer == NULL)
{
return;
}
if (tool_initializer !=
application->tool_initializer)
{
g_warning(
"Notification reçue depuis un initialiseur inconnu."
);
return;
}
switch (final_state)
{
case BACKGROUND_TASK_STATE_COMPLETED:
if (summary == NULL)
{
g_warning(
"Linitialisation des outils est terminée "
"sans résumé valide."
);
return;
}
g_message(
"Outils externes initialisés : "
"%" G_GSIZE_FORMAT " disponibles, "
"%" G_GSIZE_FORMAT " absents, "
"%" G_GSIZE_FORMAT " versions détectées, "
"%" G_GSIZE_FORMAT " versions non détectées.",
summary->available_count,
summary->missing_count,
summary->version_detected_count,
summary->version_failed_count
);
break;
case BACKGROUND_TASK_STATE_CANCELLED:
g_message(
"Linitialisation des outils externes a été annulée."
);
break;
case BACKGROUND_TASK_STATE_FAILED:
g_warning(
"Linitialisation des outils externes a échoué : %s",
error != NULL
? error->message
: "erreur inconnue"
);
application_present_error(
application,
"Initialisation des outils impossible",
error != NULL
? error->message
: "Les outils externes nont pas pu être initialisés."
);
break;
case BACKGROUND_TASK_STATE_PENDING:
case BACKGROUND_TASK_STATE_RUNNING:
default:
g_warning(
"Notification de fin reçue avec un état invalide : %d.",
(int) final_state
);
break;
}
}
/**
* @brief Démarre la détection asynchrone des outils externes.
*
@ -154,6 +255,20 @@ static void application_present_error(
);
}
static void application_on_tool_initialization_completed(
ToolInitializer *tool_initializer,
BackgroundTaskState final_state,
const ToolInitializationSummary *summary,
const GError *error,
gpointer user_data
);
static void application_present_error(
Application *application,
const char *title,
const char *message
);
/**
* @brief Installe une nouvelle session et son arbre dans l'application.
*
@ -1145,6 +1260,13 @@ Application *application_new(void)
return NULL;
}
tool_initializer_set_completed_callback(
application->tool_initializer,
application_on_tool_initialization_completed,
application,
NULL
);
application->gtk_application = gtk_application_new(
APPLICATION_ID,
G_APPLICATION_DEFAULT_FLAGS
@ -1219,6 +1341,16 @@ void application_free(
return;
}
if (application->tool_initializer != NULL)
{
tool_initializer_set_completed_callback(
application->tool_initializer,
NULL,
NULL,
NULL
);
}
task_manager_cancel_all(
application->task_manager
);

View file

@ -9,6 +9,15 @@
#include <gio/gio.h>
#include <glib.h>
typedef struct
{
gatomicrefcount reference_count;
ToolInitializerCompletedCallback callback;
gpointer user_data;
GDestroyNotify destroy_notify;
} ToolInitializerCallbackRegistration;
/**
* @struct ToolInitializer
* @brief État interne de linitialiseur doutils.
@ -24,11 +33,89 @@ struct ToolInitializer
BackgroundTask *task;
ToolInitializationSummary last_summary;
ToolInitializerCallbackRegistration *callback_registration;
gboolean has_last_summary;
gboolean is_running;
};
/**
* @brief Enregistrement immuable dun callback de fin.
*/
static ToolInitializerCallbackRegistration *
tool_initializer_callback_registration_new(
ToolInitializerCompletedCallback callback,
gpointer user_data,
GDestroyNotify destroy_notify
)
{
ToolInitializerCallbackRegistration *registration = NULL;
if (callback == NULL)
{
return NULL;
}
registration = g_new0(
ToolInitializerCallbackRegistration,
1
);
g_atomic_ref_count_init(
&registration->reference_count
);
registration->callback = callback;
registration->user_data = user_data;
registration->destroy_notify = destroy_notify;
return registration;
}
static ToolInitializerCallbackRegistration *
tool_initializer_callback_registration_ref(
ToolInitializerCallbackRegistration *registration
)
{
if (registration == NULL)
{
return NULL;
}
g_atomic_ref_count_inc(
&registration->reference_count
);
return registration;
}
static void tool_initializer_callback_registration_unref(
ToolInitializerCallbackRegistration *registration
)
{
if (registration == NULL)
{
return;
}
if (!g_atomic_ref_count_dec(
&registration->reference_count
))
{
return;
}
if (registration->destroy_notify != NULL)
{
registration->destroy_notify(
registration->user_data
);
}
g_free(
registration
);
}
static gboolean tool_initializer_ensure_catalog_registered(
ToolInitializer *tool_initializer,
GError **error
@ -69,6 +156,8 @@ static void tool_initializer_unref_internal(
BackgroundTask *task = NULL;
ToolRegistry *tool_registry = NULL;
ToolInitializerCallbackRegistration *callback_registration = NULL;
if (tool_initializer == NULL)
{
return;
@ -85,11 +174,18 @@ static void tool_initializer_unref_internal(
&tool_initializer->mutex
);
task = tool_initializer->task;
tool_registry = tool_initializer->tool_registry;
task =
tool_initializer->task;
tool_registry =
tool_initializer->tool_registry;
callback_registration =
tool_initializer->callback_registration;
tool_initializer->task = NULL;
tool_initializer->tool_registry = NULL;
tool_initializer->callback_registration = NULL;
tool_initializer->task_manager = NULL;
tool_initializer->is_running = FALSE;
@ -101,6 +197,10 @@ static void tool_initializer_unref_internal(
task
);
tool_initializer_callback_registration_unref(
callback_registration
);
tool_registry_free(
tool_registry
);
@ -771,8 +871,16 @@ static void tool_initializer_bootstrap_completed(
ToolInitializer *tool_initializer =
user_data;
const ToolInitializationSummary *summary =
NULL;
ToolInitializerCallbackRegistration *callback_registration = NULL;
BackgroundTaskState final_state;
const ToolInitializationSummary *task_summary = NULL;
const ToolInitializationSummary *callback_summary = NULL;
ToolInitializationSummary summary_copy = {0};
GError *callback_error = NULL;
if (task == NULL ||
tool_initializer == NULL)
@ -780,11 +888,31 @@ static void tool_initializer_bootstrap_completed(
return;
}
if (background_task_get_state(
final_state =
background_task_get_state(
task
) == BACKGROUND_TASK_STATE_COMPLETED)
);
if (final_state == BACKGROUND_TASK_STATE_COMPLETED)
{
summary = background_task_get_result(
task_summary =
background_task_get_result(
task
);
if (task_summary != NULL)
{
summary_copy =
*task_summary;
callback_summary =
&summary_copy;
}
}
else if (final_state == BACKGROUND_TASK_STATE_FAILED)
{
callback_error =
background_task_dup_error(
task
);
}
@ -793,20 +921,56 @@ static void tool_initializer_bootstrap_completed(
&tool_initializer->mutex
);
if (summary != NULL)
if (callback_summary != NULL)
{
tool_initializer->last_summary =
*summary;
summary_copy;
tool_initializer->has_last_summary =
TRUE;
}
else
{
tool_initializer->last_summary =
(ToolInitializationSummary) {0};
tool_initializer->has_last_summary =
FALSE;
}
tool_initializer->is_running = FALSE;
callback_registration =
tool_initializer_callback_registration_ref(
tool_initializer->callback_registration
);
g_mutex_unlock(
&tool_initializer->mutex
);
/*
* Le callback utilisateur est toujours appelé hors du mutex.
* Il peut donc consulter linitialiseur ou remplacer son callback.
*/
if (callback_registration != NULL)
{
callback_registration->callback(
tool_initializer,
final_state,
callback_summary,
callback_error,
callback_registration->user_data
);
}
tool_initializer_callback_registration_unref(
callback_registration
);
g_clear_error(
&callback_error
);
}
GQuark tool_initializer_error_quark(void)
@ -877,6 +1041,17 @@ void tool_initializer_free(
return;
}
/*
* Le propriétaire public abandonne linitialiseur.
* Aucun callback ne doit désormais utiliser son user_data.
*/
tool_initializer_set_completed_callback(
tool_initializer,
NULL,
NULL,
NULL
);
task = tool_initializer_get_task(
tool_initializer
);
@ -1031,16 +1206,16 @@ gboolean tool_initializer_start(
tool_initializer->is_running = FALSE;
g_mutex_unlock(
&tool_initializer->mutex
);
tool_initializer->last_summary =
previous_summary;
tool_initializer->has_last_summary =
previous_has_last_summary;
g_mutex_unlock(
&tool_initializer->mutex
);
tool_initializer_set_wrapped_error(
error,
TOOL_INITIALIZER_ERROR_TASK_REGISTRATION,
@ -1175,6 +1350,77 @@ gboolean tool_initializer_start(
return TRUE;
}
void tool_initializer_set_completed_callback(
ToolInitializer *tool_initializer,
ToolInitializerCompletedCallback callback,
gpointer user_data,
GDestroyNotify destroy_notify
)
{
ToolInitializerCallbackRegistration *new_registration = NULL;
ToolInitializerCallbackRegistration *previous_registration = NULL;
if (callback != NULL)
{
new_registration =
tool_initializer_callback_registration_new(
callback,
user_data,
destroy_notify
);
}
if (tool_initializer == NULL)
{
tool_initializer_callback_registration_unref(
new_registration
);
/*
* Aucun enregistrement na pu prendre possession des données.
*/
if (new_registration == NULL &&
destroy_notify != NULL)
{
destroy_notify(
user_data
);
}
return;
}
g_mutex_lock(
&tool_initializer->mutex
);
previous_registration =
tool_initializer->callback_registration;
tool_initializer->callback_registration =
new_registration;
g_mutex_unlock(
&tool_initializer->mutex
);
tool_initializer_callback_registration_unref(
previous_registration
);
/*
* callback == NULL signifie suppression.
* Les éventuelles nouvelles données ne sont pas conservées.
*/
if (callback == NULL &&
destroy_notify != NULL)
{
destroy_notify(
user_data
);
}
}
gboolean tool_initializer_cancel(
ToolInitializer *tool_initializer,
GError **error

Binary file not shown.

File diff suppressed because it is too large Load diff