glib/gobject/gsignal.c
Tim Janik bbc5a3adac changed collect_format, collect_value() and lcopy_format, lcopy_value() in
Sat Feb 17 04:55:35 2001  Tim Janik  <timj@gtk.org>

        * gtype.[hc]: changed collect_format, collect_value() and lcopy_format,
        lcopy_value() in the GTypeValueTable. the collect functions are now
        called only once per value, collect_format/lcopy_format are strings
        that enlist all necessary GTypeCValues to be varargs-collected.

        * gvalue.h: ranamed STATIC_TAG to G_VALUE_NOCOPY_CONTENTS to indicate that
        a value shouldn't copy its contents.

        * gvaluecollector.h: changed G_VALUE_COLLECT() and G_VALUE_LCOPY()
        macros to carry an additional argument (flags) that can be used
        to pass G_VALUE_NOCOPY_CONTENTS along to the collection functions.

        * *.c: adapted collect_value() and lcopy_value() functions to the new
        prototypes, support G_VALUE_NOCOPY_CONTENTS where apropriate.

        * gsignal.[hc]: introduced a G_SIGNAL_TYPE_STATIC_SCOPE flag that can
        be passed along (ORed) with the parameter types, indicating that the
        emission arguments are to be considered static for the scope of the
        emission. should be used with care and only if the caller knows that
        a parameter cannot be destroyed/freed from signal handlers connected
        to an emission.
2001-02-17 05:32:00 +00:00

2049 lines
56 KiB
C

