/* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include "gtksignal.h" #define SIGNAL_BLOCK_SIZE (100) #define HANDLER_BLOCK_SIZE (200) #define EMISSION_BLOCK_SIZE (100) #define DISCONNECT_INFO_BLOCK_SIZE (64) #define MAX_SIGNAL_PARAMS (32) enum { EMISSION_CONTINUE, EMISSION_RESTART, EMISSION_DONE }; #define GTK_RUN_TYPE(x) ((x) & GTK_RUN_MASK) typedef struct _GtkSignal GtkSignal; typedef struct _GtkSignalHash GtkSignalHash; typedef struct _GtkHandler GtkHandler; typedef struct _GtkHandlerInfo GtkHandlerInfo; typedef struct _GtkEmission GtkEmission; typedef union _GtkEmissionAllocator GtkEmissionAllocator; typedef struct _GtkDisconnectInfo GtkDisconnectInfo; typedef void (*GtkSignalMarshaller0) (GtkObject *object, gpointer data); struct _GtkSignal { guint signal_id; GtkType object_type; gchar *name; guint function_offset; GtkSignalRunType run_type; GtkSignalMarshaller marshaller; GtkType return_val; GtkType *params; guint nparams; }; struct _GtkSignalHash { GtkType object_type; guint name_key_id; guint signal_id; }; struct _GtkHandler { guint id; guint blocked : 20; guint object_signal : 1; guint after : 1; guint no_marshal : 1; guint16 ref_count; guint16 signal_id; GtkSignalFunc func; gpointer func_data; GtkSignalDestroy destroy_func; GtkHandler *prev; GtkHandler *next; }; struct _GtkHandlerInfo { GtkObject *object; GtkSignalMarshaller marshaller; GtkArg *params; GtkType *param_types; GtkType return_val; GtkSignalRunType run_type; guint nparams; guint signal_id; }; struct _GtkEmission { GtkObject *object; guint signal_id; }; union _GtkEmissionAllocator { GtkEmissionAllocator *next; GtkEmission emission; }; struct _GtkDisconnectInfo { GtkObject *object1; guint disconnect_handler1; guint signal_handler; GtkObject *object2; guint disconnect_handler2; }; static guint gtk_signal_hash (const gpointer h); static gint gtk_signal_compare (const gpointer h1, const gpointer h2); static GtkHandler* gtk_signal_handler_new (void); static void gtk_signal_handler_ref (GtkHandler *handler); static void gtk_signal_handler_unref (GtkHandler *handler, GtkObject *object); static void gtk_signal_handler_insert (GtkObject *object, GtkHandler *handler); static void gtk_signal_real_emit (GtkObject *object, GtkSignal *signal, GtkArg *params); static GtkHandler* gtk_signal_get_handlers (GtkObject *object, guint signal_type); static guint gtk_signal_connect_by_type (GtkObject *object, guint signal_id, GtkSignalFunc func, gpointer func_data, GtkSignalDestroy destroy_func, gint object_signal, gint after, gint no_marshal); static guint gtk_alive_disconnecter (GtkDisconnectInfo *info); static GtkEmission* gtk_emission_new (void); static void gtk_emission_add (GList **emissions, GtkObject *object, guint signal_type); static void gtk_emission_remove (GList **emissions, GtkObject *object, guint signal_type); static gint gtk_emission_check (GList *emissions, GtkObject *object, guint signal_type); static gint gtk_handlers_run (GtkHandler *handlers, GtkHandlerInfo *info, gint after); static void gtk_params_get (GtkArg *params, guint nparams, GtkType *param_types, GtkType return_val, va_list args); #define LOOKUP_SIGNAL_ID(signal_id) ( \ signal_id > 0 && signal_id < gtk_n_signals ? \ (GtkSignal*) gtk_signals + signal_id : \ (GtkSignal*) 0 \ ) static GtkSignalMarshal global_marshaller = NULL; static GtkSignalDestroy global_destroy_notify = NULL; static guint gtk_handler_id = 1; static guint handler_key_id = 0; static GHashTable *gtk_signal_hash_table = NULL; static GtkSignal *gtk_signals = NULL; static guint gtk_n_signals = 0; static GMemChunk *gtk_signal_hash_mem_chunk = NULL; static GMemChunk *gtk_disconnect_info_mem_chunk = NULL; static GtkHandler *gtk_handler_free_list = NULL; static GtkEmissionAllocator *gtk_emission_free_list = NULL; static GList *current_emissions = NULL; static GList *stop_emissions = NULL; static GList *restart_emissions = NULL; static GtkSignal* gtk_signal_next_and_invalidate (void) { static guint gtk_n_free_signals = 0; register GtkSignal *signal; register guint new_signal_id; /* don't keep *any* GtkSignal pointers across invokation of this function!!! */ if (gtk_n_free_signals == 0) { register guint i; register guint size; /* nearest pow */ size = gtk_n_signals + SIGNAL_BLOCK_SIZE; size *= sizeof (GtkSignal); i = 1; while (i < size) i <<= 1; size = i; gtk_signals = g_realloc (gtk_signals, size); gtk_n_free_signals = size / sizeof (GtkSignal) - gtk_n_signals; memset (gtk_signals + gtk_n_signals, 0, gtk_n_free_signals * sizeof (GtkSignal)); } new_signal_id = gtk_n_signals++; gtk_n_free_signals--; signal = LOOKUP_SIGNAL_ID (new_signal_id); if (signal) signal->signal_id = new_signal_id; return signal; } void gtk_signal_init (void) { if (!handler_key_id) { GtkSignal *zero; zero = gtk_signal_next_and_invalidate (); g_assert (zero == NULL); handler_key_id = gtk_object_data_force_id ("gtk-signal-handlers"); gtk_signal_hash_mem_chunk = g_mem_chunk_new ("GtkSignalHash mem chunk", sizeof (GtkSignalHash), sizeof (GtkSignalHash) * SIGNAL_BLOCK_SIZE, G_ALLOC_ONLY); gtk_disconnect_info_mem_chunk = g_mem_chunk_new ("GtkDisconnectInfo mem chunk", sizeof (GtkDisconnectInfo), sizeof (GtkDisconnectInfo) * DISCONNECT_INFO_BLOCK_SIZE, G_ALLOC_AND_FREE); gtk_handler_free_list = NULL; gtk_emission_free_list = NULL; gtk_signal_hash_table = g_hash_table_new (gtk_signal_hash, gtk_signal_compare); } } guint gtk_signal_newv (const gchar *r_name, GtkSignalRunType run_type, GtkType object_type, guint function_offset, GtkSignalMarshaller marshaller, GtkType return_val, guint nparams, GtkType *params) { GtkSignal *signal; GtkSignalHash *hash; guint id; guint i; gchar *name; g_return_val_if_fail (r_name != NULL, 0); g_return_val_if_fail (marshaller != NULL, 0); g_return_val_if_fail (nparams <= MAX_SIGNAL_PARAMS, 0); if (nparams) g_return_val_if_fail (params != NULL, 0); if (!handler_key_id) gtk_signal_init (); name = g_strdup (r_name); g_strdelimit (name, NULL, '_'); id = gtk_signal_lookup (name, object_type); if (id) { g_warning ("gtk_signal_newv(): signal \"%s\" already exists in the `%s' class ancestry\n", r_name, gtk_type_name (object_type)); g_free (name); return 0; } if (return_val != GTK_TYPE_NONE && (run_type & GTK_RUN_BOTH) == GTK_RUN_FIRST) { g_warning ("gtk_signal_newv(): signal \"%s\" with return value `%s' excludes GTK_RUN_LAST", name, gtk_type_name (return_val)); return 0; } signal = gtk_signal_next_and_invalidate (); /* signal->signal_id already set */ signal->object_type = object_type; signal->name = name; signal->function_offset = function_offset; signal->run_type = run_type; signal->marshaller = marshaller; signal->return_val = return_val; signal->nparams = nparams; if (nparams > 0) { signal->params = g_new (GtkType, nparams); for (i = 0; i < nparams; i++) signal->params[i] = params[i]; } else signal->params = NULL; /* insert "signal_name" into hash table */ hash = g_chunk_new (GtkSignalHash, gtk_signal_hash_mem_chunk); hash->object_type = object_type; hash->name_key_id = gtk_object_data_force_id (signal->name); hash->signal_id = signal->signal_id; g_hash_table_insert (gtk_signal_hash_table, hash, GUINT_TO_POINTER (hash->signal_id)); /* insert "signal-name" into hash table */ g_strdelimit (signal->name, NULL, '-'); id = gtk_object_data_force_id (signal->name); if (id != hash->name_key_id) { hash = g_chunk_new (GtkSignalHash, gtk_signal_hash_mem_chunk); hash->object_type = object_type; hash->name_key_id = id; hash->signal_id = signal->signal_id; g_hash_table_insert (gtk_signal_hash_table, hash, GUINT_TO_POINTER (hash->signal_id)); } return signal->signal_id; } guint gtk_signal_new (const gchar *name, GtkSignalRunType run_type, GtkType object_type, guint function_offset, GtkSignalMarshaller marshaller, GtkType return_val, guint nparams, ...) { GtkType *params; guint i; va_list args; guint signal_id; g_return_val_if_fail (nparams <= MAX_SIGNAL_PARAMS, 0); if (nparams > 0) { params = g_new (GtkType, nparams); va_start (args, nparams); for (i = 0; i < nparams; i++) params[i] = va_arg (args, GtkType); va_end (args); } else params = NULL; signal_id = gtk_signal_newv (name, run_type, object_type, function_offset, marshaller, return_val, nparams, params); g_free (params); return signal_id; } guint gtk_signal_lookup (const gchar *name, GtkType object_type) { GtkSignalHash hash; g_return_val_if_fail (name != NULL, 0); g_return_val_if_fail (gtk_type_is_a (object_type, GTK_TYPE_OBJECT), 0); hash.name_key_id = gtk_object_data_try_key (name); if (hash.name_key_id) { while (object_type) { guint signal_id; hash.object_type = object_type; signal_id = GPOINTER_TO_UINT (g_hash_table_lookup (gtk_signal_hash_table, &hash)); if (signal_id) return signal_id; object_type = gtk_type_parent (object_type); } } return 0; } GtkSignalQuery* gtk_signal_query (guint signal_id) { GtkSignalQuery *query; GtkSignal *signal; g_return_val_if_fail (signal_id >= 1, NULL); signal = LOOKUP_SIGNAL_ID (signal_id); if (signal) { query = g_new (GtkSignalQuery, 1); query->object_type = signal->object_type; query->signal_id = signal_id; query->signal_name = signal->name; query->is_user_signal = signal->function_offset == 0; query->run_type = signal->run_type; query->return_val = signal->return_val; query->nparams = signal->nparams; query->params = signal->params; } else query = NULL; return query; } gchar* gtk_signal_name (guint signal_id) { GtkSignal *signal; g_return_val_if_fail (signal_id >= 1, NULL); signal = LOOKUP_SIGNAL_ID (signal_id); if (signal) return signal->name; return NULL; } void gtk_signal_emitv (GtkObject *object, guint signal_id, GtkArg *params) { GtkSignal *signal; g_return_if_fail (object != NULL); g_return_if_fail (signal_id >= 1); g_return_if_fail (params != NULL); signal = LOOKUP_SIGNAL_ID (signal_id); g_return_if_fail (signal != NULL); g_return_if_fail (gtk_type_is_a (GTK_OBJECT_TYPE (object), signal->object_type)); gtk_signal_real_emit (object, signal, params); } void gtk_signal_emit (GtkObject *object, guint signal_id, ...) { GtkSignal *signal; va_list args; GtkArg params[MAX_SIGNAL_PARAMS]; g_return_if_fail (object != NULL); g_return_if_fail (signal_id >= 1); signal = LOOKUP_SIGNAL_ID (signal_id); g_return_if_fail (signal != NULL); g_return_if_fail (gtk_type_is_a (GTK_OBJECT_TYPE (object), signal->object_type)); va_start (args, signal_id); gtk_params_get (params, signal->nparams, signal->params, signal->return_val, args); va_end (args); gtk_signal_real_emit (object, signal, params); } void gtk_signal_emitv_by_name (GtkObject *object, const gchar *name, GtkArg *params) { guint signal_id; g_return_if_fail (object != NULL); g_return_if_fail (name != NULL); g_return_if_fail (params != NULL); signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object)); if (signal_id >= 1) { GtkSignal *signal; signal = LOOKUP_SIGNAL_ID (signal_id); g_return_if_fail (signal != NULL); g_return_if_fail (gtk_type_is_a (GTK_OBJECT_TYPE (object), signal->object_type)); gtk_signal_real_emit (object, signal, params); } else { g_warning ("gtk_signal_emitv_by_name(): could not find signal \"%s\" in the `%s' class ancestry", name, gtk_type_name (GTK_OBJECT_TYPE (object))); } } void gtk_signal_emit_by_name (GtkObject *object, const gchar *name, ...) { guint signal_id; g_return_if_fail (object != NULL); g_return_if_fail (name != NULL); signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object)); if (signal_id >= 1) { GtkSignal *signal; GtkArg params[MAX_SIGNAL_PARAMS]; va_list args; signal = LOOKUP_SIGNAL_ID (signal_id); g_return_if_fail (signal != NULL); g_return_if_fail (gtk_type_is_a (GTK_OBJECT_TYPE (object), signal->object_type)); va_start (args, name); gtk_params_get (params, signal->nparams, signal->params, signal->return_val, args); va_end (args); gtk_signal_real_emit (object, signal, params); } else { g_warning ("gtk_signal_emit_by_name(): could not find signal \"%s\" in the `%s' class ancestry", name, gtk_type_name (GTK_OBJECT_TYPE (object))); } } void gtk_signal_emit_stop (GtkObject *object, guint signal_id) { g_return_if_fail (object != NULL); g_return_if_fail (signal_id >= 1); if (gtk_emission_check (current_emissions, object, signal_id)) gtk_emission_add (&stop_emissions, object, signal_id); else g_warning ("gtk_signal_emit_stop(): no current emission (%u) for object `%s'", signal_id, gtk_type_name (GTK_OBJECT_TYPE (object))); } void gtk_signal_emit_stop_by_name (GtkObject *object, const gchar *name) { guint signal_id; g_return_if_fail (object != NULL); g_return_if_fail (name != NULL); signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object)); if (signal_id) gtk_signal_emit_stop (object, signal_id); else g_warning ("gtk_signal_emit_stop_by_name(): could not find signal \"%s\" in the `%s' class ancestry", name, gtk_type_name (GTK_OBJECT_TYPE (object))); } guint gtk_signal_n_emissions (GtkObject *object, guint signal_id) { GList *list; guint n; g_return_val_if_fail (object != NULL, 0); g_return_val_if_fail (GTK_IS_OBJECT (object), 0); n = 0; for (list = current_emissions; list; list = list->next) { GtkEmission *emission; emission = list->data; if ((emission->object == object) && (emission->signal_id == signal_id)) n++; } return n; } guint gtk_signal_n_emissions_by_name (GtkObject *object, const gchar *name) { guint signal_id; guint n; g_return_val_if_fail (object != NULL, 0); g_return_val_if_fail (GTK_IS_OBJECT (object), 0); g_return_val_if_fail (name != NULL, 0); signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object)); if (signal_id) n = gtk_signal_n_emissions (object, signal_id); else { g_warning ("gtk_signal_n_emissions_by_name(): could not find signal \"%s\" in the `%s' class ancestry", name, gtk_type_name (GTK_OBJECT_TYPE (object))); n = 0; } return n; } guint gtk_signal_connect (GtkObject *object, const gchar *name, GtkSignalFunc func, gpointer func_data) { guint signal_id; g_return_val_if_fail (object != NULL, 0); g_return_val_if_fail (GTK_IS_OBJECT (object), 0); signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object)); if (!signal_id) { g_warning ("gtk_signal_connect(): could not find signal \"%s\" in the `%s' class ancestry", name, gtk_type_name (GTK_OBJECT_TYPE (object))); return 0; } return gtk_signal_connect_by_type (object, signal_id, func, func_data, NULL, FALSE, FALSE, FALSE); } guint gtk_signal_connect_after (GtkObject *object, const gchar *name, GtkSignalFunc func, gpointer func_data) { guint signal_id; g_return_val_if_fail (object != NULL, 0); signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object)); if (!signal_id) { g_warning ("gtk_signal_connect_after(): could not find signal \"%s\" in the `%s' class ancestry", name, gtk_type_name (GTK_OBJECT_TYPE (object))); return 0; } return gtk_signal_connect_by_type (object, signal_id, func, func_data, NULL, FALSE, TRUE, FALSE); } guint gtk_signal_connect_full (GtkObject *object, const gchar *name, GtkSignalFunc func, GtkCallbackMarshal marshal, gpointer func_data, GtkDestroyNotify destroy_func, gint object_signal, gint after) { guint signal_id; g_return_val_if_fail (object != NULL, 0); signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object)); if (!signal_id) { g_warning ("gtk_signal_connect_full(): could not find signal \"%s\" in the `%s' class ancestry", name, gtk_type_name (GTK_OBJECT_TYPE (object))); return 0; } if (marshal) return gtk_signal_connect_by_type (object, signal_id, (GtkSignalFunc) marshal, func_data, destroy_func, object_signal, after, TRUE); else return gtk_signal_connect_by_type (object, signal_id, func, func_data, destroy_func, object_signal, after, FALSE); } guint gtk_signal_connect_interp (GtkObject *object, const gchar *name, GtkCallbackMarshal func, gpointer func_data, GtkDestroyNotify destroy_func, gint after) { return gtk_signal_connect_full (object, name, NULL, func, func_data, destroy_func, FALSE, after); } guint gtk_signal_connect_object (GtkObject *object, const gchar *name, GtkSignalFunc func, GtkObject *slot_object) { guint signal_id; g_return_val_if_fail (object != NULL, 0); /* slot_object needs to be treated as ordinary pointer */ signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object)); if (!signal_id) { g_warning ("gtk_signal_connect_object(): could not find signal \"%s\" in the `%s' class ancestry", name, gtk_type_name (GTK_OBJECT_TYPE (object))); return 0; } return gtk_signal_connect_by_type (object, signal_id, func, slot_object, NULL, TRUE, FALSE, FALSE); } guint gtk_signal_connect_object_after (GtkObject *object, const gchar *name, GtkSignalFunc func, GtkObject *slot_object) { guint signal_id; g_return_val_if_fail (object != NULL, 0); signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object)); if (!signal_id) { g_warning ("gtk_signal_connect_object_after(): could not find signal \"%s\" in the `%s' class ancestry", name, gtk_type_name (GTK_OBJECT_TYPE (object))); return 0; } return gtk_signal_connect_by_type (object, signal_id, func, slot_object, NULL, TRUE, TRUE, FALSE); } void gtk_signal_connect_while_alive (GtkObject *object, const gchar *signal, GtkSignalFunc func, gpointer func_data, GtkObject *alive_object) { GtkDisconnectInfo *info; g_return_if_fail (object != NULL); g_return_if_fail (GTK_IS_OBJECT (object)); g_return_if_fail (signal != NULL); g_return_if_fail (func != NULL); g_return_if_fail (alive_object != NULL); g_return_if_fail (GTK_IS_OBJECT (alive_object)); info = g_chunk_new (GtkDisconnectInfo, gtk_disconnect_info_mem_chunk); info->object1 = object; info->object2 = alive_object; info->signal_handler = gtk_signal_connect (object, signal, func, func_data); info->disconnect_handler1 = gtk_signal_connect_object (info->object1, "destroy", GTK_SIGNAL_FUNC (gtk_alive_disconnecter), (GtkObject*) info); info->disconnect_handler2 = gtk_signal_connect_object (info->object2, "destroy", GTK_SIGNAL_FUNC (gtk_alive_disconnecter), (GtkObject*) info); } void gtk_signal_connect_object_while_alive (GtkObject *object, const gchar *signal, GtkSignalFunc func, GtkObject *alive_object) { GtkDisconnectInfo *info; g_return_if_fail (object != NULL); g_return_if_fail (GTK_IS_OBJECT (object)); g_return_if_fail (signal != NULL); g_return_if_fail (func != NULL); g_return_if_fail (alive_object != NULL); g_return_if_fail (GTK_IS_OBJECT (alive_object)); info = g_chunk_new (GtkDisconnectInfo, gtk_disconnect_info_mem_chunk); info->object1 = object; info->object2 = alive_object; info->signal_handler = gtk_signal_connect_object (object, signal, func, alive_object); info->disconnect_handler1 = gtk_signal_connect_object (info->object1, "destroy", GTK_SIGNAL_FUNC (gtk_alive_disconnecter), (GtkObject*) info); info->disconnect_handler2 = gtk_signal_connect_object (info->object2, "destroy", GTK_SIGNAL_FUNC (gtk_alive_disconnecter), (GtkObject*) info); } void gtk_signal_disconnect (GtkObject *object, guint handler_id) { GtkHandler *handler; g_return_if_fail (object != NULL); g_return_if_fail (handler_id > 0); handler = gtk_object_get_data_by_id (object, handler_key_id); while (handler) { if (handler->id == handler_id) { handler->id = 0; handler->blocked += 1; gtk_signal_handler_unref (handler, object); return; } handler = handler->next; } g_warning ("gtk_signal_disconnect(): could not find handler (%u)", handler_id); } void gtk_signal_disconnect_by_func (GtkObject *object, GtkSignalFunc func, gpointer data) { GtkHandler *handler; gint found_one; g_return_if_fail (object != NULL); g_return_if_fail (func != NULL); found_one = FALSE; handler = gtk_object_get_data_by_id (object, handler_key_id); while (handler) { GtkHandler *handler_next; handler_next = handler->next; if ((handler->id > 0) && (handler->func == func) && (handler->func_data == data)) { found_one = TRUE; handler->id = 0; handler->blocked += 1; gtk_signal_handler_unref (handler, object); } handler = handler_next; } if (!found_one) g_warning ("gtk_signal_disconnect_by_func(): could not find handler (0x%0lX) containing data (0x%0lX)", (long) func, (long) data); } void gtk_signal_disconnect_by_data (GtkObject *object, gpointer data) { GtkHandler *handler; gint found_one; g_return_if_fail (object != NULL); found_one = FALSE; handler = gtk_object_get_data_by_id (object, handler_key_id); while (handler) { GtkHandler *handler_next; handler_next = handler->next; if ((handler->id > 0) && (handler->func_data == data)) { found_one = TRUE; handler->id = 0; handler->blocked += 1; gtk_signal_handler_unref (handler, object); } handler = handler_next; } if (!found_one) g_warning ("gtk_signal_disconnect_by_data(): could not find handler containing data (0x%0lX)", (long) data); } void gtk_signal_handler_block (GtkObject *object, guint handler_id) { GtkHandler *handler; g_return_if_fail (object != NULL); g_return_if_fail (handler_id > 0); handler = gtk_object_get_data_by_id (object, handler_key_id); while (handler) { if (handler->id == handler_id) { handler->blocked += 1; return; } handler = handler->next; } g_warning ("gtk_signal_handler_block(): could not find handler (%u)", handler_id); } void gtk_signal_handler_block_by_func (GtkObject *object, GtkSignalFunc func, gpointer data) { GtkHandler *handler; gint found_one; g_return_if_fail (object != NULL); g_return_if_fail (func != NULL); found_one = FALSE; handler = gtk_object_get_data_by_id (object, handler_key_id); while (handler) { if ((handler->id > 0) && (handler->func == func) && (handler->func_data == data)) { found_one = TRUE; handler->blocked += 1; } handler = handler->next; } if (!found_one) g_warning ("gtk_signal_handler_block_by_func(): could not find handler (0x%0lX) containing data (0x%0lX)", (long) func, (long) data); } void gtk_signal_handler_block_by_data (GtkObject *object, gpointer data) { GtkHandler *handler; gint found_one; g_return_if_fail (object != NULL); found_one = FALSE; handler = gtk_object_get_data_by_id (object, handler_key_id); while (handler) { if ((handler->id > 0) && (handler->func_data == data)) { found_one = TRUE; handler->blocked += 1; } handler = handler->next; } if (!found_one) g_warning ("gtk_signal_handler_block_by_data(): could not find handler containing data (0x%0lX)", (long) data); } void gtk_signal_handler_unblock (GtkObject *object, guint handler_id) { GtkHandler *handler; g_return_if_fail (object != NULL); g_return_if_fail (handler_id > 0); handler = gtk_object_get_data_by_id (object, handler_key_id); while (handler) { if (handler->id == handler_id) { if (handler->blocked > 0) handler->blocked -= 1; else g_warning ("gtk_signal_handler_unblock(): handler (%u) is not blocked", handler_id); return; } handler = handler->next; } g_warning ("gtk_signal_handler_unblock(): could not find handler (%u)", handler_id); } void gtk_signal_handler_unblock_by_func (GtkObject *object, GtkSignalFunc func, gpointer data) { GtkHandler *handler; gint found_one; g_return_if_fail (object != NULL); g_return_if_fail (func != NULL); found_one = FALSE; handler = gtk_object_get_data_by_id (object, handler_key_id); while (handler) { if ((handler->id > 0) && (handler->func == func) && (handler->func_data == data) && (handler->blocked > 0)) { handler->blocked -= 1; found_one = TRUE; } handler = handler->next; } if (!found_one) g_warning ("gtk_signal_handler_unblock_by_func(): could not find blocked handler (0x%0lX) containing data (0x%0lX)", (long) func, (long) data); } void gtk_signal_handler_unblock_by_data (GtkObject *object, gpointer data) { GtkHandler *handler; gint found_one; g_return_if_fail (object != NULL); found_one = FALSE; handler = gtk_object_get_data_by_id (object, handler_key_id); while (handler) { if ((handler->id > 0) && (handler->func_data == data) && (handler->blocked > 0)) { handler->blocked -= 1; found_one = TRUE; } handler = handler->next; } if (!found_one) g_warning ("gtk_signal_handler_unblock_by_data(): could not find blocked handler containing data (0x%0lX)", (long) data); } void gtk_signal_handlers_destroy (GtkObject *object) { GtkHandler *handler; /* we make the "optimization" of destroying the first handler in the last * place, since we don't want gtk_signal_handler_unref() to reset the objects * handler_key data on each removal */ handler = gtk_object_get_data_by_id (object, handler_key_id); if (handler) { handler = handler->next; while (handler) { GtkHandler *next; next = handler->next; gtk_signal_handler_unref (handler, object); handler = next; } handler = gtk_object_get_data_by_id (object, handler_key_id); gtk_signal_handler_unref (handler, object); } } void gtk_signal_default_marshaller (GtkObject *object, GtkSignalFunc func, gpointer func_data, GtkArg *params) { GtkSignalMarshaller0 rfunc; rfunc = (GtkSignalMarshaller0) func; (* rfunc) (object, func_data); } void gtk_signal_set_funcs (GtkSignalMarshal marshal_func, GtkSignalDestroy destroy_func) { global_marshaller = marshal_func; global_destroy_notify = destroy_func; } static guint gtk_signal_hash (const gpointer h) { register GtkSignalHash *hash = h; return hash->object_type ^ hash->name_key_id; } static gint gtk_signal_compare (const gpointer h1, const gpointer h2) { register GtkSignalHash *hash1 = h1; register GtkSignalHash *hash2 = h2; return (hash1->name_key_id == hash2->name_key_id && hash1->object_type == hash2->object_type); } static guint gtk_alive_disconnecter (GtkDisconnectInfo *info) { g_return_val_if_fail (info != NULL, 0); gtk_signal_disconnect (info->object1, info->disconnect_handler1); gtk_signal_disconnect (info->object1, info->signal_handler); gtk_signal_disconnect (info->object2, info->disconnect_handler2); g_mem_chunk_free (gtk_disconnect_info_mem_chunk, info); return 0; } static GtkHandler* gtk_signal_handler_new (void) { GtkHandler *handler; if (!gtk_handler_free_list) { GtkHandler *handler_block; guint i; handler_block = g_new0 (GtkHandler, HANDLER_BLOCK_SIZE); for (i = 1; i < HANDLER_BLOCK_SIZE; i++) { (handler_block + i)->next = gtk_handler_free_list; gtk_handler_free_list = (handler_block + i); } handler = handler_block; } else { handler = gtk_handler_free_list; gtk_handler_free_list = handler->next; } handler->id = 0; handler->blocked = 0; handler->signal_id = 0; handler->object_signal = FALSE; handler->after = FALSE; handler->no_marshal = FALSE; handler->ref_count = 1; handler->func = NULL; handler->func_data = NULL; handler->destroy_func = NULL; handler->prev = NULL; handler->next = NULL; return handler; } static void gtk_signal_handler_ref (GtkHandler *handler) { handler->ref_count += 1; } static void gtk_signal_handler_unref (GtkHandler *handler, GtkObject *object) { if (!handler->ref_count) { /* FIXME: i wanna get removed somewhen */ g_warning ("gtk_signal_handler_unref(): handler with ref_count==0!"); return; } handler->ref_count -= 1; if (handler->ref_count == 0) { if (handler->destroy_func) (* handler->destroy_func) (handler->func_data); else if (!handler->func && global_destroy_notify) (* global_destroy_notify) (handler->func_data); if (handler->prev) handler->prev->next = handler->next; else if (handler->next) gtk_object_set_data_by_id (object, handler_key_id, handler->next); else { GTK_OBJECT_UNSET_FLAGS (object, GTK_CONNECTED); gtk_object_set_data_by_id (object, handler_key_id, NULL); } if (handler->next) handler->next->prev = handler->prev; handler->next = gtk_handler_free_list; gtk_handler_free_list = handler; } } static void gtk_signal_handler_insert (GtkObject *object, GtkHandler *handler) { GtkHandler *tmp; /* FIXME: remove */ g_assert (handler->next == NULL); /* FIXME: remove */ g_assert (handler->prev == NULL); tmp = gtk_object_get_data_by_id (object, handler_key_id); if (!tmp) { GTK_OBJECT_SET_FLAGS (object, GTK_CONNECTED); gtk_object_set_data_by_id (object, handler_key_id, handler); } else while (tmp) { if (tmp->signal_id < handler->signal_id) { if (tmp->prev) { tmp->prev->next = handler; handler->prev = tmp->prev; } else gtk_object_set_data_by_id (object, handler_key_id, handler); tmp->prev = handler; handler->next = tmp; break; } if (!tmp->next) { tmp->next = handler; handler->prev = tmp; break; } tmp = tmp->next; } } static void gtk_signal_real_emit (GtkObject *object, GtkSignal *signal, GtkArg *params) { GtkHandler *handlers; GtkHandlerInfo info; guchar **signal_func_offset; register guint signal_id = signal->signal_id; if(gtk_debug_flags & GTK_DEBUG_SIGNALS) g_print("Sending signal %s to object %p (%s)\n", signal->name, object, gtk_type_name(object->klass->type)); if ((signal->run_type & GTK_RUN_NO_RECURSE) && gtk_emission_check (current_emissions, object, signal_id)) { gtk_emission_add (&restart_emissions, object, signal_id); return; } gtk_object_ref (object); gtk_emission_add (¤t_emissions, object, signal_id); emission_restart: if (GTK_RUN_TYPE (signal->run_type) != GTK_RUN_LAST && signal->function_offset != 0) { signal_func_offset = (guchar**) ((guchar*) object->klass + signal->function_offset); if (*signal_func_offset) (* signal->marshaller) (object, (GtkSignalFunc) *signal_func_offset, NULL, params); } if (GTK_OBJECT_CONNECTED (object)) { handlers = gtk_signal_get_handlers (object, signal_id); if (handlers) { info.object = object; info.marshaller = signal->marshaller; info.params = params; info.param_types = signal->params; info.return_val = signal->return_val; info.nparams = signal->nparams; info.run_type = signal->run_type; info.signal_id = signal_id; switch (gtk_handlers_run (handlers, &info, FALSE)) { case EMISSION_CONTINUE: break; case EMISSION_RESTART: goto emission_restart; case EMISSION_DONE: goto emission_done; } } else info.object = NULL; } else info.object = NULL; if (GTK_RUN_TYPE (signal->run_type) != GTK_RUN_FIRST && signal->function_offset != 0) { signal_func_offset = (guchar**) ((guchar*) object->klass + signal->function_offset); if (*signal_func_offset) (* signal->marshaller) (object, (GtkSignalFunc) *signal_func_offset, NULL, params); } if (GTK_OBJECT_CONNECTED (object)) { handlers = gtk_signal_get_handlers (object, signal_id); if (handlers) { if (!info.object) { info.object = object; info.marshaller = signal->marshaller; info.params = params; info.param_types = signal->params; info.return_val = signal->return_val; info.nparams = signal->nparams; info.run_type = signal->run_type; info.signal_id = signal_id; } switch (gtk_handlers_run (handlers, &info, TRUE)) { case EMISSION_CONTINUE: break; case EMISSION_RESTART: goto emission_restart; case EMISSION_DONE: goto emission_done; } } } emission_done: gtk_emission_remove (¤t_emissions, object, signal_id); if (signal->run_type & GTK_RUN_NO_RECURSE) gtk_emission_remove (&restart_emissions, object, signal_id); gtk_object_unref (object); } static GtkHandler* gtk_signal_get_handlers (GtkObject *object, guint signal_id) { GtkHandler *handlers; handlers = gtk_object_get_data_by_id (object, handler_key_id); while (handlers) { if (handlers->signal_id == signal_id) return handlers; handlers = handlers->next; } return NULL; } guint gtk_signal_handler_pending (GtkObject *object, guint signal_id, gboolean may_be_blocked) { GtkHandler *handlers; guint handler_id; g_return_val_if_fail (object != NULL, 0); g_return_val_if_fail (signal_id >= 1, 0); if (GTK_OBJECT_CONNECTED (object)) handlers = gtk_signal_get_handlers (object, signal_id); else return 0; handler_id = 0; while (handlers && handlers->signal_id == signal_id) { if (handlers->id > 0 && (may_be_blocked || handlers->blocked == 0)) { handler_id = handlers->id; break; } handlers = handlers->next; } return handler_id; } static guint gtk_signal_connect_by_type (GtkObject *object, guint signal_id, GtkSignalFunc func, gpointer func_data, GtkSignalDestroy destroy_func, gint object_signal, gint after, gint no_marshal) { GtkObjectClass *class; GtkHandler *handler; gint found_it; g_return_val_if_fail (object != NULL, 0); g_return_val_if_fail (object->klass != NULL, 0); /* Search through the signals for this object and make * sure the one we are adding is valid. We need to perform * the lookup on the objects parents as well. If it isn't * valid then issue a warning and return. */ found_it = FALSE; class = object->klass; while (class) { GtkType parent; guint *object_signals; guint nsignals; guint i; object_signals = class->signals; nsignals = class->nsignals; for (i = 0; i < nsignals; i++) if (object_signals[i] == signal_id) { found_it = TRUE; break; } parent = gtk_type_parent (class->type); if (parent) class = gtk_type_class (parent); else class = NULL; } if (!found_it) { g_warning ("gtk_signal_connect_by_type(): could not find signal id (%u) in the `%s' class ancestry", signal_id, gtk_type_name (object->klass->type)); return 0; } handler = gtk_signal_handler_new (); handler->id = gtk_handler_id++; handler->signal_id = signal_id; handler->object_signal = object_signal; handler->func = func; handler->func_data = func_data; handler->destroy_func = destroy_func; handler->after = after != FALSE; handler->no_marshal = no_marshal; gtk_signal_handler_insert (object, handler); return handler->id; } static GtkEmission* gtk_emission_new (void) { GtkEmission *emission; if (!gtk_emission_free_list) { GtkEmissionAllocator *emission_block; guint i; emission_block = g_new0 (GtkEmissionAllocator, EMISSION_BLOCK_SIZE); for (i = 1; i < EMISSION_BLOCK_SIZE; i++) { (emission_block + i)->next = gtk_emission_free_list; gtk_emission_free_list = (emission_block + i); } emission = &emission_block->emission; } else { emission = >k_emission_free_list->emission; gtk_emission_free_list = gtk_emission_free_list->next; } emission->object = NULL; emission->signal_id = 0; return emission; } static void gtk_emission_add (GList **emissions, GtkObject *object, guint signal_id) { GtkEmission *emission; g_return_if_fail (emissions != NULL); g_return_if_fail (object != NULL); emission = gtk_emission_new (); emission->object = object; emission->signal_id = signal_id; *emissions = g_list_prepend (*emissions, emission); } static void gtk_emission_remove (GList **emissions, GtkObject *object, guint signal_id) { GList *tmp; g_return_if_fail (emissions != NULL); tmp = *emissions; while (tmp) { GtkEmissionAllocator *ea; ea = tmp->data; if ((ea->emission.object == object) && (ea->emission.signal_id == signal_id)) { *emissions = g_list_remove_link (*emissions, tmp); g_list_free (tmp); ea->next = gtk_emission_free_list; gtk_emission_free_list = ea; break; } tmp = tmp->next; } } static gint gtk_emission_check (GList *emissions, GtkObject *object, guint signal_id) { GtkEmission *emission; GList *tmp; tmp = emissions; while (tmp) { emission = tmp->data; tmp = tmp->next; if ((emission->object == object) && (emission->signal_id == signal_id)) return TRUE; } return FALSE; } static gint gtk_handlers_run (GtkHandler *handlers, GtkHandlerInfo *info, gint after) { while (handlers && handlers->signal_id == info->signal_id) { GtkHandler *handlers_next; gtk_signal_handler_ref (handlers); if (handlers->blocked == 0 && (handlers->after == after)) { if (handlers->func) { if (handlers->no_marshal) (* (GtkCallbackMarshal) handlers->func) (info->object, handlers->func_data, info->nparams, info->params); else if (handlers->object_signal) (* info->marshaller) ((GtkObject*) handlers->func_data, /* don't GTK_OBJECT() cast */ handlers->func, handlers->func_data, info->params); else (* info->marshaller) (info->object, handlers->func, handlers->func_data, info->params); } else if (global_marshaller) (* global_marshaller) (info->object, handlers->func_data, info->nparams, info->params, info->param_types, info->return_val); if (gtk_emission_check (stop_emissions, info->object, info->signal_id)) { gtk_emission_remove (&stop_emissions, info->object, info->signal_id); if (info->run_type & GTK_RUN_NO_RECURSE) gtk_emission_remove (&restart_emissions, info->object, info->signal_id); gtk_signal_handler_unref (handlers, info->object); return EMISSION_DONE; } else if ((info->run_type & GTK_RUN_NO_RECURSE) && gtk_emission_check (restart_emissions, info->object, info->signal_id)) { gtk_emission_remove (&restart_emissions, info->object, info->signal_id); gtk_signal_handler_unref (handlers, info->object); return EMISSION_RESTART; } } handlers_next = handlers->next; gtk_signal_handler_unref (handlers, info->object); handlers = handlers_next; } return EMISSION_CONTINUE; } static void gtk_params_get (GtkArg *params, guint nparams, GtkType *param_types, GtkType return_val, va_list args) { gint i; for (i = 0; i < nparams; i++) { params[i].type = param_types[i]; params[i].name = NULL; switch (GTK_FUNDAMENTAL_TYPE (param_types[i])) { case GTK_TYPE_INVALID: break; case GTK_TYPE_NONE: break; case GTK_TYPE_CHAR: GTK_VALUE_CHAR(params[i]) = va_arg (args, gint); break; case GTK_TYPE_BOOL: GTK_VALUE_BOOL(params[i]) = va_arg (args, gint); break; case GTK_TYPE_INT: GTK_VALUE_INT(params[i]) = va_arg (args, gint); break; case GTK_TYPE_UINT: GTK_VALUE_UINT(params[i]) = va_arg (args, guint); break; case GTK_TYPE_ENUM: GTK_VALUE_ENUM(params[i]) = va_arg (args, gint); break; case GTK_TYPE_FLAGS: GTK_VALUE_FLAGS(params[i]) = va_arg (args, gint); break; case GTK_TYPE_LONG: GTK_VALUE_LONG(params[i]) = va_arg (args, glong); break; case GTK_TYPE_ULONG: GTK_VALUE_ULONG(params[i]) = va_arg (args, gulong); break; case GTK_TYPE_FLOAT: GTK_VALUE_FLOAT(params[i]) = va_arg (args, gfloat); break; case GTK_TYPE_DOUBLE: GTK_VALUE_DOUBLE(params[i]) = va_arg (args, gdouble); break; case GTK_TYPE_STRING: GTK_VALUE_STRING(params[i]) = va_arg (args, gchar*); break; case GTK_TYPE_POINTER: GTK_VALUE_POINTER(params[i]) = va_arg (args, gpointer); break; case GTK_TYPE_BOXED: GTK_VALUE_BOXED(params[i]) = va_arg (args, gpointer); break; case GTK_TYPE_SIGNAL: GTK_VALUE_SIGNAL(params[i]).f = va_arg (args, GtkFunction); GTK_VALUE_SIGNAL(params[i]).d = va_arg (args, gpointer); break; case GTK_TYPE_FOREIGN: GTK_VALUE_FOREIGN(params[i]).data = va_arg (args, gpointer); GTK_VALUE_FOREIGN(params[i]).notify = va_arg (args, GtkDestroyNotify); break; case GTK_TYPE_CALLBACK: GTK_VALUE_CALLBACK(params[i]).marshal = va_arg (args, GtkCallbackMarshal); GTK_VALUE_CALLBACK(params[i]).data = va_arg (args, gpointer); GTK_VALUE_CALLBACK(params[i]).notify = va_arg (args, GtkDestroyNotify); break; case GTK_TYPE_C_CALLBACK: GTK_VALUE_C_CALLBACK(params[i]).func = va_arg (args, GtkFunction); GTK_VALUE_C_CALLBACK(params[i]).func_data = va_arg (args, gpointer); break; case GTK_TYPE_ARGS: GTK_VALUE_ARGS(params[i]).n_args = va_arg (args, gint); GTK_VALUE_ARGS(params[i]).args = va_arg (args, GtkArg*); break; case GTK_TYPE_OBJECT: GTK_VALUE_OBJECT(params[i]) = va_arg (args, GtkObject*); if (GTK_VALUE_OBJECT(params[i]) != NULL && !GTK_CHECK_TYPE (GTK_VALUE_OBJECT(params[i]), params[i].type)) g_warning ("signal arg `%s' is not of type `%s'", gtk_type_name (GTK_OBJECT_TYPE (GTK_VALUE_OBJECT(params[i]))), gtk_type_name (params[i].type)); break; default: g_error ("unsupported type `%s' in signal arg", gtk_type_name (params[i].type)); break; } } params[i].type = return_val; params[i].name = NULL; switch (GTK_FUNDAMENTAL_TYPE (return_val)) { case GTK_TYPE_INVALID: break; case GTK_TYPE_NONE: break; case GTK_TYPE_CHAR: params[i].d.pointer_data = va_arg (args, gchar*); break; case GTK_TYPE_BOOL: params[i].d.pointer_data = va_arg (args, gint*); break; case GTK_TYPE_INT: params[i].d.pointer_data = va_arg (args, gint*); break; case GTK_TYPE_UINT: params[i].d.pointer_data = va_arg (args, guint*); break; case GTK_TYPE_ENUM: params[i].d.pointer_data = va_arg (args, gint*); break; case GTK_TYPE_FLAGS: params[i].d.pointer_data = va_arg (args, gint*); break; case GTK_TYPE_LONG: params[i].d.pointer_data = va_arg (args, glong*); break; case GTK_TYPE_ULONG: params[i].d.pointer_data = va_arg (args, gulong*); break; case GTK_TYPE_FLOAT: params[i].d.pointer_data = va_arg (args, gfloat*); break; case GTK_TYPE_DOUBLE: params[i].d.pointer_data = va_arg (args, gdouble*); break; case GTK_TYPE_STRING: params[i].d.pointer_data = va_arg (args, gchar**); break; case GTK_TYPE_POINTER: params[i].d.pointer_data = va_arg (args, gpointer*); break; case GTK_TYPE_BOXED: params[i].d.pointer_data = va_arg (args, gpointer*); break; case GTK_TYPE_OBJECT: params[i].d.pointer_data = va_arg (args, GtkObject**); break; case GTK_TYPE_SIGNAL: case GTK_TYPE_FOREIGN: case GTK_TYPE_CALLBACK: case GTK_TYPE_C_CALLBACK: case GTK_TYPE_ARGS: default: g_error ("unsupported type `%s' in signal return", gtk_type_name (return_val)); break; } }