glib/gobject/gsignal.c
Alex Richardson 9769cd0d24 gsignal.c: drop an optimization that is undefined behaviour
Comparing reallocated pointers is UB, but this happens to work for now
on most compilers. However, for CHERI systems if g_bsearch_array_insert()
reallocs in-place then the new `hlbsa` pointer may have larger bounds
than `o` and using the old pointer with the smaller bounds can result
in a bounds error. I don't think this code is performance critical, so
removing the optimization and inserting unconditionally should be fine.

Currently, this realloc() UB rarely causes issues, but newer versions of
GCC with _FORTIFY_SOURCE=3 might also be able to observe the valid
memory range (assuming sufficient inlining).
See https://developers.redhat.com/articles/2022/09/17/gccs-new-fortification-level
2024-01-05 16:46:29 -08:00

4179 lines
128 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2000-2001 Red Hat, Inc.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* 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.1 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, see <http://www.gnu.org/licenses/>.
*
* this code is based on the original GtkSignal implementation
* for the Gtk+ library by Peter Mattis <petm@xcf.berkeley.edu>
*/
/*
* MT safe
*/
#include "config.h"
#include <string.h>
#include <signal.h>
#include "gsignal.h"
#include "gtype-private.h"
#include "gbsearcharray.h"
#include "gvaluecollector.h"
#include "gvaluetypes.h"
#include "gobject.h"
#include "genums.h"
#include "gobject_trace.h"
#define REPORT_BUG "please report occurrence circumstances to https://gitlab.gnome.org/GNOME/glib/issues/new"
/* --- 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 (const gchar *name,
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 (guint signal_id,
gpointer instance,
gboolean after);
static void handler_insert (guint signal_id,
gpointer instance,
Handler *handler);
static Handler* handler_lookup (gpointer instance,
gulong handler_id,
GClosure *closure,
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 gint handler_lists_cmp (gconstpointer node1,
gconstpointer node2);
static inline void emission_push (Emission *emission);
static inline void emission_pop (Emission *emission);
static inline Emission* emission_find (guint signal_id,
GQuark detail,
gpointer instance);
static gint class_closures_cmp (gconstpointer node1,
gconstpointer node2);
static gint signal_key_cmp (gconstpointer node1,
gconstpointer node2);
static gboolean signal_emit_unlocked_R (SignalNode *node,
GQuark detail,
gpointer instance,
GValue *return_value,
const GValue *instance_and_params);
static void add_invalid_closure_notify (Handler *handler,
gpointer instance);
static void remove_invalid_closure_notify (Handler *handler,
gpointer instance);
static void invalid_closure_notify (gpointer data,
GClosure *closure);
static const gchar * type_debug_name (GType type);
static void node_check_deprecated (const SignalNode *node);
static void node_update_single_va_closure (SignalNode *node);
/* --- structures --- */
typedef struct
{
GSignalAccumulator func;
gpointer data;
} SignalAccumulator;
typedef struct
{
GHook hook;
GQuark detail;
} SignalHook;
#define SIGNAL_HOOK(hook) ((SignalHook*) (hook))
struct _SignalNode
{
/* permanent portion */
guint signal_id;
GType itype;
const gchar *name;
guint destroyed : 1;
/* reinitializable portion */
guint flags : 9;
guint n_params : 8;
guint single_va_closure_is_valid : 1;
guint single_va_closure_is_after : 1;
GType *param_types; /* mangled with G_SIGNAL_TYPE_STATIC_SCOPE flag */
GType return_type; /* mangled with G_SIGNAL_TYPE_STATIC_SCOPE flag */
GBSearchArray *class_closure_bsa;
SignalAccumulator *accumulator;
GSignalCMarshaller c_marshaller;
GSignalCVaMarshaller va_marshaller;
GHookList *emission_hooks;
GClosure *single_va_closure;
};
#define SINGLE_VA_CLOSURE_EMPTY_MAGIC GINT_TO_POINTER(1) /* indicates single_va_closure is valid but empty */
struct _SignalKey
{
GType itype;
GQuark quark;
guint signal_id;
};
struct _Emission
{
Emission *next;
gpointer instance;
GSignalInvocationHint ihint;
EmissionState state;
GType chain_type;
};
struct _HandlerList
{
guint signal_id;
Handler *handlers;
Handler *tail_before; /* normal signal handlers are appended here */
Handler *tail_after; /* CONNECT_AFTER handlers are appended here */
};
struct _Handler
{
gulong sequential_number;
Handler *next;
Handler *prev;
GQuark detail;
guint signal_id;
guint ref_count;
guint block_count : 16;
#define HANDLER_MAX_BLOCK_COUNT (1 << 16)
guint after : 1;
guint has_invalid_closure_notify : 1;
GClosure *closure;
gpointer instance;
};
struct _HandlerMatch
{
Handler *handler;
HandlerMatch *next;
guint signal_id;
};
typedef struct
{
GType instance_type; /* 0 for default closure */
GClosure *closure;
} ClassClosure;
/* --- variables --- */
static GBSearchArray *g_signal_key_bsa = NULL;
static const GBSearchConfig g_signal_key_bconfig = {
sizeof (SignalKey),
signal_key_cmp,
G_BSEARCH_ARRAY_ALIGN_POWER2,
};
static GBSearchConfig g_signal_hlbsa_bconfig = {
sizeof (HandlerList),
handler_lists_cmp,
0,
};
static GBSearchConfig g_class_closure_bconfig = {
sizeof (ClassClosure),
class_closures_cmp,
0,
};
static GHashTable *g_handler_list_bsa_ht = NULL;
static Emission *g_emissions = NULL;
static gulong g_handler_sequential_number = 1;
static GHashTable *g_handlers = NULL;
G_LOCK_DEFINE_STATIC (g_signal_mutex);
#define SIGNAL_LOCK() G_LOCK (g_signal_mutex)
#define SIGNAL_UNLOCK() G_UNLOCK (g_signal_mutex)
/* --- signal nodes --- */
static guint g_n_signal_nodes = 0;
static SignalNode **g_signal_nodes = NULL;
static inline SignalNode*
LOOKUP_SIGNAL_NODE (guint signal_id)
{
if (signal_id < g_n_signal_nodes)
return g_signal_nodes[signal_id];
else
return NULL;
}
/* --- functions --- */
/* @key must have already been validated with is_valid()
* Modifies @key in place. */
static void
canonicalize_key (gchar *key)
{
gchar *p;
for (p = key; *p != 0; p++)
{
gchar c = *p;
if (c == '_')
*p = '-';
}
}
/* @key must have already been validated with is_valid() */
static gboolean
is_canonical (const gchar *key)
{
return (strchr (key, '_') == NULL);
}
/**
* g_signal_is_valid_name:
* @name: the canonical name of the signal
*
* Validate a signal name. This can be useful for dynamically-generated signals
* which need to be validated at run-time before actually trying to create them.
*
* See [canonical parameter names][canonical-parameter-names] for details of
* the rules for valid names. The rules for signal names are the same as those
* for property names.
*
* Returns: %TRUE if @name is a valid signal name, %FALSE otherwise.
* Since: 2.66
*/
gboolean
g_signal_is_valid_name (const gchar *name)
{
/* FIXME: We allow this, against our own documentation (the leading `-` is
* invalid), because GTK has historically used this. */
if (g_str_equal (name, "-gtk-private-changed"))
return TRUE;
return g_param_spec_is_valid_name (name);
}
static inline guint
signal_id_lookup (const gchar *name,
GType itype)
{
GQuark quark;
GType *ifaces, type = itype;
SignalKey key;
guint n_ifaces;
quark = g_quark_try_string (name);
key.quark = quark;
/* try looking up signals for this type and its ancestors */
do
{
SignalKey *signal_key;
key.itype = type;
signal_key = g_bsearch_array_lookup (g_signal_key_bsa, &g_signal_key_bconfig, &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, &g_signal_key_bconfig, &key);
if (signal_key)
{
g_free (ifaces);
return signal_key->signal_id;
}
}
g_free (ifaces);
/* If the @name is non-canonical, try again. This is the slow path — people
* should use canonical names in their queries if they want performance. */
if (!is_canonical (name))
{
guint signal_id;
gchar *name_copy = g_strdup (name);
canonicalize_key (name_copy);
signal_id = signal_id_lookup (name_copy, itype);
g_free (name_copy);
return signal_id;
}
return 0;
}
static gint
class_closures_cmp (gconstpointer node1,
gconstpointer node2)
{
const ClassClosure *c1 = node1, *c2 = node2;
return G_BSEARCH_ARRAY_CMP (c1->instance_type, c2->instance_type);
}
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;
key.signal_id = signal_id;
key.handlers = NULL;
key.tail_before = NULL;
key.tail_after = NULL;
if (!hlbsa)
{
hlbsa = g_bsearch_array_create (&g_signal_hlbsa_bconfig);
}
hlbsa = g_bsearch_array_insert (hlbsa, &g_signal_hlbsa_bconfig, &key);
g_hash_table_insert (g_handler_list_bsa_ht, instance, hlbsa);
return g_bsearch_array_lookup (hlbsa, &g_signal_hlbsa_bconfig, &key);
}
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, &g_signal_hlbsa_bconfig, &key) : NULL;
}
static guint
handler_hash (gconstpointer key)
{
return (guint)((Handler*)key)->sequential_number;
}
static gboolean
handler_equal (gconstpointer a, gconstpointer b)
{
Handler *ha = (Handler *)a;
Handler *hb = (Handler *)b;
return (ha->sequential_number == hb->sequential_number) &&
(ha->instance == hb->instance);
}
static Handler*
handler_lookup (gpointer instance,
gulong handler_id,
GClosure *closure,
guint *signal_id_p)
{
GBSearchArray *hlbsa;
if (handler_id)
{
Handler key;
key.sequential_number = handler_id;
key.instance = instance;
return g_hash_table_lookup (g_handlers, &key);
}
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, &g_signal_hlbsa_bconfig, i);
Handler *handler;
for (handler = hlist->handlers; handler; handler = handler->next)
if (closure ? (handler->closure == closure) : (handler->sequential_number == 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;
node = g_slice_new (HandlerMatch);
node->handler = handler;
node->next = list;
node->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->signal_id, instance, node->handler);
g_slice_free (HandlerMatch, 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->sequential_number &&
((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 &&
G_REAL_CLOSURE (handler->closure)->meta_marshal == NULL &&
((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, &g_signal_hlbsa_bconfig, 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->sequential_number &&
((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 &&
G_REAL_CLOSURE (handler->closure)->meta_marshal == NULL &&
((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 (guint signal_id, gpointer instance, gboolean after)
{
Handler *handler = g_slice_new (Handler);
#ifndef G_DISABLE_CHECKS
if (g_handler_sequential_number < 1)
g_error (G_STRLOC ": handler id overflow, %s", REPORT_BUG);
#endif
handler->sequential_number = g_handler_sequential_number++;
handler->prev = NULL;
handler->next = NULL;
handler->detail = 0;
handler->signal_id = signal_id;
handler->instance = instance;
handler->ref_count = 1;
handler->block_count = 0;
handler->after = after != FALSE;
handler->closure = NULL;
handler->has_invalid_closure_notify = 0;
g_hash_table_add (g_handlers, handler);
return handler;
}
static inline void
handler_ref (Handler *handler)
{
g_return_if_fail (handler->ref_count > 0);
handler->ref_count++;
}
static inline void
handler_unref_R (guint signal_id,
gpointer instance,
Handler *handler)
{
g_return_if_fail (handler->ref_count > 0);
handler->ref_count--;
if (G_UNLIKELY (handler->ref_count == 0))
{
HandlerList *hlist = NULL;
if (handler->next)
handler->next->prev = handler->prev;
if (handler->prev) /* watch out for g_signal_handlers_destroy()! */
handler->prev->next = handler->next;
else
{
hlist = handler_list_lookup (signal_id, instance);
g_assert (hlist != NULL);
hlist->handlers = handler->next;
}
if (instance)
{
/* check if we are removing the handler pointed to by tail_before */
if (!handler->after && (!handler->next || handler->next->after))
{
if (!hlist)
hlist = handler_list_lookup (signal_id, instance);
if (hlist)
{
g_assert (hlist->tail_before == handler); /* paranoid */
hlist->tail_before = handler->prev;
}
}
/* check if we are removing the handler pointed to by tail_after */
if (!handler->next)
{
if (!hlist)
hlist = handler_list_lookup (signal_id, instance);
if (hlist)
{
g_assert (hlist->tail_after == handler); /* paranoid */
hlist->tail_after = handler->prev;
}
}
}
SIGNAL_UNLOCK ();
g_closure_unref (handler->closure);
SIGNAL_LOCK ();
g_slice_free (Handler, 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;
if (!handler->after)
hlist->tail_before = handler;
}
else if (handler->after)
{
handler->prev = hlist->tail_after;
hlist->tail_after->next = handler;
}
else
{
if (hlist->tail_before)
{
handler->next = hlist->tail_before->next;
if (handler->next)
handler->next->prev = handler;
handler->prev = hlist->tail_before;
hlist->tail_before->next = handler;
}
else /* insert !after handler into a list of only after handlers */
{
handler->next = hlist->handlers;
if (handler->next)
handler->next->prev = handler;
hlist->handlers = handler;
}
hlist->tail_before = handler;
}
if (!handler->next)
hlist->tail_after = handler;
}
static void
node_update_single_va_closure (SignalNode *node)
{
GClosure *closure = NULL;
gboolean is_after = FALSE;
/* Fast path single-handler without boxing the arguments in GValues */
if (G_TYPE_IS_OBJECT (node->itype) &&
(node->flags & (G_SIGNAL_MUST_COLLECT)) == 0 &&
(node->emission_hooks == NULL || node->emission_hooks->hooks == NULL))
{
GSignalFlags run_type;
ClassClosure * cc;
GBSearchArray *bsa = node->class_closure_bsa;
if (bsa == NULL || bsa->n_nodes == 0)
closure = SINGLE_VA_CLOSURE_EMPTY_MAGIC;
else if (bsa->n_nodes == 1)
{
/* Look for default class closure (can't support non-default as it
chains up using GValues */
cc = g_bsearch_array_get_nth (bsa, &g_class_closure_bconfig, 0);
if (cc->instance_type == 0)
{
run_type = node->flags & (G_SIGNAL_RUN_FIRST|G_SIGNAL_RUN_LAST|G_SIGNAL_RUN_CLEANUP);
/* Only support *one* of run-first or run-last, not multiple or cleanup */
if (run_type == G_SIGNAL_RUN_FIRST ||
run_type == G_SIGNAL_RUN_LAST)
{
closure = cc->closure;
is_after = (run_type == G_SIGNAL_RUN_LAST);
}
}
}
}
node->single_va_closure_is_valid = TRUE;
node->single_va_closure = closure;
node->single_va_closure_is_after = is_after;
}
static inline void
emission_push (Emission *emission)
{
emission->next = g_emissions;
g_emissions = emission;
}
static inline void
emission_pop (Emission *emission)
{
Emission *node, *last = NULL;
for (node = g_emissions; node; last = node, node = last->next)
if (node == emission)
{
if (last)
last->next = node->next;
else
g_emissions = node->next;
return;
}
g_assert_not_reached ();
}
static inline Emission*
emission_find (guint signal_id,
GQuark detail,
gpointer instance)
{
Emission *emission;
for (emission = g_emissions; emission; emission = emission->next)
if (emission->instance == instance &&
emission->ihint.signal_id == signal_id &&
emission->ihint.detail == detail)
return emission;
return NULL;
}
static inline Emission*
emission_find_innermost (gpointer instance)
{
Emission *emission;
for (emission = g_emissions; emission; emission = emission->next)
if (emission->instance == instance)
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)
{
SIGNAL_LOCK ();
if (!g_n_signal_nodes)
{
/* 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);
g_signal_key_bsa = g_bsearch_array_create (&g_signal_key_bconfig);
/* 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_handlers = g_hash_table_new (handler_hash, handler_equal);
}
SIGNAL_UNLOCK ();
}
void
_g_signals_destroy (GType itype)
{
guint i;
SIGNAL_LOCK ();
for (i = 1; i < g_n_signal_nodes; i++)
{
SignalNode *node = g_signal_nodes[i];
if (node->itype == itype)
{
if (node->destroyed)
g_critical (G_STRLOC ": signal \"%s\" of type '%s' already destroyed",
node->name,
type_debug_name (node->itype));
else
signal_destroy_R (node);
}
}
SIGNAL_UNLOCK ();
}
/**
* g_signal_stop_emission:
* @instance: (type GObject.Object): the object whose signal handlers you wish to stop.
* @signal_id: the signal identifier, as returned by g_signal_lookup().
* @detail: the detail which the signal was emitted with.
*
* Stops a signal's current emission.
*
* This will prevent the default method from running, if the signal was
* %G_SIGNAL_RUN_LAST and you connected normally (i.e. without the "after"
* flag).
*
* Prints a warning if used on a signal which isn't being emitted.
*/
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);
SIGNAL_LOCK ();
node = LOOKUP_SIGNAL_NODE (signal_id);
if (node && detail && !(node->flags & G_SIGNAL_DETAILED))
{
g_critical ("%s: signal id '%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
SIGNAL_UNLOCK ();
return;
}
if (node && g_type_is_a (G_TYPE_FROM_INSTANCE (instance), node->itype))
{
Emission *emission = emission_find (signal_id, detail, instance);
if (emission)
{
if (emission->state == EMISSION_HOOK)
g_critical (G_STRLOC ": emission of signal \"%s\" for instance '%p' cannot be stopped from emission hook",
node->name, instance);
else if (emission->state == EMISSION_RUN)
emission->state = EMISSION_STOP;
}
else
g_critical (G_STRLOC ": no emission of signal \"%s\" to stop for instance '%p'",
node->name, instance);
}
else
g_critical ("%s: signal id '%u' is invalid for instance '%p'", G_STRLOC, signal_id, instance);
SIGNAL_UNLOCK ();
}
static void
signal_finalize_hook (GHookList *hook_list,
GHook *hook)
{
GDestroyNotify destroy = hook->destroy;
if (destroy)
{
hook->destroy = NULL;
SIGNAL_UNLOCK ();
destroy (hook->data);
SIGNAL_LOCK ();
}
}
/**
* g_signal_add_emission_hook:
* @signal_id: the signal identifier, as returned by g_signal_lookup().
* @detail: the detail on which to call the hook.
* @hook_func: (not nullable): a #GSignalEmissionHook function.
* @hook_data: (nullable) (closure hook_func): user data for @hook_func.
* @data_destroy: (nullable) (destroy hook_data): a #GDestroyNotify for @hook_data.
*
* Adds an emission hook for a signal, which will get called for any emission
* of that signal, independent of the instance. This is possible only
* for signals which don't have %G_SIGNAL_NO_HOOKS flag set.
*
* Returns: the hook id, for later use with g_signal_remove_emission_hook().
*/
gulong
g_signal_add_emission_hook (guint signal_id,
GQuark detail,
GSignalEmissionHook hook_func,
gpointer hook_data,
GDestroyNotify data_destroy)
{
static gulong seq_hook_id = 1;
SignalNode *node;
GHook *hook;
SignalHook *signal_hook;
g_return_val_if_fail (signal_id > 0, 0);
g_return_val_if_fail (hook_func != NULL, 0);
SIGNAL_LOCK ();
node = LOOKUP_SIGNAL_NODE (signal_id);
if (!node || node->destroyed)
{
g_critical ("%s: invalid signal id '%u'", G_STRLOC, signal_id);
SIGNAL_UNLOCK ();
return 0;
}
if (node->flags & G_SIGNAL_NO_HOOKS)
{
g_critical ("%s: signal id '%u' does not support emission hooks (G_SIGNAL_NO_HOOKS flag set)", G_STRLOC, signal_id);
SIGNAL_UNLOCK ();
return 0;
}
if (detail && !(node->flags & G_SIGNAL_DETAILED))
{
g_critical ("%s: signal id '%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
SIGNAL_UNLOCK ();
return 0;
}
node->single_va_closure_is_valid = FALSE;
if (!node->emission_hooks)
{
node->emission_hooks = g_new (GHookList, 1);
g_hook_list_init (node->emission_hooks, sizeof (SignalHook));
node->emission_hooks->finalize_hook = signal_finalize_hook;
}
node_check_deprecated (node);
hook = g_hook_alloc (node->emission_hooks);
hook->data = hook_data;
hook->func = (gpointer) hook_func;
hook->destroy = data_destroy;
signal_hook = SIGNAL_HOOK (hook);
signal_hook->detail = detail;
node->emission_hooks->seq_id = seq_hook_id;
g_hook_append (node->emission_hooks, hook);
seq_hook_id = node->emission_hooks->seq_id;
SIGNAL_UNLOCK ();
return hook->hook_id;
}
/**
* g_signal_remove_emission_hook:
* @signal_id: the id of the signal
* @hook_id: the id of the emission hook, as returned by
* g_signal_add_emission_hook()
*
* Deletes an emission hook.
*/
void
g_signal_remove_emission_hook (guint signal_id,
gulong hook_id)
{
SignalNode *node;
g_return_if_fail (signal_id > 0);
g_return_if_fail (hook_id > 0);
SIGNAL_LOCK ();
node = LOOKUP_SIGNAL_NODE (signal_id);
if (!node || node->destroyed)
{
g_critical ("%s: invalid signal id '%u'", G_STRLOC, signal_id);
goto out;
}
else if (!node->emission_hooks || !g_hook_destroy (node->emission_hooks, hook_id))
g_critical ("%s: signal \"%s\" had no hook (%lu) to remove", G_STRLOC, node->name, hook_id);
node->single_va_closure_is_valid = FALSE;
out:
SIGNAL_UNLOCK ();
}
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 (name, itype);
if (signal_id && detail_p)
*detail_p = 0;
}
else if (colon[1] == ':')
{
gchar buffer[32];
guint l = colon - name;
if (colon[2] == '\0')
return 0;
if (l < 32)
{
memcpy (buffer, name, l);
buffer[l] = 0;
signal_id = signal_id_lookup (buffer, itype);
}
else
{
gchar *signal = g_new (gchar, l + 1);
memcpy (signal, name, l);
signal[l] = 0;
signal_id = signal_id_lookup (signal, itype);
g_free (signal);
}
if (signal_id && detail_p)
*detail_p = (force_quark ? g_quark_from_string : g_quark_try_string) (colon + 2);
}
else
signal_id = 0;
return signal_id;
}
/**
* g_signal_parse_name:
* @detailed_signal: a string of the form "signal-name::detail".
* @itype: The interface/instance type that introduced "signal-name".
* @signal_id_p: (out): Location to store the signal id.
* @detail_p: (out): Location to store the detail quark.
* @force_detail_quark: %TRUE forces creation of a #GQuark for the detail.
*
* Internal function to parse a signal name into its @signal_id
* and @detail quark.
*
* Returns: Whether the signal name could successfully be parsed and @signal_id_p and @detail_p contain valid return values.
*/
gboolean
g_signal_parse_name (const gchar *detailed_signal,
GType itype,
guint *signal_id_p,
GQuark *detail_p,
gboolean force_detail_quark)
{
SignalNode *node;
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);
SIGNAL_LOCK ();
signal_id = signal_parse_name (detailed_signal, itype, &detail, force_detail_quark);
node = signal_id ? LOOKUP_SIGNAL_NODE (signal_id) : NULL;
if (!node || node->destroyed ||
(detail && !(node->flags & G_SIGNAL_DETAILED)))
{
SIGNAL_UNLOCK ();
return FALSE;
}
SIGNAL_UNLOCK ();
if (signal_id_p)
*signal_id_p = signal_id;
if (detail_p)
*detail_p = detail;
return TRUE;
}
/**
* g_signal_stop_emission_by_name:
* @instance: (type GObject.Object): the object whose signal handlers you wish to stop.
* @detailed_signal: a string of the form "signal-name::detail".
*
* Stops a signal's current emission.
*
* This is just like g_signal_stop_emission() except it will look up the
* signal id for you.
*/
void
g_signal_stop_emission_by_name (gpointer instance,
const gchar *detailed_signal)
{
guint signal_id;
GQuark detail = 0;
GType itype;
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
g_return_if_fail (detailed_signal != NULL);
SIGNAL_LOCK ();
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_critical ("%s: signal '%s' does not support details", G_STRLOC, detailed_signal);
else if (!g_type_is_a (itype, node->itype))
g_critical ("%s: signal '%s' is invalid for instance '%p' of type '%s'",
G_STRLOC, detailed_signal, instance, g_type_name (itype));
else
{
Emission *emission = emission_find (signal_id, detail, instance);
if (emission)
{
if (emission->state == EMISSION_HOOK)
g_critical (G_STRLOC ": emission of signal \"%s\" for instance '%p' cannot be stopped from emission hook",
node->name, instance);
else if (emission->state == EMISSION_RUN)
emission->state = EMISSION_STOP;
}
else
g_critical (G_STRLOC ": no emission of signal \"%s\" to stop for instance '%p'",
node->name, instance);
}
}
else
g_critical ("%s: signal '%s' is invalid for instance '%p' of type '%s'",
G_STRLOC, detailed_signal, instance, g_type_name (itype));
SIGNAL_UNLOCK ();
}
/**
* g_signal_lookup:
* @name: the signal's name.
* @itype: the type that the signal operates on.
*
* Given the name of the signal and the type of object it connects to, gets
* the signal's identifying integer. Emitting the signal by number is
* somewhat faster than using the name each time.
*
* Also tries the ancestors of the given type.
*
* The type class passed as @itype must already have been instantiated (for
* example, using g_type_class_ref()) for this function to work, as signals are
* always installed during class initialization.
*
* See g_signal_new() for details on allowed signal names.
*
* Returns: the signal's identifying number, or 0 if no signal was found.
*/
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);
SIGNAL_LOCK ();
signal_id = signal_id_lookup (name, itype);
SIGNAL_UNLOCK ();
if (!signal_id)
{
/* give elaborate warnings */
if (!g_type_name (itype))
g_critical (G_STRLOC ": unable to look up signal \"%s\" for invalid type id '%"G_GUINTPTR_FORMAT"'",
name, (guintptr) itype);
else if (!g_signal_is_valid_name (name))
g_critical (G_STRLOC ": unable to look up invalid signal name \"%s\" on type '%s'",
name, g_type_name (itype));
}
return signal_id;
}
/**
* g_signal_list_ids:
* @itype: Instance or interface type.
* @n_ids: Location to store the number of signal ids for @itype.
*
* Lists the signals by id that a certain instance or interface type
* created. Further information about the signals can be acquired through
* g_signal_query().
*
* Returns: (array length=n_ids) (transfer full): Newly allocated array of signal IDs.
*/
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);
SIGNAL_LOCK ();
keys = g_bsearch_array_get_nth (g_signal_key_bsa, &g_signal_key_bconfig, 0);
n_nodes = g_bsearch_array_get_n_nodes (g_signal_key_bsa);
result = g_array_new (FALSE, FALSE, sizeof (guint));
for (i = 0; i < n_nodes; i++)
if (keys[i].itype == itype)
{
g_array_append_val (result, keys[i].signal_id);
}
*n_ids = result->len;
SIGNAL_UNLOCK ();
if (!n_nodes)
{
/* give elaborate warnings */
if (!g_type_name (itype))
g_critical (G_STRLOC ": unable to list signals for invalid type id '%"G_GUINTPTR_FORMAT"'",
(guintptr) itype);
else if (!G_TYPE_IS_INSTANTIATABLE (itype) && !G_TYPE_IS_INTERFACE (itype))
g_critical (G_STRLOC ": unable to list signals of non instantiatable type '%s'",
g_type_name (itype));
else if (!g_type_class_peek (itype) && !G_TYPE_IS_INTERFACE (itype))
g_critical (G_STRLOC ": unable to list signals of unloaded type '%s'",
g_type_name (itype));
}
return (guint*) g_array_free (result, FALSE);
}
/**
* g_signal_name:
* @signal_id: the signal's identifying number.
*
* Given the signal's identifier, finds its name.
*
* Two different signals may have the same name, if they have differing types.
*
* Returns: (nullable): the signal name, or %NULL if the signal number was invalid.
*/
const gchar *
g_signal_name (guint signal_id)
{
SignalNode *node;
const gchar *name;
SIGNAL_LOCK ();
node = LOOKUP_SIGNAL_NODE (signal_id);
name = node ? node->name : NULL;
SIGNAL_UNLOCK ();
return (char*) name;
}
/**
* g_signal_query:
* @signal_id: The signal id of the signal to query information for.
* @query: (out caller-allocates) (not optional): A user provided structure that is
* filled in with constant values upon success.
*
* Queries the signal system for in-depth information about a
* specific signal. This function will fill in a user-provided
* structure to hold signal-specific information. If an invalid
* signal id is passed in, the @signal_id member of the #GSignalQuery
* is 0. All members filled into the #GSignalQuery structure should
* be considered constant and have to be left untouched.
*/
void
g_signal_query (guint signal_id,
GSignalQuery *query)
{
SignalNode *node;
g_return_if_fail (query != NULL);
SIGNAL_LOCK ();
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;
}
SIGNAL_UNLOCK ();
}
/**
* g_signal_new:
* @signal_name: the name for the signal
* @itype: the type this signal pertains to. It will also pertain to
* types which are derived from this type.
* @signal_flags: a combination of #GSignalFlags specifying detail of when
* the default handler is to be invoked. You should at least specify
* %G_SIGNAL_RUN_FIRST or %G_SIGNAL_RUN_LAST.
* @class_offset: The offset of the function pointer in the class structure
* for this type. Used to invoke a class method generically. Pass 0 to
* not associate a class method slot with this signal.
* @accumulator: (nullable) (scope forever): the accumulator for this signal; may be %NULL.
* @accu_data: (nullable) (closure accumulator): user data for the @accumulator.
* @c_marshaller: (nullable): the function to translate arrays of parameter
* values to signal emissions into C language callback invocations or %NULL.
* @return_type: the type of return value, or %G_TYPE_NONE for a signal
* without a return value.
* @n_params: the number of parameter types to follow.
* @...: a list of types, one for each parameter.
*
* Creates a new signal. (This is usually done in the class initializer.)
*
* A signal name consists of segments consisting of ASCII letters and
* digits, separated by either the `-` or `_` character. The first
* character of a signal name must be a letter. Names which violate these
* rules lead to undefined behaviour. These are the same rules as for property
* naming (see g_param_spec_internal()).
*
* When registering a signal and looking up a signal, either separator can
* be used, but they cannot be mixed. Using `-` is considerably more efficient.
* Using `_` is discouraged.
*
* If 0 is used for @class_offset subclasses cannot override the class handler
* in their class_init method by doing super_class->signal_handler = my_signal_handler.
* Instead they will have to use g_signal_override_class_handler().
*
* If @c_marshaller is %NULL, g_cclosure_marshal_generic() will be used as
* the marshaller for this signal. In some simple cases, g_signal_new()
* will use a more optimized c_marshaller and va_marshaller for the signal
* instead of g_cclosure_marshal_generic().
*
* If @c_marshaller is non-%NULL, you need to also specify a va_marshaller
* using g_signal_set_va_marshaller() or the generic va_marshaller will
* be used.
*
* Returns: the signal id
*/
guint
g_signal_new (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
guint class_offset,
GSignalAccumulator accumulator,
gpointer accu_data,
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,
class_offset ? g_signal_type_cclosure_new (itype, class_offset) : NULL,
accumulator, accu_data, c_marshaller,
return_type, n_params, args);
va_end (args);
return signal_id;
}
/**
* g_signal_new_class_handler:
* @signal_name: the name for the signal
* @itype: the type this signal pertains to. It will also pertain to
* types which are derived from this type.
* @signal_flags: a combination of #GSignalFlags specifying detail of when
* the default handler is to be invoked. You should at least specify
* %G_SIGNAL_RUN_FIRST or %G_SIGNAL_RUN_LAST.
* @class_handler: (nullable) (scope forever): a #GCallback which acts as class implementation of
* this signal. Used to invoke a class method generically. Pass %NULL to
* not associate a class method with this signal.
* @accumulator: (nullable) (scope forever): the accumulator for this signal; may be %NULL.
* @accu_data: (nullable) (closure accumulator): user data for the @accumulator.
* @c_marshaller: (nullable): the function to translate arrays of parameter
* values to signal emissions into C language callback invocations or %NULL.
* @return_type: the type of return value, or %G_TYPE_NONE for a signal
* without a return value.
* @n_params: the number of parameter types to follow.
* @...: a list of types, one for each parameter.
*
* Creates a new signal. (This is usually done in the class initializer.)
*
* This is a variant of g_signal_new() that takes a C callback instead
* of a class offset for the signal's class handler. This function
* doesn't need a function pointer exposed in the class structure of
* an object definition, instead the function pointer is passed
* directly and can be overridden by derived classes with
* g_signal_override_class_closure() or
* g_signal_override_class_handler() and chained to with
* g_signal_chain_from_overridden() or
* g_signal_chain_from_overridden_handler().
*
* See g_signal_new() for information about signal names.
*
* If c_marshaller is %NULL, g_cclosure_marshal_generic() will be used as
* the marshaller for this signal.
*
* Returns: the signal id
*
* Since: 2.18
*/
guint
g_signal_new_class_handler (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
GCallback class_handler,
GSignalAccumulator accumulator,
gpointer accu_data,
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,
class_handler ? g_cclosure_new (class_handler, NULL, NULL) : NULL,
accumulator, accu_data, c_marshaller,
return_type, n_params, args);
va_end (args);
return signal_id;
}
static inline ClassClosure*
signal_find_class_closure (SignalNode *node,
GType itype)
{
GBSearchArray *bsa = node->class_closure_bsa;
ClassClosure *cc;
if (bsa)
{
ClassClosure key;
/* cc->instance_type is 0 for default closure */
if (g_bsearch_array_get_n_nodes (bsa) == 1)
{
cc = g_bsearch_array_get_nth (bsa, &g_class_closure_bconfig, 0);
if (cc && cc->instance_type == 0) /* check for default closure */
return cc;
}
key.instance_type = itype;
cc = g_bsearch_array_lookup (bsa, &g_class_closure_bconfig, &key);
while (!cc && key.instance_type)
{
key.instance_type = g_type_parent (key.instance_type);
cc = g_bsearch_array_lookup (bsa, &g_class_closure_bconfig, &key);
}
}
else
cc = NULL;
return cc;
}
static inline GClosure*
signal_lookup_closure (SignalNode *node,
GTypeInstance *instance)
{
ClassClosure *cc;
cc = signal_find_class_closure (node, G_TYPE_FROM_INSTANCE (instance));
return cc ? cc->closure : NULL;
}
static void
signal_add_class_closure (SignalNode *node,
GType itype,
GClosure *closure)
{
ClassClosure key;
node->single_va_closure_is_valid = FALSE;
if (!node->class_closure_bsa)
node->class_closure_bsa = g_bsearch_array_create (&g_class_closure_bconfig);
key.instance_type = itype;
key.closure = g_closure_ref (closure);
node->class_closure_bsa = g_bsearch_array_insert (node->class_closure_bsa,
&g_class_closure_bconfig,
&key);
g_closure_sink (closure);
if (node->c_marshaller && closure && G_CLOSURE_NEEDS_MARSHAL (closure))
{
g_closure_set_marshal (closure, node->c_marshaller);
if (node->va_marshaller)
_g_closure_set_va_marshal (closure, node->va_marshaller);
}
}
/**
* g_signal_newv:
* @signal_name: the name for the signal
* @itype: the type this signal pertains to. It will also pertain to
* types which are derived from this type
* @signal_flags: a combination of #GSignalFlags specifying detail of when
* the default handler is to be invoked. You should at least specify
* %G_SIGNAL_RUN_FIRST or %G_SIGNAL_RUN_LAST
* @class_closure: (nullable): The closure to invoke on signal emission;
* may be %NULL
* @accumulator: (nullable) (scope forever): the accumulator for this signal; may be %NULL
* @accu_data: (nullable) (closure accumulator): user data for the @accumulator
* @c_marshaller: (nullable): the function to translate arrays of
* parameter values to signal emissions into C language callback
* invocations or %NULL
* @return_type: the type of return value, or %G_TYPE_NONE for a signal
* without a return value
* @n_params: the length of @param_types
* @param_types: (array length=n_params) (nullable): an array of types, one for
* each parameter (may be %NULL if @n_params is zero)
*
* Creates a new signal. (This is usually done in the class initializer.)
*
* See g_signal_new() for details on allowed signal names.
*
* If c_marshaller is %NULL, g_cclosure_marshal_generic() will be used as
* the marshaller for this signal.
*
* Returns: the signal id
*/
guint
g_signal_newv (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
GClosure *class_closure,
GSignalAccumulator accumulator,
gpointer accu_data,
GSignalCMarshaller c_marshaller,
GType return_type,
guint n_params,
GType *param_types)
{
const gchar *name;
gchar *signal_name_copy = NULL;
guint signal_id, i;
SignalNode *node;
GSignalCMarshaller builtin_c_marshaller;
GSignalCVaMarshaller builtin_va_marshaller;
GSignalCVaMarshaller va_marshaller;
g_return_val_if_fail (signal_name != NULL, 0);
g_return_val_if_fail (g_signal_is_valid_name (signal_name), 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);
g_return_val_if_fail ((return_type & G_SIGNAL_TYPE_STATIC_SCOPE) == 0, 0);
if (return_type == (G_TYPE_NONE & ~G_SIGNAL_TYPE_STATIC_SCOPE))
g_return_val_if_fail (accumulator == NULL, 0);
if (!accumulator)
g_return_val_if_fail (accu_data == NULL, 0);
g_return_val_if_fail ((signal_flags & G_SIGNAL_ACCUMULATOR_FIRST_RUN) == 0, 0);
if (!is_canonical (signal_name))
{
signal_name_copy = g_strdup (signal_name);
canonicalize_key (signal_name_copy);
name = signal_name_copy;
}
else
{
name = signal_name;
}
SIGNAL_LOCK ();
signal_id = signal_id_lookup (name, itype);
node = LOOKUP_SIGNAL_NODE (signal_id);
if (node && !node->destroyed)
{
g_critical (G_STRLOC ": signal \"%s\" already exists in the '%s' %s",
name,
type_debug_name (node->itype),
G_TYPE_IS_INTERFACE (node->itype) ? "interface" : "class ancestry");
g_free (signal_name_copy);
SIGNAL_UNLOCK ();
return 0;
}
if (node && node->itype != itype)
{
g_critical (G_STRLOC ": signal \"%s\" for type '%s' was previously created for type '%s'",
name,
type_debug_name (itype),
type_debug_name (node->itype));
g_free (signal_name_copy);
SIGNAL_UNLOCK ();
return 0;
}
for (i = 0; i < n_params; i++)
if (!G_TYPE_IS_VALUE (param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE))
{
g_critical (G_STRLOC ": parameter %d of type '%s' for signal \"%s::%s\" is not a value type",
i + 1, type_debug_name (param_types[i]), type_debug_name (itype), name);
g_free (signal_name_copy);
SIGNAL_UNLOCK ();
return 0;
}
if (return_type != G_TYPE_NONE && !G_TYPE_IS_VALUE (return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE))
{
g_critical (G_STRLOC ": return value of type '%s' for signal \"%s::%s\" is not a value type",
type_debug_name (return_type), type_debug_name (itype), name);
g_free (signal_name_copy);
SIGNAL_UNLOCK ();
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;
key.itype = itype;
key.signal_id = signal_id;
node->name = g_intern_string (name);
key.quark = g_quark_from_string (name);
g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key);
TRACE(GOBJECT_SIGNAL_NEW(signal_id, name, itype));
}
node->destroyed = FALSE;
/* setup reinitializable portion */
node->single_va_closure_is_valid = FALSE;
node->flags = signal_flags & G_SIGNAL_FLAGS_MASK;
node->n_params = n_params;
node->param_types = g_memdup2 (param_types, sizeof (GType) * n_params);
node->return_type = return_type;
node->class_closure_bsa = NULL;
if (accumulator)
{
node->accumulator = g_new (SignalAccumulator, 1);
node->accumulator->func = accumulator;
node->accumulator->data = accu_data;
}
else
node->accumulator = NULL;
builtin_c_marshaller = NULL;
builtin_va_marshaller = NULL;
/* Pick up built-in va marshallers for standard types, and
instead of generic marshaller if no marshaller specified */
if (n_params == 0 && return_type == G_TYPE_NONE)
{
builtin_c_marshaller = g_cclosure_marshal_VOID__VOID;
builtin_va_marshaller = g_cclosure_marshal_VOID__VOIDv;
}
else if (n_params == 1 && return_type == G_TYPE_NONE)
{
#define ADD_CHECK(__type__) \
else if (g_type_is_a (param_types[0] & ~G_SIGNAL_TYPE_STATIC_SCOPE, G_TYPE_ ##__type__)) \
{ \
builtin_c_marshaller = g_cclosure_marshal_VOID__ ## __type__; \
builtin_va_marshaller = g_cclosure_marshal_VOID__ ## __type__ ##v; \
}
if (0) {}
ADD_CHECK (BOOLEAN)
ADD_CHECK (CHAR)
ADD_CHECK (UCHAR)
ADD_CHECK (INT)
ADD_CHECK (UINT)
ADD_CHECK (LONG)
ADD_CHECK (ULONG)
ADD_CHECK (ENUM)
ADD_CHECK (FLAGS)
ADD_CHECK (FLOAT)
ADD_CHECK (DOUBLE)
ADD_CHECK (STRING)
ADD_CHECK (PARAM)
ADD_CHECK (BOXED)
ADD_CHECK (POINTER)
ADD_CHECK (OBJECT)
ADD_CHECK (VARIANT)
}
if (c_marshaller == NULL)
{
if (builtin_c_marshaller)
{
c_marshaller = builtin_c_marshaller;
va_marshaller = builtin_va_marshaller;
}
else
{
c_marshaller = g_cclosure_marshal_generic;
va_marshaller = g_cclosure_marshal_generic_va;
}
}
else
va_marshaller = NULL;
node->c_marshaller = c_marshaller;
node->va_marshaller = va_marshaller;
node->emission_hooks = NULL;
if (class_closure)
signal_add_class_closure (node, 0, class_closure);
SIGNAL_UNLOCK ();
g_free (signal_name_copy);
return signal_id;
}
/**
* g_signal_set_va_marshaller:
* @signal_id: the signal id
* @instance_type: the instance type on which to set the marshaller.
* @va_marshaller: the marshaller to set.
*
* Change the #GSignalCVaMarshaller used for a given signal. This is a
* specialised form of the marshaller that can often be used for the
* common case of a single connected signal handler and avoids the
* overhead of #GValue. Its use is optional.
*
* Since: 2.32
*/
void
g_signal_set_va_marshaller (guint signal_id,
GType instance_type,
GSignalCVaMarshaller va_marshaller)
{
SignalNode *node;
g_return_if_fail (signal_id > 0);
g_return_if_fail (va_marshaller != NULL);
SIGNAL_LOCK ();
node = LOOKUP_SIGNAL_NODE (signal_id);
if (node)
{
node->va_marshaller = va_marshaller;
if (node->class_closure_bsa)
{
ClassClosure *cc = g_bsearch_array_get_nth (node->class_closure_bsa, &g_class_closure_bconfig, 0);
if (cc->closure->marshal == node->c_marshaller)
_g_closure_set_va_marshal (cc->closure, va_marshaller);
}
node->single_va_closure_is_valid = FALSE;
}
SIGNAL_UNLOCK ();
}
/**
* g_signal_new_valist:
* @signal_name: the name for the signal
* @itype: the type this signal pertains to. It will also pertain to
* types which are derived from this type.
* @signal_flags: a combination of #GSignalFlags specifying detail of when
* the default handler is to be invoked. You should at least specify
* %G_SIGNAL_RUN_FIRST or %G_SIGNAL_RUN_LAST.
* @class_closure: (nullable): The closure to invoke on signal emission; may be %NULL.
* @accumulator: (nullable) (scope forever): the accumulator for this signal; may be %NULL.
* @accu_data: (nullable) (closure accumulator): user data for the @accumulator.
* @c_marshaller: (nullable): the function to translate arrays of parameter
* values to signal emissions into C language callback invocations or %NULL.
* @return_type: the type of return value, or %G_TYPE_NONE for a signal
* without a return value.
* @n_params: the number of parameter types in @args.
* @args: va_list of #GType, one for each parameter.
*
* Creates a new signal. (This is usually done in the class initializer.)
*
* See g_signal_new() for details on allowed signal names.
*
* If c_marshaller is %NULL, g_cclosure_marshal_generic() will be used as
* the marshaller for this signal.
*
* Returns: the signal id
*/
guint
g_signal_new_valist (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
GClosure *class_closure,
GSignalAccumulator accumulator,
gpointer accu_data,
GSignalCMarshaller c_marshaller,
GType return_type,
guint n_params,
va_list args)
{
/* Somewhat arbitrarily reserve 200 bytes. That should cover the majority
* of cases where n_params is small and still be small enough for what we
* want to put on the stack. */
GType param_types_stack[200 / sizeof (GType)];
GType *param_types_heap = NULL;
GType *param_types;
guint i;
guint signal_id;
param_types = param_types_stack;
if (n_params > 0)
{
if (G_UNLIKELY (n_params > G_N_ELEMENTS (param_types_stack)))
{
param_types_heap = g_new (GType, n_params);
param_types = param_types_heap;
}
for (i = 0; i < n_params; i++)
param_types[i] = va_arg (args, GType);
}
signal_id = g_signal_newv (signal_name, itype, signal_flags,
class_closure, accumulator, accu_data, c_marshaller,
return_type, n_params, param_types);
g_free (param_types_heap);
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->single_va_closure_is_valid = FALSE;
signal_node->n_params = 0;
signal_node->param_types = NULL;
signal_node->return_type = 0;
signal_node->class_closure_bsa = NULL;
signal_node->accumulator = NULL;
signal_node->c_marshaller = NULL;
signal_node->va_marshaller = NULL;
signal_node->emission_hooks = NULL;
#ifdef G_ENABLE_DEBUG
/* check current emissions */
{
Emission *emission;
for (emission = g_emissions; emission; emission = emission->next)
if (emission->ihint.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
*/
SIGNAL_UNLOCK ();
g_free (node.param_types);
if (node.class_closure_bsa)
{
guint i;
for (i = 0; i < node.class_closure_bsa->n_nodes; i++)
{
ClassClosure *cc = g_bsearch_array_get_nth (node.class_closure_bsa, &g_class_closure_bconfig, i);
g_closure_unref (cc->closure);
}
g_bsearch_array_free (node.class_closure_bsa, &g_class_closure_bconfig);
}
g_free (node.accumulator);
if (node.emission_hooks)
{
g_hook_list_clear (node.emission_hooks);
g_free (node.emission_hooks);
}
SIGNAL_LOCK ();
}
/**
* g_signal_override_class_closure:
* @signal_id: the signal id
* @instance_type: the instance type on which to override the class closure
* for the signal.
* @class_closure: the closure.
*
* Overrides the class closure (i.e. the default handler) for the given signal
* for emissions on instances of @instance_type. @instance_type must be derived
* from the type to which the signal belongs.
*
* See g_signal_chain_from_overridden() and
* g_signal_chain_from_overridden_handler() for how to chain up to the
* parent class closure from inside the overridden one.
*/
void
g_signal_override_class_closure (guint signal_id,
GType instance_type,
GClosure *class_closure)
{
SignalNode *node;
g_return_if_fail (signal_id > 0);
g_return_if_fail (class_closure != NULL);
SIGNAL_LOCK ();
node = LOOKUP_SIGNAL_NODE (signal_id);
node_check_deprecated (node);
if (!g_type_is_a (instance_type, node->itype))
g_critical ("%s: type '%s' cannot be overridden for signal id '%u'", G_STRLOC, type_debug_name (instance_type), signal_id);
else
{
ClassClosure *cc = signal_find_class_closure (node, instance_type);
if (cc && cc->instance_type == instance_type)
g_critical ("%s: type '%s' is already overridden for signal id '%u'", G_STRLOC, type_debug_name (instance_type), signal_id);
else
signal_add_class_closure (node, instance_type, class_closure);
}
SIGNAL_UNLOCK ();
}
/**
* g_signal_override_class_handler:
* @signal_name: the name for the signal
* @instance_type: the instance type on which to override the class handler
* for the signal.
* @class_handler: (scope forever): the handler.
*
* Overrides the class closure (i.e. the default handler) for the
* given signal for emissions on instances of @instance_type with
* callback @class_handler. @instance_type must be derived from the
* type to which the signal belongs.
*
* See g_signal_chain_from_overridden() and
* g_signal_chain_from_overridden_handler() for how to chain up to the
* parent class closure from inside the overridden one.
*
* Since: 2.18
*/
void
g_signal_override_class_handler (const gchar *signal_name,
GType instance_type,
GCallback class_handler)
{
guint signal_id;
g_return_if_fail (signal_name != NULL);
g_return_if_fail (instance_type != G_TYPE_NONE);
g_return_if_fail (class_handler != NULL);
signal_id = g_signal_lookup (signal_name, instance_type);
if (signal_id)
g_signal_override_class_closure (signal_id, instance_type,
g_cclosure_new (class_handler, NULL, NULL));
else
g_critical ("%s: signal name '%s' is invalid for type id '%"G_GUINTPTR_FORMAT"'",
G_STRLOC, signal_name, (guintptr) instance_type);
}
/**
* g_signal_chain_from_overridden:
* @instance_and_params: (array): the argument list of the signal emission.
* The first element in the array is a #GValue for the instance the signal
* is being emitted on. The rest are any arguments to be passed to the signal.
* @return_value: Location for the return value.
*
* Calls the original class closure of a signal. This function should only
* be called from an overridden class closure; see
* g_signal_override_class_closure() and
* g_signal_override_class_handler().
*/
void
g_signal_chain_from_overridden (const GValue *instance_and_params,
GValue *return_value)
{
GType chain_type = 0, restore_type = 0;
Emission *emission = NULL;
GClosure *closure = NULL;
guint n_params = 0;
gpointer instance;
g_return_if_fail (instance_and_params != NULL);
instance = g_value_peek_pointer (instance_and_params);
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
SIGNAL_LOCK ();
emission = emission_find_innermost (instance);
if (emission)
{
SignalNode *node = LOOKUP_SIGNAL_NODE (emission->ihint.signal_id);
g_assert (node != NULL); /* paranoid */
/* we should probably do the same parameter checks as g_signal_emit() here.
*/
if (emission->chain_type != G_TYPE_NONE)
{
ClassClosure *cc = signal_find_class_closure (node, emission->chain_type);
g_assert (cc != NULL); /* closure currently in call stack */
n_params = node->n_params;
restore_type = cc->instance_type;
cc = signal_find_class_closure (node, g_type_parent (cc->instance_type));
if (cc && cc->instance_type != restore_type)
{
closure = cc->closure;
chain_type = cc->instance_type;
}
}
else
g_critical ("%s: signal id '%u' cannot be chained from current emission stage for instance '%p'", G_STRLOC, node->signal_id, instance);
}
else
g_critical ("%s: no signal is currently being emitted for instance '%p'", G_STRLOC, instance);
if (closure)
{
emission->chain_type = chain_type;
SIGNAL_UNLOCK ();
g_closure_invoke (closure,
return_value,
n_params + 1,
instance_and_params,
&emission->ihint);
SIGNAL_LOCK ();
emission->chain_type = restore_type;
}
SIGNAL_UNLOCK ();
}
/**
* g_signal_chain_from_overridden_handler: (skip)
* @instance: (type GObject.TypeInstance): the instance the signal is being
* emitted on.
* @...: parameters to be passed to the parent class closure, followed by a
* location for the return value. If the return type of the signal
* is %G_TYPE_NONE, the return value location can be omitted.
*
* Calls the original class closure of a signal. This function should
* only be called from an overridden class closure; see
* g_signal_override_class_closure() and
* g_signal_override_class_handler().
*
* Since: 2.18
*/
void
g_signal_chain_from_overridden_handler (gpointer instance,
...)
{
GType chain_type = 0, restore_type = 0;
Emission *emission = NULL;
GClosure *closure = NULL;
SignalNode *node = NULL;
guint n_params = 0;
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
SIGNAL_LOCK ();
emission = emission_find_innermost (instance);
if (emission)
{
node = LOOKUP_SIGNAL_NODE (emission->ihint.signal_id);
g_assert (node != NULL); /* paranoid */
/* we should probably do the same parameter checks as g_signal_emit() here.
*/
if (emission->chain_type != G_TYPE_NONE)
{
ClassClosure *cc = signal_find_class_closure (node, emission->chain_type);
g_assert (cc != NULL); /* closure currently in call stack */
n_params = node->n_params;
restore_type = cc->instance_type;
cc = signal_find_class_closure (node, g_type_parent (cc->instance_type));
if (cc && cc->instance_type != restore_type)
{
closure = cc->closure;
chain_type = cc->instance_type;
}
}
else
g_critical ("%s: signal id '%u' cannot be chained from current emission stage for instance '%p'", G_STRLOC, node->signal_id, instance);
}
else
g_critical ("%s: no signal is currently being emitted for instance '%p'", G_STRLOC, instance);
if (closure)
{
GValue *instance_and_params;
GType signal_return_type;
GValue *param_values;
va_list var_args;
guint i;
va_start (var_args, instance);
signal_return_type = node->return_type;
instance_and_params = g_newa0 (GValue, n_params + 1);
param_values = instance_and_params + 1;
for (i = 0; i < node->n_params; i++)
{
gchar *error;
GType ptype = node->param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE;
gboolean static_scope = node->param_types[i] & G_SIGNAL_TYPE_STATIC_SCOPE;
SIGNAL_UNLOCK ();
G_VALUE_COLLECT_INIT (param_values + i, ptype,
var_args,
static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,
&error);
if (error)
{
g_critical ("%s: %s", G_STRLOC, error);
g_free (error);
/* we purposely leak the value here, it might not be
* in a correct state if an error condition occurred
*/
while (i--)
g_value_unset (param_values + i);
va_end (var_args);
return;
}
SIGNAL_LOCK ();
}
SIGNAL_UNLOCK ();
g_value_init_from_instance (instance_and_params, instance);
SIGNAL_LOCK ();
emission->chain_type = chain_type;
SIGNAL_UNLOCK ();
if (signal_return_type == G_TYPE_NONE)
{
g_closure_invoke (closure,
NULL,
n_params + 1,
instance_and_params,
&emission->ihint);
}
else
{
GValue return_value = G_VALUE_INIT;
gchar *error = NULL;
GType rtype = signal_return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE;
gboolean static_scope = signal_return_type & G_SIGNAL_TYPE_STATIC_SCOPE;
g_value_init (&return_value, rtype);
g_closure_invoke (closure,
&return_value,
n_params + 1,
instance_and_params,
&emission->ihint);
G_VALUE_LCOPY (&return_value,
var_args,
static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,
&error);
if (!error)
{
g_value_unset (&return_value);
}
else
{
g_critical ("%s: %s", G_STRLOC, error);
g_free (error);
/* we purposely leak the value here, it might not be
* in a correct state if an error condition occurred
*/
}
}
for (i = 0; i < n_params; i++)
g_value_unset (param_values + i);
g_value_unset (instance_and_params);
va_end (var_args);
SIGNAL_LOCK ();
emission->chain_type = restore_type;
}
SIGNAL_UNLOCK ();
}
/**
* g_signal_get_invocation_hint:
* @instance: (type GObject.Object): the instance to query
*
* Returns the invocation hint of the innermost signal emission of instance.
*
* Returns: (transfer none) (nullable): the invocation hint of the innermost
* signal emission, or %NULL if not found.
*/
GSignalInvocationHint*
g_signal_get_invocation_hint (gpointer instance)
{
Emission *emission = NULL;
g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), NULL);
SIGNAL_LOCK ();
emission = emission_find_innermost (instance);
SIGNAL_UNLOCK ();
return emission ? &emission->ihint : NULL;
}
/**
* g_signal_connect_closure_by_id:
* @instance: (type GObject.Object): the instance to connect to.
* @signal_id: the id of the signal.
* @detail: the detail.
* @closure: (not nullable): the closure to connect.
* @after: whether the handler should be called before or after the
* default handler of the signal.
*
* Connects a closure to a signal for a particular object.
*
* If @closure is a floating reference (see g_closure_sink()), this function
* takes ownership of @closure.
*
* Returns: the handler ID (always greater than 0 for successful connections)
*/
gulong
g_signal_connect_closure_by_id (gpointer instance,
guint signal_id,
GQuark detail,
GClosure *closure,
gboolean after)
{
SignalNode *node;
gulong handler_seq_no = 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);
SIGNAL_LOCK ();
node = LOOKUP_SIGNAL_NODE (signal_id);
if (node)
{
if (detail && !(node->flags & G_SIGNAL_DETAILED))
g_critical ("%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_critical ("%s: signal id '%u' is invalid for instance '%p'", G_STRLOC, signal_id, instance);
else
{
Handler *handler = handler_new (signal_id, instance, after);
if (G_TYPE_IS_OBJECT (node->itype))
_g_object_set_has_signal_handler ((GObject *) instance, signal_id);
handler_seq_no = handler->sequential_number;
handler->detail = detail;
handler->closure = g_closure_ref (closure);
g_closure_sink (closure);
add_invalid_closure_notify (handler, instance);
handler_insert (signal_id, instance, handler);
if (node->c_marshaller && G_CLOSURE_NEEDS_MARSHAL (closure))
{
g_closure_set_marshal (closure, node->c_marshaller);
if (node->va_marshaller)
_g_closure_set_va_marshal (closure, node->va_marshaller);
}
}
}
else
g_critical ("%s: signal id '%u' is invalid for instance '%p'", G_STRLOC, signal_id, instance);
SIGNAL_UNLOCK ();
return handler_seq_no;
}
/**
* g_signal_connect_closure:
* @instance: (type GObject.Object): the instance to connect to.
* @detailed_signal: a string of the form "signal-name::detail".
* @closure: (not nullable): the closure to connect.
* @after: whether the handler should be called before or after the
* default handler of the signal.
*
* Connects a closure to a signal for a particular object.
*
* If @closure is a floating reference (see g_closure_sink()), this function
* takes ownership of @closure.
*
* Returns: the handler ID (always greater than 0 for successful connections)
*/
gulong
g_signal_connect_closure (gpointer instance,
const gchar *detailed_signal,
GClosure *closure,
gboolean after)
{
guint signal_id;
gulong handler_seq_no = 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);
SIGNAL_LOCK ();
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_critical ("%s: signal '%s' does not support details", G_STRLOC, detailed_signal);
else if (!g_type_is_a (itype, node->itype))
g_critical ("%s: signal '%s' is invalid for instance '%p' of type '%s'",
G_STRLOC, detailed_signal, instance, g_type_name (itype));
else
{
Handler *handler = handler_new (signal_id, instance, after);
if (G_TYPE_IS_OBJECT (node->itype))
_g_object_set_has_signal_handler ((GObject *) instance, signal_id);
handler_seq_no = handler->sequential_number;
handler->detail = detail;
handler->closure = g_closure_ref (closure);
g_closure_sink (closure);
add_invalid_closure_notify (handler, instance);
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);
if (node->va_marshaller)
_g_closure_set_va_marshal (handler->closure, node->va_marshaller);
}
}
}
else
g_critical ("%s: signal '%s' is invalid for instance '%p' of type '%s'",
G_STRLOC, detailed_signal, instance, g_type_name (itype));
SIGNAL_UNLOCK ();
return handler_seq_no;
}
static void
node_check_deprecated (const SignalNode *node)
{
static const gchar * g_enable_diagnostic = NULL;
if (G_UNLIKELY (!g_enable_diagnostic))
{
g_enable_diagnostic = g_getenv ("G_ENABLE_DIAGNOSTIC");
if (!g_enable_diagnostic)
g_enable_diagnostic = "0";
}
if (g_enable_diagnostic[0] == '1')
{
if (node->flags & G_SIGNAL_DEPRECATED)
{
g_warning ("The signal %s::%s is deprecated and shouldn't be used "
"anymore. It will be removed in a future version.",
type_debug_name (node->itype), node->name);
}
}
}
/**
* g_signal_connect_data:
* @instance: (type GObject.Object): the instance to connect to.
* @detailed_signal: a string of the form "signal-name::detail".
* @c_handler: (not nullable): the #GCallback to connect.
* @data: (nullable) (closure c_handler): data to pass to @c_handler calls.
* @destroy_data: (nullable) (destroy data): a #GClosureNotify for @data.
* @connect_flags: a combination of #GConnectFlags.
*
* Connects a #GCallback function to a signal for a particular object. Similar
* to g_signal_connect(), but allows to provide a #GClosureNotify for the data
* which will be called when the signal handler is disconnected and no longer
* used. Specify @connect_flags if you need `..._after()` or
* `..._swapped()` variants of this function.
*
* Returns: the handler ID (always greater than 0 for successful connections)
*/
gulong
g_signal_connect_data (gpointer instance,
const gchar *detailed_signal,
GCallback c_handler,
gpointer data,
GClosureNotify destroy_data,
GConnectFlags connect_flags)
{
guint signal_id;
gulong handler_seq_no = 0;
GQuark detail = 0;
GType itype;
gboolean swapped, after;
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);
swapped = (connect_flags & G_CONNECT_SWAPPED) != FALSE;
after = (connect_flags & G_CONNECT_AFTER) != FALSE;
SIGNAL_LOCK ();
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);
node_check_deprecated (node);
if (detail && !(node->flags & G_SIGNAL_DETAILED))
g_critical ("%s: signal '%s' does not support details", G_STRLOC, detailed_signal);
else if (!g_type_is_a (itype, node->itype))
g_critical ("%s: signal '%s' is invalid for instance '%p' of type '%s'",
G_STRLOC, detailed_signal, instance, g_type_name (itype));
else
{
Handler *handler = handler_new (signal_id, instance, after);
if (G_TYPE_IS_OBJECT (node->itype))
_g_object_set_has_signal_handler ((GObject *) instance, signal_id);
handler_seq_no = handler->sequential_number;
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);
if (node->va_marshaller)
_g_closure_set_va_marshal (handler->closure, node->va_marshaller);
}
}
}
else
g_critical ("%s: signal '%s' is invalid for instance '%p' of type '%s'",
G_STRLOC, detailed_signal, instance, g_type_name (itype));
SIGNAL_UNLOCK ();
return handler_seq_no;
}
static void
signal_handler_block_unlocked (gpointer instance,
gulong handler_id);
/**
* g_signal_handler_block:
* @instance: (type GObject.Object): The instance to block the signal handler of.
* @handler_id: Handler id of the handler to be blocked.
*
* Blocks a handler of an instance so it will not be called during any
* signal emissions unless it is unblocked again. Thus "blocking" a
* signal handler means to temporarily deactivate it, a signal handler
* has to be unblocked exactly the same amount of times it has been
* blocked before to become active again.
*
* The @handler_id has to be a valid signal handler id, connected to a
* signal of @instance.
*/
void
g_signal_handler_block (gpointer instance,
gulong handler_id)
{
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
g_return_if_fail (handler_id > 0);
SIGNAL_LOCK ();
signal_handler_block_unlocked (instance, handler_id);
SIGNAL_UNLOCK ();
}
static void
signal_handler_block_unlocked (gpointer instance,
gulong handler_id)
{
Handler *handler;
handler = handler_lookup (instance, handler_id, NULL, 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_critical ("%s: instance '%p' has no handler with id '%lu'", G_STRLOC, instance, handler_id);
}
static void
signal_handler_unblock_unlocked (gpointer instance,
gulong handler_id);
/**
* g_signal_handler_unblock:
* @instance: (type GObject.Object): The instance to unblock the signal handler of.
* @handler_id: Handler id of the handler to be unblocked.
*
* Undoes the effect of a previous g_signal_handler_block() call. A
* blocked handler is skipped during signal emissions and will not be
* invoked, unblocking it (for exactly the amount of times it has been
* blocked before) reverts its "blocked" state, so the handler will be
* recognized by the signal system and is called upon future or
* currently ongoing signal emissions (since the order in which
* handlers are called during signal emissions is deterministic,
* whether the unblocked handler in question is called as part of a
* currently ongoing emission depends on how far that emission has
* proceeded yet).
*
* The @handler_id has to be a valid id of a signal handler that is
* connected to a signal of @instance and is currently blocked.
*/
void
g_signal_handler_unblock (gpointer instance,
gulong handler_id)
{
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
g_return_if_fail (handler_id > 0);
SIGNAL_LOCK ();
signal_handler_unblock_unlocked (instance, handler_id);
SIGNAL_UNLOCK ();
}
static void
signal_handler_unblock_unlocked (gpointer instance,
gulong handler_id)
{
Handler *handler;
handler = handler_lookup (instance, handler_id, NULL, NULL);
if (handler)
{
if (handler->block_count)
handler->block_count -= 1;
else
g_critical (G_STRLOC ": handler '%lu' of instance '%p' is not blocked", handler_id, instance);
}
else
g_critical ("%s: instance '%p' has no handler with id '%lu'", G_STRLOC, instance, handler_id);
}
static void
signal_handler_disconnect_unlocked (gpointer instance,
gulong handler_id);
/**
* g_signal_handler_disconnect:
* @instance: (type GObject.Object): The instance to remove the signal handler from.
* @handler_id: Handler id of the handler to be disconnected.
*
* Disconnects a handler from an instance so it will not be called during
* any future or currently ongoing emissions of the signal it has been
* connected to. The @handler_id becomes invalid and may be reused.
*
* The @handler_id has to be a valid signal handler id, connected to a
* signal of @instance.
*/
void
g_signal_handler_disconnect (gpointer instance,
gulong handler_id)
{
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
g_return_if_fail (handler_id > 0);
SIGNAL_LOCK ();
signal_handler_disconnect_unlocked (instance, handler_id);
SIGNAL_UNLOCK ();
}
static void
signal_handler_disconnect_unlocked (gpointer instance,
gulong handler_id)
{
Handler *handler;
handler = handler_lookup (instance, handler_id, 0, 0);
if (handler)
{
g_hash_table_remove (g_handlers, handler);
handler->sequential_number = 0;
handler->block_count = 1;
remove_invalid_closure_notify (handler, instance);
handler_unref_R (handler->signal_id, instance, handler);
}
else
g_critical ("%s: instance '%p' has no handler with id '%lu'", G_STRLOC, instance, handler_id);
}
/**
* g_signal_handler_is_connected:
* @instance: (type GObject.Object): The instance where a signal handler is sought.
* @handler_id: the handler ID.
*
* Returns whether @handler_id is the ID of a handler connected to @instance.
*
* Returns: whether @handler_id identifies a handler connected to @instance.
*/
gboolean
g_signal_handler_is_connected (gpointer instance,
gulong handler_id)
{
Handler *handler;
gboolean connected;
g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), FALSE);
SIGNAL_LOCK ();
handler = handler_lookup (instance, handler_id, NULL, NULL);
connected = handler != NULL;
SIGNAL_UNLOCK ();
return connected;
}
/**
* g_signal_handlers_destroy:
* @instance: (type GObject.Object): The instance whose signal handlers are destroyed
*
* Destroy all signal handlers of a type instance. This function is
* an implementation detail of the #GObject dispose implementation,
* and should not be used outside of the type system.
*/
void
g_signal_handlers_destroy (gpointer instance)
{
GBSearchArray *hlbsa;
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
SIGNAL_LOCK ();
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, &g_signal_hlbsa_bconfig, 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->sequential_number)
{
g_hash_table_remove (g_handlers, tmp);
remove_invalid_closure_notify (tmp, instance);
tmp->sequential_number = 0;
handler_unref_R (0, NULL, tmp);
}
}
}
g_bsearch_array_free (hlbsa, &g_signal_hlbsa_bconfig);
}
SIGNAL_UNLOCK ();
}
/**
* g_signal_handler_find:
* @instance: (type GObject.Object): The instance owning the signal handler to be found.
* @mask: Mask indicating which of @signal_id, @detail, @closure, @func
* and/or @data the handler has to match.
* @signal_id: Signal the handler has to be connected to.
* @detail: Signal detail the handler has to be connected to.
* @closure: (nullable): The closure the handler will invoke.
* @func: The C closure callback of the handler (useless for non-C closures).
* @data: (nullable) (closure closure): The closure data of the handler's closure.
*
* Finds the first signal handler that matches certain selection criteria.
* The criteria mask is passed as an OR-ed combination of #GSignalMatchType
* flags, and the criteria values are passed as arguments.
* The match @mask has to be non-0 for successful matches.
* If no handler was found, 0 is returned.
*
* Returns: A valid non-0 signal handler id for a successful match.
*/
gulong
g_signal_handler_find (gpointer instance,
GSignalMatchType mask,
guint signal_id,
GQuark detail,
GClosure *closure,
gpointer func,
gpointer data)
{
gulong handler_seq_no = 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;
SIGNAL_LOCK ();
mlist = handlers_find (instance, mask, signal_id, detail, closure, func, data, TRUE);
if (mlist)
{
handler_seq_no = mlist->handler->sequential_number;
handler_match_free1_R (mlist, instance);
}
SIGNAL_UNLOCK ();
}
return handler_seq_no;
}
typedef void (*CallbackHandlerFunc) (gpointer instance, gulong handler_seq_no);
static guint
signal_handlers_foreach_matched_unlocked_R (gpointer instance,
GSignalMatchType mask,
guint signal_id,
GQuark detail,
GClosure *closure,
gpointer func,
gpointer data,
CallbackHandlerFunc callback)
{
HandlerMatch *mlist;
guint n_handlers = 0;
mlist = handlers_find (instance, mask, signal_id, detail, closure, func, data, FALSE);
while (mlist)
{
n_handlers++;
if (mlist->handler->sequential_number)
callback (instance, mlist->handler->sequential_number);
mlist = handler_match_free1_R (mlist, instance);
}
return n_handlers;
}
/**
* g_signal_handlers_block_matched:
* @instance: (type GObject.Object): The instance to block handlers from.
* @mask: Mask indicating which of @signal_id, @detail, @closure, @func
* and/or @data the handlers have to match.
* @signal_id: Signal the handlers have to be connected to.
* @detail: Signal detail the handlers have to be connected to.
* @closure: (nullable): The closure the handlers will invoke.
* @func: The C closure callback of the handlers (useless for non-C closures).
* @data: (nullable) (closure closure): The closure data of the handlers' closures.
*
* Blocks all handlers on an instance that match a certain selection criteria.
*
* The criteria mask is passed as a combination of #GSignalMatchType flags, and
* the criteria values are passed as arguments. A handler must match on all
* flags set in @mask to be blocked (i.e. the match is conjunctive).
*
* Passing at least one of the %G_SIGNAL_MATCH_ID, %G_SIGNAL_MATCH_CLOSURE,
* %G_SIGNAL_MATCH_FUNC
* or %G_SIGNAL_MATCH_DATA match flags is required for successful matches.
* If no handlers were found, 0 is returned, the number of blocked handlers
* otherwise.
*
* Support for %G_SIGNAL_MATCH_ID was added in GLib 2.78.
*
* Returns: The number of handlers that matched.
*/
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), 0);
g_return_val_if_fail ((mask & ~G_SIGNAL_MATCH_MASK) == 0, 0);
if (mask & (G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_CLOSURE | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA))
{
SIGNAL_LOCK ();
n_handlers =
signal_handlers_foreach_matched_unlocked_R (instance, mask, signal_id, detail,
closure, func, data,
signal_handler_block_unlocked);
SIGNAL_UNLOCK ();
}
return n_handlers;
}
/**
* g_signal_handlers_unblock_matched:
* @instance: (type GObject.Object): The instance to unblock handlers from.
* @mask: Mask indicating which of @signal_id, @detail, @closure, @func
* and/or @data the handlers have to match.
* @signal_id: Signal the handlers have to be connected to.
* @detail: Signal detail the handlers have to be connected to.
* @closure: (nullable): The closure the handlers will invoke.
* @func: The C closure callback of the handlers (useless for non-C closures).
* @data: (nullable) (closure closure): The closure data of the handlers' closures.
*
* Unblocks all handlers on an instance that match a certain selection
* criteria.
*
* The criteria mask is passed as a combination of #GSignalMatchType flags, and
* the criteria values are passed as arguments. A handler must match on all
* flags set in @mask to be unblocked (i.e. the match is conjunctive).
*
* Passing at least one of the %G_SIGNAL_MATCH_ID, %G_SIGNAL_MATCH_CLOSURE,
* %G_SIGNAL_MATCH_FUNC
* or %G_SIGNAL_MATCH_DATA match flags is required for successful matches.
* If no handlers were found, 0 is returned, the number of unblocked handlers
* otherwise. The match criteria should not apply to any handlers that are
* not currently blocked.
*
* Support for %G_SIGNAL_MATCH_ID was added in GLib 2.78.
*
* Returns: The number of handlers that matched.
*/
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), 0);
g_return_val_if_fail ((mask & ~G_SIGNAL_MATCH_MASK) == 0, 0);
if (mask & (G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_CLOSURE | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA))
{
SIGNAL_LOCK ();
n_handlers =
signal_handlers_foreach_matched_unlocked_R (instance, mask, signal_id, detail,
closure, func, data,
signal_handler_unblock_unlocked);
SIGNAL_UNLOCK ();
}
return n_handlers;
}
/**
* g_signal_handlers_disconnect_matched:
* @instance: (type GObject.Object): The instance to remove handlers from.
* @mask: Mask indicating which of @signal_id, @detail, @closure, @func
* and/or @data the handlers have to match.
* @signal_id: Signal the handlers have to be connected to.
* @detail: Signal detail the handlers have to be connected to.
* @closure: (nullable): The closure the handlers will invoke.
* @func: The C closure callback of the handlers (useless for non-C closures).
* @data: (nullable) (closure closure): The closure data of the handlers' closures.
*
* Disconnects all handlers on an instance that match a certain
* selection criteria.
*
* The criteria mask is passed as a combination of #GSignalMatchType flags, and
* the criteria values are passed as arguments. A handler must match on all
* flags set in @mask to be disconnected (i.e. the match is conjunctive).
*
* Passing at least one of the %G_SIGNAL_MATCH_ID, %G_SIGNAL_MATCH_CLOSURE,
* %G_SIGNAL_MATCH_FUNC or
* %G_SIGNAL_MATCH_DATA match flags is required for successful
* matches. If no handlers were found, 0 is returned, the number of
* disconnected handlers otherwise.
*
* Support for %G_SIGNAL_MATCH_ID was added in GLib 2.78.
*
* Returns: The number of handlers that matched.
*/
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), 0);
g_return_val_if_fail ((mask & ~G_SIGNAL_MATCH_MASK) == 0, 0);
if (mask & (G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_CLOSURE | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA))
{
SIGNAL_LOCK ();
n_handlers =
signal_handlers_foreach_matched_unlocked_R (instance, mask, signal_id, detail,
closure, func, data,
signal_handler_disconnect_unlocked);
SIGNAL_UNLOCK ();
}
return n_handlers;
}
/**
* g_signal_has_handler_pending:
* @instance: (type GObject.Object): the object whose signal handlers are sought.
* @signal_id: the signal id.
* @detail: the detail.
* @may_be_blocked: whether blocked handlers should count as match.
*
* Returns whether there are any handlers connected to @instance for the
* given signal id and detail.
*
* If @detail is 0 then it will only match handlers that were connected
* without detail. If @detail is non-zero then it will match handlers
* connected both without detail and with the given detail. This is
* consistent with how a signal emitted with @detail would be delivered
* to those handlers.
*
* Since 2.46 this also checks for a non-default class closure being
* installed, as this is basically always what you want.
*
* One example of when you might use this is when the arguments to the
* signal are difficult to compute. A class implementor may opt to not
* emit the signal if no one is attached anyway, thus saving the cost
* of building the arguments.
*
* Returns: %TRUE if a handler is connected to the signal, %FALSE
* otherwise.
*/
gboolean
g_signal_has_handler_pending (gpointer instance,
guint signal_id,
GQuark detail,
gboolean may_be_blocked)
{
HandlerMatch *mlist;
gboolean has_pending;
SignalNode *node;
g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), FALSE);
g_return_val_if_fail (signal_id > 0, FALSE);
SIGNAL_LOCK ();
node = LOOKUP_SIGNAL_NODE (signal_id);
if (detail)
{
if (!(node->flags & G_SIGNAL_DETAILED))
{
g_critical ("%s: signal id '%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
SIGNAL_UNLOCK ();
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
{
ClassClosure *class_closure = signal_find_class_closure (node, G_TYPE_FROM_INSTANCE (instance));
if (class_closure != NULL && class_closure->instance_type != 0)
has_pending = TRUE;
else
has_pending = FALSE;
}
SIGNAL_UNLOCK ();
return has_pending;
}
static void
signal_emitv_unlocked (const GValue *instance_and_params,
guint signal_id,
GQuark detail,
GValue *return_value);
/**
* g_signal_emitv:
* @instance_and_params: (array): argument list for the signal emission.
* The first element in the array is a #GValue for the instance the signal
* is being emitted on. The rest are any arguments to be passed to the signal.
* @signal_id: the signal id
* @detail: the detail
* @return_value: (inout) (optional): Location to
* store the return value of the signal emission. This must be provided if the
* specified signal returns a value, but may be ignored otherwise.
*
* Emits a signal. Signal emission is done synchronously.
* The method will only return control after all handlers are called or signal emission was stopped.
*
* Note that g_signal_emitv() doesn't change @return_value if no handlers are
* connected, in contrast to g_signal_emit() and g_signal_emit_valist().
*/
void
g_signal_emitv (const GValue *instance_and_params,
guint signal_id,
GQuark detail,
GValue *return_value)
{
SIGNAL_LOCK ();
signal_emitv_unlocked (instance_and_params, signal_id, detail, return_value);
SIGNAL_UNLOCK ();
}
static void
signal_emitv_unlocked (const GValue *instance_and_params,
guint signal_id,
GQuark detail,
GValue *return_value)
{
gpointer instance;
SignalNode *node;
#ifdef G_ENABLE_DEBUG
const GValue *param_values;
guint i;
#endif
g_return_if_fail (instance_and_params != NULL);
instance = g_value_peek_pointer (instance_and_params);
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
g_return_if_fail (signal_id > 0);
#ifdef G_ENABLE_DEBUG
param_values = instance_and_params + 1;
#endif
node = LOOKUP_SIGNAL_NODE (signal_id);
if (!node || !g_type_is_a (G_TYPE_FROM_INSTANCE (instance), node->itype))
{
g_critical ("%s: signal id '%u' is invalid for instance '%p'", G_STRLOC, signal_id, instance);
return;
}
#ifdef G_ENABLE_DEBUG
if (detail && !(node->flags & G_SIGNAL_DETAILED))
{
g_critical ("%s: signal id '%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
return;
}
for (i = 0; i < node->n_params; i++)
if (!G_TYPE_CHECK_VALUE_TYPE (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,
type_debug_name (node->param_types[i]),
i,
node->name,
G_VALUE_TYPE_NAME (param_values + i));
return;
}
if (node->return_type != G_TYPE_NONE)
{
if (!return_value)
{
g_critical ("%s: return value '%s' for signal \"%s\" is (NULL)",
G_STRLOC,
type_debug_name (node->return_type),
node->name);
return;
}
else if (!node->accumulator && !G_TYPE_CHECK_VALUE_TYPE (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,
type_debug_name (node->return_type),
node->name,
G_VALUE_TYPE_NAME (return_value));
return;
}
}
else
return_value = NULL;
#endif /* G_ENABLE_DEBUG */
/* optimize NOP emissions */
if (!node->single_va_closure_is_valid)
node_update_single_va_closure (node);
if (node->single_va_closure != NULL &&
(node->single_va_closure == SINGLE_VA_CLOSURE_EMPTY_MAGIC ||
_g_closure_is_void (node->single_va_closure, instance)))
{
HandlerList* hlist;
/* single_va_closure is only true for GObjects, so fast path if no handler ever connected to the signal */
if (_g_object_has_signal_handler ((GObject *)instance))
hlist = handler_list_lookup (node->signal_id, instance);
else
hlist = NULL;
if (hlist == NULL || hlist->handlers == NULL)
{
/* nothing to do to emit this signal */
/* g_printerr ("omitting emission of \"%s\"\n", node->name); */
return;
}
}
/* Pass a stable node pointer, whose address can't change even if the
* g_signal_nodes array gets reallocated. */
SignalNode node_copy = *node;
signal_emit_unlocked_R (&node_copy, detail, instance, return_value, instance_and_params);
}
static inline gboolean
accumulate (GSignalInvocationHint *ihint,
GValue *return_accu,
GValue *handler_return,
SignalAccumulator *accumulator)
{
gboolean continue_emission;
if (!accumulator)
return TRUE;
continue_emission = accumulator->func (ihint, return_accu, handler_return, accumulator->data);
g_value_reset (handler_return);
ihint->run_type &= ~G_SIGNAL_ACCUMULATOR_FIRST_RUN;
return continue_emission;
}
static gboolean
signal_emit_valist_unlocked (gpointer instance,
guint signal_id,
GQuark detail,
va_list var_args);
/**
* g_signal_emit_valist: (skip)
* @instance: (type GObject.TypeInstance): the instance the signal is being
* emitted on.
* @signal_id: the signal id
* @detail: the detail
* @var_args: a list of parameters to be passed to the signal, followed by a
* location for the return value. If the return type of the signal
* is %G_TYPE_NONE, the return value location can be omitted.
*
* Emits a signal. Signal emission is done synchronously.
* The method will only return control after all handlers are called or signal emission was stopped.
*
* Note that g_signal_emit_valist() resets the return value to the default
* if no handlers are connected, in contrast to g_signal_emitv().
*/
void
g_signal_emit_valist (gpointer instance,
guint signal_id,
GQuark detail,
va_list var_args)
{
SIGNAL_LOCK ();
if (signal_emit_valist_unlocked (instance, signal_id, detail, var_args))
SIGNAL_UNLOCK ();
}
/*<private>
* signal_emit_valist_unlocked:
* @instance: The instance to emit from
* @signal_id: Signal id to emit
* @detail: Signal detail
* @var_args: Call arguments
*
* Returns: %TRUE if the signal mutex has been left locked
*/
static gboolean
signal_emit_valist_unlocked (gpointer instance,
guint signal_id,
GQuark detail,
va_list var_args)
{
GValue *instance_and_params;
GValue *param_values;
SignalNode *node;
guint i;
g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), TRUE);
g_return_val_if_fail (signal_id > 0, TRUE);
node = LOOKUP_SIGNAL_NODE (signal_id);
if (!node || !g_type_is_a (G_TYPE_FROM_INSTANCE (instance), node->itype))
{
g_critical ("%s: signal id '%u' is invalid for instance '%p'", G_STRLOC, signal_id, instance);
return TRUE;
}
#ifndef G_DISABLE_CHECKS
if (detail && !(node->flags & G_SIGNAL_DETAILED))
{
g_critical ("%s: signal id '%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
return TRUE;
}
#endif /* !G_DISABLE_CHECKS */
if (!node->single_va_closure_is_valid)
node_update_single_va_closure (node);
/* There's no need to deep copy this, because a SignalNode instance won't
* ever be destroyed, given that _g_signals_destroy() is not called in any
* real program, however the SignalNode pointer could change, so just store
* the struct contents references, so that we won't try to deference a
* potentially invalid (or changed) pointer;
*/
SignalNode node_copy = *node;
if (node->single_va_closure != NULL)
{
HandlerList* hlist;
Handler *fastpath_handler = NULL;
Handler *l;
GClosure *closure = NULL;
gboolean fastpath = TRUE;
GSignalFlags run_type = G_SIGNAL_RUN_FIRST;
if (node->single_va_closure != SINGLE_VA_CLOSURE_EMPTY_MAGIC &&
!_g_closure_is_void (node->single_va_closure, instance))
{
if (_g_closure_supports_invoke_va (node->single_va_closure))
{
closure = node->single_va_closure;
if (node->single_va_closure_is_after)
run_type = G_SIGNAL_RUN_LAST;
else
run_type = G_SIGNAL_RUN_FIRST;
}
else
fastpath = FALSE;
}
/* single_va_closure is only true for GObjects, so fast path if no handler ever connected to the signal */
if (_g_object_has_signal_handler ((GObject *)instance))
hlist = handler_list_lookup (node->signal_id, instance);
else
hlist = NULL;
for (l = hlist ? hlist->handlers : NULL; fastpath && l != NULL; l = l->next)
{
if (!l->block_count &&
(!l->detail || l->detail == detail))
{
if (closure != NULL || !_g_closure_supports_invoke_va (l->closure))
{
fastpath = FALSE;
break;
}
else
{
fastpath_handler = l;
closure = l->closure;
if (l->after)
run_type = G_SIGNAL_RUN_LAST;
else
run_type = G_SIGNAL_RUN_FIRST;
}
}
}
if (fastpath && closure == NULL && node_copy.return_type == G_TYPE_NONE)
return TRUE;
/* Don't allow no-recurse emission as we might have to restart, which means
we will run multiple handlers and thus must ref all arguments */
if (closure != NULL && (node_copy.flags & (G_SIGNAL_NO_RECURSE)) != 0)
fastpath = FALSE;
if (fastpath)
{
Emission emission;
GValue *return_accu, accu = G_VALUE_INIT;
GType instance_type = G_TYPE_FROM_INSTANCE (instance);
GValue emission_return = G_VALUE_INIT;
GType rtype = node_copy.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE;
gboolean static_scope = node_copy.return_type & G_SIGNAL_TYPE_STATIC_SCOPE;
if (rtype == G_TYPE_NONE)
return_accu = NULL;
else if (node_copy.accumulator)
return_accu = &accu;
else
return_accu = &emission_return;
emission.instance = instance;
emission.ihint.signal_id = signal_id;
emission.ihint.detail = detail;
emission.ihint.run_type = run_type | G_SIGNAL_ACCUMULATOR_FIRST_RUN;
emission.state = EMISSION_RUN;
emission.chain_type = instance_type;
emission_push (&emission);
if (fastpath_handler)
handler_ref (fastpath_handler);
if (closure != NULL)
{
TRACE(GOBJECT_SIGNAL_EMIT(signal_id, detail, instance, instance_type));
SIGNAL_UNLOCK ();
if (rtype != G_TYPE_NONE)
g_value_init (&emission_return, rtype);
if (node_copy.accumulator)
g_value_init (&accu, rtype);
/*
* Coverity doesnt understand the paired ref/unref here and seems
* to ignore the ref, thus reports every call to g_signal_emit()
* as causing a double-free. Thats incorrect, but I cant get a
* model file to work for avoiding the false positives, so instead
* comment out the ref/unref when doing static analysis.
*/
#ifndef __COVERITY__
g_object_ref (instance);
#endif
_g_closure_invoke_va (closure,
return_accu,
instance,
var_args,
node_copy.n_params,
node_copy.param_types);
accumulate (&emission.ihint, &emission_return, &accu, node_copy.accumulator);
if (node_copy.accumulator)
g_value_unset (&accu);
SIGNAL_LOCK ();
}
emission.chain_type = G_TYPE_NONE;
emission_pop (&emission);
if (fastpath_handler)
handler_unref_R (signal_id, instance, fastpath_handler);
SIGNAL_UNLOCK ();
if (rtype != G_TYPE_NONE)
{
gchar *error = NULL;
for (i = 0; i < node_copy.n_params; i++)
{
GType ptype = node_copy.param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE;
G_VALUE_COLLECT_SKIP (ptype, var_args);
}
if (closure == NULL)
g_value_init (&emission_return, rtype);
G_VALUE_LCOPY (&emission_return,
var_args,
static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,
&error);
if (!error)
g_value_unset (&emission_return);
else
{
g_critical ("%s: %s", G_STRLOC, error);
g_free (error);
/* we purposely leak the value here, it might not be
* in a correct state if an error condition occurred
*/
}
}
TRACE(GOBJECT_SIGNAL_EMIT_END(signal_id, detail, instance, instance_type));
/* See comment above paired ref above */
#ifndef __COVERITY__
if (closure != NULL)
g_object_unref (instance);
#endif
return FALSE;
}
}
SIGNAL_UNLOCK ();
instance_and_params = g_newa0 (GValue, node_copy.n_params + 1);
param_values = instance_and_params + 1;
for (i = 0; i < node_copy.n_params; i++)
{
gchar *error;
GType ptype = node_copy.param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE;
gboolean static_scope = node_copy.param_types[i] & G_SIGNAL_TYPE_STATIC_SCOPE;
G_VALUE_COLLECT_INIT (param_values + i, ptype,
var_args,
static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,
&error);
if (error)
{
g_critical ("%s: %s", G_STRLOC, error);
g_free (error);
/* we purposely leak the value here, it might not be
* in a correct state if an error condition occurred
*/
while (i--)
g_value_unset (param_values + i);
return FALSE;
}
}
g_value_init_from_instance (instance_and_params, instance);
if (node_copy.return_type == G_TYPE_NONE)
{
SIGNAL_LOCK ();
signal_emit_unlocked_R (&node_copy, detail, instance, NULL, instance_and_params);
SIGNAL_UNLOCK ();
}
else
{
GValue return_value = G_VALUE_INIT;
gchar *error = NULL;
GType rtype = node_copy.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE;
gboolean static_scope = node_copy.return_type & G_SIGNAL_TYPE_STATIC_SCOPE;
g_value_init (&return_value, rtype);
SIGNAL_LOCK ();
signal_emit_unlocked_R (&node_copy, detail, instance, &return_value, instance_and_params);
SIGNAL_UNLOCK ();
G_VALUE_LCOPY (&return_value,
var_args,
static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,
&error);
if (!error)
g_value_unset (&return_value);
else
{
g_critical ("%s: %s", G_STRLOC, error);
g_free (error);
/* we purposely leak the value here, it might not be
* in a correct state if an error condition occurred
*/
}
}
for (i = 0; i < node_copy.n_params; i++)
g_value_unset (param_values + i);
g_value_unset (instance_and_params);
return FALSE;
}
/**
* g_signal_emit:
* @instance: (type GObject.Object): the instance the signal is being emitted on.
* @signal_id: the signal id
* @detail: the detail
* @...: parameters to be passed to the signal, followed by a
* location for the return value. If the return type of the signal
* is %G_TYPE_NONE, the return value location can be omitted.
*
* Emits a signal. Signal emission is done synchronously.
* The method will only return control after all handlers are called or signal emission was stopped.
*
* Note that g_signal_emit() resets the return value to the default
* if no handlers are connected, in contrast to g_signal_emitv().
*/
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);
}
/**
* g_signal_emit_by_name:
* @instance: (type GObject.Object): the instance the signal is being emitted on.
* @detailed_signal: a string of the form "signal-name::detail".
* @...: parameters to be passed to the signal, followed by a
* location for the return value. If the return type of the signal
* is %G_TYPE_NONE, the return value location can be omitted. The
* number of parameters to pass to this function is defined when creating the signal.
*
* Emits a signal. Signal emission is done synchronously.
* The method will only return control after all handlers are called or signal emission was stopped.
*
* Note that g_signal_emit_by_name() resets the return value to the default
* if no handlers are connected, in contrast to g_signal_emitv().
*/
void
g_signal_emit_by_name (gpointer instance,
const gchar *detailed_signal,
...)
{
GQuark detail = 0;
guint signal_id;
GType itype;
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
g_return_if_fail (detailed_signal != NULL);
itype = G_TYPE_FROM_INSTANCE (instance);
SIGNAL_LOCK ();
signal_id = signal_parse_name (detailed_signal, itype, &detail, TRUE);
if (signal_id)
{
va_list var_args;
va_start (var_args, detailed_signal);
if (signal_emit_valist_unlocked (instance, signal_id, detail, var_args))
SIGNAL_UNLOCK ();
va_end (var_args);
}
else
{
SIGNAL_UNLOCK ();
g_critical ("%s: signal name '%s' is invalid for instance '%p' of type '%s'",
G_STRLOC, detailed_signal, instance, g_type_name (itype));
}
}
G_ALWAYS_INLINE static inline GValue *
maybe_init_accumulator_unlocked (SignalNode *node,
GValue *emission_return,
GValue *accumulator_value)
{
if (node->accumulator)
{
if (accumulator_value->g_type)
return accumulator_value;
g_value_init (accumulator_value,
node->return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE);
return accumulator_value;
}
return emission_return;
}
static gboolean
signal_emit_unlocked_R (SignalNode *node,
GQuark detail,
gpointer instance,
GValue *emission_return,
const GValue *instance_and_params)
{
SignalAccumulator *accumulator;
Emission emission;
GClosure *class_closure;
HandlerList *hlist;
Handler *handler_list = NULL;
GValue *return_accu, accu = G_VALUE_INIT;
guint signal_id;
gulong max_sequential_handler_number;
gboolean return_value_altered = FALSE;
guint n_params;
TRACE(GOBJECT_SIGNAL_EMIT(node->signal_id, detail, instance, G_TYPE_FROM_INSTANCE (instance)));
/* We expect this function to be called with a stable SignalNode pointer
* that cannot change location, so accessing its stable members should
* always work even after a lock/unlock.
*/
signal_id = node->signal_id;
n_params = node->n_params + 1;
if (node->flags & G_SIGNAL_NO_RECURSE)
{
Emission *emission_node = emission_find (signal_id, detail, instance);
if (emission_node)
{
emission_node->state = EMISSION_RESTART;
return return_value_altered;
}
}
accumulator = node->accumulator;
emission.instance = instance;
emission.ihint.signal_id = node->signal_id;
emission.ihint.detail = detail;
emission.ihint.run_type = 0;
emission.state = 0;
emission.chain_type = G_TYPE_NONE;
emission_push (&emission);
class_closure = signal_lookup_closure (node, instance);
EMIT_RESTART:
if (handler_list)
handler_unref_R (signal_id, instance, handler_list);
max_sequential_handler_number = g_handler_sequential_number;
hlist = handler_list_lookup (signal_id, instance);
handler_list = hlist ? hlist->handlers : NULL;
if (handler_list)
handler_ref (handler_list);
emission.ihint.run_type = G_SIGNAL_RUN_FIRST | G_SIGNAL_ACCUMULATOR_FIRST_RUN;
if ((node->flags & G_SIGNAL_RUN_FIRST) && class_closure)
{
emission.state = EMISSION_RUN;
emission.chain_type = G_TYPE_FROM_INSTANCE (instance);
SIGNAL_UNLOCK ();
return_accu = maybe_init_accumulator_unlocked (node, emission_return, &accu);
g_closure_invoke (class_closure,
return_accu,
n_params,
instance_and_params,
&emission.ihint);
if (!accumulate (&emission.ihint, emission_return, &accu, accumulator) &&
emission.state == EMISSION_RUN)
emission.state = EMISSION_STOP;
SIGNAL_LOCK ();
emission.chain_type = G_TYPE_NONE;
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)
{
GHook *hook;
GHook *static_emission_hooks[3];
size_t n_emission_hooks = 0;
const gboolean may_recurse = TRUE;
guint i;
emission.state = EMISSION_HOOK;
/* Quick check to determine whether any hooks match this emission,
* before committing to the more complex work of calling those hooks.
* We save a few of them into a static array, to try to avoid further
* allocations.
*/
hook = g_hook_first_valid (node->emission_hooks, may_recurse);
while (hook)
{
SignalHook *signal_hook = SIGNAL_HOOK (hook);
if (!signal_hook->detail || signal_hook->detail == detail)
{
if (n_emission_hooks < G_N_ELEMENTS (static_emission_hooks))
{
static_emission_hooks[n_emission_hooks] =
g_hook_ref (node->emission_hooks, hook);
}
n_emission_hooks += 1;
}
hook = g_hook_next_valid (node->emission_hooks, hook, may_recurse);
}
/* Re-iterate back through the matching hooks and copy them into
* an array which wont change when we unlock to call the
* user-provided hook functions.
* These functions may change hook configuration for this signal,
* add / remove signal handlers, etc.
*/
if G_UNLIKELY (n_emission_hooks > 0)
{
guint8 static_hook_returns[G_N_ELEMENTS (static_emission_hooks)];
GHook **emission_hooks = NULL;
guint8 *hook_returns = NULL;
if G_LIKELY (n_emission_hooks <= G_N_ELEMENTS (static_emission_hooks))
{
emission_hooks = static_emission_hooks;
hook_returns = static_hook_returns;
}
else
{
emission_hooks = g_newa (GHook *, n_emission_hooks);
hook_returns = g_newa (guint8, n_emission_hooks);
/* We can't just memcpy the ones we have in the static array,
* to the alloca()'d one because otherwise we'd get an invalid
* ID assertion during unref
*/
i = 0;
for (hook = g_hook_first_valid (node->emission_hooks, may_recurse);
hook != NULL;
hook = g_hook_next_valid (node->emission_hooks, hook, may_recurse))
{
SignalHook *signal_hook = SIGNAL_HOOK (hook);
if (!signal_hook->detail || signal_hook->detail == detail)
{
if (i < G_N_ELEMENTS (static_emission_hooks))
{
emission_hooks[i] = g_steal_pointer (&static_emission_hooks[i]);
g_assert (emission_hooks[i] == hook);
}
else
{
emission_hooks[i] = g_hook_ref (node->emission_hooks, hook);
}
i += 1;
}
}
g_assert (i == n_emission_hooks);
}
SIGNAL_UNLOCK ();
for (i = 0; i < n_emission_hooks; ++i)
{
GSignalEmissionHook hook_func;
gboolean need_destroy;
guint old_flags;
hook = emission_hooks[i];
hook_func = (GSignalEmissionHook) hook->func;
old_flags = g_atomic_int_or (&hook->flags, G_HOOK_FLAG_IN_CALL);
need_destroy = !hook_func (&emission.ihint, n_params,
instance_and_params, hook->data);
if (!(old_flags & G_HOOK_FLAG_IN_CALL))
{
g_atomic_int_compare_and_exchange (&hook->flags,
old_flags | G_HOOK_FLAG_IN_CALL,
old_flags);
}
hook_returns[i] = !!need_destroy;
}
SIGNAL_LOCK ();
for (i = 0; i < n_emission_hooks; i++)
{
hook = emission_hooks[i];
g_hook_unref (node->emission_hooks, hook);
if (hook_returns[i])
g_hook_destroy_link (node->emission_hooks, hook);
}
}
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) &&
handler->sequential_number < max_sequential_handler_number)
{
SIGNAL_UNLOCK ();
return_accu = maybe_init_accumulator_unlocked (node, emission_return, &accu);
g_closure_invoke (handler->closure,
return_accu,
n_params,
instance_and_params,
&emission.ihint);
if (!accumulate (&emission.ihint, emission_return, &accu, accumulator) &&
emission.state == EMISSION_RUN)
emission.state = EMISSION_STOP;
SIGNAL_LOCK ();
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;
}
emission.ihint.run_type &= ~G_SIGNAL_RUN_FIRST;
emission.ihint.run_type |= G_SIGNAL_RUN_LAST;
if ((node->flags & G_SIGNAL_RUN_LAST) && class_closure)
{
emission.state = EMISSION_RUN;
emission.chain_type = G_TYPE_FROM_INSTANCE (instance);
SIGNAL_UNLOCK ();
return_accu = maybe_init_accumulator_unlocked (node, emission_return, &accu);
g_closure_invoke (class_closure,
return_accu,
n_params,
instance_and_params,
&emission.ihint);
if (!accumulate (&emission.ihint, emission_return, &accu, accumulator) &&
emission.state == EMISSION_RUN)
emission.state = EMISSION_STOP;
SIGNAL_LOCK ();
emission.chain_type = G_TYPE_NONE;
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) &&
handler->sequential_number < max_sequential_handler_number)
{
SIGNAL_UNLOCK ();
return_accu = maybe_init_accumulator_unlocked (node, emission_return, &accu);
g_closure_invoke (handler->closure,
return_accu,
n_params,
instance_and_params,
&emission.ihint);
if (!accumulate (&emission.ihint, emission_return, &accu, accumulator) &&
emission.state == EMISSION_RUN)
emission.state = EMISSION_STOP;
SIGNAL_LOCK ();
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:
emission.ihint.run_type &= ~G_SIGNAL_RUN_LAST;
emission.ihint.run_type |= G_SIGNAL_RUN_CLEANUP;
if ((node->flags & G_SIGNAL_RUN_CLEANUP) && class_closure)
{
gboolean need_unset = FALSE;
emission.state = EMISSION_STOP;
emission.chain_type = G_TYPE_FROM_INSTANCE (instance);
SIGNAL_UNLOCK ();
if (node->return_type != G_TYPE_NONE && !accumulator)
{
g_value_init (&accu, node->return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE);
need_unset = TRUE;
}
g_closure_invoke (class_closure,
node->return_type != G_TYPE_NONE ? &accu : NULL,
n_params,
instance_and_params,
&emission.ihint);
if (!accumulate (&emission.ihint, emission_return, &accu, accumulator) &&
emission.state == EMISSION_RUN)
emission.state = EMISSION_STOP;
if (need_unset)
g_value_unset (&accu);
SIGNAL_LOCK ();
return_value_altered = TRUE;
emission.chain_type = G_TYPE_NONE;
if (emission.state == EMISSION_RESTART)
goto EMIT_RESTART;
}
if (handler_list)
handler_unref_R (signal_id, instance, handler_list);
emission_pop (&emission);
if (accumulator)
g_value_unset (&accu);
TRACE(GOBJECT_SIGNAL_EMIT_END(node->signal_id, detail, instance, G_TYPE_FROM_INSTANCE (instance)));
return return_value_altered;
}
static void
add_invalid_closure_notify (Handler *handler,
gpointer instance)
{
g_closure_add_invalidate_notifier (handler->closure, instance, invalid_closure_notify);
handler->has_invalid_closure_notify = 1;
}
static void
remove_invalid_closure_notify (Handler *handler,
gpointer instance)
{
if (handler->has_invalid_closure_notify)
{
g_closure_remove_invalidate_notifier (handler->closure, instance, invalid_closure_notify);
handler->has_invalid_closure_notify = 0;
}
}
static void
invalid_closure_notify (gpointer instance,
GClosure *closure)
{
Handler *handler;
guint signal_id;
SIGNAL_LOCK ();
handler = handler_lookup (instance, 0, closure, &signal_id);
/* See https://bugzilla.gnome.org/show_bug.cgi?id=730296 for discussion about this... */
g_assert (handler != NULL);
g_assert (handler->closure == closure);
g_hash_table_remove (g_handlers, handler);
handler->sequential_number = 0;
handler->block_count = 1;
handler_unref_R (signal_id, instance, handler);
SIGNAL_UNLOCK ();
}
static const gchar*
type_debug_name (GType type)
{
if (type)
{
const char *name = g_type_name (type & ~G_SIGNAL_TYPE_STATIC_SCOPE);
return name ? name : "<unknown>";
}
else
return "<invalid>";
}
/**
* g_signal_accumulator_true_handled:
* @ihint: standard #GSignalAccumulator parameter
* @return_accu: standard #GSignalAccumulator parameter
* @handler_return: standard #GSignalAccumulator parameter
* @dummy: standard #GSignalAccumulator parameter
*
* A predefined #GSignalAccumulator for signals that return a
* boolean values. The behavior that this accumulator gives is
* that a return of %TRUE stops the signal emission: no further
* callbacks will be invoked, while a return of %FALSE allows
* the emission to continue. The idea here is that a %TRUE return
* indicates that the callback handled the signal, and no further
* handling is needed.
*
* Since: 2.4
*
* Returns: standard #GSignalAccumulator result
*/
gboolean
g_signal_accumulator_true_handled (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer dummy)
{
gboolean continue_emission;
gboolean signal_handled;
signal_handled = g_value_get_boolean (handler_return);
g_value_set_boolean (return_accu, signal_handled);
continue_emission = !signal_handled;
return continue_emission;
}
/**
* g_signal_accumulator_first_wins:
* @ihint: standard #GSignalAccumulator parameter
* @return_accu: standard #GSignalAccumulator parameter
* @handler_return: standard #GSignalAccumulator parameter
* @dummy: standard #GSignalAccumulator parameter
*
* A predefined #GSignalAccumulator for signals intended to be used as a
* hook for application code to provide a particular value. Usually
* only one such value is desired and multiple handlers for the same
* signal don't make much sense (except for the case of the default
* handler defined in the class structure, in which case you will
* usually want the signal connection to override the class handler).
*
* This accumulator will use the return value from the first signal
* handler that is run as the return value for the signal and not run
* any further handlers (ie: the first handler "wins").
*
* Returns: standard #GSignalAccumulator result
*
* Since: 2.28
**/
gboolean
g_signal_accumulator_first_wins (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer dummy)
{
g_value_copy (handler_return, return_accu);
return FALSE;
}
/**
* g_clear_signal_handler:
* @handler_id_ptr: A pointer to a handler ID (of type #gulong) of the handler to be disconnected.
* @instance: (type GObject.Object): The instance to remove the signal handler from.
* This pointer may be %NULL or invalid, if the handler ID is zero.
*
* Disconnects a handler from @instance so it will not be called during
* any future or currently ongoing emissions of the signal it has been
* connected to. The @handler_id_ptr is then set to zero, which is never a valid handler ID value (see g_signal_connect()).
*
* If the handler ID is 0 then this function does nothing.
*
* There is also a macro version of this function so that the code
* will be inlined.
*
* Since: 2.62
*/
void
(g_clear_signal_handler) (gulong *handler_id_ptr,
gpointer instance)
{
g_return_if_fail (handler_id_ptr != NULL);
#ifndef g_clear_signal_handler
#error g_clear_signal_handler() macro is not defined
#endif
g_clear_signal_handler (handler_id_ptr, instance);
}