/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2000 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
*
* this code is based on the original GtkSignal implementation
* for the Gtk+ library by Peter Mattis <petm@xcf.berkeley.edu>
*/
/*
* MT safe
*/
#include "gsignal.h"
#include "gbsearcharray.h"
#include "gvaluecollector.h"
#include <string.h>
/* pre allocation configurations
*/
#define MAX_STACK_VALUES (16)
#define BSA_PRE_ALLOC (20)
#define HANDLER_PRE_ALLOC (48)
#define EMISSION_PRE_ALLOC (16)
#define REPORT_BUG "please report occourance circumstances to gtk-devel-list@gnome.org"
#ifdef G_ENABLE_DEBUG
#define IF_DEBUG(debug_type, cond) if ((_g_type_debug_flags & G_TYPE_DEBUG_ ## debug_type) || cond)
static volatile gpointer *g_trace_instance_signals = NULL;
static volatile gpointer *g_trap_instance_signals = NULL;
#endif /* G_ENABLE_DEBUG */
/* --- generic allocation --- */
/* we special case allocations generically by replacing
* these functions with more speed/memory aware variants
*/
#ifndef DISABLE_MEM_POOLS
static inline gpointer
g_generic_node_alloc (GTrashStack **trash_stack_p,
guint sizeof_node,
guint nodes_pre_alloc)
{
gpointer node = g_trash_stack_pop (trash_stack_p);
if (!node)
{
guint8 *block;
nodes_pre_alloc = MAX (nodes_pre_alloc, 1);
block = g_malloc (sizeof_node * nodes_pre_alloc);
while (--nodes_pre_alloc)
{
g_trash_stack_push (trash_stack_p, block);
block += sizeof_node;
}
node = block;
}
return node;
}
#define g_generic_node_free(trash_stack_p, node) g_trash_stack_push (trash_stack_p, node)
#else /* !DISABLE_MEM_POOLS */
#define g_generic_node_alloc(t,sizeof_node,p) g_malloc (sizeof_node)
#define g_generic_node_free(t,node) g_free (node)
#endif /* !DISABLE_MEM_POOLS */
/* --- typedefs --- */
typedef struct _SignalNode SignalNode;
typedef struct _SignalKey SignalKey;
typedef struct _Emission Emission;
typedef struct _Handler Handler;
typedef struct _HandlerList HandlerList;
typedef struct _HandlerMatch HandlerMatch;
typedef enum
{
EMISSION_STOP,
EMISSION_RUN,
EMISSION_HOOK,
EMISSION_RESTART
} EmissionState;
/* --- prototypes --- */
static inline guint signal_id_lookup (GQuark quark,
GType itype);
static void signal_destroy_R (SignalNode *signal_node);
static inline HandlerList* handler_list_ensure (guint signal_id,
gpointer instance);
static inline HandlerList* handler_list_lookup (guint signal_id,
gpointer instance);
static inline Handler* handler_new (gboolean after);
static void handler_insert (guint signal_id,
gpointer instance,
Handler *handler);
static Handler* handler_lookup (gpointer instance,
guint handler_id,
guint *signal_id_p);
static inline HandlerMatch* handler_match_prepend (HandlerMatch *list,
Handler *handler,
guint signal_id);
static inline HandlerMatch* handler_match_free1_R (HandlerMatch *node,
gpointer instance);
static HandlerMatch* handlers_find (gpointer instance,
GSignalMatchType mask,
guint signal_id,
GQuark detail,
GClosure *closure,
gpointer func,
gpointer data,
gboolean one_and_only);
static inline void handler_ref (Handler *handler);
static inline void handler_unref_R (guint signal_id,
gpointer instance,
Handler *handler);
static inline void emission_push (Emission **emission_list_p,
guint signal_id,
GQuark detail,
gpointer instance,
EmissionState *state_p);
static inline void emission_pop (Emission **emission_list_p,
EmissionState *state_p);
static inline Emission* emission_find (Emission *emission_list,
guint signal_id,
GQuark detail,
gpointer instance);
static gboolean signal_emit_R (SignalNode *node,
GQuark detail,
gpointer instance,
GValue *return_value,
const GValue *instance_and_params);
/* --- structures --- */
struct _SignalNode
{
/* permanent portion */
guint signal_id;
GType itype;
gchar *name;
guint destroyed : 1;
/* reinitializable portion */
guint flags : 8;
guint n_params : 8;
GType *param_types; /* mangled with G_SIGNAL_TYPE_STATIC_SCOPE flag */
GType return_type; /* mangled with G_SIGNAL_TYPE_STATIC_SCOPE flag */
GClosure *class_closure;
GSignalAccumulator accumulator;
GSignalCMarshaller c_marshaller;
GHookList *emission_hooks;
};
struct _SignalKey
{
GType itype;
GQuark quark;
guint signal_id;
};
struct _Emission
{
Emission *next;
guint signal_id;
GQuark detail;
gpointer instance;
EmissionState *state_p;
};
struct _HandlerList
{
guint signal_id;
Handler *handlers;
};
struct _Handler
{
guint id;
Handler *next;
Handler *prev;
GQuark detail;
guint ref_count : 16;
#define HANDLER_MAX_REF_COUNT (1 << 16)
guint block_count : 12;
#define HANDLER_MAX_BLOCK_COUNT (1 << 12)
guint after : 1;
GClosure *closure;
};
struct _HandlerMatch
{
Handler *handler;
HandlerMatch *next;
union {
guint signal_id;
gpointer dummy;
} d;
};
/* --- variables --- */
static GBSearchArray g_signal_key_bsa = { NULL, 0, 0, 0, NULL };
static GHashTable *g_handler_list_bsa_ht = NULL;
static Emission *g_recursive_emissions = NULL;
static Emission *g_restart_emissions = NULL;
static GTrashStack *g_bsa_ts = NULL;
static GTrashStack *g_handler_ts = NULL;
static GTrashStack *g_emission_ts = NULL;
G_LOCK_DEFINE_STATIC (g_signal_mutex);
/* --- signal nodes --- */
static guint g_n_signal_nodes = 0;
static SignalNode **g_signal_nodes = NULL;
static inline SignalNode*
LOOKUP_SIGNAL_NODE (register guint signal_id)
{
if (signal_id < g_n_signal_nodes)
return g_signal_nodes[signal_id];
else
return NULL;
}
/* --- functions --- */
static inline guint
signal_id_lookup (GQuark quark,
GType itype)
{
GType *ifaces, type = itype;
SignalKey key;
guint n_ifaces;
key.quark = quark;
/* try looking up signals for this type and its anchestors */
do
{
SignalKey *signal_key;
key.itype = type;
signal_key = g_bsearch_array_lookup (&g_signal_key_bsa, &key);
if (signal_key)
return signal_key->signal_id;
type = g_type_parent (type);
}
while (type);
/* no luck, try interfaces it exports */
ifaces = g_type_interfaces (itype, &n_ifaces);
while (n_ifaces--)
{
SignalKey *signal_key;
key.itype = ifaces[n_ifaces];
signal_key = g_bsearch_array_lookup (&g_signal_key_bsa, &key);
if (signal_key)
{
g_free (ifaces);
return signal_key->signal_id;
}
}
g_free (ifaces);
return 0;
}
static gint
handler_lists_cmp (gconstpointer node1,
gconstpointer node2)
{
const HandlerList *hlist1 = node1, *hlist2 = node2;
return G_BSEARCH_ARRAY_CMP (hlist1->signal_id, hlist2->signal_id);
}
static inline HandlerList*
handler_list_ensure (guint signal_id,
gpointer instance)
{
GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance);
HandlerList key;
if (!hlbsa)
{
hlbsa = g_generic_node_alloc (&g_bsa_ts,
sizeof (GBSearchArray),
BSA_PRE_ALLOC);
hlbsa->cmp_func = handler_lists_cmp;
hlbsa->sizeof_node = sizeof (HandlerList);
hlbsa->flags = G_BSEARCH_DEFER_SHRINK;
hlbsa->n_nodes = 0;
hlbsa->nodes = NULL;
g_hash_table_insert (g_handler_list_bsa_ht, instance, hlbsa);
}
key.signal_id = signal_id;
key.handlers = NULL;
return g_bsearch_array_insert (hlbsa, &key, FALSE);
}
static inline HandlerList*
handler_list_lookup (guint signal_id,
gpointer instance)
{
GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance);
HandlerList key;
key.signal_id = signal_id;
return hlbsa ? g_bsearch_array_lookup (hlbsa, &key) : NULL;
}
static Handler*
handler_lookup (gpointer instance,
guint handler_id,
guint *signal_id_p)
{
GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance);
if (hlbsa)
{
guint i;
for (i = 0; i < hlbsa->n_nodes; i++)
{
HandlerList *hlist = g_bsearch_array_get_nth (hlbsa, i);
Handler *handler;
for (handler = hlist->handlers; handler; handler = handler->next)
if (handler->id == handler_id)
{
if (signal_id_p)
*signal_id_p = hlist->signal_id;
return handler;
}
}
}
return NULL;
}
static inline HandlerMatch*
handler_match_prepend (HandlerMatch *list,
Handler *handler,
guint signal_id)
{
HandlerMatch *node;
/* yeah, we could use our own memchunk here, introducing yet more
* rarely used cached nodes and extra allocation overhead.
* instead, we use GList* nodes, since they are exactly the size
* we need and are already cached. g_signal_init() asserts this.
*/
node = (HandlerMatch*) g_list_alloc ();
node->handler = handler;
node->next = list;
node->d.signal_id = signal_id;
handler_ref (handler);
return node;
}
static inline HandlerMatch*
handler_match_free1_R (HandlerMatch *node,
gpointer instance)
{
HandlerMatch *next = node->next;
handler_unref_R (node->d.signal_id, instance, node->handler);
g_list_free_1 ((GList*) node);
return next;
}
static HandlerMatch*
handlers_find (gpointer instance,
GSignalMatchType mask,
guint signal_id,
GQuark detail,
GClosure *closure,
gpointer func,
gpointer data,
gboolean one_and_only)
{
HandlerMatch *mlist = NULL;
if (mask & G_SIGNAL_MATCH_ID)
{
HandlerList *hlist = handler_list_lookup (signal_id, instance);
Handler *handler;
SignalNode *node = NULL;
if (mask & G_SIGNAL_MATCH_FUNC)
{
node = LOOKUP_SIGNAL_NODE (signal_id);
if (!node || !node->c_marshaller)
return NULL;
}
mask = ~mask;
for (handler = hlist ? hlist->handlers : NULL; handler; handler = handler->next)
if (handler->id &&
((mask & G_SIGNAL_MATCH_DETAIL) || handler->detail == detail) &&
((mask & G_SIGNAL_MATCH_CLOSURE) || handler->closure == closure) &&
((mask & G_SIGNAL_MATCH_DATA) || handler->closure->data == data) &&
((mask & G_SIGNAL_MATCH_UNBLOCKED) || handler->block_count == 0) &&
((mask & G_SIGNAL_MATCH_FUNC) || (handler->closure->marshal == node->c_marshaller &&
handler->closure->meta_marshal == 0 &&
((GCClosure*) handler->closure)->callback == func)))
{
mlist = handler_match_prepend (mlist, handler, signal_id);
if (one_and_only)
return mlist;
}
}
else
{
GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance);
mask = ~mask;
if (hlbsa)
{
guint i;
for (i = 0; i < hlbsa->n_nodes; i++)
{
HandlerList *hlist = g_bsearch_array_get_nth (hlbsa, i);
SignalNode *node = NULL;
Handler *handler;
if (!(mask & G_SIGNAL_MATCH_FUNC))
{
node = LOOKUP_SIGNAL_NODE (hlist->signal_id);
if (!node->c_marshaller)
continue;
}
for (handler = hlist->handlers; handler; handler = handler->next)
if (handler->id &&
((mask & G_SIGNAL_MATCH_DETAIL) || handler->detail == detail) &&
((mask & G_SIGNAL_MATCH_CLOSURE) || handler->closure == closure) &&
((mask & G_SIGNAL_MATCH_DATA) || handler->closure->data == data) &&
((mask & G_SIGNAL_MATCH_UNBLOCKED) || handler->block_count == 0) &&
((mask & G_SIGNAL_MATCH_FUNC) || (handler->closure->marshal == node->c_marshaller &&
handler->closure->meta_marshal == 0 &&
((GCClosure*) handler->closure)->callback == func)))
{
mlist = handler_match_prepend (mlist, handler, hlist->signal_id);
if (one_and_only)
return mlist;
}
}
}
}
return mlist;
}
static inline Handler*
handler_new (gboolean after)
{
static guint handler_id = 1;
Handler *handler = g_generic_node_alloc (&g_handler_ts,
sizeof (Handler),
HANDLER_PRE_ALLOC);
#ifndef G_DISABLE_CHECKS
if (handler_id == 0)
g_error (G_STRLOC ": handler id overflow, %s", REPORT_BUG);
#endif
handler->id = handler_id++;
handler->prev = NULL;
handler->next = NULL;
handler->detail = 0;
handler->ref_count = 1;
handler->block_count = 0;
handler->after = after != FALSE;
handler->closure = NULL;
return handler;
}
static inline void
handler_ref (Handler *handler)
{
g_return_if_fail (handler->ref_count > 0);
#ifndef G_DISABLE_CHECKS
if (handler->ref_count >= HANDLER_MAX_REF_COUNT - 1)
g_error (G_STRLOC ": handler ref_count overflow, %s", REPORT_BUG);
#endif
handler->ref_count += 1;
}
static inline void
handler_unref_R (guint signal_id,
gpointer instance,
Handler *handler)
{
g_return_if_fail (handler->ref_count > 0);
handler->ref_count -= 1;
if (!handler->ref_count)
{
if (handler->next)
handler->next->prev = handler->prev;
if (handler->prev) /* watch out for g_signal_handlers_destroy()! */
handler->prev->next = handler->next;
else
{
HandlerList *hlist = handler_list_lookup (signal_id, instance);
hlist->handlers = handler->next;
}
G_UNLOCK (g_signal_mutex);
g_closure_unref (handler->closure);
G_LOCK (g_signal_mutex);
g_generic_node_free (&g_handler_ts, handler);
}
}
static void
handler_insert (guint signal_id,
gpointer instance,
Handler *handler)
{
HandlerList *hlist;
g_assert (handler->prev == NULL && handler->next == NULL); /* paranoid */
hlist = handler_list_ensure (signal_id, instance);
if (!hlist->handlers)
hlist->handlers = handler;
else if (hlist->handlers->after && !handler->after)
{
handler->next = hlist->handlers;
hlist->handlers->prev = handler;
hlist->handlers = handler;
}
else
{
Handler *tmp = hlist->handlers;
if (handler->after)
while (tmp->next)
tmp = tmp->next;
else
while (tmp->next && !tmp->next->after)
tmp = tmp->next;
if (tmp->next)
tmp->next->prev = handler;
handler->next = tmp->next;
handler->prev = tmp;
tmp->next = handler;
}
}
static inline void
emission_push (Emission **emission_list_p,
guint signal_id,
GQuark detail,
gpointer instance,
EmissionState *state_p)
{
Emission *emission = g_generic_node_alloc (&g_emission_ts,
sizeof (Emission),
EMISSION_PRE_ALLOC);
emission->next = *emission_list_p;
emission->signal_id = signal_id;
emission->detail = detail;
emission->instance = instance;
emission->state_p = state_p;
*emission_list_p = emission;
}
static inline void
emission_pop (Emission **emission_list_p,
EmissionState *state_p)
{
Emission **loc = emission_list_p, *emission = *loc;
while (emission->state_p != state_p)
{
loc = &emission->next;
emission = *loc;
}
*loc = emission->next;
g_generic_node_free (&g_emission_ts, emission);
}
static inline Emission*
emission_find (Emission *emission_list,
guint signal_id,
GQuark detail,
gpointer instance)
{
Emission *emission;
for (emission = emission_list; emission; emission = emission->next)
if (emission->instance == instance &&
emission->signal_id == signal_id &&
emission->detail == detail)
return emission;
return NULL;
}
static gint
signal_key_cmp (gconstpointer node1,
gconstpointer node2)
{
const SignalKey *key1 = node1, *key2 = node2;
if (key1->itype == key2->itype)
return G_BSEARCH_ARRAY_CMP (key1->quark, key2->quark);
else
return G_BSEARCH_ARRAY_CMP (key1->itype, key2->itype);
}
void
g_signal_init (void) /* sync with gtype.c */
{
G_LOCK (g_signal_mutex);
if (!g_n_signal_nodes)
{
/* handler_id_node_prepend() requires this */
g_assert (sizeof (GList) == sizeof (HandlerMatch));
/* setup signal key array */
g_signal_key_bsa.cmp_func = signal_key_cmp;
g_signal_key_bsa.sizeof_node = sizeof (SignalKey);
g_signal_key_bsa.flags = G_BSEARCH_ALIGN_POWER2; /* alloc-only */
/* setup handler list binary searchable array hash table (in german, that'd be one word ;) */
g_handler_list_bsa_ht = g_hash_table_new (g_direct_hash, NULL);
/* invalid (0) signal_id */
g_n_signal_nodes = 1;
g_signal_nodes = g_renew (SignalNode*, g_signal_nodes, g_n_signal_nodes);
g_signal_nodes[0] = NULL;
}
G_UNLOCK (g_signal_mutex);
}
void
_g_signals_destroy (GType itype)
{
guint i;
G_LOCK (g_signal_mutex);
for (i = 1; i < g_n_signal_nodes; i++)
{
SignalNode *node = g_signal_nodes[i];
if (node->itype == itype)
{
if (node->destroyed)
g_warning (G_STRLOC ": signal \"%s\" of type `%s' already destroyed",
node->name,
g_type_name (node->itype));
else
signal_destroy_R (node);
}
}
G_UNLOCK (g_signal_mutex);
}
void
g_signal_stop_emission (gpointer instance,
guint signal_id,
GQuark detail)
{
SignalNode *node;
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
g_return_if_fail (signal_id > 0);
G_LOCK (g_signal_mutex);
node = LOOKUP_SIGNAL_NODE (signal_id);
if (node && detail && !(node->flags & G_SIGNAL_DETAILED))
{
g_warning ("%s: signal id `%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
G_UNLOCK (g_signal_mutex);
return;
}
if (node && g_type_is_a (G_TYPE_FROM_INSTANCE (instance), node->itype))
{
Emission *emission_list = node->flags & G_SIGNAL_NO_RECURSE ? g_restart_emissions : g_recursive_emissions;
Emission *emission = emission_find (emission_list, signal_id, detail, instance);
if (emission)
{
if (*emission->state_p == EMISSION_HOOK)
g_warning (G_STRLOC ": emission of signal \"%s\" for instance `%p' cannot be stopped from emission hook",
node->name, instance);
else if (*emission->state_p == EMISSION_RUN)
*emission->state_p = EMISSION_STOP;
}
else
g_warning (G_STRLOC ": no emission of signal \"%s\" to stop for instance `%p'",
node->name, instance);
}
else
g_warning ("%s: signal id `%u' is invalid for instance `%p'", G_STRLOC, signal_id, instance);
G_UNLOCK (g_signal_mutex);
}
static inline guint
signal_parse_name (const gchar *name,
GType itype,
GQuark *detail_p,
gboolean force_quark)
{
const gchar *colon = strchr (name, ':');
guint signal_id;
if (!colon)
{
signal_id = signal_id_lookup (g_quark_try_string (name), itype);
if (signal_id && detail_p)
*detail_p = 0;
}
else if (colon[1] == ':')
{
gchar buffer[32];
guint l = colon - name;
if (l < 32)
{
memcpy (buffer, name, l);
buffer[l] = 0;
signal_id = signal_id_lookup (g_quark_try_string (buffer), itype);
}
else
{
gchar *signal = g_new (gchar, l + 1);
memcpy (signal, name, l);
signal[l] = 0;
signal_id = signal_id_lookup (g_quark_try_string (signal), itype);
g_free (signal);
}
if (signal_id && detail_p)
*detail_p = colon[2] ? (force_quark ? g_quark_from_string : g_quark_try_string) (colon + 2) : 0;
}
else
signal_id = 0;
return signal_id;
}
gboolean
g_signal_parse_name (const gchar *detailed_signal,
GType itype,
guint *signal_id_p,
GQuark *detail_p,
gboolean force_detail_quark)
{
GQuark detail = 0;
guint signal_id;
g_return_val_if_fail (detailed_signal != NULL, FALSE);
g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (itype) || G_TYPE_IS_INTERFACE (itype), FALSE);
G_LOCK (g_signal_mutex);
signal_id = signal_parse_name (detailed_signal, itype, &detail, force_detail_quark);
G_UNLOCK (g_signal_mutex);
if (signal_id)
{
if (signal_id_p)
*signal_id_p = signal_id;
if (detail_p)
*detail_p = detail;
return TRUE;
}
else
return FALSE;
}
guint
g_signal_lookup (const gchar *name,
GType itype)
{
guint signal_id;
g_return_val_if_fail (name != NULL, 0);
g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (itype) || G_TYPE_IS_INTERFACE (itype), 0);
G_LOCK (g_signal_mutex);
signal_id = signal_id_lookup (g_quark_try_string (name), itype);
G_UNLOCK (g_signal_mutex);
return signal_id;
}
gchar*
g_signal_name (guint signal_id)
{
SignalNode *node;
gchar *name;
G_LOCK (g_signal_mutex);
node = LOOKUP_SIGNAL_NODE (signal_id);
name = node ? node->name : NULL;
G_UNLOCK (g_signal_mutex);
return name;
}
void
g_signal_query (guint signal_id,
GSignalQuery *query)
{
SignalNode *node;
g_return_if_fail (query != NULL);
G_LOCK (g_signal_mutex);
node = LOOKUP_SIGNAL_NODE (signal_id);
if (!node || node->destroyed)
query->signal_id = 0;
else
{
query->signal_id = node->signal_id;
query->signal_name = node->name;
query->itype = node->itype;
query->signal_flags = node->flags;
query->return_type = node->return_type;
query->n_params = node->n_params;
query->param_types = node->param_types;
}
G_UNLOCK (g_signal_mutex);
}
guint*
g_signal_list_ids (GType itype,
guint *n_ids)
{
SignalKey *keys;
GArray *result;
guint n_nodes;
guint i;
g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (itype) || G_TYPE_IS_INTERFACE (itype), NULL);
g_return_val_if_fail (n_ids != NULL, NULL);
G_LOCK (g_signal_mutex);
keys = g_signal_key_bsa.nodes;
n_nodes = g_signal_key_bsa.n_nodes;
result = g_array_new (FALSE, FALSE, sizeof (guint));
for (i = 0; i < n_nodes; i++)
if (keys[i].itype == itype)
{
gchar *name = g_quark_to_string (keys[i].quark);
/* Signal names with "_" in them are aliases to the same
* name with "-" instead of "_".
*/
if (!strchr (name, '_'))
g_array_append_val (result, keys[i].signal_id);
}
*n_ids = result->len;
G_UNLOCK (g_signal_mutex);
return (guint *) g_array_free (result, FALSE);
}
guint
g_signal_new_valist (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
GClosure *class_closure,
GSignalAccumulator accumulator,
GSignalCMarshaller c_marshaller,
GType return_type,
guint n_params,
va_list args)
{
GType *param_types;
guint i;
guint signal_id;
if (n_params > 0)
{
param_types = g_new (GType, n_params);
for (i = 0; i < n_params; i++)
param_types[i] = va_arg (args, GType);
}
else
param_types = NULL;
signal_id = g_signal_newv (signal_name, itype, signal_flags,
class_closure, accumulator, c_marshaller,
return_type, n_params, param_types);
g_free (param_types);
return signal_id;
}
guint
g_signal_newc (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
guint class_offset,
GSignalAccumulator accumulator,
GSignalCMarshaller c_marshaller,
GType return_type,
guint n_params,
...)
{
va_list args;
guint signal_id;
g_return_val_if_fail (signal_name != NULL, 0);
va_start (args, n_params);
signal_id = g_signal_new_valist (signal_name, itype, signal_flags,
g_signal_type_cclosure_new (itype, class_offset),
accumulator, c_marshaller,
return_type, n_params, args);
va_end (args);
return signal_id;
}
guint
g_signal_newv (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
GClosure *class_closure,
GSignalAccumulator accumulator,
GSignalCMarshaller c_marshaller,
GType return_type,
guint n_params,
GType *param_types)
{
gchar *name;
guint signal_id, i;
SignalNode *node;
g_return_val_if_fail (signal_name != NULL, 0);
g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (itype) || G_TYPE_IS_INTERFACE (itype), 0);
if (n_params)
g_return_val_if_fail (param_types != NULL, 0);
if (return_type != G_TYPE_NONE)
g_return_val_if_fail (accumulator == NULL, 0);
name = g_strdup (signal_name);
g_strdelimit (name, G_STR_DELIMITERS ":^", '_'); // FIXME do character checks like for types
G_LOCK (g_signal_mutex);
signal_id = signal_id_lookup (g_quark_try_string (name), itype);
node = LOOKUP_SIGNAL_NODE (signal_id);
if (node && !node->destroyed)
{
g_warning (G_STRLOC ": signal \"%s\" already exists in the `%s' %s",
name,
g_type_name (node->itype),
G_TYPE_IS_INTERFACE (node->itype) ? "interface" : "class ancestry");
g_free (name);
G_UNLOCK (g_signal_mutex);
return 0;
}
if (node && node->itype != itype)
{
g_warning (G_STRLOC ": signal \"%s\" for type `%s' was previously created for type `%s'",
name,
g_type_name (itype),
g_type_name (node->itype));
g_free (name);
G_UNLOCK (g_signal_mutex);
return 0;
}
for (i = 0; i < n_params; i++)
if (!G_TYPE_IS_VALUE (param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE) ||
(param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE) == G_TYPE_ENUM ||
(param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE) == G_TYPE_FLAGS) /* FIXME: kludge */
{
g_warning (G_STRLOC ": parameter %d of type `%s' for signal \"%s::%s\" is not a value type",
i + 1, g_type_name (param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE), g_type_name (itype), name);
g_free (name);
G_UNLOCK (g_signal_mutex);
return 0;
}
if (return_type != G_TYPE_NONE && !G_TYPE_IS_VALUE (return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE))
{
g_warning (G_STRLOC ": return value of type `%s' for signal \"%s::%s\" is not a value type",
g_type_name (return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE), g_type_name (itype), name);
g_free (name);
G_UNLOCK (g_signal_mutex);
return 0;
}
/* setup permanent portion of signal node */
if (!node)
{
SignalKey key;
signal_id = g_n_signal_nodes++;
node = g_new (SignalNode, 1);
node->signal_id = signal_id;
g_signal_nodes = g_renew (SignalNode*, g_signal_nodes, g_n_signal_nodes);
g_signal_nodes[signal_id] = node;
node->itype = itype;
node->name = name;
key.itype = itype;
key.quark = g_quark_from_string (node->name);
key.signal_id = signal_id;
g_bsearch_array_insert (&g_signal_key_bsa, &key, FALSE);
g_strdelimit (node->name, "_", '-');
key.quark = g_quark_from_static_string (node->name);
g_bsearch_array_insert (&g_signal_key_bsa, &key, FALSE);
}
node->destroyed = FALSE;
/* setup reinitializable portion */
node->flags = signal_flags & G_SIGNAL_FLAGS_MASK;
node->n_params = n_params;
node->param_types = g_memdup (param_types, sizeof (GType) * n_params);
node->return_type = return_type;
node->class_closure = class_closure ? g_closure_ref (class_closure) : NULL;
if (class_closure)
g_closure_sink (class_closure);
node->accumulator = accumulator;
node->c_marshaller = c_marshaller;
node->emission_hooks = NULL;
if (node->c_marshaller && class_closure && G_CLOSURE_NEEDS_MARSHAL (class_closure))
g_closure_set_marshal (class_closure, node->c_marshaller);
G_UNLOCK (g_signal_mutex);
return signal_id;
}
static void
signal_destroy_R (SignalNode *signal_node)
{
SignalNode node = *signal_node;
signal_node->destroyed = TRUE;
/* reentrancy caution, zero out real contents first */
signal_node->n_params = 0;
signal_node->param_types = NULL;
signal_node->return_type = 0;
signal_node->class_closure = NULL;
signal_node->accumulator = NULL;
signal_node->c_marshaller = NULL;
signal_node->emission_hooks = NULL;
#ifdef G_ENABLE_DEBUG
/* check current emissions */
{
Emission *emission;
for (emission = (node.flags & G_SIGNAL_NO_RECURSE) ? g_restart_emissions : g_recursive_emissions;
emission; emission = emission->next)
if (emission->signal_id == node.signal_id)
g_critical (G_STRLOC ": signal \"%s\" being destroyed is currently in emission (instance `%p')",
node.name, emission->instance);
}
#endif
/* free contents that need to
*/
G_UNLOCK (g_signal_mutex);
g_free (node.param_types);
g_closure_unref (node.class_closure);
if (node.emission_hooks)
{
g_hook_list_clear (node.emission_hooks);
g_free (node.emission_hooks);
}
G_LOCK (g_signal_mutex);
}
guint
g_signal_connect_closure_by_id (gpointer instance,
guint signal_id,
GQuark detail,
GClosure *closure,
gboolean after)
{
SignalNode *node;
guint handler_id = 0;
g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0);
g_return_val_if_fail (signal_id > 0, 0);
g_return_val_if_fail (closure != NULL, 0);
G_LOCK (g_signal_mutex);
node = LOOKUP_SIGNAL_NODE (signal_id);
if (node)
{
if (detail && !(node->flags & G_SIGNAL_DETAILED))
g_warning ("%s: signal id `%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
else if (!g_type_is_a (G_TYPE_FROM_INSTANCE (instance), node->itype))
g_warning ("%s: signal id `%u' is invalid for instance `%p'", G_STRLOC, signal_id, instance);
else
{
Handler *handler = handler_new (after);
handler_id = handler->id;
handler->detail = detail;
handler->closure = g_closure_ref (closure);
g_closure_sink (closure);
handler_insert (signal_id, instance, handler);
if (node->c_marshaller && G_CLOSURE_NEEDS_MARSHAL (closure))
g_closure_set_marshal (closure, node->c_marshaller);
}
}
else
g_warning ("%s: signal id `%u' is invalid for instance `%p'", G_STRLOC, signal_id, instance);
G_UNLOCK (g_signal_mutex);
return handler_id;
}
guint
g_signal_connect_closure (gpointer instance,
const gchar *detailed_signal,
GClosure *closure,
gboolean after)
{
guint signal_id, handler_id = 0;
GQuark detail = 0;
GType itype;
g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0);
g_return_val_if_fail (detailed_signal != NULL, 0);
g_return_val_if_fail (closure != NULL, 0);
G_LOCK (g_signal_mutex);
itype = G_TYPE_FROM_INSTANCE (instance);
signal_id = signal_parse_name (detailed_signal, itype, &detail, TRUE);
if (signal_id)
{
SignalNode *node = LOOKUP_SIGNAL_NODE (signal_id);
if (detail && !(node->flags & G_SIGNAL_DETAILED))
g_warning ("%s: signal `%s' does not support details", G_STRLOC, detailed_signal);
else if (!g_type_is_a (itype, node->itype))
g_warning ("%s: signal `%s' is invalid for instance `%p'", G_STRLOC, detailed_signal, instance);
else
{
Handler *handler = handler_new (after);
handler_id = handler->id;
handler->detail = detail;
handler->closure = g_closure_ref (closure);
g_closure_sink (closure);
handler_insert (signal_id, instance, handler);
if (node->c_marshaller && G_CLOSURE_NEEDS_MARSHAL (handler->closure))
g_closure_set_marshal (handler->closure, node->c_marshaller);
}
}
else
g_warning ("%s: signal `%s' is invalid for instance `%p'", G_STRLOC, detailed_signal, instance);
G_UNLOCK (g_signal_mutex);
return handler_id;
}
guint
g_signal_connect_data (gpointer instance,
const gchar *detailed_signal,
GCallback c_handler,
gpointer data,
GClosureNotify destroy_data,
gboolean swapped,
gboolean after)
{
guint signal_id, handler_id = 0;
GQuark detail = 0;
GType itype;
g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0);
g_return_val_if_fail (detailed_signal != NULL, 0);
g_return_val_if_fail (c_handler != NULL, 0);
G_LOCK (g_signal_mutex);
itype = G_TYPE_FROM_INSTANCE (instance);
signal_id = signal_parse_name (detailed_signal, itype, &detail, TRUE);
if (signal_id)
{
SignalNode *node = LOOKUP_SIGNAL_NODE (signal_id);
if (detail && !(node->flags & G_SIGNAL_DETAILED))
g_warning ("%s: signal `%s' does not support details", G_STRLOC, detailed_signal);
else if (!g_type_is_a (itype, node->itype))
g_warning ("%s: signal `%s' is invalid for instance `%p'", G_STRLOC, detailed_signal, instance);
else
{
Handler *handler = handler_new (after);
handler_id = handler->id;
handler->detail = detail;
handler->closure = g_closure_ref ((swapped ? g_cclosure_new_swap : g_cclosure_new) (c_handler, data, destroy_data));
g_closure_sink (handler->closure);
handler_insert (signal_id, instance, handler);
if (node->c_marshaller && G_CLOSURE_NEEDS_MARSHAL (handler->closure))
g_closure_set_marshal (handler->closure, node->c_marshaller);
}
}
else
g_warning ("%s: signal `%s' is invalid for instance `%p'", G_STRLOC, detailed_signal, instance);
G_UNLOCK (g_signal_mutex);
return handler_id;
}
void
g_signal_handler_block (gpointer instance,
guint handler_id)
{
Handler *handler;
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
g_return_if_fail (handler_id > 0);
G_LOCK (g_signal_mutex);
handler = handler_lookup (instance, handler_id, NULL);
if (handler)
{
#ifndef G_DISABLE_CHECKS
if (handler->block_count >= HANDLER_MAX_BLOCK_COUNT - 1)
g_error (G_STRLOC ": handler block_count overflow, %s", REPORT_BUG);
#endif
handler->block_count += 1;
}
else
g_warning ("%s: instance `%p' has no handler with id `%u'", G_STRLOC, instance, handler_id);
G_UNLOCK (g_signal_mutex);
}
void
g_signal_handler_unblock (gpointer instance,
guint handler_id)
{
Handler *handler;
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
g_return_if_fail (handler_id > 0);
G_LOCK (g_signal_mutex);
handler = handler_lookup (instance, handler_id, NULL);
if (handler)
{
if (handler->block_count)
handler->block_count -= 1;
else
g_warning (G_STRLOC ": handler `%u' of instance `%p' is not blocked", handler_id, instance);
}
else
g_warning ("%s: instance `%p' has no handler with id `%u'", G_STRLOC, instance, handler_id);
G_UNLOCK (g_signal_mutex);
}
void
g_signal_handler_disconnect (gpointer instance,
guint handler_id)
{
Handler *handler;
guint signal_id;
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
g_return_if_fail (handler_id > 0);
G_LOCK (g_signal_mutex);
handler = handler_lookup (instance, handler_id, &signal_id);
if (handler)
{
handler->id = 0;
handler->block_count = 1;
handler_unref_R (signal_id, instance, handler);
}
else
g_warning ("%s: instance `%p' has no handler with id `%u'", G_STRLOC, instance, handler_id);
G_UNLOCK (g_signal_mutex);
}
void
g_signal_handlers_destroy (gpointer instance)
{
GBSearchArray *hlbsa;
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
G_LOCK (g_signal_mutex);
hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance);
if (hlbsa)
{
guint i;
/* reentrancy caution, delete instance trace first */
g_hash_table_remove (g_handler_list_bsa_ht, instance);
for (i = 0; i < hlbsa->n_nodes; i++)
{
HandlerList *hlist = g_bsearch_array_get_nth (hlbsa, i);
Handler *handler = hlist->handlers;
while (handler)
{
Handler *tmp = handler;
handler = tmp->next;
tmp->block_count = 1;
/* cruel unlink, this works because _all_ handlers vanish */
tmp->next = NULL;
tmp->prev = tmp;
if (tmp->id)
{
tmp->id = 0;
handler_unref_R (0, NULL, tmp);
}
}
}
g_free (hlbsa->nodes);
g_generic_node_free (&g_bsa_ts, hlbsa);
}
G_UNLOCK (g_signal_mutex);
}
guint
g_signal_handler_find (gpointer instance,
GSignalMatchType mask,
guint signal_id,
GQuark detail,
GClosure *closure,
gpointer func,
gpointer data)
{
guint handler_id = 0;
g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0);
g_return_val_if_fail ((mask & ~G_SIGNAL_MATCH_MASK) == 0, 0);
if (mask & G_SIGNAL_MATCH_MASK)
{
HandlerMatch *mlist;
G_LOCK (g_signal_mutex);
mlist = handlers_find (instance, mask, signal_id, detail, closure, func, data, TRUE);
if (mlist)
{
handler_id = mlist->handler->id;
handler_match_free1_R (mlist, instance);
}
G_UNLOCK (g_signal_mutex);
}
return handler_id;
}
static guint
signal_handlers_foreach_matched_R (gpointer instance,
GSignalMatchType mask,
guint signal_id,
GQuark detail,
GClosure *closure,
gpointer func,
gpointer data,
void (*callback) (gpointer instance,
guint handler_id))
{
HandlerMatch *mlist;
guint n_handlers = 0;
mlist = handlers_find (instance, mask, signal_id, detail, closure, func, data, FALSE);
while (mlist)
{
n_handlers++;
G_UNLOCK (g_signal_mutex);
callback (instance, mlist->handler->id);
G_LOCK (g_signal_mutex);
mlist = handler_match_free1_R (mlist, instance);
}
return n_handlers;
}
guint
g_signal_handlers_block_matched (gpointer instance,
GSignalMatchType mask,
guint signal_id,
GQuark detail,
GClosure *closure,
gpointer func,
gpointer data)
{
guint n_handlers = 0;
g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), FALSE);
g_return_val_if_fail ((mask & ~G_SIGNAL_MATCH_MASK) == 0, FALSE);
if (mask & (G_SIGNAL_MATCH_CLOSURE | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA))
{
G_LOCK (g_signal_mutex);
n_handlers = signal_handlers_foreach_matched_R (instance, mask, signal_id, detail,
closure, func, data,
g_signal_handler_block);
G_UNLOCK (g_signal_mutex);
}
return n_handlers;
}
guint
g_signal_handlers_unblock_matched (gpointer instance,
GSignalMatchType mask,
guint signal_id,
GQuark detail,
GClosure *closure,
gpointer func,
gpointer data)
{
guint n_handlers = 0;
g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), FALSE);
g_return_val_if_fail ((mask & ~G_SIGNAL_MATCH_MASK) == 0, FALSE);
if (mask & (G_SIGNAL_MATCH_CLOSURE | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA))
{
G_LOCK (g_signal_mutex);
n_handlers = signal_handlers_foreach_matched_R (instance, mask, signal_id, detail,
closure, func, data,
g_signal_handler_unblock);
G_UNLOCK (g_signal_mutex);
}
return n_handlers;
}
guint
g_signal_handlers_disconnect_matched (gpointer instance,
GSignalMatchType mask,
guint signal_id,
GQuark detail,
GClosure *closure,
gpointer func,
gpointer data)
{
guint n_handlers = 0;
g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), FALSE);
g_return_val_if_fail ((mask & ~G_SIGNAL_MATCH_MASK) == 0, FALSE);
if (mask & (G_SIGNAL_MATCH_CLOSURE | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA))
{
G_LOCK (g_signal_mutex);
n_handlers = signal_handlers_foreach_matched_R (instance, mask, signal_id, detail,
closure, func, data,
g_signal_handler_disconnect);
G_UNLOCK (g_signal_mutex);
}
return n_handlers;
}
gboolean
g_signal_has_handler_pending (gpointer instance,
guint signal_id,
GQuark detail,
gboolean may_be_blocked)
{
HandlerMatch *mlist;
gboolean has_pending;
g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), FALSE);
g_return_val_if_fail (signal_id > 0, FALSE);
G_LOCK (g_signal_mutex);
if (detail)
{
SignalNode *node = LOOKUP_SIGNAL_NODE (signal_id);
if (!(node->flags & G_SIGNAL_DETAILED))
{
g_warning ("%s: signal id `%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
G_UNLOCK (g_signal_mutex);
return FALSE;
}
}
mlist = handlers_find (instance,
(G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | (may_be_blocked ? 0 : G_SIGNAL_MATCH_UNBLOCKED)),
signal_id, detail, NULL, NULL, NULL, TRUE);
if (mlist)
{
has_pending = TRUE;
handler_match_free1_R (mlist, instance);
}
else
has_pending = FALSE;
G_UNLOCK (g_signal_mutex);
return has_pending;
}
void
g_signal_emitv (const GValue *instance_and_params,
guint signal_id,
GQuark detail,
GValue *return_value)
{
const GValue *param_values;
gpointer instance;
SignalNode *node;
guint i;
g_return_if_fail (instance_and_params != NULL);
instance = g_value_get_as_pointer (instance_and_params);
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
g_return_if_fail (signal_id > 0);
param_values = instance_and_params + 1;
G_LOCK (g_signal_mutex);
node = LOOKUP_SIGNAL_NODE (signal_id);
if (!node || !g_type_is_a (G_TYPE_FROM_INSTANCE (instance), node->itype))
{
g_warning ("%s: signal id `%u' is invalid for instance `%p'", G_STRLOC, signal_id, instance);
G_UNLOCK (g_signal_mutex);
return;
}
#ifdef G_ENABLE_DEBUG
if (detail && !(node->flags & G_SIGNAL_DETAILED))
{
g_warning ("%s: signal id `%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
G_UNLOCK (g_signal_mutex);
return;
}
for (i = 0; i < node->n_params; i++)
if (!G_VALUE_HOLDS (param_values + i, node->param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE))
{
g_critical ("%s: value for `%s' parameter %u for signal \"%s\" is of type `%s'",
G_STRLOC,
g_type_name (node->param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE),
i,
node->name,
G_VALUE_TYPE_NAME (param_values + i));
G_UNLOCK (g_signal_mutex);
return;
}
if (node->return_type != G_TYPE_NONE)
{
if (!return_value)
{
g_critical ("%s: return value `%s' for signal \"%s\" is (NULL)",
G_STRLOC,
g_type_name (node->return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE),
node->name);
G_UNLOCK (g_signal_mutex);
return;
}
else if (!node->accumulator && !G_VALUE_HOLDS (return_value, node->return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE))
{
g_critical ("%s: return value `%s' for signal \"%s\" is of type `%s'",
G_STRLOC,
g_type_name (node->return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE),
node->name,
G_VALUE_TYPE_NAME (return_value));
G_UNLOCK (g_signal_mutex);
return;
}
}
else
return_value = NULL;
#endif /* G_ENABLE_DEBUG */
signal_emit_R (node, detail, instance, return_value, instance_and_params);
G_UNLOCK (g_signal_mutex);
}
void
g_signal_emit_valist (gpointer instance,
guint signal_id,
GQuark detail,
va_list var_args)
{
GValue *instance_and_params, stack_values[MAX_STACK_VALUES], *free_me = NULL;
GValue *param_values;
SignalNode *node;
guint i;
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
g_return_if_fail (signal_id > 0);
G_LOCK (g_signal_mutex);
node = LOOKUP_SIGNAL_NODE (signal_id);
if (!node || !g_type_is_a (G_TYPE_FROM_INSTANCE (instance), node->itype))
{
g_warning ("%s: signal id `%u' is invalid for instance `%p'", G_STRLOC, signal_id, instance);
G_UNLOCK (g_signal_mutex);
return;
}
#ifndef G_DISABLE_CHECKS
if (detail && !(node->flags & G_SIGNAL_DETAILED))
{
g_warning ("%s: signal id `%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
G_UNLOCK (g_signal_mutex);
return;
}
#endif /* !G_DISABLE_CHECKS */
if (node->n_params < MAX_STACK_VALUES)
instance_and_params = stack_values;
else
{
free_me = g_new (GValue, node->n_params + 1);
instance_and_params = free_me;
}
param_values = instance_and_params + 1;
for (i = 0; i < node->n_params; i++)
{
gchar *error;
param_values[i].g_type = 0;
g_value_init (param_values + i, node->param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE);
G_VALUE_COLLECT (param_values + i,
var_args,
node->param_types[i] & G_SIGNAL_TYPE_STATIC_SCOPE ? G_VALUE_NOCOPY_CONTENTS : 0,
&error);
if (error)
{
g_warning ("%s: %s", G_STRLOC, error);
g_free (error);
/* we purposely leak the value here, it might not be
* in a sane state if an error condition occoured
*/
while (i--)
g_value_unset (param_values + i);
G_UNLOCK (g_signal_mutex);
g_free (free_me);
return;
}
}
instance_and_params->g_type = 0;
g_value_init (instance_and_params, node->itype);
g_value_set_instance (instance_and_params, instance);
if (node->return_type == G_TYPE_NONE)
signal_emit_R (node, detail, instance, NULL, instance_and_params);
else
{
GValue return_value = { 0, };
gchar *error = NULL;
g_value_init (&return_value, node->return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE);
if (signal_emit_R (node, detail, instance, &return_value, instance_and_params))
G_VALUE_LCOPY (&return_value,
var_args,
node->return_type & G_SIGNAL_TYPE_STATIC_SCOPE ? G_VALUE_NOCOPY_CONTENTS : 0,
&error);
if (!error)
g_value_unset (&return_value);
else
{
g_warning ("%s: %s", G_STRLOC, error);
g_free (error);
/* we purposely leak the value here, it might not be
* in a sane state if an error condition occoured
*/
}
}
for (i = 0; i < node->n_params; i++)
g_value_unset (param_values + i);
g_value_unset (instance_and_params);
if (free_me)
g_free (free_me);
G_UNLOCK (g_signal_mutex);
}
void
g_signal_emit (gpointer instance,
guint signal_id,
GQuark detail,
...)
{
va_list var_args;
va_start (var_args, detail);
g_signal_emit_valist (instance, signal_id, detail, var_args);
va_end (var_args);
}
void
g_signal_emit_by_name (gpointer instance,
const gchar *detailed_signal,
...)
{
GQuark detail = 0;
guint signal_id;
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
g_return_if_fail (detailed_signal != NULL);
G_LOCK (g_signal_mutex);
signal_id = signal_parse_name (detailed_signal, G_TYPE_FROM_INSTANCE (instance), &detail, TRUE);
G_UNLOCK (g_signal_mutex);
if (signal_id)
{
va_list var_args;
va_start (var_args, detailed_signal);
g_signal_emit_valist (instance, signal_id, detail, var_args);
va_end (var_args);
}
else
g_warning ("%s: signal name `%s' is invalid for instance `%p'", G_STRLOC, detailed_signal, instance);
}
static gboolean
signal_emit_R (SignalNode *node,
GQuark detail,
gpointer instance,
GValue *return_value,
const GValue *instance_and_params)
{
EmissionState emission_state = 0;
GSignalAccumulator accumulator;
GSignalInvocationHint ihint;
GClosure *class_closure;
HandlerList *hlist;
Handler *handler_list = NULL;
GValue accu = { 0, };
gboolean accu_used = FALSE;
guint signal_id = node->signal_id;
gboolean return_value_altered = FALSE;
#ifdef G_ENABLE_DEBUG
IF_DEBUG (SIGNALS, g_trace_instance_signals == instance || g_trap_instance_signals == instance)
{
g_message ("%s::%s(%u) emitted (instance=%p signal-node=%p)\n",
g_type_name (G_TYPE_FROM_INSTANCE (instance)),
node->name, detail,
instance, node);
if (g_trap_instance_signals == instance)
G_BREAKPOINT ();
}
#endif /* G_ENABLE_DEBUG */
if (node->flags & G_SIGNAL_NO_RECURSE)
{
Emission *emission = emission_find (g_restart_emissions, signal_id, detail, instance);
if (emission)
{
*emission->state_p = EMISSION_RESTART;
return return_value_altered;
}
}
ihint.signal_id = node->signal_id;
ihint.detail = detail;
accumulator = node->accumulator;
if (accumulator)
g_value_init (&accu, node->return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE);
emission_push ((node->flags & G_SIGNAL_NO_RECURSE) ? &g_restart_emissions : &g_recursive_emissions,
signal_id, detail, instance, &emission_state);
class_closure = node->class_closure;
EMIT_RESTART:
if (handler_list)
handler_unref_R (signal_id, instance, handler_list);
hlist = handler_list_lookup (signal_id, instance);
handler_list = hlist ? hlist->handlers : NULL;
if (handler_list)
handler_ref (handler_list);
ihint.run_type = G_SIGNAL_RUN_FIRST;
if ((node->flags & G_SIGNAL_RUN_FIRST) && class_closure)
{
emission_state = EMISSION_RUN;
G_UNLOCK (g_signal_mutex);
if (accumulator)
{
if (accu_used)
g_value_reset (&accu);
g_closure_invoke (class_closure,
&accu,
node->n_params + 1,
instance_and_params,
&ihint);
if (!accumulator (&ihint, return_value, &accu) &&
emission_state == EMISSION_RUN)
emission_state = EMISSION_STOP;
accu_used = TRUE;
}
else
g_closure_invoke (class_closure,
return_value,
node->n_params + 1,
instance_and_params,
&ihint);
G_LOCK (g_signal_mutex);
return_value_altered = TRUE;
if (emission_state == EMISSION_STOP)
goto EMIT_CLEANUP;
else if (emission_state == EMISSION_RESTART)
goto EMIT_RESTART;
}
if (node->emission_hooks)
{
emission_state = EMISSION_HOOK;
G_UNLOCK (g_signal_mutex);
g_print ("emission_hooks()\n");
G_LOCK (g_signal_mutex);
if (emission_state == EMISSION_RESTART)
goto EMIT_RESTART;
}
if (handler_list)
{
Handler *handler = handler_list;
emission_state = EMISSION_RUN;
handler_ref (handler);
do
{
Handler *tmp;
if (handler->after)
{
handler_unref_R (signal_id, instance, handler_list);
handler_list = handler;
break;
}
else if (!handler->block_count && (!handler->detail || handler->detail == detail))
{
G_UNLOCK (g_signal_mutex);
if (accumulator)
{
if (accu_used)
g_value_reset (&accu);
g_closure_invoke (handler->closure,
&accu,
node->n_params + 1,
instance_and_params,
&ihint);
if (!accumulator (&ihint, return_value, &accu) &&
emission_state == EMISSION_RUN)
emission_state = EMISSION_STOP;
accu_used = TRUE;
}
else
g_closure_invoke (handler->closure,
return_value,
node->n_params + 1,
instance_and_params,
&ihint);
G_LOCK (g_signal_mutex);
return_value_altered = TRUE;
tmp = emission_state == EMISSION_RUN ? handler->next : NULL;
}
else
tmp = handler->next;
if (tmp)
handler_ref (tmp);
handler_unref_R (signal_id, instance, handler_list);
handler_list = handler;
handler = tmp;
}
while (handler);
if (emission_state == EMISSION_STOP)
goto EMIT_CLEANUP;
else if (emission_state == EMISSION_RESTART)
goto EMIT_RESTART;
}
ihint.run_type = G_SIGNAL_RUN_LAST;
if ((node->flags & G_SIGNAL_RUN_LAST) && class_closure)
{
emission_state = EMISSION_RUN;
G_UNLOCK (g_signal_mutex);
if (accumulator)
{
if (accu_used)
g_value_reset (&accu);
g_closure_invoke (class_closure,
&accu,
node->n_params + 1,
instance_and_params,
&ihint);
if (!accumulator (&ihint, return_value, &accu) &&
emission_state == EMISSION_RUN)
emission_state = EMISSION_STOP;
accu_used = TRUE;
}
else
g_closure_invoke (class_closure,
return_value,
node->n_params + 1,
instance_and_params,
&ihint);
G_LOCK (g_signal_mutex);
return_value_altered = TRUE;
if (emission_state == EMISSION_STOP)
goto EMIT_CLEANUP;
else if (emission_state == EMISSION_RESTART)
goto EMIT_RESTART;
}
if (handler_list)
{
Handler *handler = handler_list;
emission_state = EMISSION_RUN;
handler_ref (handler);
do
{
Handler *tmp;
if (handler->after && !handler->block_count && (!handler->detail || handler->detail == detail))
{
G_UNLOCK (g_signal_mutex);
if (accumulator)
{
if (accu_used)
g_value_reset (&accu);
g_closure_invoke (handler->closure,
&accu,
node->n_params + 1,
instance_and_params,
&ihint);
if (!accumulator (&ihint, return_value, &accu) &&
emission_state == EMISSION_RUN)
emission_state = EMISSION_STOP;
accu_used = TRUE;
}
else
g_closure_invoke (handler->closure,
return_value,
node->n_params + 1,
instance_and_params,
&ihint);
G_LOCK (g_signal_mutex);
return_value_altered = TRUE;
tmp = emission_state == EMISSION_RUN ? handler->next : NULL;
}
else
tmp = handler->next;
if (tmp)
handler_ref (tmp);
handler_unref_R (signal_id, instance, handler);
handler = tmp;
}
while (handler);
if (emission_state == EMISSION_STOP)
goto EMIT_CLEANUP;
else if (emission_state == EMISSION_RESTART)
goto EMIT_RESTART;
}
EMIT_CLEANUP:
ihint.run_type = G_SIGNAL_RUN_CLEANUP;
if ((node->flags & G_SIGNAL_RUN_CLEANUP) && class_closure)
{
gboolean need_unset = FALSE;
emission_state = EMISSION_STOP;
G_UNLOCK (g_signal_mutex);
if (node->return_type != G_TYPE_NONE)
{
if (!accumulator)
{
g_value_init (&accu, node->return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE);
need_unset = TRUE;
}
else if (accu_used)
g_value_reset (&accu);
}
g_closure_invoke (class_closure,
node->return_type != G_TYPE_NONE ? &accu : NULL,
node->n_params + 1,
instance_and_params,
&ihint);
if (need_unset)
g_value_unset (&accu);
G_LOCK (g_signal_mutex);
if (emission_state == EMISSION_RESTART)
goto EMIT_RESTART;
}
if (handler_list)
handler_unref_R (signal_id, instance, handler_list);
emission_pop ((node->flags & G_SIGNAL_NO_RECURSE) ? &g_restart_emissions : &g_recursive_emissions, &emission_state);
if (accumulator)
g_value_unset (&accu);
return return_value_altered;
}
/* --- compile standard marshallers --- */
#include "gvaluetypes.h"
#include "gmarshal.c"