// // dynamicsignal.c: new method of registering GObject signal // handlers that uses a default signal handler to read // callback argument stack and convert to an array of // GValues before running the "generic" signal handler // // Authors: // Aaron Bockover (abockover@novell.com) // // (C) 2006 Novell, Inc. // #include #include #include #include #include typedef void (* DynamicSignalHandler)(GObject *sender, guint argc, GValue *argv, gpointer userdata); typedef struct { GObject *object; gpointer userdata; DynamicSignalHandler callback; guint id; guint g_signal_id; gulong handler_id; GSignalQuery signal_details; } DynamicSignalEntry; static GList *dynamic_signal_table = NULL; static guint dynamic_signal_last_id = 1; static DynamicSignalEntry * find_entry(GObject *object, guint signal_id) { GList *current = dynamic_signal_table; while(current != NULL) { DynamicSignalEntry *entry = (DynamicSignalEntry *)current->data; if(entry->object == object && entry->g_signal_id == signal_id) { return entry; } current = current->next; } return NULL; } static DynamicSignalEntry * find_entry_by_name(GObject *object, const gchar *signal_name, guint *signal_id_out) { guint signal_id; signal_id = g_signal_lookup(signal_name, G_OBJECT_TYPE(object)); if(signal_id_out != NULL) { *signal_id_out = signal_id; } if(signal_id <= 0) { return NULL; } return find_entry(object, signal_id); } static void free_entry(DynamicSignalEntry *entry) { memset(entry, 0, sizeof(DynamicSignalEntry)); g_free(entry); } static void dynamic_signal_handler(DynamicSignalEntry *entry, ...) { va_list argp; GValue *args = NULL; guint i, n; if(entry == NULL) { return; } n = entry->signal_details.n_params; if(n <= 0) { entry->callback(entry->object, 0, NULL, entry->userdata); return; } args = g_new0(GValue, n); va_start(argp, entry); for(i = 0; i < n; i++) { GType type = entry->signal_details.param_types[i]; GValue *value = &args[i]; gchar *collect_error = NULL; if(!G_TYPE_IS_CLASSED(type) && G_TYPE_IS_VALUE_TYPE(type) && !G_TYPE_IS_FUNDAMENTAL(type) && G_TYPE_IS_DERIVED(type) && G_TYPE_HAS_VALUE_TABLE(type)) { g_value_init(value, type); value->data[0].v_pointer = va_arg(argp, gpointer); } else { g_value_init(value, type); G_VALUE_COLLECT(value, argp, 0, &collect_error); } if(collect_error != NULL) { g_warning("Could not collect value: %s", collect_error); g_free(collect_error); break; } } va_end(argp); entry->callback(entry->object, n, args, entry->userdata); } DynamicSignalEntry * g_dynamic_signal_find_registration(GObject *object, const gchar *signal_name) { return find_entry_by_name(object, signal_name, NULL); } guint g_dynamic_signal_connect(GObject *object, const gchar *signal_name, DynamicSignalHandler callback, gboolean after, gpointer userdata) { DynamicSignalEntry *entry; guint signal_id; entry = find_entry_by_name(object, signal_name, &signal_id); if(entry != NULL) { return entry->id; } entry = g_new0(DynamicSignalEntry, 1); entry->id = dynamic_signal_last_id++; entry->object = object; entry->g_signal_id = signal_id; entry->callback = callback; entry->userdata = userdata; g_signal_query(signal_id, &(entry->signal_details)); dynamic_signal_table = g_list_prepend(dynamic_signal_table, entry); entry->handler_id = g_signal_connect_data(object, signal_name, G_CALLBACK(dynamic_signal_handler), entry, NULL, G_CONNECT_SWAPPED | (after ? G_CONNECT_AFTER : 0)); return entry->id; } void g_dynamic_signal_disconnect(GObject *object, const gchar *signal_name) { DynamicSignalEntry *entry = find_entry_by_name(object, signal_name, NULL); if(entry == NULL) { return; } g_signal_handler_disconnect(object, entry->handler_id); dynamic_signal_table = g_list_remove(dynamic_signal_table, entry); free_entry(entry); entry = NULL; } void g_dynamic_signal_update_entry_userdata(DynamicSignalEntry *entry, gpointer userdata) { if(entry != NULL) { entry->userdata = userdata; } } GType g_value_type(GValue *value) { return G_VALUE_TYPE(value); }