mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 23:16:14 +01:00
13d1697b67
Making this validation code public allows projects to validate a GParamSpec name before creating it. While hard-coded GParamSpec don't need this, we can't afford crashing the main program for dynamically generated GParamSpec from user-created data. In such case, we will need to validate the param names and return errors instead of trying to create a GParamSpec with invalid names. Includes modifications from Philip Withnall and Emmanuele Bassi to rearrange the new function addition and split it into one function for GParamSpecs and one for GSignals.
4034 lines
122 KiB
C
4034 lines
122 KiB
C
/* GObject - GLib Type, Object, Parameter and Signal Library
|
|
* Copyright (C) 2000-2001 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.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"
|
|
|
|
|
|
/**
|
|
* SECTION:signals
|
|
* @short_description: A means for customization of object behaviour
|
|
* and a general purpose notification mechanism
|
|
* @title: Signals
|
|
*
|
|
* The basic concept of the signal system is that of the emission
|
|
* of a signal. Signals are introduced per-type and are identified
|
|
* through strings. Signals introduced for a parent type are available
|
|
* in derived types as well, so basically they are a per-type facility
|
|
* that is inherited.
|
|
*
|
|
* A signal emission mainly involves invocation of a certain set of
|
|
* callbacks in precisely defined manner. There are two main categories
|
|
* of such callbacks, per-object ones and user provided ones.
|
|
* (Although signals can deal with any kind of instantiatable type, I'm
|
|
* referring to those types as "object types" in the following, simply
|
|
* because that is the context most users will encounter signals in.)
|
|
* The per-object callbacks are most often referred to as "object method
|
|
* handler" or "default (signal) handler", while user provided callbacks are
|
|
* usually just called "signal handler".
|
|
*
|
|
* The object method handler is provided at signal creation time (this most
|
|
* frequently happens at the end of an object class' creation), while user
|
|
* provided handlers are frequently connected and disconnected to/from a
|
|
* certain signal on certain object instances.
|
|
*
|
|
* A signal emission consists of five stages, unless prematurely stopped:
|
|
*
|
|
* 1. Invocation of the object method handler for %G_SIGNAL_RUN_FIRST signals
|
|
*
|
|
* 2. Invocation of normal user-provided signal handlers (where the @after
|
|
* flag is not set)
|
|
*
|
|
* 3. Invocation of the object method handler for %G_SIGNAL_RUN_LAST signals
|
|
*
|
|
* 4. Invocation of user provided signal handlers (where the @after flag is set)
|
|
*
|
|
* 5. Invocation of the object method handler for %G_SIGNAL_RUN_CLEANUP signals
|
|
*
|
|
* The user-provided signal handlers are called in the order they were
|
|
* connected in.
|
|
*
|
|
* All handlers may prematurely stop a signal emission, and any number of
|
|
* handlers may be connected, disconnected, blocked or unblocked during
|
|
* a signal emission.
|
|
*
|
|
* There are certain criteria for skipping user handlers in stages 2 and 4
|
|
* of a signal emission.
|
|
*
|
|
* First, user handlers may be blocked. Blocked handlers are omitted during
|
|
* callback invocation, to return from the blocked state, a handler has to
|
|
* get unblocked exactly the same amount of times it has been blocked before.
|
|
*
|
|
* Second, upon emission of a %G_SIGNAL_DETAILED signal, an additional
|
|
* @detail argument passed in to g_signal_emit() has to match the detail
|
|
* argument of the signal handler currently subject to invocation.
|
|
* Specification of no detail argument for signal handlers (omission of the
|
|
* detail part of the signal specification upon connection) serves as a
|
|
* wildcard and matches any detail argument passed in to emission.
|
|
*
|
|
* While the @detail argument is typically used to pass an object property name
|
|
* (as with #GObject::notify), no specific format is mandated for the detail
|
|
* string, other than that it must be non-empty.
|
|
*
|
|
* ## Memory management of signal handlers # {#signal-memory-management}
|
|
*
|
|
* If you are connecting handlers to signals and using a #GObject instance as
|
|
* your signal handler user data, you should remember to pair calls to
|
|
* g_signal_connect() with calls to g_signal_handler_disconnect() or
|
|
* g_signal_handlers_disconnect_by_func(). While signal handlers are
|
|
* automatically disconnected when the object emitting the signal is finalised,
|
|
* they are not automatically disconnected when the signal handler user data is
|
|
* destroyed. If this user data is a #GObject instance, using it from a
|
|
* signal handler after it has been finalised is an error.
|
|
*
|
|
* There are two strategies for managing such user data. The first is to
|
|
* disconnect the signal handler (using g_signal_handler_disconnect() or
|
|
* g_signal_handlers_disconnect_by_func()) when the user data (object) is
|
|
* finalised; this has to be implemented manually. For non-threaded programs,
|
|
* g_signal_connect_object() can be used to implement this automatically.
|
|
* Currently, however, it is unsafe to use in threaded programs.
|
|
*
|
|
* The second is to hold a strong reference on the user data until after the
|
|
* signal is disconnected for other reasons. This can be implemented
|
|
* automatically using g_signal_connect_data().
|
|
*
|
|
* The first approach is recommended, as the second approach can result in
|
|
* effective memory leaks of the user data if the signal handler is never
|
|
* disconnected for some reason.
|
|
*/
|
|
|
|
|
|
#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);
|
|
}
|
|
else
|
|
{
|
|
GBSearchArray *o = hlbsa;
|
|
|
|
hlbsa = g_bsearch_array_insert (o, &g_signal_hlbsa_bconfig, &key);
|
|
if (hlbsa != o)
|
|
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_warning (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_warning ("%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_warning (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_warning (G_STRLOC ": no emission of signal \"%s\" to stop for instance '%p'",
|
|
node->name, instance);
|
|
}
|
|
else
|
|
g_warning ("%s: signal id '%u' is invalid for instance '%p'", G_STRLOC, signal_id, instance);
|
|
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: a #GSignalEmissionHook function.
|
|
* @hook_data: user data for @hook_func.
|
|
* @data_destroy: 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_warning ("%s: invalid signal id '%u'", G_STRLOC, signal_id);
|
|
SIGNAL_UNLOCK ();
|
|
return 0;
|
|
}
|
|
if (node->flags & G_SIGNAL_NO_HOOKS)
|
|
{
|
|
g_warning ("%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_warning ("%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_warning ("%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_warning ("%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);
|
|
SIGNAL_UNLOCK ();
|
|
|
|
node = signal_id ? LOOKUP_SIGNAL_NODE (signal_id) : NULL;
|
|
if (!node || node->destroyed ||
|
|
(detail && !(node->flags & G_SIGNAL_DETAILED)))
|
|
return FALSE;
|
|
|
|
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_warning ("%s: signal '%s' does not support details", G_STRLOC, detailed_signal);
|
|
else if (!g_type_is_a (itype, node->itype))
|
|
g_warning ("%s: signal '%s' is invalid for instance '%p' 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_warning (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_warning (G_STRLOC ": no emission of signal \"%s\" to stop for instance '%p'",
|
|
node->name, instance);
|
|
}
|
|
}
|
|
else
|
|
g_warning ("%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_warning (G_STRLOC ": unable to look up signal \"%s\" for invalid type id '%"G_GSIZE_FORMAT"'",
|
|
name, itype);
|
|
else if (!g_signal_is_valid_name (name))
|
|
g_warning (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_warning (G_STRLOC ": unable to list signals for invalid type id '%"G_GSIZE_FORMAT"'",
|
|
itype);
|
|
else if (!G_TYPE_IS_INSTANTIATABLE (itype) && !G_TYPE_IS_INTERFACE (itype))
|
|
g_warning (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_warning (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: 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): 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: the accumulator for this signal; may be %NULL.
|
|
* @accu_data: 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: 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: the accumulator for this signal; may be %NULL.
|
|
* @accu_data: 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 overriden 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): the accumulator for this signal; may be %NULL
|
|
* @accu_data: 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): an array of types, 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_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);
|
|
|
|
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_warning (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_warning (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_warning (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_warning (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;
|
|
}
|
|
if (return_type != G_TYPE_NONE &&
|
|
(signal_flags & (G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST | G_SIGNAL_RUN_CLEANUP)) == G_SIGNAL_RUN_FIRST)
|
|
{
|
|
g_warning (G_STRLOC ": signal \"%s::%s\" has return type '%s' and is only G_SIGNAL_RUN_FIRST",
|
|
type_debug_name (itype), name, type_debug_name (return_type));
|
|
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_memdup (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: The closure to invoke on signal emission; may be %NULL.
|
|
* @accumulator: the accumulator for this signal; may be %NULL.
|
|
* @accu_data: 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)
|
|
{
|
|
GType *param_types;
|
|
guint i;
|
|
guint signal_id;
|
|
|
|
if (n_params > 0)
|
|
{
|
|
param_types = g_new (GType, n_params);
|
|
|
|
for (i = 0; i < n_params; i++)
|
|
param_types[i] = va_arg (args, GType);
|
|
}
|
|
else
|
|
param_types = NULL;
|
|
|
|
signal_id = g_signal_newv (signal_name, itype, signal_flags,
|
|
class_closure, accumulator, accu_data, c_marshaller,
|
|
return_type, n_params, param_types);
|
|
g_free (param_types);
|
|
|
|
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_warning ("%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_warning ("%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: 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_warning ("%s: signal name '%s' is invalid for type id '%"G_GSIZE_FORMAT"'",
|
|
G_STRLOC, signal_name, 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_warning ("%s: signal id '%u' cannot be chained from current emission stage for instance '%p'", G_STRLOC, node->signal_id, instance);
|
|
}
|
|
else
|
|
g_warning ("%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;
|
|
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_warning ("%s: signal id '%u' cannot be chained from current emission stage for instance '%p'", G_STRLOC, node->signal_id, instance);
|
|
}
|
|
else
|
|
g_warning ("%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_alloca (sizeof (GValue) * (n_params + 1));
|
|
memset (instance_and_params, 0, sizeof (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_warning ("%s: %s", G_STRLOC, error);
|
|
g_free (error);
|
|
|
|
/* we purposely leak the value here, it might not be
|
|
* in a sane state if an error condition occurred
|
|
*/
|
|
while (i--)
|
|
g_value_unset (param_values + i);
|
|
|
|
va_end (var_args);
|
|
return;
|
|
}
|
|
SIGNAL_LOCK ();
|
|
}
|
|
|
|
SIGNAL_UNLOCK ();
|
|
instance_and_params->g_type = 0;
|
|
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_warning ("%s: %s", G_STRLOC, error);
|
|
g_free (error);
|
|
|
|
/* we purposely leak the value here, it might not be
|
|
* in a sane state if an error condition 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): the invocation hint of the innermost signal emission.
|
|
*/
|
|
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: 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.
|
|
*
|
|
* 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_warning ("%s: signal id '%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
|
|
else if (!g_type_is_a (G_TYPE_FROM_INSTANCE (instance), node->itype))
|
|
g_warning ("%s: signal id '%u' is invalid for instance '%p'", G_STRLOC, signal_id, instance);
|
|
else
|
|
{
|
|
Handler *handler = handler_new (signal_id, instance, after);
|
|
|
|
if (G_TYPE_IS_OBJECT (node->itype))
|
|
_g_object_set_has_signal_handler ((GObject *)instance);
|
|
|
|
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_warning ("%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: 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.
|
|
*
|
|
* 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_warning ("%s: signal '%s' does not support details", G_STRLOC, detailed_signal);
|
|
else if (!g_type_is_a (itype, node->itype))
|
|
g_warning ("%s: signal '%s' is invalid for instance '%p' 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);
|
|
|
|
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_warning ("%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: the #GCallback to connect.
|
|
* @data: data to pass to @c_handler calls.
|
|
* @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_warning ("%s: signal '%s' does not support details", G_STRLOC, detailed_signal);
|
|
else if (!g_type_is_a (itype, node->itype))
|
|
g_warning ("%s: signal '%s' is invalid for instance '%p' 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);
|
|
|
|
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_warning ("%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;
|
|
}
|
|
|
|
/**
|
|
* 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 deactive 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)
|
|
{
|
|
Handler *handler;
|
|
|
|
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
|
|
g_return_if_fail (handler_id > 0);
|
|
|
|
SIGNAL_LOCK ();
|
|
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_warning ("%s: instance '%p' has no handler with id '%lu'", G_STRLOC, instance, handler_id);
|
|
SIGNAL_UNLOCK ();
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
Handler *handler;
|
|
|
|
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
|
|
g_return_if_fail (handler_id > 0);
|
|
|
|
SIGNAL_LOCK ();
|
|
handler = handler_lookup (instance, handler_id, NULL, NULL);
|
|
if (handler)
|
|
{
|
|
if (handler->block_count)
|
|
handler->block_count -= 1;
|
|
else
|
|
g_warning (G_STRLOC ": handler '%lu' of instance '%p' is not blocked", handler_id, instance);
|
|
}
|
|
else
|
|
g_warning ("%s: instance '%p' has no handler with id '%lu'", G_STRLOC, instance, handler_id);
|
|
SIGNAL_UNLOCK ();
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
Handler *handler;
|
|
|
|
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
|
|
g_return_if_fail (handler_id > 0);
|
|
|
|
SIGNAL_LOCK ();
|
|
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_warning ("%s: instance '%p' has no handler with id '%lu'", G_STRLOC, instance, handler_id);
|
|
SIGNAL_UNLOCK ();
|
|
}
|
|
|
|
/**
|
|
* 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: 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;
|
|
}
|
|
|
|
static guint
|
|
signal_handlers_foreach_matched_R (gpointer instance,
|
|
GSignalMatchType mask,
|
|
guint signal_id,
|
|
GQuark detail,
|
|
GClosure *closure,
|
|
gpointer func,
|
|
gpointer data,
|
|
void (*callback) (gpointer instance,
|
|
gulong handler_seq_no))
|
|
{
|
|
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)
|
|
{
|
|
SIGNAL_UNLOCK ();
|
|
callback (instance, mlist->handler->sequential_number);
|
|
SIGNAL_LOCK ();
|
|
}
|
|
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: 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 an OR-ed combination of #GSignalMatchType
|
|
* flags, and the criteria values are passed as arguments.
|
|
* Passing at least one of the %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.
|
|
*
|
|
* 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_CLOSURE | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA))
|
|
{
|
|
SIGNAL_LOCK ();
|
|
n_handlers = signal_handlers_foreach_matched_R (instance, mask, signal_id, detail,
|
|
closure, func, data,
|
|
g_signal_handler_block);
|
|
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: 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 an OR-ed combination of
|
|
* #GSignalMatchType flags, and the criteria values are passed as arguments.
|
|
* Passing at least one of the %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.
|
|
*
|
|
* 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_CLOSURE | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA))
|
|
{
|
|
SIGNAL_LOCK ();
|
|
n_handlers = signal_handlers_foreach_matched_R (instance, mask, signal_id, detail,
|
|
closure, func, data,
|
|
g_signal_handler_unblock);
|
|
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: 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 an OR-ed
|
|
* combination of #GSignalMatchType flags, and the criteria values are
|
|
* passed as arguments. Passing at least one of the
|
|
* %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.
|
|
*
|
|
* 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_CLOSURE | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA))
|
|
{
|
|
SIGNAL_LOCK ();
|
|
n_handlers = signal_handlers_foreach_matched_R (instance, mask, signal_id, detail,
|
|
closure, func, data,
|
|
g_signal_handler_disconnect);
|
|
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_warning ("%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;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* 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)
|
|
{
|
|
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
|
|
|
|
SIGNAL_LOCK ();
|
|
node = LOOKUP_SIGNAL_NODE (signal_id);
|
|
if (!node || !g_type_is_a (G_TYPE_FROM_INSTANCE (instance), node->itype))
|
|
{
|
|
g_warning ("%s: signal id '%u' is invalid for instance '%p'", G_STRLOC, signal_id, instance);
|
|
SIGNAL_UNLOCK ();
|
|
return;
|
|
}
|
|
#ifdef G_ENABLE_DEBUG
|
|
if (detail && !(node->flags & G_SIGNAL_DETAILED))
|
|
{
|
|
g_warning ("%s: signal id '%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
|
|
SIGNAL_UNLOCK ();
|
|
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));
|
|
SIGNAL_UNLOCK ();
|
|
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);
|
|
SIGNAL_UNLOCK ();
|
|
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));
|
|
SIGNAL_UNLOCK ();
|
|
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 */
|
|
SIGNAL_UNLOCK ();
|
|
/* g_printerr ("omitting emission of \"%s\"\n", node->name); */
|
|
return;
|
|
}
|
|
}
|
|
|
|
SIGNAL_UNLOCK ();
|
|
signal_emit_unlocked_R (node, 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);
|
|
|
|
return continue_emission;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* 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)
|
|
{
|
|
GValue *instance_and_params;
|
|
GType signal_return_type;
|
|
GValue *param_values;
|
|
SignalNode *node;
|
|
guint i, n_params;
|
|
|
|
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 || !g_type_is_a (G_TYPE_FROM_INSTANCE (instance), node->itype))
|
|
{
|
|
g_warning ("%s: signal id '%u' is invalid for instance '%p'", G_STRLOC, signal_id, instance);
|
|
SIGNAL_UNLOCK ();
|
|
return;
|
|
}
|
|
#ifndef G_DISABLE_CHECKS
|
|
if (detail && !(node->flags & G_SIGNAL_DETAILED))
|
|
{
|
|
g_warning ("%s: signal id '%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
|
|
SIGNAL_UNLOCK ();
|
|
return;
|
|
}
|
|
#endif /* !G_DISABLE_CHECKS */
|
|
|
|
if (!node->single_va_closure_is_valid)
|
|
node_update_single_va_closure (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->return_type == G_TYPE_NONE)
|
|
{
|
|
SIGNAL_UNLOCK ();
|
|
return;
|
|
}
|
|
|
|
/* 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->flags & (G_SIGNAL_NO_RECURSE)) != 0)
|
|
fastpath = FALSE;
|
|
|
|
if (fastpath)
|
|
{
|
|
SignalAccumulator *accumulator;
|
|
Emission emission;
|
|
GValue *return_accu, accu = G_VALUE_INIT;
|
|
guint signal_id;
|
|
GType instance_type = G_TYPE_FROM_INSTANCE (instance);
|
|
GValue emission_return = G_VALUE_INIT;
|
|
GType rtype = node->return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE;
|
|
gboolean static_scope = node->return_type & G_SIGNAL_TYPE_STATIC_SCOPE;
|
|
|
|
signal_id = node->signal_id;
|
|
accumulator = node->accumulator;
|
|
if (rtype == G_TYPE_NONE)
|
|
return_accu = NULL;
|
|
else if (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;
|
|
emission.state = EMISSION_RUN;
|
|
emission.chain_type = instance_type;
|
|
emission_push (&emission);
|
|
|
|
if (fastpath_handler)
|
|
handler_ref (fastpath_handler);
|
|
|
|
SIGNAL_UNLOCK ();
|
|
|
|
TRACE(GOBJECT_SIGNAL_EMIT(signal_id, detail, instance, instance_type));
|
|
|
|
if (rtype != G_TYPE_NONE)
|
|
g_value_init (&emission_return, rtype);
|
|
|
|
if (accumulator)
|
|
g_value_init (&accu, rtype);
|
|
|
|
if (closure != NULL)
|
|
{
|
|
g_object_ref (instance);
|
|
_g_closure_invoke_va (closure,
|
|
return_accu,
|
|
instance,
|
|
var_args,
|
|
node->n_params,
|
|
node->param_types);
|
|
accumulate (&emission.ihint, &emission_return, &accu, accumulator);
|
|
}
|
|
|
|
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 (accumulator)
|
|
g_value_unset (&accu);
|
|
|
|
if (rtype != G_TYPE_NONE)
|
|
{
|
|
gchar *error = NULL;
|
|
for (i = 0; i < node->n_params; i++)
|
|
{
|
|
GType ptype = node->param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE;
|
|
G_VALUE_COLLECT_SKIP (ptype, var_args);
|
|
}
|
|
|
|
G_VALUE_LCOPY (&emission_return,
|
|
var_args,
|
|
static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,
|
|
&error);
|
|
if (!error)
|
|
g_value_unset (&emission_return);
|
|
else
|
|
{
|
|
g_warning ("%s: %s", G_STRLOC, error);
|
|
g_free (error);
|
|
/* we purposely leak the value here, it might not be
|
|
* in a sane state if an error condition occurred
|
|
*/
|
|
}
|
|
}
|
|
|
|
TRACE(GOBJECT_SIGNAL_EMIT_END(signal_id, detail, instance, instance_type));
|
|
|
|
if (closure != NULL)
|
|
g_object_unref (instance);
|
|
|
|
return;
|
|
}
|
|
}
|
|
SIGNAL_UNLOCK ();
|
|
|
|
n_params = node->n_params;
|
|
signal_return_type = node->return_type;
|
|
instance_and_params = g_alloca (sizeof (GValue) * (n_params + 1));
|
|
memset (instance_and_params, 0, sizeof (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;
|
|
|
|
G_VALUE_COLLECT_INIT (param_values + i, ptype,
|
|
var_args,
|
|
static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,
|
|
&error);
|
|
if (error)
|
|
{
|
|
g_warning ("%s: %s", G_STRLOC, error);
|
|
g_free (error);
|
|
|
|
/* we purposely leak the value here, it might not be
|
|
* in a sane state if an error condition occurred
|
|
*/
|
|
while (i--)
|
|
g_value_unset (param_values + i);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
instance_and_params->g_type = 0;
|
|
g_value_init_from_instance (instance_and_params, instance);
|
|
if (signal_return_type == G_TYPE_NONE)
|
|
signal_emit_unlocked_R (node, detail, instance, NULL, instance_and_params);
|
|
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);
|
|
|
|
signal_emit_unlocked_R (node, detail, instance, &return_value, instance_and_params);
|
|
|
|
G_VALUE_LCOPY (&return_value,
|
|
var_args,
|
|
static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,
|
|
&error);
|
|
if (!error)
|
|
g_value_unset (&return_value);
|
|
else
|
|
{
|
|
g_warning ("%s: %s", G_STRLOC, error);
|
|
g_free (error);
|
|
|
|
/* we purposely leak the value here, it might not be
|
|
* in a sane state if an error condition occurred
|
|
*/
|
|
}
|
|
}
|
|
for (i = 0; i < n_params; i++)
|
|
g_value_unset (param_values + i);
|
|
g_value_unset (instance_and_params);
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* 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.
|
|
*
|
|
* Emits a signal.
|
|
*
|
|
* 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);
|
|
SIGNAL_UNLOCK ();
|
|
|
|
if (signal_id)
|
|
{
|
|
va_list var_args;
|
|
|
|
va_start (var_args, detailed_signal);
|
|
g_signal_emit_valist (instance, signal_id, detail, var_args);
|
|
va_end (var_args);
|
|
}
|
|
else
|
|
g_warning ("%s: signal name '%s' is invalid for instance '%p' of type '%s'",
|
|
G_STRLOC, detailed_signal, instance, g_type_name (itype));
|
|
}
|
|
|
|
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;
|
|
|
|
TRACE(GOBJECT_SIGNAL_EMIT(node->signal_id, detail, instance, G_TYPE_FROM_INSTANCE (instance)));
|
|
|
|
SIGNAL_LOCK ();
|
|
signal_id = node->signal_id;
|
|
|
|
if (node->flags & G_SIGNAL_NO_RECURSE)
|
|
{
|
|
Emission *node = emission_find (signal_id, detail, instance);
|
|
|
|
if (node)
|
|
{
|
|
node->state = EMISSION_RESTART;
|
|
SIGNAL_UNLOCK ();
|
|
return return_value_altered;
|
|
}
|
|
}
|
|
accumulator = node->accumulator;
|
|
if (accumulator)
|
|
{
|
|
SIGNAL_UNLOCK ();
|
|
g_value_init (&accu, node->return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
return_accu = &accu;
|
|
SIGNAL_LOCK ();
|
|
}
|
|
else
|
|
return_accu = emission_return;
|
|
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;
|
|
|
|
if ((node->flags & G_SIGNAL_RUN_FIRST) && class_closure)
|
|
{
|
|
emission.state = EMISSION_RUN;
|
|
|
|
emission.chain_type = G_TYPE_FROM_INSTANCE (instance);
|
|
SIGNAL_UNLOCK ();
|
|
g_closure_invoke (class_closure,
|
|
return_accu,
|
|
node->n_params + 1,
|
|
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)
|
|
{
|
|
gboolean need_destroy, was_in_call, may_recurse = TRUE;
|
|
GHook *hook;
|
|
|
|
emission.state = EMISSION_HOOK;
|
|
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)
|
|
{
|
|
GSignalEmissionHook hook_func = (GSignalEmissionHook) hook->func;
|
|
|
|
was_in_call = G_HOOK_IN_CALL (hook);
|
|
hook->flags |= G_HOOK_FLAG_IN_CALL;
|
|
SIGNAL_UNLOCK ();
|
|
need_destroy = !hook_func (&emission.ihint, node->n_params + 1, instance_and_params, hook->data);
|
|
SIGNAL_LOCK ();
|
|
if (!was_in_call)
|
|
hook->flags &= ~G_HOOK_FLAG_IN_CALL;
|
|
if (need_destroy)
|
|
g_hook_destroy_link (node->emission_hooks, hook);
|
|
}
|
|
hook = g_hook_next_valid (node->emission_hooks, hook, may_recurse);
|
|
}
|
|
|
|
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 ();
|
|
g_closure_invoke (handler->closure,
|
|
return_accu,
|
|
node->n_params + 1,
|
|
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_LAST;
|
|
|
|
if ((node->flags & G_SIGNAL_RUN_LAST) && class_closure)
|
|
{
|
|
emission.state = EMISSION_RUN;
|
|
|
|
emission.chain_type = G_TYPE_FROM_INSTANCE (instance);
|
|
SIGNAL_UNLOCK ();
|
|
g_closure_invoke (class_closure,
|
|
return_accu,
|
|
node->n_params + 1,
|
|
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 ();
|
|
g_closure_invoke (handler->closure,
|
|
return_accu,
|
|
node->n_params + 1,
|
|
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_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,
|
|
node->n_params + 1,
|
|
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);
|
|
SIGNAL_UNLOCK ();
|
|
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);
|
|
|
|
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.
|
|
*
|
|
* 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.
|
|
*
|
|
* A macro is also included that allows this function to be used without
|
|
* pointer casts.
|
|
*
|
|
* Since: 2.62
|
|
*/
|
|
#undef g_clear_signal_handler
|
|
void
|
|
g_clear_signal_handler (gulong *handler_id_ptr,
|
|
gpointer instance)
|
|
{
|
|
g_return_if_fail (handler_id_ptr != NULL);
|
|
|
|
if (*handler_id_ptr != 0)
|
|
{
|
|
g_signal_handler_disconnect (instance, *handler_id_ptr);
|
|
*handler_id_ptr = 0;
|
|
}
|
|
}
|