mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-27 07:56:14 +01:00
18a33f72db
If we have an input parameter (or return value) we need to use (nullable). However, if it is an (inout) or (out) parameter, (optional) is sufficient. It looks like (nullable) could be used for everything according to the Annotation documentation, but (optional) is more specific.
4306 lines
135 KiB
C
4306 lines
135 KiB
C
/* GObject - GLib Type, Object, Parameter and Signal Library
|
|
* Copyright (C) 1998-1999, 2000-2001 Tim Janik and Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General
|
|
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* MT safe with regards to reference counting.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
|
|
#include "gobject.h"
|
|
#include "gtype-private.h"
|
|
#include "gvaluecollector.h"
|
|
#include "gsignal.h"
|
|
#include "gparamspecs.h"
|
|
#include "gvaluetypes.h"
|
|
#include "gobject_trace.h"
|
|
#include "gconstructor.h"
|
|
|
|
/**
|
|
* SECTION:objects
|
|
* @title: GObject
|
|
* @short_description: The base object type
|
|
* @see_also: #GParamSpecObject, g_param_spec_object()
|
|
*
|
|
* GObject is the fundamental type providing the common attributes and
|
|
* methods for all object types in GTK+, Pango and other libraries
|
|
* based on GObject. The GObject class provides methods for object
|
|
* construction and destruction, property access methods, and signal
|
|
* support. Signals are described in detail [here][gobject-Signals].
|
|
*
|
|
* For a tutorial on implementing a new GObject class, see [How to define and
|
|
* implement a new GObject][howto-gobject]. For a list of naming conventions for
|
|
* GObjects and their methods, see the [GType conventions][gtype-conventions].
|
|
* For the high-level concepts behind GObject, read [Instantiable classed types:
|
|
* Objects][gtype-instantiable-classed].
|
|
*
|
|
* ## Floating references # {#floating-ref}
|
|
*
|
|
* GInitiallyUnowned is derived from GObject. The only difference between
|
|
* the two is that the initial reference of a GInitiallyUnowned is flagged
|
|
* as a "floating" reference. This means that it is not specifically
|
|
* claimed to be "owned" by any code portion. The main motivation for
|
|
* providing floating references is C convenience. In particular, it
|
|
* allows code to be written as:
|
|
* |[<!-- language="C" -->
|
|
* container = create_container ();
|
|
* container_add_child (container, create_child());
|
|
* ]|
|
|
* If container_add_child() calls g_object_ref_sink() on the passed-in child,
|
|
* no reference of the newly created child is leaked. Without floating
|
|
* references, container_add_child() can only g_object_ref() the new child,
|
|
* so to implement this code without reference leaks, it would have to be
|
|
* written as:
|
|
* |[<!-- language="C" -->
|
|
* Child *child;
|
|
* container = create_container ();
|
|
* child = create_child ();
|
|
* container_add_child (container, child);
|
|
* g_object_unref (child);
|
|
* ]|
|
|
* The floating reference can be converted into an ordinary reference by
|
|
* calling g_object_ref_sink(). For already sunken objects (objects that
|
|
* don't have a floating reference anymore), g_object_ref_sink() is equivalent
|
|
* to g_object_ref() and returns a new reference.
|
|
*
|
|
* Since floating references are useful almost exclusively for C convenience,
|
|
* language bindings that provide automated reference and memory ownership
|
|
* maintenance (such as smart pointers or garbage collection) should not
|
|
* expose floating references in their API.
|
|
*
|
|
* Some object implementations may need to save an objects floating state
|
|
* across certain code portions (an example is #GtkMenu), to achieve this,
|
|
* the following sequence can be used:
|
|
*
|
|
* |[<!-- language="C" -->
|
|
* // save floating state
|
|
* gboolean was_floating = g_object_is_floating (object);
|
|
* g_object_ref_sink (object);
|
|
* // protected code portion
|
|
*
|
|
* ...
|
|
*
|
|
* // restore floating state
|
|
* if (was_floating)
|
|
* g_object_force_floating (object);
|
|
* else
|
|
* g_object_unref (object); // release previously acquired reference
|
|
* ]|
|
|
*/
|
|
|
|
|
|
/* --- macros --- */
|
|
#define PARAM_SPEC_PARAM_ID(pspec) ((pspec)->param_id)
|
|
#define PARAM_SPEC_SET_PARAM_ID(pspec, id) ((pspec)->param_id = (id))
|
|
|
|
#define OBJECT_HAS_TOGGLE_REF_FLAG 0x1
|
|
#define OBJECT_HAS_TOGGLE_REF(object) \
|
|
((g_datalist_get_flags (&(object)->qdata) & OBJECT_HAS_TOGGLE_REF_FLAG) != 0)
|
|
#define OBJECT_FLOATING_FLAG 0x2
|
|
|
|
#define CLASS_HAS_PROPS_FLAG 0x1
|
|
#define CLASS_HAS_PROPS(class) \
|
|
((class)->flags & CLASS_HAS_PROPS_FLAG)
|
|
#define CLASS_HAS_CUSTOM_CONSTRUCTOR(class) \
|
|
((class)->constructor != g_object_constructor)
|
|
#define CLASS_HAS_CUSTOM_CONSTRUCTED(class) \
|
|
((class)->constructed != g_object_constructed)
|
|
|
|
#define CLASS_HAS_DERIVED_CLASS_FLAG 0x2
|
|
#define CLASS_HAS_DERIVED_CLASS(class) \
|
|
((class)->flags & CLASS_HAS_DERIVED_CLASS_FLAG)
|
|
|
|
/* --- signals --- */
|
|
enum {
|
|
NOTIFY,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
|
|
/* --- properties --- */
|
|
enum {
|
|
PROP_NONE
|
|
};
|
|
|
|
|
|
/* --- prototypes --- */
|
|
static void g_object_base_class_init (GObjectClass *class);
|
|
static void g_object_base_class_finalize (GObjectClass *class);
|
|
static void g_object_do_class_init (GObjectClass *class);
|
|
static void g_object_init (GObject *object,
|
|
GObjectClass *class);
|
|
static GObject* g_object_constructor (GType type,
|
|
guint n_construct_properties,
|
|
GObjectConstructParam *construct_params);
|
|
static void g_object_constructed (GObject *object);
|
|
static void g_object_real_dispose (GObject *object);
|
|
static void g_object_finalize (GObject *object);
|
|
static void g_object_do_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void g_object_do_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
static void g_value_object_init (GValue *value);
|
|
static void g_value_object_free_value (GValue *value);
|
|
static void g_value_object_copy_value (const GValue *src_value,
|
|
GValue *dest_value);
|
|
static void g_value_object_transform_value (const GValue *src_value,
|
|
GValue *dest_value);
|
|
static gpointer g_value_object_peek_pointer (const GValue *value);
|
|
static gchar* g_value_object_collect_value (GValue *value,
|
|
guint n_collect_values,
|
|
GTypeCValue *collect_values,
|
|
guint collect_flags);
|
|
static gchar* g_value_object_lcopy_value (const GValue *value,
|
|
guint n_collect_values,
|
|
GTypeCValue *collect_values,
|
|
guint collect_flags);
|
|
static void g_object_dispatch_properties_changed (GObject *object,
|
|
guint n_pspecs,
|
|
GParamSpec **pspecs);
|
|
static guint object_floating_flag_handler (GObject *object,
|
|
gint job);
|
|
|
|
static void object_interface_check_properties (gpointer check_data,
|
|
gpointer g_iface);
|
|
|
|
/* --- typedefs --- */
|
|
typedef struct _GObjectNotifyQueue GObjectNotifyQueue;
|
|
|
|
struct _GObjectNotifyQueue
|
|
{
|
|
GSList *pspecs;
|
|
guint16 n_pspecs;
|
|
guint16 freeze_count;
|
|
};
|
|
|
|
/* --- variables --- */
|
|
G_LOCK_DEFINE_STATIC (closure_array_mutex);
|
|
G_LOCK_DEFINE_STATIC (weak_refs_mutex);
|
|
G_LOCK_DEFINE_STATIC (toggle_refs_mutex);
|
|
static GQuark quark_closure_array = 0;
|
|
static GQuark quark_weak_refs = 0;
|
|
static GQuark quark_toggle_refs = 0;
|
|
static GQuark quark_notify_queue;
|
|
static GQuark quark_in_construction;
|
|
static GParamSpecPool *pspec_pool = NULL;
|
|
static gulong gobject_signals[LAST_SIGNAL] = { 0, };
|
|
static guint (*floating_flag_handler) (GObject*, gint) = object_floating_flag_handler;
|
|
/* qdata pointing to GSList<GWeakRef *>, protected by weak_locations_lock */
|
|
static GQuark quark_weak_locations = 0;
|
|
static GRWLock weak_locations_lock;
|
|
|
|
G_LOCK_DEFINE_STATIC(notify_lock);
|
|
|
|
/* --- functions --- */
|
|
static void
|
|
g_object_notify_queue_free (gpointer data)
|
|
{
|
|
GObjectNotifyQueue *nqueue = data;
|
|
|
|
g_slist_free (nqueue->pspecs);
|
|
g_slice_free (GObjectNotifyQueue, nqueue);
|
|
}
|
|
|
|
static GObjectNotifyQueue*
|
|
g_object_notify_queue_freeze (GObject *object,
|
|
gboolean conditional)
|
|
{
|
|
GObjectNotifyQueue *nqueue;
|
|
|
|
G_LOCK(notify_lock);
|
|
nqueue = g_datalist_id_get_data (&object->qdata, quark_notify_queue);
|
|
if (!nqueue)
|
|
{
|
|
if (conditional)
|
|
{
|
|
G_UNLOCK(notify_lock);
|
|
return NULL;
|
|
}
|
|
|
|
nqueue = g_slice_new0 (GObjectNotifyQueue);
|
|
g_datalist_id_set_data_full (&object->qdata, quark_notify_queue,
|
|
nqueue, g_object_notify_queue_free);
|
|
}
|
|
|
|
if (nqueue->freeze_count >= 65535)
|
|
g_critical("Free queue for %s (%p) is larger than 65535,"
|
|
" called g_object_freeze_notify() too often."
|
|
" Forgot to call g_object_thaw_notify() or infinite loop",
|
|
G_OBJECT_TYPE_NAME (object), object);
|
|
else
|
|
nqueue->freeze_count++;
|
|
G_UNLOCK(notify_lock);
|
|
|
|
return nqueue;
|
|
}
|
|
|
|
static void
|
|
g_object_notify_queue_thaw (GObject *object,
|
|
GObjectNotifyQueue *nqueue)
|
|
{
|
|
GParamSpec *pspecs_mem[16], **pspecs, **free_me = NULL;
|
|
GSList *slist;
|
|
guint n_pspecs = 0;
|
|
|
|
g_return_if_fail (nqueue->freeze_count > 0);
|
|
g_return_if_fail (g_atomic_int_get(&object->ref_count) > 0);
|
|
|
|
G_LOCK(notify_lock);
|
|
|
|
/* Just make sure we never get into some nasty race condition */
|
|
if (G_UNLIKELY(nqueue->freeze_count == 0)) {
|
|
G_UNLOCK(notify_lock);
|
|
g_warning ("%s: property-changed notification for %s(%p) is not frozen",
|
|
G_STRFUNC, G_OBJECT_TYPE_NAME (object), object);
|
|
return;
|
|
}
|
|
|
|
nqueue->freeze_count--;
|
|
if (nqueue->freeze_count) {
|
|
G_UNLOCK(notify_lock);
|
|
return;
|
|
}
|
|
|
|
pspecs = nqueue->n_pspecs > 16 ? free_me = g_new (GParamSpec*, nqueue->n_pspecs) : pspecs_mem;
|
|
|
|
for (slist = nqueue->pspecs; slist; slist = slist->next)
|
|
{
|
|
pspecs[n_pspecs++] = slist->data;
|
|
}
|
|
g_datalist_id_set_data (&object->qdata, quark_notify_queue, NULL);
|
|
|
|
G_UNLOCK(notify_lock);
|
|
|
|
if (n_pspecs)
|
|
G_OBJECT_GET_CLASS (object)->dispatch_properties_changed (object, n_pspecs, pspecs);
|
|
g_free (free_me);
|
|
}
|
|
|
|
static void
|
|
g_object_notify_queue_add (GObject *object,
|
|
GObjectNotifyQueue *nqueue,
|
|
GParamSpec *pspec)
|
|
{
|
|
G_LOCK(notify_lock);
|
|
|
|
g_assert (nqueue->n_pspecs < 65535);
|
|
|
|
if (g_slist_find (nqueue->pspecs, pspec) == NULL)
|
|
{
|
|
nqueue->pspecs = g_slist_prepend (nqueue->pspecs, pspec);
|
|
nqueue->n_pspecs++;
|
|
}
|
|
|
|
G_UNLOCK(notify_lock);
|
|
}
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
G_LOCK_DEFINE_STATIC (debug_objects);
|
|
static guint debug_objects_count = 0;
|
|
static GHashTable *debug_objects_ht = NULL;
|
|
|
|
static void
|
|
debug_objects_foreach (gpointer key,
|
|
gpointer value,
|
|
gpointer user_data)
|
|
{
|
|
GObject *object = value;
|
|
|
|
g_message ("[%p] stale %s\tref_count=%u",
|
|
object,
|
|
G_OBJECT_TYPE_NAME (object),
|
|
object->ref_count);
|
|
}
|
|
|
|
#ifdef G_HAS_CONSTRUCTORS
|
|
#ifdef G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA
|
|
#pragma G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(debug_objects_atexit)
|
|
#endif
|
|
G_DEFINE_DESTRUCTOR(debug_objects_atexit)
|
|
#endif /* G_HAS_CONSTRUCTORS */
|
|
|
|
static void
|
|
debug_objects_atexit (void)
|
|
{
|
|
GOBJECT_IF_DEBUG (OBJECTS,
|
|
{
|
|
G_LOCK (debug_objects);
|
|
g_message ("stale GObjects: %u", debug_objects_count);
|
|
g_hash_table_foreach (debug_objects_ht, debug_objects_foreach, NULL);
|
|
G_UNLOCK (debug_objects);
|
|
});
|
|
}
|
|
#endif /* G_ENABLE_DEBUG */
|
|
|
|
void
|
|
_g_object_type_init (void)
|
|
{
|
|
static gboolean initialized = FALSE;
|
|
static const GTypeFundamentalInfo finfo = {
|
|
G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE | G_TYPE_FLAG_DERIVABLE | G_TYPE_FLAG_DEEP_DERIVABLE,
|
|
};
|
|
GTypeInfo info = {
|
|
sizeof (GObjectClass),
|
|
(GBaseInitFunc) g_object_base_class_init,
|
|
(GBaseFinalizeFunc) g_object_base_class_finalize,
|
|
(GClassInitFunc) g_object_do_class_init,
|
|
NULL /* class_destroy */,
|
|
NULL /* class_data */,
|
|
sizeof (GObject),
|
|
0 /* n_preallocs */,
|
|
(GInstanceInitFunc) g_object_init,
|
|
NULL, /* value_table */
|
|
};
|
|
static const GTypeValueTable value_table = {
|
|
g_value_object_init, /* value_init */
|
|
g_value_object_free_value, /* value_free */
|
|
g_value_object_copy_value, /* value_copy */
|
|
g_value_object_peek_pointer, /* value_peek_pointer */
|
|
"p", /* collect_format */
|
|
g_value_object_collect_value, /* collect_value */
|
|
"p", /* lcopy_format */
|
|
g_value_object_lcopy_value, /* lcopy_value */
|
|
};
|
|
GType type;
|
|
|
|
g_return_if_fail (initialized == FALSE);
|
|
initialized = TRUE;
|
|
|
|
/* G_TYPE_OBJECT
|
|
*/
|
|
info.value_table = &value_table;
|
|
type = g_type_register_fundamental (G_TYPE_OBJECT, g_intern_static_string ("GObject"), &info, &finfo, 0);
|
|
g_assert (type == G_TYPE_OBJECT);
|
|
g_value_register_transform_func (G_TYPE_OBJECT, G_TYPE_OBJECT, g_value_object_transform_value);
|
|
|
|
#if G_ENABLE_DEBUG
|
|
/* We cannot use GOBJECT_IF_DEBUG here because of the G_HAS_CONSTRUCTORS
|
|
* conditional in between, as the C spec leaves conditionals inside macro
|
|
* expansions as undefined behavior. Only GCC and Clang are known to work
|
|
* but compilation breaks on MSVC.
|
|
*
|
|
* See: https://bugzilla.gnome.org/show_bug.cgi?id=769504
|
|
*/
|
|
if (_g_type_debug_flags & G_TYPE_DEBUG_OBJECTS) \
|
|
{
|
|
debug_objects_ht = g_hash_table_new (g_direct_hash, NULL);
|
|
# ifndef G_HAS_CONSTRUCTORS
|
|
g_atexit (debug_objects_atexit);
|
|
# endif /* G_HAS_CONSTRUCTORS */
|
|
}
|
|
#endif /* G_ENABLE_DEBUG */
|
|
}
|
|
|
|
static void
|
|
g_object_base_class_init (GObjectClass *class)
|
|
{
|
|
GObjectClass *pclass = g_type_class_peek_parent (class);
|
|
|
|
/* Don't inherit HAS_DERIVED_CLASS flag from parent class */
|
|
class->flags &= ~CLASS_HAS_DERIVED_CLASS_FLAG;
|
|
|
|
if (pclass)
|
|
pclass->flags |= CLASS_HAS_DERIVED_CLASS_FLAG;
|
|
|
|
/* reset instance specific fields and methods that don't get inherited */
|
|
class->construct_properties = pclass ? g_slist_copy (pclass->construct_properties) : NULL;
|
|
class->get_property = NULL;
|
|
class->set_property = NULL;
|
|
}
|
|
|
|
static void
|
|
g_object_base_class_finalize (GObjectClass *class)
|
|
{
|
|
GList *list, *node;
|
|
|
|
_g_signals_destroy (G_OBJECT_CLASS_TYPE (class));
|
|
|
|
g_slist_free (class->construct_properties);
|
|
class->construct_properties = NULL;
|
|
list = g_param_spec_pool_list_owned (pspec_pool, G_OBJECT_CLASS_TYPE (class));
|
|
for (node = list; node; node = node->next)
|
|
{
|
|
GParamSpec *pspec = node->data;
|
|
|
|
g_param_spec_pool_remove (pspec_pool, pspec);
|
|
PARAM_SPEC_SET_PARAM_ID (pspec, 0);
|
|
g_param_spec_unref (pspec);
|
|
}
|
|
g_list_free (list);
|
|
}
|
|
|
|
static void
|
|
g_object_do_class_init (GObjectClass *class)
|
|
{
|
|
/* read the comment about typedef struct CArray; on why not to change this quark */
|
|
quark_closure_array = g_quark_from_static_string ("GObject-closure-array");
|
|
|
|
quark_weak_refs = g_quark_from_static_string ("GObject-weak-references");
|
|
quark_weak_locations = g_quark_from_static_string ("GObject-weak-locations");
|
|
quark_toggle_refs = g_quark_from_static_string ("GObject-toggle-references");
|
|
quark_notify_queue = g_quark_from_static_string ("GObject-notify-queue");
|
|
quark_in_construction = g_quark_from_static_string ("GObject-in-construction");
|
|
pspec_pool = g_param_spec_pool_new (TRUE);
|
|
|
|
class->constructor = g_object_constructor;
|
|
class->constructed = g_object_constructed;
|
|
class->set_property = g_object_do_set_property;
|
|
class->get_property = g_object_do_get_property;
|
|
class->dispose = g_object_real_dispose;
|
|
class->finalize = g_object_finalize;
|
|
class->dispatch_properties_changed = g_object_dispatch_properties_changed;
|
|
class->notify = NULL;
|
|
|
|
/**
|
|
* GObject::notify:
|
|
* @gobject: the object which received the signal.
|
|
* @pspec: the #GParamSpec of the property which changed.
|
|
*
|
|
* The notify signal is emitted on an object when one of its
|
|
* properties has been changed. Note that getting this signal
|
|
* doesn't guarantee that the value of the property has actually
|
|
* changed, it may also be emitted when the setter for the property
|
|
* is called to reinstate the previous value.
|
|
*
|
|
* This signal is typically used to obtain change notification for a
|
|
* single property, by specifying the property name as a detail in the
|
|
* g_signal_connect() call, like this:
|
|
* |[<!-- language="C" -->
|
|
* g_signal_connect (text_view->buffer, "notify::paste-target-list",
|
|
* G_CALLBACK (gtk_text_view_target_list_notify),
|
|
* text_view)
|
|
* ]|
|
|
* It is important to note that you must use
|
|
* [canonical][canonical-parameter-name] parameter names as
|
|
* detail strings for the notify signal.
|
|
*/
|
|
gobject_signals[NOTIFY] =
|
|
g_signal_new (g_intern_static_string ("notify"),
|
|
G_TYPE_FROM_CLASS (class),
|
|
G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS | G_SIGNAL_ACTION,
|
|
G_STRUCT_OFFSET (GObjectClass, notify),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__PARAM,
|
|
G_TYPE_NONE,
|
|
1, G_TYPE_PARAM);
|
|
|
|
/* Install a check function that we'll use to verify that classes that
|
|
* implement an interface implement all properties for that interface
|
|
*/
|
|
g_type_add_interface_check (NULL, object_interface_check_properties);
|
|
}
|
|
|
|
static inline void
|
|
install_property_internal (GType g_type,
|
|
guint property_id,
|
|
GParamSpec *pspec)
|
|
{
|
|
if (g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type, FALSE))
|
|
{
|
|
g_warning ("When installing property: type '%s' already has a property named '%s'",
|
|
g_type_name (g_type),
|
|
pspec->name);
|
|
return;
|
|
}
|
|
|
|
g_param_spec_ref_sink (pspec);
|
|
PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
|
|
g_param_spec_pool_insert (pspec_pool, pspec, g_type);
|
|
}
|
|
|
|
/**
|
|
* g_object_class_install_property:
|
|
* @oclass: a #GObjectClass
|
|
* @property_id: the id for the new property
|
|
* @pspec: the #GParamSpec for the new property
|
|
*
|
|
* Installs a new property.
|
|
*
|
|
* All properties should be installed during the class initializer. It
|
|
* is possible to install properties after that, but doing so is not
|
|
* recommend, and specifically, is not guaranteed to be thread-safe vs.
|
|
* use of properties on the same type on other threads.
|
|
*
|
|
* Note that it is possible to redefine a property in a derived class,
|
|
* by installing a property with the same name. This can be useful at times,
|
|
* e.g. to change the range of allowed values or the default value.
|
|
*/
|
|
void
|
|
g_object_class_install_property (GObjectClass *class,
|
|
guint property_id,
|
|
GParamSpec *pspec)
|
|
{
|
|
g_return_if_fail (G_IS_OBJECT_CLASS (class));
|
|
g_return_if_fail (G_IS_PARAM_SPEC (pspec));
|
|
|
|
if (CLASS_HAS_DERIVED_CLASS (class))
|
|
g_error ("Attempt to add property %s::%s to class after it was derived", G_OBJECT_CLASS_NAME (class), pspec->name);
|
|
|
|
class->flags |= CLASS_HAS_PROPS_FLAG;
|
|
|
|
g_return_if_fail (pspec->flags & (G_PARAM_READABLE | G_PARAM_WRITABLE));
|
|
if (pspec->flags & G_PARAM_WRITABLE)
|
|
g_return_if_fail (class->set_property != NULL);
|
|
if (pspec->flags & G_PARAM_READABLE)
|
|
g_return_if_fail (class->get_property != NULL);
|
|
g_return_if_fail (property_id > 0);
|
|
g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */
|
|
if (pspec->flags & G_PARAM_CONSTRUCT)
|
|
g_return_if_fail ((pspec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
|
|
if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
|
|
g_return_if_fail (pspec->flags & G_PARAM_WRITABLE);
|
|
|
|
install_property_internal (G_OBJECT_CLASS_TYPE (class), property_id, pspec);
|
|
|
|
if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
|
|
class->construct_properties = g_slist_append (class->construct_properties, pspec);
|
|
|
|
/* for property overrides of construct properties, we have to get rid
|
|
* of the overidden inherited construct property
|
|
*/
|
|
pspec = g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type_parent (G_OBJECT_CLASS_TYPE (class)), TRUE);
|
|
if (pspec && pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
|
|
class->construct_properties = g_slist_remove (class->construct_properties, pspec);
|
|
}
|
|
|
|
/**
|
|
* g_object_class_install_properties:
|
|
* @oclass: a #GObjectClass
|
|
* @n_pspecs: the length of the #GParamSpecs array
|
|
* @pspecs: (array length=n_pspecs): the #GParamSpecs array
|
|
* defining the new properties
|
|
*
|
|
* Installs new properties from an array of #GParamSpecs.
|
|
*
|
|
* All properties should be installed during the class initializer. It
|
|
* is possible to install properties after that, but doing so is not
|
|
* recommend, and specifically, is not guaranteed to be thread-safe vs.
|
|
* use of properties on the same type on other threads.
|
|
*
|
|
* The property id of each property is the index of each #GParamSpec in
|
|
* the @pspecs array.
|
|
*
|
|
* The property id of 0 is treated specially by #GObject and it should not
|
|
* be used to store a #GParamSpec.
|
|
*
|
|
* This function should be used if you plan to use a static array of
|
|
* #GParamSpecs and g_object_notify_by_pspec(). For instance, this
|
|
* class initialization:
|
|
*
|
|
* |[<!-- language="C" -->
|
|
* enum {
|
|
* PROP_0, PROP_FOO, PROP_BAR, N_PROPERTIES
|
|
* };
|
|
*
|
|
* static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
|
|
*
|
|
* static void
|
|
* my_object_class_init (MyObjectClass *klass)
|
|
* {
|
|
* GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
*
|
|
* obj_properties[PROP_FOO] =
|
|
* g_param_spec_int ("foo", "Foo", "Foo",
|
|
* -1, G_MAXINT,
|
|
* 0,
|
|
* G_PARAM_READWRITE);
|
|
*
|
|
* obj_properties[PROP_BAR] =
|
|
* g_param_spec_string ("bar", "Bar", "Bar",
|
|
* NULL,
|
|
* G_PARAM_READWRITE);
|
|
*
|
|
* gobject_class->set_property = my_object_set_property;
|
|
* gobject_class->get_property = my_object_get_property;
|
|
* g_object_class_install_properties (gobject_class,
|
|
* N_PROPERTIES,
|
|
* obj_properties);
|
|
* }
|
|
* ]|
|
|
*
|
|
* allows calling g_object_notify_by_pspec() to notify of property changes:
|
|
*
|
|
* |[<!-- language="C" -->
|
|
* void
|
|
* my_object_set_foo (MyObject *self, gint foo)
|
|
* {
|
|
* if (self->foo != foo)
|
|
* {
|
|
* self->foo = foo;
|
|
* g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_FOO]);
|
|
* }
|
|
* }
|
|
* ]|
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
void
|
|
g_object_class_install_properties (GObjectClass *oclass,
|
|
guint n_pspecs,
|
|
GParamSpec **pspecs)
|
|
{
|
|
GType oclass_type, parent_type;
|
|
gint i;
|
|
|
|
g_return_if_fail (G_IS_OBJECT_CLASS (oclass));
|
|
g_return_if_fail (n_pspecs > 1);
|
|
g_return_if_fail (pspecs[0] == NULL);
|
|
|
|
if (CLASS_HAS_DERIVED_CLASS (oclass))
|
|
g_error ("Attempt to add properties to %s after it was derived",
|
|
G_OBJECT_CLASS_NAME (oclass));
|
|
|
|
oclass_type = G_OBJECT_CLASS_TYPE (oclass);
|
|
parent_type = g_type_parent (oclass_type);
|
|
|
|
/* we skip the first element of the array as it would have a 0 prop_id */
|
|
for (i = 1; i < n_pspecs; i++)
|
|
{
|
|
GParamSpec *pspec = pspecs[i];
|
|
|
|
g_return_if_fail (pspec != NULL);
|
|
|
|
if (pspec->flags & G_PARAM_WRITABLE)
|
|
g_return_if_fail (oclass->set_property != NULL);
|
|
if (pspec->flags & G_PARAM_READABLE)
|
|
g_return_if_fail (oclass->get_property != NULL);
|
|
g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */
|
|
if (pspec->flags & G_PARAM_CONSTRUCT)
|
|
g_return_if_fail ((pspec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
|
|
if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
|
|
g_return_if_fail (pspec->flags & G_PARAM_WRITABLE);
|
|
|
|
oclass->flags |= CLASS_HAS_PROPS_FLAG;
|
|
install_property_internal (oclass_type, i, pspec);
|
|
|
|
if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
|
|
oclass->construct_properties = g_slist_append (oclass->construct_properties, pspec);
|
|
|
|
/* for property overrides of construct properties, we have to get rid
|
|
* of the overidden inherited construct property
|
|
*/
|
|
pspec = g_param_spec_pool_lookup (pspec_pool, pspec->name, parent_type, TRUE);
|
|
if (pspec && pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
|
|
oclass->construct_properties = g_slist_remove (oclass->construct_properties, pspec);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* g_object_interface_install_property:
|
|
* @g_iface: (type GObject.TypeInterface): any interface vtable for the
|
|
* interface, or the default
|
|
* vtable for the interface.
|
|
* @pspec: the #GParamSpec for the new property
|
|
*
|
|
* Add a property to an interface; this is only useful for interfaces
|
|
* that are added to GObject-derived types. Adding a property to an
|
|
* interface forces all objects classes with that interface to have a
|
|
* compatible property. The compatible property could be a newly
|
|
* created #GParamSpec, but normally
|
|
* g_object_class_override_property() will be used so that the object
|
|
* class only needs to provide an implementation and inherits the
|
|
* property description, default value, bounds, and so forth from the
|
|
* interface property.
|
|
*
|
|
* This function is meant to be called from the interface's default
|
|
* vtable initialization function (the @class_init member of
|
|
* #GTypeInfo.) It must not be called after after @class_init has
|
|
* been called for any object types implementing this interface.
|
|
*
|
|
* Since: 2.4
|
|
*/
|
|
void
|
|
g_object_interface_install_property (gpointer g_iface,
|
|
GParamSpec *pspec)
|
|
{
|
|
GTypeInterface *iface_class = g_iface;
|
|
|
|
g_return_if_fail (G_TYPE_IS_INTERFACE (iface_class->g_type));
|
|
g_return_if_fail (G_IS_PARAM_SPEC (pspec));
|
|
g_return_if_fail (!G_IS_PARAM_SPEC_OVERRIDE (pspec)); /* paranoid */
|
|
g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */
|
|
|
|
g_return_if_fail (pspec->flags & (G_PARAM_READABLE | G_PARAM_WRITABLE));
|
|
if (pspec->flags & G_PARAM_CONSTRUCT)
|
|
g_return_if_fail ((pspec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
|
|
if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
|
|
g_return_if_fail (pspec->flags & G_PARAM_WRITABLE);
|
|
|
|
install_property_internal (iface_class->g_type, 0, pspec);
|
|
}
|
|
|
|
/**
|
|
* g_object_class_find_property:
|
|
* @oclass: a #GObjectClass
|
|
* @property_name: the name of the property to look up
|
|
*
|
|
* Looks up the #GParamSpec for a property of a class.
|
|
*
|
|
* Returns: (transfer none): the #GParamSpec for the property, or
|
|
* %NULL if the class doesn't have a property of that name
|
|
*/
|
|
GParamSpec*
|
|
g_object_class_find_property (GObjectClass *class,
|
|
const gchar *property_name)
|
|
{
|
|
GParamSpec *pspec;
|
|
GParamSpec *redirect;
|
|
|
|
g_return_val_if_fail (G_IS_OBJECT_CLASS (class), NULL);
|
|
g_return_val_if_fail (property_name != NULL, NULL);
|
|
|
|
pspec = g_param_spec_pool_lookup (pspec_pool,
|
|
property_name,
|
|
G_OBJECT_CLASS_TYPE (class),
|
|
TRUE);
|
|
if (pspec)
|
|
{
|
|
redirect = g_param_spec_get_redirect_target (pspec);
|
|
if (redirect)
|
|
return redirect;
|
|
else
|
|
return pspec;
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* g_object_interface_find_property:
|
|
* @g_iface: (type GObject.TypeInterface): any interface vtable for the
|
|
* interface, or the default vtable for the interface
|
|
* @property_name: name of a property to lookup.
|
|
*
|
|
* Find the #GParamSpec with the given name for an
|
|
* interface. Generally, the interface vtable passed in as @g_iface
|
|
* will be the default vtable from g_type_default_interface_ref(), or,
|
|
* if you know the interface has already been loaded,
|
|
* g_type_default_interface_peek().
|
|
*
|
|
* Since: 2.4
|
|
*
|
|
* Returns: (transfer none): the #GParamSpec for the property of the
|
|
* interface with the name @property_name, or %NULL if no
|
|
* such property exists.
|
|
*/
|
|
GParamSpec*
|
|
g_object_interface_find_property (gpointer g_iface,
|
|
const gchar *property_name)
|
|
{
|
|
GTypeInterface *iface_class = g_iface;
|
|
|
|
g_return_val_if_fail (G_TYPE_IS_INTERFACE (iface_class->g_type), NULL);
|
|
g_return_val_if_fail (property_name != NULL, NULL);
|
|
|
|
return g_param_spec_pool_lookup (pspec_pool,
|
|
property_name,
|
|
iface_class->g_type,
|
|
FALSE);
|
|
}
|
|
|
|
/**
|
|
* g_object_class_override_property:
|
|
* @oclass: a #GObjectClass
|
|
* @property_id: the new property ID
|
|
* @name: the name of a property registered in a parent class or
|
|
* in an interface of this class.
|
|
*
|
|
* Registers @property_id as referring to a property with the name
|
|
* @name in a parent class or in an interface implemented by @oclass.
|
|
* This allows this class to "override" a property implementation in
|
|
* a parent class or to provide the implementation of a property from
|
|
* an interface.
|
|
*
|
|
* Internally, overriding is implemented by creating a property of type
|
|
* #GParamSpecOverride; generally operations that query the properties of
|
|
* the object class, such as g_object_class_find_property() or
|
|
* g_object_class_list_properties() will return the overridden
|
|
* property. However, in one case, the @construct_properties argument of
|
|
* the @constructor virtual function, the #GParamSpecOverride is passed
|
|
* instead, so that the @param_id field of the #GParamSpec will be
|
|
* correct. For virtually all uses, this makes no difference. If you
|
|
* need to get the overridden property, you can call
|
|
* g_param_spec_get_redirect_target().
|
|
*
|
|
* Since: 2.4
|
|
*/
|
|
void
|
|
g_object_class_override_property (GObjectClass *oclass,
|
|
guint property_id,
|
|
const gchar *name)
|
|
{
|
|
GParamSpec *overridden = NULL;
|
|
GParamSpec *new;
|
|
GType parent_type;
|
|
|
|
g_return_if_fail (G_IS_OBJECT_CLASS (oclass));
|
|
g_return_if_fail (property_id > 0);
|
|
g_return_if_fail (name != NULL);
|
|
|
|
/* Find the overridden property; first check parent types
|
|
*/
|
|
parent_type = g_type_parent (G_OBJECT_CLASS_TYPE (oclass));
|
|
if (parent_type != G_TYPE_NONE)
|
|
overridden = g_param_spec_pool_lookup (pspec_pool,
|
|
name,
|
|
parent_type,
|
|
TRUE);
|
|
if (!overridden)
|
|
{
|
|
GType *ifaces;
|
|
guint n_ifaces;
|
|
|
|
/* Now check interfaces
|
|
*/
|
|
ifaces = g_type_interfaces (G_OBJECT_CLASS_TYPE (oclass), &n_ifaces);
|
|
while (n_ifaces-- && !overridden)
|
|
{
|
|
overridden = g_param_spec_pool_lookup (pspec_pool,
|
|
name,
|
|
ifaces[n_ifaces],
|
|
FALSE);
|
|
}
|
|
|
|
g_free (ifaces);
|
|
}
|
|
|
|
if (!overridden)
|
|
{
|
|
g_warning ("%s: Can't find property to override for '%s::%s'",
|
|
G_STRFUNC, G_OBJECT_CLASS_NAME (oclass), name);
|
|
return;
|
|
}
|
|
|
|
new = g_param_spec_override (name, overridden);
|
|
g_object_class_install_property (oclass, property_id, new);
|
|
}
|
|
|
|
/**
|
|
* g_object_class_list_properties:
|
|
* @oclass: a #GObjectClass
|
|
* @n_properties: (out): return location for the length of the returned array
|
|
*
|
|
* Get an array of #GParamSpec* for all properties of a class.
|
|
*
|
|
* Returns: (array length=n_properties) (transfer container): an array of
|
|
* #GParamSpec* which should be freed after use
|
|
*/
|
|
GParamSpec** /* free result */
|
|
g_object_class_list_properties (GObjectClass *class,
|
|
guint *n_properties_p)
|
|
{
|
|
GParamSpec **pspecs;
|
|
guint n;
|
|
|
|
g_return_val_if_fail (G_IS_OBJECT_CLASS (class), NULL);
|
|
|
|
pspecs = g_param_spec_pool_list (pspec_pool,
|
|
G_OBJECT_CLASS_TYPE (class),
|
|
&n);
|
|
if (n_properties_p)
|
|
*n_properties_p = n;
|
|
|
|
return pspecs;
|
|
}
|
|
|
|
/**
|
|
* g_object_interface_list_properties:
|
|
* @g_iface: (type GObject.TypeInterface): any interface vtable for the
|
|
* interface, or the default vtable for the interface
|
|
* @n_properties_p: (out): location to store number of properties returned.
|
|
*
|
|
* Lists the properties of an interface.Generally, the interface
|
|
* vtable passed in as @g_iface will be the default vtable from
|
|
* g_type_default_interface_ref(), or, if you know the interface has
|
|
* already been loaded, g_type_default_interface_peek().
|
|
*
|
|
* Since: 2.4
|
|
*
|
|
* Returns: (array length=n_properties_p) (transfer container): a
|
|
* pointer to an array of pointers to #GParamSpec
|
|
* structures. The paramspecs are owned by GLib, but the
|
|
* array should be freed with g_free() when you are done with
|
|
* it.
|
|
*/
|
|
GParamSpec**
|
|
g_object_interface_list_properties (gpointer g_iface,
|
|
guint *n_properties_p)
|
|
{
|
|
GTypeInterface *iface_class = g_iface;
|
|
GParamSpec **pspecs;
|
|
guint n;
|
|
|
|
g_return_val_if_fail (G_TYPE_IS_INTERFACE (iface_class->g_type), NULL);
|
|
|
|
pspecs = g_param_spec_pool_list (pspec_pool,
|
|
iface_class->g_type,
|
|
&n);
|
|
if (n_properties_p)
|
|
*n_properties_p = n;
|
|
|
|
return pspecs;
|
|
}
|
|
|
|
static inline gboolean
|
|
object_in_construction (GObject *object)
|
|
{
|
|
return g_datalist_id_get_data (&object->qdata, quark_in_construction) != NULL;
|
|
}
|
|
|
|
static void
|
|
g_object_init (GObject *object,
|
|
GObjectClass *class)
|
|
{
|
|
object->ref_count = 1;
|
|
object->qdata = NULL;
|
|
|
|
if (CLASS_HAS_PROPS (class))
|
|
{
|
|
/* freeze object's notification queue, g_object_newv() preserves pairedness */
|
|
g_object_notify_queue_freeze (object, FALSE);
|
|
}
|
|
|
|
if (CLASS_HAS_CUSTOM_CONSTRUCTOR (class))
|
|
{
|
|
/* mark object in-construction for notify_queue_thaw() and to allow construct-only properties */
|
|
g_datalist_id_set_data (&object->qdata, quark_in_construction, object);
|
|
}
|
|
|
|
GOBJECT_IF_DEBUG (OBJECTS,
|
|
{
|
|
G_LOCK (debug_objects);
|
|
debug_objects_count++;
|
|
g_hash_table_insert (debug_objects_ht, object, object);
|
|
G_UNLOCK (debug_objects);
|
|
});
|
|
}
|
|
|
|
static void
|
|
g_object_do_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (property_id)
|
|
{
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
g_object_do_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (property_id)
|
|
{
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
g_object_real_dispose (GObject *object)
|
|
{
|
|
g_signal_handlers_destroy (object);
|
|
g_datalist_id_set_data (&object->qdata, quark_closure_array, NULL);
|
|
g_datalist_id_set_data (&object->qdata, quark_weak_refs, NULL);
|
|
}
|
|
|
|
static void
|
|
g_object_finalize (GObject *object)
|
|
{
|
|
if (object_in_construction (object))
|
|
{
|
|
g_critical ("object %s %p finalized while still in-construction",
|
|
G_OBJECT_TYPE_NAME (object), object);
|
|
}
|
|
|
|
g_datalist_clear (&object->qdata);
|
|
|
|
GOBJECT_IF_DEBUG (OBJECTS,
|
|
{
|
|
G_LOCK (debug_objects);
|
|
g_assert (g_hash_table_lookup (debug_objects_ht, object) == object);
|
|
g_hash_table_remove (debug_objects_ht, object);
|
|
debug_objects_count--;
|
|
G_UNLOCK (debug_objects);
|
|
});
|
|
}
|
|
|
|
static void
|
|
g_object_dispatch_properties_changed (GObject *object,
|
|
guint n_pspecs,
|
|
GParamSpec **pspecs)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < n_pspecs; i++)
|
|
g_signal_emit (object, gobject_signals[NOTIFY], g_param_spec_get_name_quark (pspecs[i]), pspecs[i]);
|
|
}
|
|
|
|
/**
|
|
* g_object_run_dispose:
|
|
* @object: a #GObject
|
|
*
|
|
* Releases all references to other objects. This can be used to break
|
|
* reference cycles.
|
|
*
|
|
* This function should only be called from object system implementations.
|
|
*/
|
|
void
|
|
g_object_run_dispose (GObject *object)
|
|
{
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (object->ref_count > 0);
|
|
|
|
g_object_ref (object);
|
|
TRACE (GOBJECT_OBJECT_DISPOSE(object,G_TYPE_FROM_INSTANCE(object), 0));
|
|
G_OBJECT_GET_CLASS (object)->dispose (object);
|
|
TRACE (GOBJECT_OBJECT_DISPOSE_END(object,G_TYPE_FROM_INSTANCE(object), 0));
|
|
g_object_unref (object);
|
|
}
|
|
|
|
/**
|
|
* g_object_freeze_notify:
|
|
* @object: a #GObject
|
|
*
|
|
* Increases the freeze count on @object. If the freeze count is
|
|
* non-zero, the emission of "notify" signals on @object is
|
|
* stopped. The signals are queued until the freeze count is decreased
|
|
* to zero. Duplicate notifications are squashed so that at most one
|
|
* #GObject::notify signal is emitted for each property modified while the
|
|
* object is frozen.
|
|
*
|
|
* This is necessary for accessors that modify multiple properties to prevent
|
|
* premature notification while the object is still being modified.
|
|
*/
|
|
void
|
|
g_object_freeze_notify (GObject *object)
|
|
{
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
|
|
if (g_atomic_int_get (&object->ref_count) == 0)
|
|
return;
|
|
|
|
g_object_ref (object);
|
|
g_object_notify_queue_freeze (object, FALSE);
|
|
g_object_unref (object);
|
|
}
|
|
|
|
static GParamSpec *
|
|
get_notify_pspec (GParamSpec *pspec)
|
|
{
|
|
GParamSpec *redirected;
|
|
|
|
/* we don't notify on non-READABLE parameters */
|
|
if (~pspec->flags & G_PARAM_READABLE)
|
|
return NULL;
|
|
|
|
/* if the paramspec is redirected, notify on the target */
|
|
redirected = g_param_spec_get_redirect_target (pspec);
|
|
if (redirected != NULL)
|
|
return redirected;
|
|
|
|
/* else, notify normally */
|
|
return pspec;
|
|
}
|
|
|
|
static inline void
|
|
g_object_notify_by_spec_internal (GObject *object,
|
|
GParamSpec *pspec)
|
|
{
|
|
GParamSpec *notify_pspec;
|
|
|
|
notify_pspec = get_notify_pspec (pspec);
|
|
|
|
if (notify_pspec != NULL)
|
|
{
|
|
GObjectNotifyQueue *nqueue;
|
|
|
|
/* conditional freeze: only increase freeze count if already frozen */
|
|
nqueue = g_object_notify_queue_freeze (object, TRUE);
|
|
|
|
if (nqueue != NULL)
|
|
{
|
|
/* we're frozen, so add to the queue and release our freeze */
|
|
g_object_notify_queue_add (object, nqueue, notify_pspec);
|
|
g_object_notify_queue_thaw (object, nqueue);
|
|
}
|
|
else
|
|
/* not frozen, so just dispatch the notification directly */
|
|
G_OBJECT_GET_CLASS (object)
|
|
->dispatch_properties_changed (object, 1, ¬ify_pspec);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* g_object_notify:
|
|
* @object: a #GObject
|
|
* @property_name: the name of a property installed on the class of @object.
|
|
*
|
|
* Emits a "notify" signal for the property @property_name on @object.
|
|
*
|
|
* When possible, eg. when signaling a property change from within the class
|
|
* that registered the property, you should use g_object_notify_by_pspec()
|
|
* instead.
|
|
*
|
|
* Note that emission of the notify signal may be blocked with
|
|
* g_object_freeze_notify(). In this case, the signal emissions are queued
|
|
* and will be emitted (in reverse order) when g_object_thaw_notify() is
|
|
* called.
|
|
*/
|
|
void
|
|
g_object_notify (GObject *object,
|
|
const gchar *property_name)
|
|
{
|
|
GParamSpec *pspec;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (property_name != NULL);
|
|
if (g_atomic_int_get (&object->ref_count) == 0)
|
|
return;
|
|
|
|
g_object_ref (object);
|
|
/* We don't need to get the redirect target
|
|
* (by, e.g. calling g_object_class_find_property())
|
|
* because g_object_notify_queue_add() does that
|
|
*/
|
|
pspec = g_param_spec_pool_lookup (pspec_pool,
|
|
property_name,
|
|
G_OBJECT_TYPE (object),
|
|
TRUE);
|
|
|
|
if (!pspec)
|
|
g_warning ("%s: object class '%s' has no property named '%s'",
|
|
G_STRFUNC,
|
|
G_OBJECT_TYPE_NAME (object),
|
|
property_name);
|
|
else
|
|
g_object_notify_by_spec_internal (object, pspec);
|
|
g_object_unref (object);
|
|
}
|
|
|
|
/**
|
|
* g_object_notify_by_pspec:
|
|
* @object: a #GObject
|
|
* @pspec: the #GParamSpec of a property installed on the class of @object.
|
|
*
|
|
* Emits a "notify" signal for the property specified by @pspec on @object.
|
|
*
|
|
* This function omits the property name lookup, hence it is faster than
|
|
* g_object_notify().
|
|
*
|
|
* One way to avoid using g_object_notify() from within the
|
|
* class that registered the properties, and using g_object_notify_by_pspec()
|
|
* instead, is to store the GParamSpec used with
|
|
* g_object_class_install_property() inside a static array, e.g.:
|
|
*
|
|
*|[<!-- language="C" -->
|
|
* enum
|
|
* {
|
|
* PROP_0,
|
|
* PROP_FOO,
|
|
* PROP_LAST
|
|
* };
|
|
*
|
|
* static GParamSpec *properties[PROP_LAST];
|
|
*
|
|
* static void
|
|
* my_object_class_init (MyObjectClass *klass)
|
|
* {
|
|
* properties[PROP_FOO] = g_param_spec_int ("foo", "Foo", "The foo",
|
|
* 0, 100,
|
|
* 50,
|
|
* G_PARAM_READWRITE);
|
|
* g_object_class_install_property (gobject_class,
|
|
* PROP_FOO,
|
|
* properties[PROP_FOO]);
|
|
* }
|
|
* ]|
|
|
*
|
|
* and then notify a change on the "foo" property with:
|
|
*
|
|
* |[<!-- language="C" -->
|
|
* g_object_notify_by_pspec (self, properties[PROP_FOO]);
|
|
* ]|
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
void
|
|
g_object_notify_by_pspec (GObject *object,
|
|
GParamSpec *pspec)
|
|
{
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (G_IS_PARAM_SPEC (pspec));
|
|
|
|
if (g_atomic_int_get (&object->ref_count) == 0)
|
|
return;
|
|
|
|
g_object_ref (object);
|
|
g_object_notify_by_spec_internal (object, pspec);
|
|
g_object_unref (object);
|
|
}
|
|
|
|
/**
|
|
* g_object_thaw_notify:
|
|
* @object: a #GObject
|
|
*
|
|
* Reverts the effect of a previous call to
|
|
* g_object_freeze_notify(). The freeze count is decreased on @object
|
|
* and when it reaches zero, queued "notify" signals are emitted.
|
|
*
|
|
* Duplicate notifications for each property are squashed so that at most one
|
|
* #GObject::notify signal is emitted for each property, in the reverse order
|
|
* in which they have been queued.
|
|
*
|
|
* It is an error to call this function when the freeze count is zero.
|
|
*/
|
|
void
|
|
g_object_thaw_notify (GObject *object)
|
|
{
|
|
GObjectNotifyQueue *nqueue;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
if (g_atomic_int_get (&object->ref_count) == 0)
|
|
return;
|
|
|
|
g_object_ref (object);
|
|
|
|
/* FIXME: Freezing is the only way to get at the notify queue.
|
|
* So we freeze once and then thaw twice.
|
|
*/
|
|
nqueue = g_object_notify_queue_freeze (object, FALSE);
|
|
g_object_notify_queue_thaw (object, nqueue);
|
|
g_object_notify_queue_thaw (object, nqueue);
|
|
|
|
g_object_unref (object);
|
|
}
|
|
|
|
static void
|
|
consider_issuing_property_deprecation_warning (const GParamSpec *pspec)
|
|
{
|
|
static GHashTable *already_warned_table;
|
|
static const gchar *enable_diagnostic;
|
|
static GMutex already_warned_lock;
|
|
gboolean already;
|
|
|
|
if (!(pspec->flags & G_PARAM_DEPRECATED))
|
|
return;
|
|
|
|
if (g_once_init_enter (&enable_diagnostic))
|
|
{
|
|
const gchar *value = g_getenv ("G_ENABLE_DIAGNOSTIC");
|
|
|
|
if (!value)
|
|
value = "0";
|
|
|
|
g_once_init_leave (&enable_diagnostic, value);
|
|
}
|
|
|
|
if (enable_diagnostic[0] == '0')
|
|
return;
|
|
|
|
/* We hash only on property names: this means that we could end up in
|
|
* a situation where we fail to emit a warning about a pair of
|
|
* same-named deprecated properties used on two separate types.
|
|
* That's pretty unlikely to occur, and even if it does, you'll still
|
|
* have seen the warning for the first one...
|
|
*
|
|
* Doing it this way lets us hash directly on the (interned) property
|
|
* name pointers.
|
|
*/
|
|
g_mutex_lock (&already_warned_lock);
|
|
|
|
if (already_warned_table == NULL)
|
|
already_warned_table = g_hash_table_new (NULL, NULL);
|
|
|
|
already = g_hash_table_contains (already_warned_table, (gpointer) pspec->name);
|
|
if (!already)
|
|
g_hash_table_add (already_warned_table, (gpointer) pspec->name);
|
|
|
|
g_mutex_unlock (&already_warned_lock);
|
|
|
|
if (!already)
|
|
g_warning ("The property %s:%s is deprecated and shouldn't be used "
|
|
"anymore. It will be removed in a future version.",
|
|
g_type_name (pspec->owner_type), pspec->name);
|
|
}
|
|
|
|
static inline void
|
|
object_get_property (GObject *object,
|
|
GParamSpec *pspec,
|
|
GValue *value)
|
|
{
|
|
GObjectClass *class = g_type_class_peek (pspec->owner_type);
|
|
guint param_id = PARAM_SPEC_PARAM_ID (pspec);
|
|
GParamSpec *redirect;
|
|
|
|
if (class == NULL)
|
|
{
|
|
g_warning ("'%s::%s' is not a valid property name; '%s' is not a GObject subtype",
|
|
g_type_name (pspec->owner_type), pspec->name, g_type_name (pspec->owner_type));
|
|
return;
|
|
}
|
|
|
|
redirect = g_param_spec_get_redirect_target (pspec);
|
|
if (redirect)
|
|
pspec = redirect;
|
|
|
|
consider_issuing_property_deprecation_warning (pspec);
|
|
|
|
class->get_property (object, param_id, value, pspec);
|
|
}
|
|
|
|
static inline void
|
|
object_set_property (GObject *object,
|
|
GParamSpec *pspec,
|
|
const GValue *value,
|
|
GObjectNotifyQueue *nqueue)
|
|
{
|
|
GValue tmp_value = G_VALUE_INIT;
|
|
GObjectClass *class = g_type_class_peek (pspec->owner_type);
|
|
guint param_id = PARAM_SPEC_PARAM_ID (pspec);
|
|
GParamSpec *redirect;
|
|
|
|
if (class == NULL)
|
|
{
|
|
g_warning ("'%s::%s' is not a valid property name; '%s' is not a GObject subtype",
|
|
g_type_name (pspec->owner_type), pspec->name, g_type_name (pspec->owner_type));
|
|
return;
|
|
}
|
|
|
|
redirect = g_param_spec_get_redirect_target (pspec);
|
|
if (redirect)
|
|
pspec = redirect;
|
|
|
|
/* provide a copy to work from, convert (if necessary) and validate */
|
|
g_value_init (&tmp_value, pspec->value_type);
|
|
if (!g_value_transform (value, &tmp_value))
|
|
g_warning ("unable to set property '%s' of type '%s' from value of type '%s'",
|
|
pspec->name,
|
|
g_type_name (pspec->value_type),
|
|
G_VALUE_TYPE_NAME (value));
|
|
else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION))
|
|
{
|
|
gchar *contents = g_strdup_value_contents (value);
|
|
|
|
g_warning ("value \"%s\" of type '%s' is invalid or out of range for property '%s' of type '%s'",
|
|
contents,
|
|
G_VALUE_TYPE_NAME (value),
|
|
pspec->name,
|
|
g_type_name (pspec->value_type));
|
|
g_free (contents);
|
|
}
|
|
else
|
|
{
|
|
class->set_property (object, param_id, &tmp_value, pspec);
|
|
|
|
if (~pspec->flags & G_PARAM_EXPLICIT_NOTIFY)
|
|
{
|
|
GParamSpec *notify_pspec;
|
|
|
|
notify_pspec = get_notify_pspec (pspec);
|
|
|
|
if (notify_pspec != NULL)
|
|
g_object_notify_queue_add (object, nqueue, notify_pspec);
|
|
}
|
|
}
|
|
g_value_unset (&tmp_value);
|
|
}
|
|
|
|
static void
|
|
object_interface_check_properties (gpointer check_data,
|
|
gpointer g_iface)
|
|
{
|
|
GTypeInterface *iface_class = g_iface;
|
|
GObjectClass *class;
|
|
GType iface_type = iface_class->g_type;
|
|
GParamSpec **pspecs;
|
|
guint n;
|
|
|
|
class = g_type_class_ref (iface_class->g_instance_type);
|
|
|
|
if (class == NULL)
|
|
return;
|
|
|
|
if (!G_IS_OBJECT_CLASS (class))
|
|
goto out;
|
|
|
|
pspecs = g_param_spec_pool_list (pspec_pool, iface_type, &n);
|
|
|
|
while (n--)
|
|
{
|
|
GParamSpec *class_pspec = g_param_spec_pool_lookup (pspec_pool,
|
|
pspecs[n]->name,
|
|
G_OBJECT_CLASS_TYPE (class),
|
|
TRUE);
|
|
|
|
if (!class_pspec)
|
|
{
|
|
g_critical ("Object class %s doesn't implement property "
|
|
"'%s' from interface '%s'",
|
|
g_type_name (G_OBJECT_CLASS_TYPE (class)),
|
|
pspecs[n]->name,
|
|
g_type_name (iface_type));
|
|
|
|
continue;
|
|
}
|
|
|
|
/* We do a number of checks on the properties of an interface to
|
|
* make sure that all classes implementing the interface are
|
|
* overriding the properties in a sane way.
|
|
*
|
|
* We do the checks in order of importance so that we can give
|
|
* more useful error messages first.
|
|
*
|
|
* First, we check that the implementation doesn't remove the
|
|
* basic functionality (readability, writability) advertised by
|
|
* the interface. Next, we check that it doesn't introduce
|
|
* additional restrictions (such as construct-only). Finally, we
|
|
* make sure the types are compatible.
|
|
*/
|
|
|
|
#define SUBSET(a,b,mask) (((a) & ~(b) & (mask)) == 0)
|
|
/* If the property on the interface is readable then the
|
|
* implementation must be readable. If the interface is writable
|
|
* then the implementation must be writable.
|
|
*/
|
|
if (!SUBSET (pspecs[n]->flags, class_pspec->flags, G_PARAM_READABLE | G_PARAM_WRITABLE))
|
|
{
|
|
g_critical ("Flags for property '%s' on class '%s' remove functionality compared with the "
|
|
"property on interface '%s'\n", pspecs[n]->name,
|
|
g_type_name (G_OBJECT_CLASS_TYPE (class)), g_type_name (iface_type));
|
|
continue;
|
|
}
|
|
|
|
/* If the property on the interface is writable then we need to
|
|
* make sure the implementation doesn't introduce new restrictions
|
|
* on that writability (ie: construct-only).
|
|
*
|
|
* If the interface was not writable to begin with then we don't
|
|
* really have any problems here because "writable at construct
|
|
* time only" is still more permissive than "read only".
|
|
*/
|
|
if (pspecs[n]->flags & G_PARAM_WRITABLE)
|
|
{
|
|
if (!SUBSET (class_pspec->flags, pspecs[n]->flags, G_PARAM_CONSTRUCT_ONLY))
|
|
{
|
|
g_critical ("Flags for property '%s' on class '%s' introduce additional restrictions on "
|
|
"writability compared with the property on interface '%s'\n", pspecs[n]->name,
|
|
g_type_name (G_OBJECT_CLASS_TYPE (class)), g_type_name (iface_type));
|
|
continue;
|
|
}
|
|
}
|
|
#undef SUBSET
|
|
|
|
/* If the property on the interface is readable then we are
|
|
* effectively advertising that reading the property will return a
|
|
* value of a specific type. All implementations of the interface
|
|
* need to return items of this type -- but may be more
|
|
* restrictive. For example, it is legal to have:
|
|
*
|
|
* GtkWidget *get_item();
|
|
*
|
|
* that is implemented by a function that always returns a
|
|
* GtkEntry. In short: readability implies that the
|
|
* implementation value type must be equal or more restrictive.
|
|
*
|
|
* Similarly, if the property on the interface is writable then
|
|
* must be able to accept the property being set to any value of
|
|
* that type, including subclasses. In this case, we may also be
|
|
* less restrictive. For example, it is legal to have:
|
|
*
|
|
* set_item (GtkEntry *);
|
|
*
|
|
* that is implemented by a function that will actually work with
|
|
* any GtkWidget. In short: writability implies that the
|
|
* implementation value type must be equal or less restrictive.
|
|
*
|
|
* In the case that the property is both readable and writable
|
|
* then the only way that both of the above can be satisfied is
|
|
* with a type that is exactly equal.
|
|
*/
|
|
switch (pspecs[n]->flags & (G_PARAM_READABLE | G_PARAM_WRITABLE))
|
|
{
|
|
case G_PARAM_READABLE | G_PARAM_WRITABLE:
|
|
/* class pspec value type must have exact equality with interface */
|
|
if (pspecs[n]->value_type != class_pspec->value_type)
|
|
g_critical ("Read/writable property '%s' on class '%s' has type '%s' which is not exactly equal to the "
|
|
"type '%s' of the property on the interface '%s'\n", pspecs[n]->name,
|
|
g_type_name (G_OBJECT_CLASS_TYPE (class)), g_type_name (G_PARAM_SPEC_VALUE_TYPE (class_pspec)),
|
|
g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspecs[n])), g_type_name (iface_type));
|
|
break;
|
|
|
|
case G_PARAM_READABLE:
|
|
/* class pspec value type equal or more restrictive than interface */
|
|
if (!g_type_is_a (class_pspec->value_type, pspecs[n]->value_type))
|
|
g_critical ("Read-only property '%s' on class '%s' has type '%s' which is not equal to or more "
|
|
"restrictive than the type '%s' of the property on the interface '%s'\n", pspecs[n]->name,
|
|
g_type_name (G_OBJECT_CLASS_TYPE (class)), g_type_name (G_PARAM_SPEC_VALUE_TYPE (class_pspec)),
|
|
g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspecs[n])), g_type_name (iface_type));
|
|
break;
|
|
|
|
case G_PARAM_WRITABLE:
|
|
/* class pspec value type equal or less restrictive than interface */
|
|
if (!g_type_is_a (pspecs[n]->value_type, class_pspec->value_type))
|
|
g_critical ("Write-only property '%s' on class '%s' has type '%s' which is not equal to or less "
|
|
"restrictive than the type '%s' of the property on the interface '%s' \n", pspecs[n]->name,
|
|
g_type_name (G_OBJECT_CLASS_TYPE (class)), g_type_name (G_PARAM_SPEC_VALUE_TYPE (class_pspec)),
|
|
g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspecs[n])), g_type_name (iface_type));
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
g_free (pspecs);
|
|
|
|
out:
|
|
g_type_class_unref (class);
|
|
}
|
|
|
|
GType
|
|
g_object_get_type (void)
|
|
{
|
|
return G_TYPE_OBJECT;
|
|
}
|
|
|
|
/**
|
|
* g_object_new: (skip)
|
|
* @object_type: the type id of the #GObject subtype to instantiate
|
|
* @first_property_name: the name of the first property
|
|
* @...: the value of the first property, followed optionally by more
|
|
* name/value pairs, followed by %NULL
|
|
*
|
|
* Creates a new instance of a #GObject subtype and sets its properties.
|
|
*
|
|
* Construction parameters (see #G_PARAM_CONSTRUCT, #G_PARAM_CONSTRUCT_ONLY)
|
|
* which are not explicitly specified are set to their default values.
|
|
*
|
|
* Returns: (transfer full) (type GObject.Object) : a new instance of
|
|
* @object_type
|
|
*/
|
|
gpointer
|
|
g_object_new (GType object_type,
|
|
const gchar *first_property_name,
|
|
...)
|
|
{
|
|
GObject *object;
|
|
va_list var_args;
|
|
|
|
g_return_val_if_fail (G_TYPE_IS_OBJECT (object_type), NULL);
|
|
|
|
/* short circuit for calls supplying no properties */
|
|
if (!first_property_name)
|
|
return g_object_newv (object_type, 0, NULL);
|
|
|
|
va_start (var_args, first_property_name);
|
|
object = g_object_new_valist (object_type, first_property_name, var_args);
|
|
va_end (var_args);
|
|
|
|
return object;
|
|
}
|
|
|
|
static gpointer
|
|
g_object_new_with_custom_constructor (GObjectClass *class,
|
|
GObjectConstructParam *params,
|
|
guint n_params)
|
|
{
|
|
GObjectNotifyQueue *nqueue = NULL;
|
|
gboolean newly_constructed;
|
|
GObjectConstructParam *cparams;
|
|
GObject *object;
|
|
GValue *cvalues;
|
|
gint n_cparams;
|
|
gint cvals_used;
|
|
GSList *node;
|
|
gint i;
|
|
|
|
/* If we have ->constructed() then we have to do a lot more work.
|
|
* It's possible that this is a singleton and it's also possible
|
|
* that the user's constructor() will attempt to modify the values
|
|
* that we pass in, so we'll need to allocate copies of them.
|
|
* It's also possible that the user may attempt to call
|
|
* g_object_set() from inside of their constructor, so we need to
|
|
* add ourselves to a list of objects for which that is allowed
|
|
* while their constructor() is running.
|
|
*/
|
|
|
|
/* Create the array of GObjectConstructParams for constructor() */
|
|
n_cparams = g_slist_length (class->construct_properties);
|
|
cparams = g_new (GObjectConstructParam, n_cparams);
|
|
cvalues = g_new0 (GValue, n_cparams);
|
|
cvals_used = 0;
|
|
i = 0;
|
|
|
|
/* As above, we may find the value in the passed-in params list.
|
|
*
|
|
* If we have the value passed in then we can use the GValue from
|
|
* it directly because it is safe to modify. If we use the
|
|
* default value from the class, we had better not pass that in
|
|
* and risk it being modified, so we create a new one.
|
|
* */
|
|
for (node = class->construct_properties; node; node = node->next)
|
|
{
|
|
GParamSpec *pspec;
|
|
GValue *value;
|
|
gint j;
|
|
|
|
pspec = node->data;
|
|
value = NULL; /* to silence gcc... */
|
|
|
|
for (j = 0; j < n_params; j++)
|
|
if (params[j].pspec == pspec)
|
|
{
|
|
consider_issuing_property_deprecation_warning (pspec);
|
|
value = params[j].value;
|
|
break;
|
|
}
|
|
|
|
if (j == n_params)
|
|
{
|
|
value = &cvalues[cvals_used++];
|
|
g_value_init (value, pspec->value_type);
|
|
g_param_value_set_default (pspec, value);
|
|
}
|
|
|
|
cparams[i].pspec = pspec;
|
|
cparams[i].value = value;
|
|
i++;
|
|
}
|
|
|
|
/* construct object from construction parameters */
|
|
object = class->constructor (class->g_type_class.g_type, n_cparams, cparams);
|
|
/* free construction values */
|
|
g_free (cparams);
|
|
while (cvals_used--)
|
|
g_value_unset (&cvalues[cvals_used]);
|
|
g_free (cvalues);
|
|
|
|
/* There is code in the wild that relies on being able to return NULL
|
|
* from its custom constructor. This was never a supported operation,
|
|
* but since the code is already out there...
|
|
*/
|
|
if (object == NULL)
|
|
{
|
|
g_critical ("Custom constructor for class %s returned NULL (which is invalid). "
|
|
"Please use GInitable instead.", G_OBJECT_CLASS_NAME (class));
|
|
return NULL;
|
|
}
|
|
|
|
/* g_object_init() will have marked the object as being in-construction.
|
|
* Check if the returned object still is so marked, or if this is an
|
|
* already-existing singleton (in which case we should not do 'constructed').
|
|
*/
|
|
newly_constructed = object_in_construction (object);
|
|
if (newly_constructed)
|
|
g_datalist_id_set_data (&object->qdata, quark_in_construction, NULL);
|
|
|
|
if (CLASS_HAS_PROPS (class))
|
|
{
|
|
/* If this object was newly_constructed then g_object_init()
|
|
* froze the queue. We need to freeze it here in order to get
|
|
* the handle so that we can thaw it below (otherwise it will
|
|
* be frozen forever).
|
|
*
|
|
* We also want to do a freeze if we have any params to set,
|
|
* even on a non-newly_constructed object.
|
|
*
|
|
* It's possible that we have the case of non-newly created
|
|
* singleton and all of the passed-in params were construct
|
|
* properties so n_params > 0 but we will actually set no
|
|
* properties. This is a pretty lame case to optimise, so
|
|
* just ignore it and freeze anyway.
|
|
*/
|
|
if (newly_constructed || n_params)
|
|
nqueue = g_object_notify_queue_freeze (object, FALSE);
|
|
|
|
/* Remember: if it was newly_constructed then g_object_init()
|
|
* already did a freeze, so we now have two. Release one.
|
|
*/
|
|
if (newly_constructed)
|
|
g_object_notify_queue_thaw (object, nqueue);
|
|
}
|
|
|
|
/* run 'constructed' handler if there is a custom one */
|
|
if (newly_constructed && CLASS_HAS_CUSTOM_CONSTRUCTED (class))
|
|
class->constructed (object);
|
|
|
|
/* set remaining properties */
|
|
for (i = 0; i < n_params; i++)
|
|
if (!(params[i].pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)))
|
|
{
|
|
consider_issuing_property_deprecation_warning (params[i].pspec);
|
|
object_set_property (object, params[i].pspec, params[i].value, nqueue);
|
|
}
|
|
|
|
/* If nqueue is non-NULL then we are frozen. Thaw it. */
|
|
if (nqueue)
|
|
g_object_notify_queue_thaw (object, nqueue);
|
|
|
|
return object;
|
|
}
|
|
|
|
static gpointer
|
|
g_object_new_internal (GObjectClass *class,
|
|
GObjectConstructParam *params,
|
|
guint n_params)
|
|
{
|
|
GObjectNotifyQueue *nqueue = NULL;
|
|
GObject *object;
|
|
|
|
if G_UNLIKELY (CLASS_HAS_CUSTOM_CONSTRUCTOR (class))
|
|
return g_object_new_with_custom_constructor (class, params, n_params);
|
|
|
|
object = (GObject *) g_type_create_instance (class->g_type_class.g_type);
|
|
|
|
if (CLASS_HAS_PROPS (class))
|
|
{
|
|
GSList *node;
|
|
|
|
/* This will have been setup in g_object_init() */
|
|
nqueue = g_datalist_id_get_data (&object->qdata, quark_notify_queue);
|
|
g_assert (nqueue != NULL);
|
|
|
|
/* We will set exactly n_construct_properties construct
|
|
* properties, but they may come from either the class default
|
|
* values or the passed-in parameter list.
|
|
*/
|
|
for (node = class->construct_properties; node; node = node->next)
|
|
{
|
|
const GValue *value;
|
|
GParamSpec *pspec;
|
|
gint j;
|
|
|
|
pspec = node->data;
|
|
value = NULL; /* to silence gcc... */
|
|
|
|
for (j = 0; j < n_params; j++)
|
|
if (params[j].pspec == pspec)
|
|
{
|
|
consider_issuing_property_deprecation_warning (pspec);
|
|
value = params[j].value;
|
|
break;
|
|
}
|
|
|
|
if (j == n_params)
|
|
value = g_param_spec_get_default_value (pspec);
|
|
|
|
object_set_property (object, pspec, value, nqueue);
|
|
}
|
|
}
|
|
|
|
/* run 'constructed' handler if there is a custom one */
|
|
if (CLASS_HAS_CUSTOM_CONSTRUCTED (class))
|
|
class->constructed (object);
|
|
|
|
if (nqueue)
|
|
{
|
|
gint i;
|
|
|
|
/* Set remaining properties. The construct properties will
|
|
* already have been taken, so set only the non-construct
|
|
* ones.
|
|
*/
|
|
for (i = 0; i < n_params; i++)
|
|
if (!(params[i].pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)))
|
|
{
|
|
consider_issuing_property_deprecation_warning (params[i].pspec);
|
|
object_set_property (object, params[i].pspec, params[i].value, nqueue);
|
|
}
|
|
|
|
g_object_notify_queue_thaw (object, nqueue);
|
|
}
|
|
|
|
return object;
|
|
}
|
|
|
|
/**
|
|
* g_object_newv: (rename-to g_object_new)
|
|
* @object_type: the type id of the #GObject subtype to instantiate
|
|
* @n_parameters: the length of the @parameters array
|
|
* @parameters: (array length=n_parameters): an array of #GParameter
|
|
*
|
|
* Creates a new instance of a #GObject subtype and sets its properties.
|
|
*
|
|
* Construction parameters (see #G_PARAM_CONSTRUCT, #G_PARAM_CONSTRUCT_ONLY)
|
|
* which are not explicitly specified are set to their default values.
|
|
*
|
|
* Returns: (type GObject.Object) (transfer full): a new instance of
|
|
* @object_type
|
|
*/
|
|
gpointer
|
|
g_object_newv (GType object_type,
|
|
guint n_parameters,
|
|
GParameter *parameters)
|
|
{
|
|
GObjectClass *class, *unref_class = NULL;
|
|
GObject *object;
|
|
|
|
g_return_val_if_fail (G_TYPE_IS_OBJECT (object_type), NULL);
|
|
g_return_val_if_fail (n_parameters == 0 || parameters != NULL, NULL);
|
|
|
|
/* Try to avoid thrashing the ref_count if we don't need to (since
|
|
* it's a locked operation).
|
|
*/
|
|
class = g_type_class_peek_static (object_type);
|
|
|
|
if (!class)
|
|
class = unref_class = g_type_class_ref (object_type);
|
|
|
|
if (n_parameters)
|
|
{
|
|
GObjectConstructParam *cparams;
|
|
guint i, j;
|
|
|
|
cparams = g_newa (GObjectConstructParam, n_parameters);
|
|
j = 0;
|
|
|
|
for (i = 0; i < n_parameters; i++)
|
|
{
|
|
GParamSpec *pspec;
|
|
gint k;
|
|
|
|
pspec = g_param_spec_pool_lookup (pspec_pool, parameters[i].name, object_type, TRUE);
|
|
|
|
if G_UNLIKELY (!pspec)
|
|
{
|
|
g_critical ("%s: object class '%s' has no property named '%s'",
|
|
G_STRFUNC, g_type_name (object_type), parameters[i].name);
|
|
continue;
|
|
}
|
|
|
|
if G_UNLIKELY (~pspec->flags & G_PARAM_WRITABLE)
|
|
{
|
|
g_critical ("%s: property '%s' of object class '%s' is not writable",
|
|
G_STRFUNC, pspec->name, g_type_name (object_type));
|
|
continue;
|
|
}
|
|
|
|
if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
|
|
{
|
|
for (k = 0; k < j; k++)
|
|
if (cparams[k].pspec == pspec)
|
|
break;
|
|
if G_UNLIKELY (k != j)
|
|
{
|
|
g_critical ("%s: construct property '%s' for type '%s' cannot be set twice",
|
|
G_STRFUNC, parameters[i].name, g_type_name (object_type));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
cparams[j].pspec = pspec;
|
|
cparams[j].value = ¶meters[i].value;
|
|
j++;
|
|
}
|
|
|
|
object = g_object_new_internal (class, cparams, j);
|
|
}
|
|
else
|
|
/* Fast case: no properties passed in. */
|
|
object = g_object_new_internal (class, NULL, 0);
|
|
|
|
if (unref_class)
|
|
g_type_class_unref (unref_class);
|
|
|
|
return object;
|
|
}
|
|
|
|
/**
|
|
* g_object_new_valist: (skip)
|
|
* @object_type: the type id of the #GObject subtype to instantiate
|
|
* @first_property_name: the name of the first property
|
|
* @var_args: the value of the first property, followed optionally by more
|
|
* name/value pairs, followed by %NULL
|
|
*
|
|
* Creates a new instance of a #GObject subtype and sets its properties.
|
|
*
|
|
* Construction parameters (see #G_PARAM_CONSTRUCT, #G_PARAM_CONSTRUCT_ONLY)
|
|
* which are not explicitly specified are set to their default values.
|
|
*
|
|
* Returns: a new instance of @object_type
|
|
*/
|
|
GObject*
|
|
g_object_new_valist (GType object_type,
|
|
const gchar *first_property_name,
|
|
va_list var_args)
|
|
{
|
|
GObjectClass *class, *unref_class = NULL;
|
|
GObject *object;
|
|
|
|
g_return_val_if_fail (G_TYPE_IS_OBJECT (object_type), NULL);
|
|
|
|
/* Try to avoid thrashing the ref_count if we don't need to (since
|
|
* it's a locked operation).
|
|
*/
|
|
class = g_type_class_peek_static (object_type);
|
|
|
|
if (!class)
|
|
class = unref_class = g_type_class_ref (object_type);
|
|
|
|
if (first_property_name)
|
|
{
|
|
GObjectConstructParam stack_params[16];
|
|
GObjectConstructParam *params;
|
|
const gchar *name;
|
|
gint n_params = 0;
|
|
|
|
name = first_property_name;
|
|
params = stack_params;
|
|
|
|
do
|
|
{
|
|
gchar *error = NULL;
|
|
GParamSpec *pspec;
|
|
gint i;
|
|
|
|
pspec = g_param_spec_pool_lookup (pspec_pool, name, object_type, TRUE);
|
|
|
|
if G_UNLIKELY (!pspec)
|
|
{
|
|
g_critical ("%s: object class '%s' has no property named '%s'",
|
|
G_STRFUNC, g_type_name (object_type), name);
|
|
/* Can't continue because arg list will be out of sync. */
|
|
break;
|
|
}
|
|
|
|
if G_UNLIKELY (~pspec->flags & G_PARAM_WRITABLE)
|
|
{
|
|
g_critical ("%s: property '%s' of object class '%s' is not writable",
|
|
G_STRFUNC, pspec->name, g_type_name (object_type));
|
|
break;
|
|
}
|
|
|
|
if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
|
|
{
|
|
for (i = 0; i < n_params; i++)
|
|
if (params[i].pspec == pspec)
|
|
break;
|
|
if G_UNLIKELY (i != n_params)
|
|
{
|
|
g_critical ("%s: property '%s' for type '%s' cannot be set twice",
|
|
G_STRFUNC, name, g_type_name (object_type));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (n_params == 16)
|
|
{
|
|
params = g_new (GObjectConstructParam, n_params + 1);
|
|
memcpy (params, stack_params, sizeof stack_params);
|
|
}
|
|
else if (n_params > 16)
|
|
params = g_renew (GObjectConstructParam, params, n_params + 1);
|
|
|
|
params[n_params].pspec = pspec;
|
|
params[n_params].value = g_newa (GValue, 1);
|
|
memset (params[n_params].value, 0, sizeof (GValue));
|
|
|
|
G_VALUE_COLLECT_INIT (params[n_params].value, pspec->value_type, var_args, 0, &error);
|
|
|
|
if (error)
|
|
{
|
|
g_critical ("%s: %s", G_STRFUNC, error);
|
|
g_value_unset (params[n_params].value);
|
|
g_free (error);
|
|
break;
|
|
}
|
|
|
|
n_params++;
|
|
}
|
|
while ((name = va_arg (var_args, const gchar *)));
|
|
|
|
object = g_object_new_internal (class, params, n_params);
|
|
|
|
while (n_params--)
|
|
g_value_unset (params[n_params].value);
|
|
|
|
if (params != stack_params)
|
|
g_free (params);
|
|
}
|
|
else
|
|
/* Fast case: no properties passed in. */
|
|
object = g_object_new_internal (class, NULL, 0);
|
|
|
|
if (unref_class)
|
|
g_type_class_unref (unref_class);
|
|
|
|
return object;
|
|
}
|
|
|
|
static GObject*
|
|
g_object_constructor (GType type,
|
|
guint n_construct_properties,
|
|
GObjectConstructParam *construct_params)
|
|
{
|
|
GObject *object;
|
|
|
|
/* create object */
|
|
object = (GObject*) g_type_create_instance (type);
|
|
|
|
/* set construction parameters */
|
|
if (n_construct_properties)
|
|
{
|
|
GObjectNotifyQueue *nqueue = g_object_notify_queue_freeze (object, FALSE);
|
|
|
|
/* set construct properties */
|
|
while (n_construct_properties--)
|
|
{
|
|
GValue *value = construct_params->value;
|
|
GParamSpec *pspec = construct_params->pspec;
|
|
|
|
construct_params++;
|
|
object_set_property (object, pspec, value, nqueue);
|
|
}
|
|
g_object_notify_queue_thaw (object, nqueue);
|
|
/* the notification queue is still frozen from g_object_init(), so
|
|
* we don't need to handle it here, g_object_newv() takes
|
|
* care of that
|
|
*/
|
|
}
|
|
|
|
return object;
|
|
}
|
|
|
|
static void
|
|
g_object_constructed (GObject *object)
|
|
{
|
|
/* empty default impl to allow unconditional upchaining */
|
|
}
|
|
|
|
/**
|
|
* g_object_set_valist: (skip)
|
|
* @object: a #GObject
|
|
* @first_property_name: name of the first property to set
|
|
* @var_args: value for the first property, followed optionally by more
|
|
* name/value pairs, followed by %NULL
|
|
*
|
|
* Sets properties on an object.
|
|
*/
|
|
void
|
|
g_object_set_valist (GObject *object,
|
|
const gchar *first_property_name,
|
|
va_list var_args)
|
|
{
|
|
GObjectNotifyQueue *nqueue;
|
|
const gchar *name;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
|
|
g_object_ref (object);
|
|
nqueue = g_object_notify_queue_freeze (object, FALSE);
|
|
|
|
name = first_property_name;
|
|
while (name)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
GParamSpec *pspec;
|
|
gchar *error = NULL;
|
|
|
|
pspec = g_param_spec_pool_lookup (pspec_pool,
|
|
name,
|
|
G_OBJECT_TYPE (object),
|
|
TRUE);
|
|
if (!pspec)
|
|
{
|
|
g_warning ("%s: object class '%s' has no property named '%s'",
|
|
G_STRFUNC,
|
|
G_OBJECT_TYPE_NAME (object),
|
|
name);
|
|
break;
|
|
}
|
|
if (!(pspec->flags & G_PARAM_WRITABLE))
|
|
{
|
|
g_warning ("%s: property '%s' of object class '%s' is not writable",
|
|
G_STRFUNC,
|
|
pspec->name,
|
|
G_OBJECT_TYPE_NAME (object));
|
|
break;
|
|
}
|
|
if ((pspec->flags & G_PARAM_CONSTRUCT_ONLY) && !object_in_construction (object))
|
|
{
|
|
g_warning ("%s: construct property \"%s\" for object '%s' can't be set after construction",
|
|
G_STRFUNC, pspec->name, G_OBJECT_TYPE_NAME (object));
|
|
break;
|
|
}
|
|
|
|
G_VALUE_COLLECT_INIT (&value, pspec->value_type, var_args,
|
|
0, &error);
|
|
if (error)
|
|
{
|
|
g_warning ("%s: %s", G_STRFUNC, error);
|
|
g_free (error);
|
|
g_value_unset (&value);
|
|
break;
|
|
}
|
|
|
|
consider_issuing_property_deprecation_warning (pspec);
|
|
object_set_property (object, pspec, &value, nqueue);
|
|
g_value_unset (&value);
|
|
|
|
name = va_arg (var_args, gchar*);
|
|
}
|
|
|
|
g_object_notify_queue_thaw (object, nqueue);
|
|
g_object_unref (object);
|
|
}
|
|
|
|
/**
|
|
* g_object_get_valist: (skip)
|
|
* @object: a #GObject
|
|
* @first_property_name: name of the first property to get
|
|
* @var_args: return location for the first property, followed optionally by more
|
|
* name/return location pairs, followed by %NULL
|
|
*
|
|
* Gets properties of an object.
|
|
*
|
|
* In general, a copy is made of the property contents and the caller
|
|
* is responsible for freeing the memory in the appropriate manner for
|
|
* the type, for instance by calling g_free() or g_object_unref().
|
|
*
|
|
* See g_object_get().
|
|
*/
|
|
void
|
|
g_object_get_valist (GObject *object,
|
|
const gchar *first_property_name,
|
|
va_list var_args)
|
|
{
|
|
const gchar *name;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
|
|
g_object_ref (object);
|
|
|
|
name = first_property_name;
|
|
|
|
while (name)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
GParamSpec *pspec;
|
|
gchar *error;
|
|
|
|
pspec = g_param_spec_pool_lookup (pspec_pool,
|
|
name,
|
|
G_OBJECT_TYPE (object),
|
|
TRUE);
|
|
if (!pspec)
|
|
{
|
|
g_warning ("%s: object class '%s' has no property named '%s'",
|
|
G_STRFUNC,
|
|
G_OBJECT_TYPE_NAME (object),
|
|
name);
|
|
break;
|
|
}
|
|
if (!(pspec->flags & G_PARAM_READABLE))
|
|
{
|
|
g_warning ("%s: property '%s' of object class '%s' is not readable",
|
|
G_STRFUNC,
|
|
pspec->name,
|
|
G_OBJECT_TYPE_NAME (object));
|
|
break;
|
|
}
|
|
|
|
g_value_init (&value, pspec->value_type);
|
|
|
|
object_get_property (object, pspec, &value);
|
|
|
|
G_VALUE_LCOPY (&value, var_args, 0, &error);
|
|
if (error)
|
|
{
|
|
g_warning ("%s: %s", G_STRFUNC, error);
|
|
g_free (error);
|
|
g_value_unset (&value);
|
|
break;
|
|
}
|
|
|
|
g_value_unset (&value);
|
|
|
|
name = va_arg (var_args, gchar*);
|
|
}
|
|
|
|
g_object_unref (object);
|
|
}
|
|
|
|
/**
|
|
* g_object_set: (skip)
|
|
* @object: (type GObject.Object): a #GObject
|
|
* @first_property_name: name of the first property to set
|
|
* @...: value for the first property, followed optionally by more
|
|
* name/value pairs, followed by %NULL
|
|
*
|
|
* Sets properties on an object.
|
|
*
|
|
* Note that the "notify" signals are queued and only emitted (in
|
|
* reverse order) after all properties have been set. See
|
|
* g_object_freeze_notify().
|
|
*/
|
|
void
|
|
g_object_set (gpointer _object,
|
|
const gchar *first_property_name,
|
|
...)
|
|
{
|
|
GObject *object = _object;
|
|
va_list var_args;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
|
|
va_start (var_args, first_property_name);
|
|
g_object_set_valist (object, first_property_name, var_args);
|
|
va_end (var_args);
|
|
}
|
|
|
|
/**
|
|
* g_object_get: (skip)
|
|
* @object: (type GObject.Object): a #GObject
|
|
* @first_property_name: name of the first property to get
|
|
* @...: return location for the first property, followed optionally by more
|
|
* name/return location pairs, followed by %NULL
|
|
*
|
|
* Gets properties of an object.
|
|
*
|
|
* In general, a copy is made of the property contents and the caller
|
|
* is responsible for freeing the memory in the appropriate manner for
|
|
* the type, for instance by calling g_free() or g_object_unref().
|
|
*
|
|
* Here is an example of using g_object_get() to get the contents
|
|
* of three properties: an integer, a string and an object:
|
|
* |[<!-- language="C" -->
|
|
* gint intval;
|
|
* gchar *strval;
|
|
* GObject *objval;
|
|
*
|
|
* g_object_get (my_object,
|
|
* "int-property", &intval,
|
|
* "str-property", &strval,
|
|
* "obj-property", &objval,
|
|
* NULL);
|
|
*
|
|
* // Do something with intval, strval, objval
|
|
*
|
|
* g_free (strval);
|
|
* g_object_unref (objval);
|
|
* ]|
|
|
*/
|
|
void
|
|
g_object_get (gpointer _object,
|
|
const gchar *first_property_name,
|
|
...)
|
|
{
|
|
GObject *object = _object;
|
|
va_list var_args;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
|
|
va_start (var_args, first_property_name);
|
|
g_object_get_valist (object, first_property_name, var_args);
|
|
va_end (var_args);
|
|
}
|
|
|
|
/**
|
|
* g_object_set_property:
|
|
* @object: a #GObject
|
|
* @property_name: the name of the property to set
|
|
* @value: the value
|
|
*
|
|
* Sets a property on an object.
|
|
*/
|
|
void
|
|
g_object_set_property (GObject *object,
|
|
const gchar *property_name,
|
|
const GValue *value)
|
|
{
|
|
GObjectNotifyQueue *nqueue;
|
|
GParamSpec *pspec;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (property_name != NULL);
|
|
g_return_if_fail (G_IS_VALUE (value));
|
|
|
|
g_object_ref (object);
|
|
nqueue = g_object_notify_queue_freeze (object, FALSE);
|
|
|
|
pspec = g_param_spec_pool_lookup (pspec_pool,
|
|
property_name,
|
|
G_OBJECT_TYPE (object),
|
|
TRUE);
|
|
if (!pspec)
|
|
g_warning ("%s: object class '%s' has no property named '%s'",
|
|
G_STRFUNC,
|
|
G_OBJECT_TYPE_NAME (object),
|
|
property_name);
|
|
else if (!(pspec->flags & G_PARAM_WRITABLE))
|
|
g_warning ("%s: property '%s' of object class '%s' is not writable",
|
|
G_STRFUNC,
|
|
pspec->name,
|
|
G_OBJECT_TYPE_NAME (object));
|
|
else if ((pspec->flags & G_PARAM_CONSTRUCT_ONLY) && !object_in_construction (object))
|
|
g_warning ("%s: construct property \"%s\" for object '%s' can't be set after construction",
|
|
G_STRFUNC, pspec->name, G_OBJECT_TYPE_NAME (object));
|
|
else
|
|
{
|
|
consider_issuing_property_deprecation_warning (pspec);
|
|
object_set_property (object, pspec, value, nqueue);
|
|
}
|
|
|
|
g_object_notify_queue_thaw (object, nqueue);
|
|
g_object_unref (object);
|
|
}
|
|
|
|
/**
|
|
* g_object_get_property:
|
|
* @object: a #GObject
|
|
* @property_name: the name of the property to get
|
|
* @value: return location for the property value
|
|
*
|
|
* Gets a property of an object. @value must have been initialized to the
|
|
* expected type of the property (or a type to which the expected type can be
|
|
* transformed) using g_value_init().
|
|
*
|
|
* In general, a copy is made of the property contents and the caller is
|
|
* responsible for freeing the memory by calling g_value_unset().
|
|
*
|
|
* Note that g_object_get_property() is really intended for language
|
|
* bindings, g_object_get() is much more convenient for C programming.
|
|
*/
|
|
void
|
|
g_object_get_property (GObject *object,
|
|
const gchar *property_name,
|
|
GValue *value)
|
|
{
|
|
GParamSpec *pspec;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (property_name != NULL);
|
|
g_return_if_fail (G_IS_VALUE (value));
|
|
|
|
g_object_ref (object);
|
|
|
|
pspec = g_param_spec_pool_lookup (pspec_pool,
|
|
property_name,
|
|
G_OBJECT_TYPE (object),
|
|
TRUE);
|
|
if (!pspec)
|
|
g_warning ("%s: object class '%s' has no property named '%s'",
|
|
G_STRFUNC,
|
|
G_OBJECT_TYPE_NAME (object),
|
|
property_name);
|
|
else if (!(pspec->flags & G_PARAM_READABLE))
|
|
g_warning ("%s: property '%s' of object class '%s' is not readable",
|
|
G_STRFUNC,
|
|
pspec->name,
|
|
G_OBJECT_TYPE_NAME (object));
|
|
else
|
|
{
|
|
GValue *prop_value, tmp_value = G_VALUE_INIT;
|
|
|
|
/* auto-conversion of the callers value type
|
|
*/
|
|
if (G_VALUE_TYPE (value) == pspec->value_type)
|
|
{
|
|
g_value_reset (value);
|
|
prop_value = value;
|
|
}
|
|
else if (!g_value_type_transformable (pspec->value_type, G_VALUE_TYPE (value)))
|
|
{
|
|
g_warning ("%s: can't retrieve property '%s' of type '%s' as value of type '%s'",
|
|
G_STRFUNC, pspec->name,
|
|
g_type_name (pspec->value_type),
|
|
G_VALUE_TYPE_NAME (value));
|
|
g_object_unref (object);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
g_value_init (&tmp_value, pspec->value_type);
|
|
prop_value = &tmp_value;
|
|
}
|
|
object_get_property (object, pspec, prop_value);
|
|
if (prop_value != value)
|
|
{
|
|
g_value_transform (prop_value, value);
|
|
g_value_unset (&tmp_value);
|
|
}
|
|
}
|
|
|
|
g_object_unref (object);
|
|
}
|
|
|
|
/**
|
|
* g_object_connect: (skip)
|
|
* @object: (type GObject.Object): a #GObject
|
|
* @signal_spec: the spec for the first signal
|
|
* @...: #GCallback for the first signal, followed by data for the
|
|
* first signal, followed optionally by more signal
|
|
* spec/callback/data triples, followed by %NULL
|
|
*
|
|
* A convenience function to connect multiple signals at once.
|
|
*
|
|
* The signal specs expected by this function have the form
|
|
* "modifier::signal_name", where modifier can be one of the following:
|
|
* * - signal: equivalent to g_signal_connect_data (..., NULL, 0)
|
|
* - object-signal, object_signal: equivalent to g_signal_connect_object (..., 0)
|
|
* - swapped-signal, swapped_signal: equivalent to g_signal_connect_data (..., NULL, G_CONNECT_SWAPPED)
|
|
* - swapped_object_signal, swapped-object-signal: equivalent to g_signal_connect_object (..., G_CONNECT_SWAPPED)
|
|
* - signal_after, signal-after: equivalent to g_signal_connect_data (..., NULL, G_CONNECT_AFTER)
|
|
* - object_signal_after, object-signal-after: equivalent to g_signal_connect_object (..., G_CONNECT_AFTER)
|
|
* - swapped_signal_after, swapped-signal-after: equivalent to g_signal_connect_data (..., NULL, G_CONNECT_SWAPPED | G_CONNECT_AFTER)
|
|
* - swapped_object_signal_after, swapped-object-signal-after: equivalent to g_signal_connect_object (..., G_CONNECT_SWAPPED | G_CONNECT_AFTER)
|
|
*
|
|
* |[<!-- language="C" -->
|
|
* menu->toplevel = g_object_connect (g_object_new (GTK_TYPE_WINDOW,
|
|
* "type", GTK_WINDOW_POPUP,
|
|
* "child", menu,
|
|
* NULL),
|
|
* "signal::event", gtk_menu_window_event, menu,
|
|
* "signal::size_request", gtk_menu_window_size_request, menu,
|
|
* "signal::destroy", gtk_widget_destroyed, &menu->toplevel,
|
|
* NULL);
|
|
* ]|
|
|
*
|
|
* Returns: (transfer none) (type GObject.Object): @object
|
|
*/
|
|
gpointer
|
|
g_object_connect (gpointer _object,
|
|
const gchar *signal_spec,
|
|
...)
|
|
{
|
|
GObject *object = _object;
|
|
va_list var_args;
|
|
|
|
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
|
g_return_val_if_fail (object->ref_count > 0, object);
|
|
|
|
va_start (var_args, signal_spec);
|
|
while (signal_spec)
|
|
{
|
|
GCallback callback = va_arg (var_args, GCallback);
|
|
gpointer data = va_arg (var_args, gpointer);
|
|
|
|
if (strncmp (signal_spec, "signal::", 8) == 0)
|
|
g_signal_connect_data (object, signal_spec + 8,
|
|
callback, data, NULL,
|
|
0);
|
|
else if (strncmp (signal_spec, "object_signal::", 15) == 0 ||
|
|
strncmp (signal_spec, "object-signal::", 15) == 0)
|
|
g_signal_connect_object (object, signal_spec + 15,
|
|
callback, data,
|
|
0);
|
|
else if (strncmp (signal_spec, "swapped_signal::", 16) == 0 ||
|
|
strncmp (signal_spec, "swapped-signal::", 16) == 0)
|
|
g_signal_connect_data (object, signal_spec + 16,
|
|
callback, data, NULL,
|
|
G_CONNECT_SWAPPED);
|
|
else if (strncmp (signal_spec, "swapped_object_signal::", 23) == 0 ||
|
|
strncmp (signal_spec, "swapped-object-signal::", 23) == 0)
|
|
g_signal_connect_object (object, signal_spec + 23,
|
|
callback, data,
|
|
G_CONNECT_SWAPPED);
|
|
else if (strncmp (signal_spec, "signal_after::", 14) == 0 ||
|
|
strncmp (signal_spec, "signal-after::", 14) == 0)
|
|
g_signal_connect_data (object, signal_spec + 14,
|
|
callback, data, NULL,
|
|
G_CONNECT_AFTER);
|
|
else if (strncmp (signal_spec, "object_signal_after::", 21) == 0 ||
|
|
strncmp (signal_spec, "object-signal-after::", 21) == 0)
|
|
g_signal_connect_object (object, signal_spec + 21,
|
|
callback, data,
|
|
G_CONNECT_AFTER);
|
|
else if (strncmp (signal_spec, "swapped_signal_after::", 22) == 0 ||
|
|
strncmp (signal_spec, "swapped-signal-after::", 22) == 0)
|
|
g_signal_connect_data (object, signal_spec + 22,
|
|
callback, data, NULL,
|
|
G_CONNECT_SWAPPED | G_CONNECT_AFTER);
|
|
else if (strncmp (signal_spec, "swapped_object_signal_after::", 29) == 0 ||
|
|
strncmp (signal_spec, "swapped-object-signal-after::", 29) == 0)
|
|
g_signal_connect_object (object, signal_spec + 29,
|
|
callback, data,
|
|
G_CONNECT_SWAPPED | G_CONNECT_AFTER);
|
|
else
|
|
{
|
|
g_warning ("%s: invalid signal spec \"%s\"", G_STRFUNC, signal_spec);
|
|
break;
|
|
}
|
|
signal_spec = va_arg (var_args, gchar*);
|
|
}
|
|
va_end (var_args);
|
|
|
|
return object;
|
|
}
|
|
|
|
/**
|
|
* g_object_disconnect: (skip)
|
|
* @object: (type GObject.Object): a #GObject
|
|
* @signal_spec: the spec for the first signal
|
|
* @...: #GCallback for the first signal, followed by data for the first signal,
|
|
* followed optionally by more signal spec/callback/data triples,
|
|
* followed by %NULL
|
|
*
|
|
* A convenience function to disconnect multiple signals at once.
|
|
*
|
|
* The signal specs expected by this function have the form
|
|
* "any_signal", which means to disconnect any signal with matching
|
|
* callback and data, or "any_signal::signal_name", which only
|
|
* disconnects the signal named "signal_name".
|
|
*/
|
|
void
|
|
g_object_disconnect (gpointer _object,
|
|
const gchar *signal_spec,
|
|
...)
|
|
{
|
|
GObject *object = _object;
|
|
va_list var_args;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (object->ref_count > 0);
|
|
|
|
va_start (var_args, signal_spec);
|
|
while (signal_spec)
|
|
{
|
|
GCallback callback = va_arg (var_args, GCallback);
|
|
gpointer data = va_arg (var_args, gpointer);
|
|
guint sid = 0, detail = 0, mask = 0;
|
|
|
|
if (strncmp (signal_spec, "any_signal::", 12) == 0 ||
|
|
strncmp (signal_spec, "any-signal::", 12) == 0)
|
|
{
|
|
signal_spec += 12;
|
|
mask = G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA;
|
|
}
|
|
else if (strcmp (signal_spec, "any_signal") == 0 ||
|
|
strcmp (signal_spec, "any-signal") == 0)
|
|
{
|
|
signal_spec += 10;
|
|
mask = G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA;
|
|
}
|
|
else
|
|
{
|
|
g_warning ("%s: invalid signal spec \"%s\"", G_STRFUNC, signal_spec);
|
|
break;
|
|
}
|
|
|
|
if ((mask & G_SIGNAL_MATCH_ID) &&
|
|
!g_signal_parse_name (signal_spec, G_OBJECT_TYPE (object), &sid, &detail, FALSE))
|
|
g_warning ("%s: invalid signal name \"%s\"", G_STRFUNC, signal_spec);
|
|
else if (!g_signal_handlers_disconnect_matched (object, mask | (detail ? G_SIGNAL_MATCH_DETAIL : 0),
|
|
sid, detail,
|
|
NULL, (gpointer)callback, data))
|
|
g_warning ("%s: signal handler %p(%p) is not connected", G_STRFUNC, callback, data);
|
|
signal_spec = va_arg (var_args, gchar*);
|
|
}
|
|
va_end (var_args);
|
|
}
|
|
|
|
typedef struct {
|
|
GObject *object;
|
|
guint n_weak_refs;
|
|
struct {
|
|
GWeakNotify notify;
|
|
gpointer data;
|
|
} weak_refs[1]; /* flexible array */
|
|
} WeakRefStack;
|
|
|
|
static void
|
|
weak_refs_notify (gpointer data)
|
|
{
|
|
WeakRefStack *wstack = data;
|
|
guint i;
|
|
|
|
for (i = 0; i < wstack->n_weak_refs; i++)
|
|
wstack->weak_refs[i].notify (wstack->weak_refs[i].data, wstack->object);
|
|
g_free (wstack);
|
|
}
|
|
|
|
/**
|
|
* g_object_weak_ref: (skip)
|
|
* @object: #GObject to reference weakly
|
|
* @notify: callback to invoke before the object is freed
|
|
* @data: extra data to pass to notify
|
|
*
|
|
* Adds a weak reference callback to an object. Weak references are
|
|
* used for notification when an object is finalized. They are called
|
|
* "weak references" because they allow you to safely hold a pointer
|
|
* to an object without calling g_object_ref() (g_object_ref() adds a
|
|
* strong reference, that is, forces the object to stay alive).
|
|
*
|
|
* Note that the weak references created by this method are not
|
|
* thread-safe: they cannot safely be used in one thread if the
|
|
* object's last g_object_unref() might happen in another thread.
|
|
* Use #GWeakRef if thread-safety is required.
|
|
*/
|
|
void
|
|
g_object_weak_ref (GObject *object,
|
|
GWeakNotify notify,
|
|
gpointer data)
|
|
{
|
|
WeakRefStack *wstack;
|
|
guint i;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (notify != NULL);
|
|
g_return_if_fail (object->ref_count >= 1);
|
|
|
|
G_LOCK (weak_refs_mutex);
|
|
wstack = g_datalist_id_remove_no_notify (&object->qdata, quark_weak_refs);
|
|
if (wstack)
|
|
{
|
|
i = wstack->n_weak_refs++;
|
|
wstack = g_realloc (wstack, sizeof (*wstack) + sizeof (wstack->weak_refs[0]) * i);
|
|
}
|
|
else
|
|
{
|
|
wstack = g_renew (WeakRefStack, NULL, 1);
|
|
wstack->object = object;
|
|
wstack->n_weak_refs = 1;
|
|
i = 0;
|
|
}
|
|
wstack->weak_refs[i].notify = notify;
|
|
wstack->weak_refs[i].data = data;
|
|
g_datalist_id_set_data_full (&object->qdata, quark_weak_refs, wstack, weak_refs_notify);
|
|
G_UNLOCK (weak_refs_mutex);
|
|
}
|
|
|
|
/**
|
|
* g_object_weak_unref: (skip)
|
|
* @object: #GObject to remove a weak reference from
|
|
* @notify: callback to search for
|
|
* @data: data to search for
|
|
*
|
|
* Removes a weak reference callback to an object.
|
|
*/
|
|
void
|
|
g_object_weak_unref (GObject *object,
|
|
GWeakNotify notify,
|
|
gpointer data)
|
|
{
|
|
WeakRefStack *wstack;
|
|
gboolean found_one = FALSE;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (notify != NULL);
|
|
|
|
G_LOCK (weak_refs_mutex);
|
|
wstack = g_datalist_id_get_data (&object->qdata, quark_weak_refs);
|
|
if (wstack)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < wstack->n_weak_refs; i++)
|
|
if (wstack->weak_refs[i].notify == notify &&
|
|
wstack->weak_refs[i].data == data)
|
|
{
|
|
found_one = TRUE;
|
|
wstack->n_weak_refs -= 1;
|
|
if (i != wstack->n_weak_refs)
|
|
wstack->weak_refs[i] = wstack->weak_refs[wstack->n_weak_refs];
|
|
|
|
break;
|
|
}
|
|
}
|
|
G_UNLOCK (weak_refs_mutex);
|
|
if (!found_one)
|
|
g_warning ("%s: couldn't find weak ref %p(%p)", G_STRFUNC, notify, data);
|
|
}
|
|
|
|
/**
|
|
* g_object_add_weak_pointer: (skip)
|
|
* @object: The object that should be weak referenced.
|
|
* @weak_pointer_location: (inout) (not optional): The memory address
|
|
* of a pointer.
|
|
*
|
|
* Adds a weak reference from weak_pointer to @object to indicate that
|
|
* the pointer located at @weak_pointer_location is only valid during
|
|
* the lifetime of @object. When the @object is finalized,
|
|
* @weak_pointer will be set to %NULL.
|
|
*
|
|
* Note that as with g_object_weak_ref(), the weak references created by
|
|
* this method are not thread-safe: they cannot safely be used in one
|
|
* thread if the object's last g_object_unref() might happen in another
|
|
* thread. Use #GWeakRef if thread-safety is required.
|
|
*/
|
|
void
|
|
g_object_add_weak_pointer (GObject *object,
|
|
gpointer *weak_pointer_location)
|
|
{
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (weak_pointer_location != NULL);
|
|
|
|
g_object_weak_ref (object,
|
|
(GWeakNotify) g_nullify_pointer,
|
|
weak_pointer_location);
|
|
}
|
|
|
|
/**
|
|
* g_object_remove_weak_pointer: (skip)
|
|
* @object: The object that is weak referenced.
|
|
* @weak_pointer_location: (inout) (not optional): The memory address
|
|
* of a pointer.
|
|
*
|
|
* Removes a weak reference from @object that was previously added
|
|
* using g_object_add_weak_pointer(). The @weak_pointer_location has
|
|
* to match the one used with g_object_add_weak_pointer().
|
|
*/
|
|
void
|
|
g_object_remove_weak_pointer (GObject *object,
|
|
gpointer *weak_pointer_location)
|
|
{
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (weak_pointer_location != NULL);
|
|
|
|
g_object_weak_unref (object,
|
|
(GWeakNotify) g_nullify_pointer,
|
|
weak_pointer_location);
|
|
}
|
|
|
|
static guint
|
|
object_floating_flag_handler (GObject *object,
|
|
gint job)
|
|
{
|
|
switch (job)
|
|
{
|
|
gpointer oldvalue;
|
|
case +1: /* force floating if possible */
|
|
do
|
|
oldvalue = g_atomic_pointer_get (&object->qdata);
|
|
while (!g_atomic_pointer_compare_and_exchange ((void**) &object->qdata, oldvalue,
|
|
(gpointer) ((gsize) oldvalue | OBJECT_FLOATING_FLAG)));
|
|
return (gsize) oldvalue & OBJECT_FLOATING_FLAG;
|
|
case -1: /* sink if possible */
|
|
do
|
|
oldvalue = g_atomic_pointer_get (&object->qdata);
|
|
while (!g_atomic_pointer_compare_and_exchange ((void**) &object->qdata, oldvalue,
|
|
(gpointer) ((gsize) oldvalue & ~(gsize) OBJECT_FLOATING_FLAG)));
|
|
return (gsize) oldvalue & OBJECT_FLOATING_FLAG;
|
|
default: /* check floating */
|
|
return 0 != ((gsize) g_atomic_pointer_get (&object->qdata) & OBJECT_FLOATING_FLAG);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* g_object_is_floating:
|
|
* @object: (type GObject.Object): a #GObject
|
|
*
|
|
* Checks whether @object has a [floating][floating-ref] reference.
|
|
*
|
|
* Since: 2.10
|
|
*
|
|
* Returns: %TRUE if @object has a floating reference
|
|
*/
|
|
gboolean
|
|
g_object_is_floating (gpointer _object)
|
|
{
|
|
GObject *object = _object;
|
|
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
|
return floating_flag_handler (object, 0);
|
|
}
|
|
|
|
/**
|
|
* g_object_ref_sink:
|
|
* @object: (type GObject.Object): a #GObject
|
|
*
|
|
* Increase the reference count of @object, and possibly remove the
|
|
* [floating][floating-ref] reference, if @object has a floating reference.
|
|
*
|
|
* In other words, if the object is floating, then this call "assumes
|
|
* ownership" of the floating reference, converting it to a normal
|
|
* reference by clearing the floating flag while leaving the reference
|
|
* count unchanged. If the object is not floating, then this call
|
|
* adds a new normal reference increasing the reference count by one.
|
|
*
|
|
* Since: 2.10
|
|
*
|
|
* Returns: (type GObject.Object) (transfer none): @object
|
|
*/
|
|
gpointer
|
|
g_object_ref_sink (gpointer _object)
|
|
{
|
|
GObject *object = _object;
|
|
gboolean was_floating;
|
|
g_return_val_if_fail (G_IS_OBJECT (object), object);
|
|
g_return_val_if_fail (object->ref_count >= 1, object);
|
|
g_object_ref (object);
|
|
was_floating = floating_flag_handler (object, -1);
|
|
if (was_floating)
|
|
g_object_unref (object);
|
|
return object;
|
|
}
|
|
|
|
/**
|
|
* g_object_force_floating:
|
|
* @object: a #GObject
|
|
*
|
|
* This function is intended for #GObject implementations to re-enforce
|
|
* a [floating][floating-ref] object reference. Doing this is seldom
|
|
* required: all #GInitiallyUnowneds are created with a floating reference
|
|
* which usually just needs to be sunken by calling g_object_ref_sink().
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
void
|
|
g_object_force_floating (GObject *object)
|
|
{
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (object->ref_count >= 1);
|
|
|
|
floating_flag_handler (object, +1);
|
|
}
|
|
|
|
typedef struct {
|
|
GObject *object;
|
|
guint n_toggle_refs;
|
|
struct {
|
|
GToggleNotify notify;
|
|
gpointer data;
|
|
} toggle_refs[1]; /* flexible array */
|
|
} ToggleRefStack;
|
|
|
|
static void
|
|
toggle_refs_notify (GObject *object,
|
|
gboolean is_last_ref)
|
|
{
|
|
ToggleRefStack tstack, *tstackptr;
|
|
|
|
G_LOCK (toggle_refs_mutex);
|
|
tstackptr = g_datalist_id_get_data (&object->qdata, quark_toggle_refs);
|
|
tstack = *tstackptr;
|
|
G_UNLOCK (toggle_refs_mutex);
|
|
|
|
/* Reentrancy here is not as tricky as it seems, because a toggle reference
|
|
* will only be notified when there is exactly one of them.
|
|
*/
|
|
g_assert (tstack.n_toggle_refs == 1);
|
|
tstack.toggle_refs[0].notify (tstack.toggle_refs[0].data, tstack.object, is_last_ref);
|
|
}
|
|
|
|
/**
|
|
* g_object_add_toggle_ref: (skip)
|
|
* @object: a #GObject
|
|
* @notify: a function to call when this reference is the
|
|
* last reference to the object, or is no longer
|
|
* the last reference.
|
|
* @data: data to pass to @notify
|
|
*
|
|
* Increases the reference count of the object by one and sets a
|
|
* callback to be called when all other references to the object are
|
|
* dropped, or when this is already the last reference to the object
|
|
* and another reference is established.
|
|
*
|
|
* This functionality is intended for binding @object to a proxy
|
|
* object managed by another memory manager. This is done with two
|
|
* paired references: the strong reference added by
|
|
* g_object_add_toggle_ref() and a reverse reference to the proxy
|
|
* object which is either a strong reference or weak reference.
|
|
*
|
|
* The setup is that when there are no other references to @object,
|
|
* only a weak reference is held in the reverse direction from @object
|
|
* to the proxy object, but when there are other references held to
|
|
* @object, a strong reference is held. The @notify callback is called
|
|
* when the reference from @object to the proxy object should be
|
|
* "toggled" from strong to weak (@is_last_ref true) or weak to strong
|
|
* (@is_last_ref false).
|
|
*
|
|
* Since a (normal) reference must be held to the object before
|
|
* calling g_object_add_toggle_ref(), the initial state of the reverse
|
|
* link is always strong.
|
|
*
|
|
* Multiple toggle references may be added to the same gobject,
|
|
* however if there are multiple toggle references to an object, none
|
|
* of them will ever be notified until all but one are removed. For
|
|
* this reason, you should only ever use a toggle reference if there
|
|
* is important state in the proxy object.
|
|
*
|
|
* Since: 2.8
|
|
*/
|
|
void
|
|
g_object_add_toggle_ref (GObject *object,
|
|
GToggleNotify notify,
|
|
gpointer data)
|
|
{
|
|
ToggleRefStack *tstack;
|
|
guint i;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (notify != NULL);
|
|
g_return_if_fail (object->ref_count >= 1);
|
|
|
|
g_object_ref (object);
|
|
|
|
G_LOCK (toggle_refs_mutex);
|
|
tstack = g_datalist_id_remove_no_notify (&object->qdata, quark_toggle_refs);
|
|
if (tstack)
|
|
{
|
|
i = tstack->n_toggle_refs++;
|
|
/* allocate i = tstate->n_toggle_refs - 1 positions beyond the 1 declared
|
|
* in tstate->toggle_refs */
|
|
tstack = g_realloc (tstack, sizeof (*tstack) + sizeof (tstack->toggle_refs[0]) * i);
|
|
}
|
|
else
|
|
{
|
|
tstack = g_renew (ToggleRefStack, NULL, 1);
|
|
tstack->object = object;
|
|
tstack->n_toggle_refs = 1;
|
|
i = 0;
|
|
}
|
|
|
|
/* Set a flag for fast lookup after adding the first toggle reference */
|
|
if (tstack->n_toggle_refs == 1)
|
|
g_datalist_set_flags (&object->qdata, OBJECT_HAS_TOGGLE_REF_FLAG);
|
|
|
|
tstack->toggle_refs[i].notify = notify;
|
|
tstack->toggle_refs[i].data = data;
|
|
g_datalist_id_set_data_full (&object->qdata, quark_toggle_refs, tstack,
|
|
(GDestroyNotify)g_free);
|
|
G_UNLOCK (toggle_refs_mutex);
|
|
}
|
|
|
|
/**
|
|
* g_object_remove_toggle_ref: (skip)
|
|
* @object: a #GObject
|
|
* @notify: a function to call when this reference is the
|
|
* last reference to the object, or is no longer
|
|
* the last reference.
|
|
* @data: data to pass to @notify
|
|
*
|
|
* Removes a reference added with g_object_add_toggle_ref(). The
|
|
* reference count of the object is decreased by one.
|
|
*
|
|
* Since: 2.8
|
|
*/
|
|
void
|
|
g_object_remove_toggle_ref (GObject *object,
|
|
GToggleNotify notify,
|
|
gpointer data)
|
|
{
|
|
ToggleRefStack *tstack;
|
|
gboolean found_one = FALSE;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (notify != NULL);
|
|
|
|
G_LOCK (toggle_refs_mutex);
|
|
tstack = g_datalist_id_get_data (&object->qdata, quark_toggle_refs);
|
|
if (tstack)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < tstack->n_toggle_refs; i++)
|
|
if (tstack->toggle_refs[i].notify == notify &&
|
|
tstack->toggle_refs[i].data == data)
|
|
{
|
|
found_one = TRUE;
|
|
tstack->n_toggle_refs -= 1;
|
|
if (i != tstack->n_toggle_refs)
|
|
tstack->toggle_refs[i] = tstack->toggle_refs[tstack->n_toggle_refs];
|
|
|
|
if (tstack->n_toggle_refs == 0)
|
|
g_datalist_unset_flags (&object->qdata, OBJECT_HAS_TOGGLE_REF_FLAG);
|
|
|
|
break;
|
|
}
|
|
}
|
|
G_UNLOCK (toggle_refs_mutex);
|
|
|
|
if (found_one)
|
|
g_object_unref (object);
|
|
else
|
|
g_warning ("%s: couldn't find toggle ref %p(%p)", G_STRFUNC, notify, data);
|
|
}
|
|
|
|
/**
|
|
* g_object_ref:
|
|
* @object: (type GObject.Object): a #GObject
|
|
*
|
|
* Increases the reference count of @object.
|
|
*
|
|
* Returns: (type GObject.Object) (transfer none): the same @object
|
|
*/
|
|
gpointer
|
|
g_object_ref (gpointer _object)
|
|
{
|
|
GObject *object = _object;
|
|
gint old_val;
|
|
|
|
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
|
g_return_val_if_fail (object->ref_count > 0, NULL);
|
|
|
|
old_val = g_atomic_int_add (&object->ref_count, 1);
|
|
|
|
if (old_val == 1 && OBJECT_HAS_TOGGLE_REF (object))
|
|
toggle_refs_notify (object, FALSE);
|
|
|
|
TRACE (GOBJECT_OBJECT_REF(object,G_TYPE_FROM_INSTANCE(object),old_val));
|
|
|
|
return object;
|
|
}
|
|
|
|
/**
|
|
* g_object_unref:
|
|
* @object: (type GObject.Object): a #GObject
|
|
*
|
|
* Decreases the reference count of @object. When its reference count
|
|
* drops to 0, the object is finalized (i.e. its memory is freed).
|
|
*
|
|
* If the pointer to the #GObject may be reused in future (for example, if it is
|
|
* an instance variable of another object), it is recommended to clear the
|
|
* pointer to %NULL rather than retain a dangling pointer to a potentially
|
|
* invalid #GObject instance. Use g_clear_object() for this.
|
|
*/
|
|
void
|
|
g_object_unref (gpointer _object)
|
|
{
|
|
GObject *object = _object;
|
|
gint old_ref;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (object->ref_count > 0);
|
|
|
|
/* here we want to atomically do: if (ref_count>1) { ref_count--; return; } */
|
|
retry_atomic_decrement1:
|
|
old_ref = g_atomic_int_get (&object->ref_count);
|
|
if (old_ref > 1)
|
|
{
|
|
/* valid if last 2 refs are owned by this call to unref and the toggle_ref */
|
|
gboolean has_toggle_ref = OBJECT_HAS_TOGGLE_REF (object);
|
|
|
|
if (!g_atomic_int_compare_and_exchange ((int *)&object->ref_count, old_ref, old_ref - 1))
|
|
goto retry_atomic_decrement1;
|
|
|
|
TRACE (GOBJECT_OBJECT_UNREF(object,G_TYPE_FROM_INSTANCE(object),old_ref));
|
|
|
|
/* if we went from 2->1 we need to notify toggle refs if any */
|
|
if (old_ref == 2 && has_toggle_ref) /* The last ref being held in this case is owned by the toggle_ref */
|
|
toggle_refs_notify (object, TRUE);
|
|
}
|
|
else
|
|
{
|
|
GSList **weak_locations;
|
|
|
|
/* The only way that this object can live at this point is if
|
|
* there are outstanding weak references already established
|
|
* before we got here.
|
|
*
|
|
* If there were not already weak references then no more can be
|
|
* established at this time, because the other thread would have
|
|
* to hold a strong ref in order to call
|
|
* g_object_add_weak_pointer() and then we wouldn't be here.
|
|
*/
|
|
weak_locations = g_datalist_id_get_data (&object->qdata, quark_weak_locations);
|
|
|
|
if (weak_locations != NULL)
|
|
{
|
|
g_rw_lock_writer_lock (&weak_locations_lock);
|
|
|
|
/* It is possible that one of the weak references beat us to
|
|
* the lock. Make sure the refcount is still what we expected
|
|
* it to be.
|
|
*/
|
|
old_ref = g_atomic_int_get (&object->ref_count);
|
|
if (old_ref != 1)
|
|
{
|
|
g_rw_lock_writer_unlock (&weak_locations_lock);
|
|
goto retry_atomic_decrement1;
|
|
}
|
|
|
|
/* We got the lock first, so the object will definitely die
|
|
* now. Clear out all the weak references.
|
|
*/
|
|
while (*weak_locations)
|
|
{
|
|
GWeakRef *weak_ref_location = (*weak_locations)->data;
|
|
|
|
weak_ref_location->priv.p = NULL;
|
|
*weak_locations = g_slist_delete_link (*weak_locations, *weak_locations);
|
|
}
|
|
|
|
g_rw_lock_writer_unlock (&weak_locations_lock);
|
|
}
|
|
|
|
/* we are about to remove the last reference */
|
|
TRACE (GOBJECT_OBJECT_DISPOSE(object,G_TYPE_FROM_INSTANCE(object), 1));
|
|
G_OBJECT_GET_CLASS (object)->dispose (object);
|
|
TRACE (GOBJECT_OBJECT_DISPOSE_END(object,G_TYPE_FROM_INSTANCE(object), 1));
|
|
|
|
/* may have been re-referenced meanwhile */
|
|
retry_atomic_decrement2:
|
|
old_ref = g_atomic_int_get ((int *)&object->ref_count);
|
|
if (old_ref > 1)
|
|
{
|
|
/* valid if last 2 refs are owned by this call to unref and the toggle_ref */
|
|
gboolean has_toggle_ref = OBJECT_HAS_TOGGLE_REF (object);
|
|
|
|
if (!g_atomic_int_compare_and_exchange ((int *)&object->ref_count, old_ref, old_ref - 1))
|
|
goto retry_atomic_decrement2;
|
|
|
|
TRACE (GOBJECT_OBJECT_UNREF(object,G_TYPE_FROM_INSTANCE(object),old_ref));
|
|
|
|
/* if we went from 2->1 we need to notify toggle refs if any */
|
|
if (old_ref == 2 && has_toggle_ref) /* The last ref being held in this case is owned by the toggle_ref */
|
|
toggle_refs_notify (object, TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
/* we are still in the process of taking away the last ref */
|
|
g_datalist_id_set_data (&object->qdata, quark_closure_array, NULL);
|
|
g_signal_handlers_destroy (object);
|
|
g_datalist_id_set_data (&object->qdata, quark_weak_refs, NULL);
|
|
|
|
/* decrement the last reference */
|
|
old_ref = g_atomic_int_add (&object->ref_count, -1);
|
|
|
|
TRACE (GOBJECT_OBJECT_UNREF(object,G_TYPE_FROM_INSTANCE(object),old_ref));
|
|
|
|
/* may have been re-referenced meanwhile */
|
|
if (G_LIKELY (old_ref == 1))
|
|
{
|
|
TRACE (GOBJECT_OBJECT_FINALIZE(object,G_TYPE_FROM_INSTANCE(object)));
|
|
G_OBJECT_GET_CLASS (object)->finalize (object);
|
|
|
|
TRACE (GOBJECT_OBJECT_FINALIZE_END(object,G_TYPE_FROM_INSTANCE(object)));
|
|
|
|
GOBJECT_IF_DEBUG (OBJECTS,
|
|
{
|
|
/* catch objects not chaining finalize handlers */
|
|
G_LOCK (debug_objects);
|
|
g_assert (g_hash_table_lookup (debug_objects_ht, object) == NULL);
|
|
G_UNLOCK (debug_objects);
|
|
});
|
|
g_type_free_instance ((GTypeInstance*) object);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* g_clear_object: (skip)
|
|
* @object_ptr: a pointer to a #GObject reference
|
|
*
|
|
* Clears a reference to a #GObject.
|
|
*
|
|
* @object_ptr must not be %NULL.
|
|
*
|
|
* If the reference is %NULL then this function does nothing.
|
|
* Otherwise, the reference count of the object is decreased and the
|
|
* pointer is set to %NULL.
|
|
*
|
|
* A macro is also included that allows this function to be used without
|
|
* pointer casts.
|
|
*
|
|
* Since: 2.28
|
|
**/
|
|
#undef g_clear_object
|
|
void
|
|
g_clear_object (volatile GObject **object_ptr)
|
|
{
|
|
g_clear_pointer (object_ptr, g_object_unref);
|
|
}
|
|
|
|
/**
|
|
* g_object_get_qdata:
|
|
* @object: The GObject to get a stored user data pointer from
|
|
* @quark: A #GQuark, naming the user data pointer
|
|
*
|
|
* This function gets back user data pointers stored via
|
|
* g_object_set_qdata().
|
|
*
|
|
* Returns: (transfer none): The user data pointer set, or %NULL
|
|
*/
|
|
gpointer
|
|
g_object_get_qdata (GObject *object,
|
|
GQuark quark)
|
|
{
|
|
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
|
|
|
return quark ? g_datalist_id_get_data (&object->qdata, quark) : NULL;
|
|
}
|
|
|
|
/**
|
|
* g_object_set_qdata: (skip)
|
|
* @object: The GObject to set store a user data pointer
|
|
* @quark: A #GQuark, naming the user data pointer
|
|
* @data: An opaque user data pointer
|
|
*
|
|
* This sets an opaque, named pointer on an object.
|
|
* The name is specified through a #GQuark (retrived e.g. via
|
|
* g_quark_from_static_string()), and the pointer
|
|
* can be gotten back from the @object with g_object_get_qdata()
|
|
* until the @object is finalized.
|
|
* Setting a previously set user data pointer, overrides (frees)
|
|
* the old pointer set, using #NULL as pointer essentially
|
|
* removes the data stored.
|
|
*/
|
|
void
|
|
g_object_set_qdata (GObject *object,
|
|
GQuark quark,
|
|
gpointer data)
|
|
{
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (quark > 0);
|
|
|
|
g_datalist_id_set_data (&object->qdata, quark, data);
|
|
}
|
|
|
|
/**
|
|
* g_object_dup_qdata:
|
|
* @object: the #GObject to store user data on
|
|
* @quark: a #GQuark, naming the user data pointer
|
|
* @dup_func: (nullable): function to dup the value
|
|
* @user_data: (nullable): passed as user_data to @dup_func
|
|
*
|
|
* This is a variant of g_object_get_qdata() which returns
|
|
* a 'duplicate' of the value. @dup_func defines the
|
|
* meaning of 'duplicate' in this context, it could e.g.
|
|
* take a reference on a ref-counted object.
|
|
*
|
|
* If the @quark is not set on the object then @dup_func
|
|
* will be called with a %NULL argument.
|
|
*
|
|
* Note that @dup_func is called while user data of @object
|
|
* is locked.
|
|
*
|
|
* This function can be useful to avoid races when multiple
|
|
* threads are using object data on the same key on the same
|
|
* object.
|
|
*
|
|
* Returns: the result of calling @dup_func on the value
|
|
* associated with @quark on @object, or %NULL if not set.
|
|
* If @dup_func is %NULL, the value is returned
|
|
* unmodified.
|
|
*
|
|
* Since: 2.34
|
|
*/
|
|
gpointer
|
|
g_object_dup_qdata (GObject *object,
|
|
GQuark quark,
|
|
GDuplicateFunc dup_func,
|
|
gpointer user_data)
|
|
{
|
|
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
|
g_return_val_if_fail (quark > 0, NULL);
|
|
|
|
return g_datalist_id_dup_data (&object->qdata, quark, dup_func, user_data);
|
|
}
|
|
|
|
/**
|
|
* g_object_replace_qdata:
|
|
* @object: the #GObject to store user data on
|
|
* @quark: a #GQuark, naming the user data pointer
|
|
* @oldval: (nullable): the old value to compare against
|
|
* @newval: (nullable): the new value
|
|
* @destroy: (nullable): a destroy notify for the new value
|
|
* @old_destroy: (nullable): destroy notify for the existing value
|
|
*
|
|
* Compares the user data for the key @quark on @object with
|
|
* @oldval, and if they are the same, replaces @oldval with
|
|
* @newval.
|
|
*
|
|
* This is like a typical atomic compare-and-exchange
|
|
* operation, for user data on an object.
|
|
*
|
|
* If the previous value was replaced then ownership of the
|
|
* old value (@oldval) is passed to the caller, including
|
|
* the registered destroy notify for it (passed out in @old_destroy).
|
|
* Its up to the caller to free this as he wishes, which may
|
|
* or may not include using @old_destroy as sometimes replacement
|
|
* should not destroy the object in the normal way.
|
|
*
|
|
* Returns: %TRUE if the existing value for @quark was replaced
|
|
* by @newval, %FALSE otherwise.
|
|
*
|
|
* Since: 2.34
|
|
*/
|
|
gboolean
|
|
g_object_replace_qdata (GObject *object,
|
|
GQuark quark,
|
|
gpointer oldval,
|
|
gpointer newval,
|
|
GDestroyNotify destroy,
|
|
GDestroyNotify *old_destroy)
|
|
{
|
|
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
|
g_return_val_if_fail (quark > 0, FALSE);
|
|
|
|
return g_datalist_id_replace_data (&object->qdata, quark,
|
|
oldval, newval, destroy,
|
|
old_destroy);
|
|
}
|
|
|
|
/**
|
|
* g_object_set_qdata_full: (skip)
|
|
* @object: The GObject to set store a user data pointer
|
|
* @quark: A #GQuark, naming the user data pointer
|
|
* @data: An opaque user data pointer
|
|
* @destroy: Function to invoke with @data as argument, when @data
|
|
* needs to be freed
|
|
*
|
|
* This function works like g_object_set_qdata(), but in addition,
|
|
* a void (*destroy) (gpointer) function may be specified which is
|
|
* called with @data as argument when the @object is finalized, or
|
|
* the data is being overwritten by a call to g_object_set_qdata()
|
|
* with the same @quark.
|
|
*/
|
|
void
|
|
g_object_set_qdata_full (GObject *object,
|
|
GQuark quark,
|
|
gpointer data,
|
|
GDestroyNotify destroy)
|
|
{
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (quark > 0);
|
|
|
|
g_datalist_id_set_data_full (&object->qdata, quark, data,
|
|
data ? destroy : (GDestroyNotify) NULL);
|
|
}
|
|
|
|
/**
|
|
* g_object_steal_qdata:
|
|
* @object: The GObject to get a stored user data pointer from
|
|
* @quark: A #GQuark, naming the user data pointer
|
|
*
|
|
* This function gets back user data pointers stored via
|
|
* g_object_set_qdata() and removes the @data from object
|
|
* without invoking its destroy() function (if any was
|
|
* set).
|
|
* Usually, calling this function is only required to update
|
|
* user data pointers with a destroy notifier, for example:
|
|
* |[<!-- language="C" -->
|
|
* void
|
|
* object_add_to_user_list (GObject *object,
|
|
* const gchar *new_string)
|
|
* {
|
|
* // the quark, naming the object data
|
|
* GQuark quark_string_list = g_quark_from_static_string ("my-string-list");
|
|
* // retrive the old string list
|
|
* GList *list = g_object_steal_qdata (object, quark_string_list);
|
|
*
|
|
* // prepend new string
|
|
* list = g_list_prepend (list, g_strdup (new_string));
|
|
* // this changed 'list', so we need to set it again
|
|
* g_object_set_qdata_full (object, quark_string_list, list, free_string_list);
|
|
* }
|
|
* static void
|
|
* free_string_list (gpointer data)
|
|
* {
|
|
* GList *node, *list = data;
|
|
*
|
|
* for (node = list; node; node = node->next)
|
|
* g_free (node->data);
|
|
* g_list_free (list);
|
|
* }
|
|
* ]|
|
|
* Using g_object_get_qdata() in the above example, instead of
|
|
* g_object_steal_qdata() would have left the destroy function set,
|
|
* and thus the partial string list would have been freed upon
|
|
* g_object_set_qdata_full().
|
|
*
|
|
* Returns: (transfer full): The user data pointer set, or %NULL
|
|
*/
|
|
gpointer
|
|
g_object_steal_qdata (GObject *object,
|
|
GQuark quark)
|
|
{
|
|
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
|
g_return_val_if_fail (quark > 0, NULL);
|
|
|
|
return g_datalist_id_remove_no_notify (&object->qdata, quark);
|
|
}
|
|
|
|
/**
|
|
* g_object_get_data:
|
|
* @object: #GObject containing the associations
|
|
* @key: name of the key for that association
|
|
*
|
|
* Gets a named field from the objects table of associations (see g_object_set_data()).
|
|
*
|
|
* Returns: (transfer none): the data if found, or %NULL if no such data exists.
|
|
*/
|
|
gpointer
|
|
g_object_get_data (GObject *object,
|
|
const gchar *key)
|
|
{
|
|
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
|
g_return_val_if_fail (key != NULL, NULL);
|
|
|
|
return g_datalist_get_data (&object->qdata, key);
|
|
}
|
|
|
|
/**
|
|
* g_object_set_data:
|
|
* @object: #GObject containing the associations.
|
|
* @key: name of the key
|
|
* @data: data to associate with that key
|
|
*
|
|
* Each object carries around a table of associations from
|
|
* strings to pointers. This function lets you set an association.
|
|
*
|
|
* If the object already had an association with that name,
|
|
* the old association will be destroyed.
|
|
*/
|
|
void
|
|
g_object_set_data (GObject *object,
|
|
const gchar *key,
|
|
gpointer data)
|
|
{
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (key != NULL);
|
|
|
|
g_datalist_id_set_data (&object->qdata, g_quark_from_string (key), data);
|
|
}
|
|
|
|
/**
|
|
* g_object_dup_data:
|
|
* @object: the #GObject to store user data on
|
|
* @key: a string, naming the user data pointer
|
|
* @dup_func: (nullable): function to dup the value
|
|
* @user_data: (nullable): passed as user_data to @dup_func
|
|
*
|
|
* This is a variant of g_object_get_data() which returns
|
|
* a 'duplicate' of the value. @dup_func defines the
|
|
* meaning of 'duplicate' in this context, it could e.g.
|
|
* take a reference on a ref-counted object.
|
|
*
|
|
* If the @key is not set on the object then @dup_func
|
|
* will be called with a %NULL argument.
|
|
*
|
|
* Note that @dup_func is called while user data of @object
|
|
* is locked.
|
|
*
|
|
* This function can be useful to avoid races when multiple
|
|
* threads are using object data on the same key on the same
|
|
* object.
|
|
*
|
|
* Returns: the result of calling @dup_func on the value
|
|
* associated with @key on @object, or %NULL if not set.
|
|
* If @dup_func is %NULL, the value is returned
|
|
* unmodified.
|
|
*
|
|
* Since: 2.34
|
|
*/
|
|
gpointer
|
|
g_object_dup_data (GObject *object,
|
|
const gchar *key,
|
|
GDuplicateFunc dup_func,
|
|
gpointer user_data)
|
|
{
|
|
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
|
g_return_val_if_fail (key != NULL, NULL);
|
|
|
|
return g_datalist_id_dup_data (&object->qdata,
|
|
g_quark_from_string (key),
|
|
dup_func, user_data);
|
|
}
|
|
|
|
/**
|
|
* g_object_replace_data:
|
|
* @object: the #GObject to store user data on
|
|
* @key: a string, naming the user data pointer
|
|
* @oldval: (nullable): the old value to compare against
|
|
* @newval: (nullable): the new value
|
|
* @destroy: (nullable): a destroy notify for the new value
|
|
* @old_destroy: (nullable): destroy notify for the existing value
|
|
*
|
|
* Compares the user data for the key @key on @object with
|
|
* @oldval, and if they are the same, replaces @oldval with
|
|
* @newval.
|
|
*
|
|
* This is like a typical atomic compare-and-exchange
|
|
* operation, for user data on an object.
|
|
*
|
|
* If the previous value was replaced then ownership of the
|
|
* old value (@oldval) is passed to the caller, including
|
|
* the registered destroy notify for it (passed out in @old_destroy).
|
|
* Its up to the caller to free this as he wishes, which may
|
|
* or may not include using @old_destroy as sometimes replacement
|
|
* should not destroy the object in the normal way.
|
|
*
|
|
* Returns: %TRUE if the existing value for @key was replaced
|
|
* by @newval, %FALSE otherwise.
|
|
*
|
|
* Since: 2.34
|
|
*/
|
|
gboolean
|
|
g_object_replace_data (GObject *object,
|
|
const gchar *key,
|
|
gpointer oldval,
|
|
gpointer newval,
|
|
GDestroyNotify destroy,
|
|
GDestroyNotify *old_destroy)
|
|
{
|
|
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
|
g_return_val_if_fail (key != NULL, FALSE);
|
|
|
|
return g_datalist_id_replace_data (&object->qdata,
|
|
g_quark_from_string (key),
|
|
oldval, newval, destroy,
|
|
old_destroy);
|
|
}
|
|
|
|
/**
|
|
* g_object_set_data_full: (skip)
|
|
* @object: #GObject containing the associations
|
|
* @key: name of the key
|
|
* @data: data to associate with that key
|
|
* @destroy: function to call when the association is destroyed
|
|
*
|
|
* Like g_object_set_data() except it adds notification
|
|
* for when the association is destroyed, either by setting it
|
|
* to a different value or when the object is destroyed.
|
|
*
|
|
* Note that the @destroy callback is not called if @data is %NULL.
|
|
*/
|
|
void
|
|
g_object_set_data_full (GObject *object,
|
|
const gchar *key,
|
|
gpointer data,
|
|
GDestroyNotify destroy)
|
|
{
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (key != NULL);
|
|
|
|
g_datalist_id_set_data_full (&object->qdata, g_quark_from_string (key), data,
|
|
data ? destroy : (GDestroyNotify) NULL);
|
|
}
|
|
|
|
/**
|
|
* g_object_steal_data:
|
|
* @object: #GObject containing the associations
|
|
* @key: name of the key
|
|
*
|
|
* Remove a specified datum from the object's data associations,
|
|
* without invoking the association's destroy handler.
|
|
*
|
|
* Returns: (transfer full): the data if found, or %NULL if no such data exists.
|
|
*/
|
|
gpointer
|
|
g_object_steal_data (GObject *object,
|
|
const gchar *key)
|
|
{
|
|
GQuark quark;
|
|
|
|
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
|
g_return_val_if_fail (key != NULL, NULL);
|
|
|
|
quark = g_quark_try_string (key);
|
|
|
|
return quark ? g_datalist_id_remove_no_notify (&object->qdata, quark) : NULL;
|
|
}
|
|
|
|
static void
|
|
g_value_object_init (GValue *value)
|
|
{
|
|
value->data[0].v_pointer = NULL;
|
|
}
|
|
|
|
static void
|
|
g_value_object_free_value (GValue *value)
|
|
{
|
|
if (value->data[0].v_pointer)
|
|
g_object_unref (value->data[0].v_pointer);
|
|
}
|
|
|
|
static void
|
|
g_value_object_copy_value (const GValue *src_value,
|
|
GValue *dest_value)
|
|
{
|
|
if (src_value->data[0].v_pointer)
|
|
dest_value->data[0].v_pointer = g_object_ref (src_value->data[0].v_pointer);
|
|
else
|
|
dest_value->data[0].v_pointer = NULL;
|
|
}
|
|
|
|
static void
|
|
g_value_object_transform_value (const GValue *src_value,
|
|
GValue *dest_value)
|
|
{
|
|
if (src_value->data[0].v_pointer && g_type_is_a (G_OBJECT_TYPE (src_value->data[0].v_pointer), G_VALUE_TYPE (dest_value)))
|
|
dest_value->data[0].v_pointer = g_object_ref (src_value->data[0].v_pointer);
|
|
else
|
|
dest_value->data[0].v_pointer = NULL;
|
|
}
|
|
|
|
static gpointer
|
|
g_value_object_peek_pointer (const GValue *value)
|
|
{
|
|
return value->data[0].v_pointer;
|
|
}
|
|
|
|
static gchar*
|
|
g_value_object_collect_value (GValue *value,
|
|
guint n_collect_values,
|
|
GTypeCValue *collect_values,
|
|
guint collect_flags)
|
|
{
|
|
if (collect_values[0].v_pointer)
|
|
{
|
|
GObject *object = collect_values[0].v_pointer;
|
|
|
|
if (object->g_type_instance.g_class == NULL)
|
|
return g_strconcat ("invalid unclassed object pointer for value type '",
|
|
G_VALUE_TYPE_NAME (value),
|
|
"'",
|
|
NULL);
|
|
else if (!g_value_type_compatible (G_OBJECT_TYPE (object), G_VALUE_TYPE (value)))
|
|
return g_strconcat ("invalid object type '",
|
|
G_OBJECT_TYPE_NAME (object),
|
|
"' for value type '",
|
|
G_VALUE_TYPE_NAME (value),
|
|
"'",
|
|
NULL);
|
|
/* never honour G_VALUE_NOCOPY_CONTENTS for ref-counted types */
|
|
value->data[0].v_pointer = g_object_ref (object);
|
|
}
|
|
else
|
|
value->data[0].v_pointer = NULL;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static gchar*
|
|
g_value_object_lcopy_value (const GValue *value,
|
|
guint n_collect_values,
|
|
GTypeCValue *collect_values,
|
|
guint collect_flags)
|
|
{
|
|
GObject **object_p = collect_values[0].v_pointer;
|
|
|
|
if (!object_p)
|
|
return g_strdup_printf ("value location for '%s' passed as NULL", G_VALUE_TYPE_NAME (value));
|
|
|
|
if (!value->data[0].v_pointer)
|
|
*object_p = NULL;
|
|
else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
|
|
*object_p = value->data[0].v_pointer;
|
|
else
|
|
*object_p = g_object_ref (value->data[0].v_pointer);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* g_value_set_object:
|
|
* @value: a valid #GValue of %G_TYPE_OBJECT derived type
|
|
* @v_object: (type GObject.Object) (nullable): object value to be set
|
|
*
|
|
* Set the contents of a %G_TYPE_OBJECT derived #GValue to @v_object.
|
|
*
|
|
* g_value_set_object() increases the reference count of @v_object
|
|
* (the #GValue holds a reference to @v_object). If you do not wish
|
|
* to increase the reference count of the object (i.e. you wish to
|
|
* pass your current reference to the #GValue because you no longer
|
|
* need it), use g_value_take_object() instead.
|
|
*
|
|
* It is important that your #GValue holds a reference to @v_object (either its
|
|
* own, or one it has taken) to ensure that the object won't be destroyed while
|
|
* the #GValue still exists).
|
|
*/
|
|
void
|
|
g_value_set_object (GValue *value,
|
|
gpointer v_object)
|
|
{
|
|
GObject *old;
|
|
|
|
g_return_if_fail (G_VALUE_HOLDS_OBJECT (value));
|
|
|
|
old = value->data[0].v_pointer;
|
|
|
|
if (v_object)
|
|
{
|
|
g_return_if_fail (G_IS_OBJECT (v_object));
|
|
g_return_if_fail (g_value_type_compatible (G_OBJECT_TYPE (v_object), G_VALUE_TYPE (value)));
|
|
|
|
value->data[0].v_pointer = v_object;
|
|
g_object_ref (value->data[0].v_pointer);
|
|
}
|
|
else
|
|
value->data[0].v_pointer = NULL;
|
|
|
|
if (old)
|
|
g_object_unref (old);
|
|
}
|
|
|
|
/**
|
|
* g_value_set_object_take_ownership: (skip)
|
|
* @value: a valid #GValue of %G_TYPE_OBJECT derived type
|
|
* @v_object: (nullable): object value to be set
|
|
*
|
|
* This is an internal function introduced mainly for C marshallers.
|
|
*
|
|
* Deprecated: 2.4: Use g_value_take_object() instead.
|
|
*/
|
|
void
|
|
g_value_set_object_take_ownership (GValue *value,
|
|
gpointer v_object)
|
|
{
|
|
g_value_take_object (value, v_object);
|
|
}
|
|
|
|
/**
|
|
* g_value_take_object: (skip)
|
|
* @value: a valid #GValue of %G_TYPE_OBJECT derived type
|
|
* @v_object: (nullable): object value to be set
|
|
*
|
|
* Sets the contents of a %G_TYPE_OBJECT derived #GValue to @v_object
|
|
* and takes over the ownership of the callers reference to @v_object;
|
|
* the caller doesn't have to unref it any more (i.e. the reference
|
|
* count of the object is not increased).
|
|
*
|
|
* If you want the #GValue to hold its own reference to @v_object, use
|
|
* g_value_set_object() instead.
|
|
*
|
|
* Since: 2.4
|
|
*/
|
|
void
|
|
g_value_take_object (GValue *value,
|
|
gpointer v_object)
|
|
{
|
|
g_return_if_fail (G_VALUE_HOLDS_OBJECT (value));
|
|
|
|
if (value->data[0].v_pointer)
|
|
{
|
|
g_object_unref (value->data[0].v_pointer);
|
|
value->data[0].v_pointer = NULL;
|
|
}
|
|
|
|
if (v_object)
|
|
{
|
|
g_return_if_fail (G_IS_OBJECT (v_object));
|
|
g_return_if_fail (g_value_type_compatible (G_OBJECT_TYPE (v_object), G_VALUE_TYPE (value)));
|
|
|
|
value->data[0].v_pointer = v_object; /* we take over the reference count */
|
|
}
|
|
}
|
|
|
|
/**
|
|
* g_value_get_object:
|
|
* @value: a valid #GValue of %G_TYPE_OBJECT derived type
|
|
*
|
|
* Get the contents of a %G_TYPE_OBJECT derived #GValue.
|
|
*
|
|
* Returns: (type GObject.Object) (transfer none): object contents of @value
|
|
*/
|
|
gpointer
|
|
g_value_get_object (const GValue *value)
|
|
{
|
|
g_return_val_if_fail (G_VALUE_HOLDS_OBJECT (value), NULL);
|
|
|
|
return value->data[0].v_pointer;
|
|
}
|
|
|
|
/**
|
|
* g_value_dup_object:
|
|
* @value: a valid #GValue whose type is derived from %G_TYPE_OBJECT
|
|
*
|
|
* Get the contents of a %G_TYPE_OBJECT derived #GValue, increasing
|
|
* its reference count. If the contents of the #GValue are %NULL, then
|
|
* %NULL will be returned.
|
|
*
|
|
* Returns: (type GObject.Object) (transfer full): object content of @value,
|
|
* should be unreferenced when no longer needed.
|
|
*/
|
|
gpointer
|
|
g_value_dup_object (const GValue *value)
|
|
{
|
|
g_return_val_if_fail (G_VALUE_HOLDS_OBJECT (value), NULL);
|
|
|
|
return value->data[0].v_pointer ? g_object_ref (value->data[0].v_pointer) : NULL;
|
|
}
|
|
|
|
/**
|
|
* g_signal_connect_object: (skip)
|
|
* @instance: (type GObject.TypeInstance): the instance to connect to.
|
|
* @detailed_signal: a string of the form "signal-name::detail".
|
|
* @c_handler: the #GCallback to connect.
|
|
* @gobject: (type GObject.Object) (nullable): the object to pass as data
|
|
* to @c_handler.
|
|
* @connect_flags: a combination of #GConnectFlags.
|
|
*
|
|
* This is similar to g_signal_connect_data(), but uses a closure which
|
|
* ensures that the @gobject stays alive during the call to @c_handler
|
|
* by temporarily adding a reference count to @gobject.
|
|
*
|
|
* When the @gobject is destroyed the signal handler will be automatically
|
|
* disconnected. Note that this is not currently threadsafe (ie:
|
|
* emitting a signal while @gobject is being destroyed in another thread
|
|
* is not safe).
|
|
*
|
|
* Returns: the handler id.
|
|
*/
|
|
gulong
|
|
g_signal_connect_object (gpointer instance,
|
|
const gchar *detailed_signal,
|
|
GCallback c_handler,
|
|
gpointer gobject,
|
|
GConnectFlags connect_flags)
|
|
{
|
|
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);
|
|
|
|
if (gobject)
|
|
{
|
|
GClosure *closure;
|
|
|
|
g_return_val_if_fail (G_IS_OBJECT (gobject), 0);
|
|
|
|
closure = ((connect_flags & G_CONNECT_SWAPPED) ? g_cclosure_new_object_swap : g_cclosure_new_object) (c_handler, gobject);
|
|
|
|
return g_signal_connect_closure (instance, detailed_signal, closure, connect_flags & G_CONNECT_AFTER);
|
|
}
|
|
else
|
|
return g_signal_connect_data (instance, detailed_signal, c_handler, NULL, NULL, connect_flags);
|
|
}
|
|
|
|
typedef struct {
|
|
GObject *object;
|
|
guint n_closures;
|
|
GClosure *closures[1]; /* flexible array */
|
|
} CArray;
|
|
/* don't change this structure without supplying an accessor for
|
|
* watched closures, e.g.:
|
|
* GSList* g_object_list_watched_closures (GObject *object)
|
|
* {
|
|
* CArray *carray;
|
|
* g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
|
* carray = g_object_get_data (object, "GObject-closure-array");
|
|
* if (carray)
|
|
* {
|
|
* GSList *slist = NULL;
|
|
* guint i;
|
|
* for (i = 0; i < carray->n_closures; i++)
|
|
* slist = g_slist_prepend (slist, carray->closures[i]);
|
|
* return slist;
|
|
* }
|
|
* return NULL;
|
|
* }
|
|
*/
|
|
|
|
static void
|
|
object_remove_closure (gpointer data,
|
|
GClosure *closure)
|
|
{
|
|
GObject *object = data;
|
|
CArray *carray;
|
|
guint i;
|
|
|
|
G_LOCK (closure_array_mutex);
|
|
carray = g_object_get_qdata (object, quark_closure_array);
|
|
for (i = 0; i < carray->n_closures; i++)
|
|
if (carray->closures[i] == closure)
|
|
{
|
|
carray->n_closures--;
|
|
if (i < carray->n_closures)
|
|
carray->closures[i] = carray->closures[carray->n_closures];
|
|
G_UNLOCK (closure_array_mutex);
|
|
return;
|
|
}
|
|
G_UNLOCK (closure_array_mutex);
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
static void
|
|
destroy_closure_array (gpointer data)
|
|
{
|
|
CArray *carray = data;
|
|
GObject *object = carray->object;
|
|
guint i, n = carray->n_closures;
|
|
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
GClosure *closure = carray->closures[i];
|
|
|
|
/* removing object_remove_closure() upfront is probably faster than
|
|
* letting it fiddle with quark_closure_array which is empty anyways
|
|
*/
|
|
g_closure_remove_invalidate_notifier (closure, object, object_remove_closure);
|
|
g_closure_invalidate (closure);
|
|
}
|
|
g_free (carray);
|
|
}
|
|
|
|
/**
|
|
* g_object_watch_closure:
|
|
* @object: GObject restricting lifetime of @closure
|
|
* @closure: GClosure to watch
|
|
*
|
|
* This function essentially limits the life time of the @closure to
|
|
* the life time of the object. That is, when the object is finalized,
|
|
* the @closure is invalidated by calling g_closure_invalidate() on
|
|
* it, in order to prevent invocations of the closure with a finalized
|
|
* (nonexisting) object. Also, g_object_ref() and g_object_unref() are
|
|
* added as marshal guards to the @closure, to ensure that an extra
|
|
* reference count is held on @object during invocation of the
|
|
* @closure. Usually, this function will be called on closures that
|
|
* use this @object as closure data.
|
|
*/
|
|
void
|
|
g_object_watch_closure (GObject *object,
|
|
GClosure *closure)
|
|
{
|
|
CArray *carray;
|
|
guint i;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
g_return_if_fail (closure != NULL);
|
|
g_return_if_fail (closure->is_invalid == FALSE);
|
|
g_return_if_fail (closure->in_marshal == FALSE);
|
|
g_return_if_fail (object->ref_count > 0); /* this doesn't work on finalizing objects */
|
|
|
|
g_closure_add_invalidate_notifier (closure, object, object_remove_closure);
|
|
g_closure_add_marshal_guards (closure,
|
|
object, (GClosureNotify) g_object_ref,
|
|
object, (GClosureNotify) g_object_unref);
|
|
G_LOCK (closure_array_mutex);
|
|
carray = g_datalist_id_remove_no_notify (&object->qdata, quark_closure_array);
|
|
if (!carray)
|
|
{
|
|
carray = g_renew (CArray, NULL, 1);
|
|
carray->object = object;
|
|
carray->n_closures = 1;
|
|
i = 0;
|
|
}
|
|
else
|
|
{
|
|
i = carray->n_closures++;
|
|
carray = g_realloc (carray, sizeof (*carray) + sizeof (carray->closures[0]) * i);
|
|
}
|
|
carray->closures[i] = closure;
|
|
g_datalist_id_set_data_full (&object->qdata, quark_closure_array, carray, destroy_closure_array);
|
|
G_UNLOCK (closure_array_mutex);
|
|
}
|
|
|
|
/**
|
|
* g_closure_new_object:
|
|
* @sizeof_closure: the size of the structure to allocate, must be at least
|
|
* `sizeof (GClosure)`
|
|
* @object: a #GObject pointer to store in the @data field of the newly
|
|
* allocated #GClosure
|
|
*
|
|
* A variant of g_closure_new_simple() which stores @object in the
|
|
* @data field of the closure and calls g_object_watch_closure() on
|
|
* @object and the created closure. This function is mainly useful
|
|
* when implementing new types of closures.
|
|
*
|
|
* Returns: (transfer full): a newly allocated #GClosure
|
|
*/
|
|
GClosure*
|
|
g_closure_new_object (guint sizeof_closure,
|
|
GObject *object)
|
|
{
|
|
GClosure *closure;
|
|
|
|
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
|
g_return_val_if_fail (object->ref_count > 0, NULL); /* this doesn't work on finalizing objects */
|
|
|
|
closure = g_closure_new_simple (sizeof_closure, object);
|
|
g_object_watch_closure (object, closure);
|
|
|
|
return closure;
|
|
}
|
|
|
|
/**
|
|
* g_cclosure_new_object: (skip)
|
|
* @callback_func: the function to invoke
|
|
* @object: a #GObject pointer to pass to @callback_func
|
|
*
|
|
* A variant of g_cclosure_new() which uses @object as @user_data and
|
|
* calls g_object_watch_closure() on @object and the created
|
|
* closure. This function is useful when you have a callback closely
|
|
* associated with a #GObject, and want the callback to no longer run
|
|
* after the object is is freed.
|
|
*
|
|
* Returns: a new #GCClosure
|
|
*/
|
|
GClosure*
|
|
g_cclosure_new_object (GCallback callback_func,
|
|
GObject *object)
|
|
{
|
|
GClosure *closure;
|
|
|
|
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
|
g_return_val_if_fail (object->ref_count > 0, NULL); /* this doesn't work on finalizing objects */
|
|
g_return_val_if_fail (callback_func != NULL, NULL);
|
|
|
|
closure = g_cclosure_new (callback_func, object, NULL);
|
|
g_object_watch_closure (object, closure);
|
|
|
|
return closure;
|
|
}
|
|
|
|
/**
|
|
* g_cclosure_new_object_swap: (skip)
|
|
* @callback_func: the function to invoke
|
|
* @object: a #GObject pointer to pass to @callback_func
|
|
*
|
|
* A variant of g_cclosure_new_swap() which uses @object as @user_data
|
|
* and calls g_object_watch_closure() on @object and the created
|
|
* closure. This function is useful when you have a callback closely
|
|
* associated with a #GObject, and want the callback to no longer run
|
|
* after the object is is freed.
|
|
*
|
|
* Returns: a new #GCClosure
|
|
*/
|
|
GClosure*
|
|
g_cclosure_new_object_swap (GCallback callback_func,
|
|
GObject *object)
|
|
{
|
|
GClosure *closure;
|
|
|
|
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
|
g_return_val_if_fail (object->ref_count > 0, NULL); /* this doesn't work on finalizing objects */
|
|
g_return_val_if_fail (callback_func != NULL, NULL);
|
|
|
|
closure = g_cclosure_new_swap (callback_func, object, NULL);
|
|
g_object_watch_closure (object, closure);
|
|
|
|
return closure;
|
|
}
|
|
|
|
gsize
|
|
g_object_compat_control (gsize what,
|
|
gpointer data)
|
|
{
|
|
switch (what)
|
|
{
|
|
gpointer *pp;
|
|
case 1: /* floating base type */
|
|
return G_TYPE_INITIALLY_UNOWNED;
|
|
case 2: /* FIXME: remove this once GLib/Gtk+ break ABI again */
|
|
floating_flag_handler = (guint(*)(GObject*,gint)) data;
|
|
return 1;
|
|
case 3: /* FIXME: remove this once GLib/Gtk+ break ABI again */
|
|
pp = data;
|
|
*pp = floating_flag_handler;
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
G_DEFINE_TYPE (GInitiallyUnowned, g_initially_unowned, G_TYPE_OBJECT);
|
|
|
|
static void
|
|
g_initially_unowned_init (GInitiallyUnowned *object)
|
|
{
|
|
g_object_force_floating (object);
|
|
}
|
|
|
|
static void
|
|
g_initially_unowned_class_init (GInitiallyUnownedClass *klass)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* GWeakRef:
|
|
*
|
|
* A structure containing a weak reference to a #GObject. It can either
|
|
* be empty (i.e. point to %NULL), or point to an object for as long as
|
|
* at least one "strong" reference to that object exists. Before the
|
|
* object's #GObjectClass.dispose method is called, every #GWeakRef
|
|
* associated with becomes empty (i.e. points to %NULL).
|
|
*
|
|
* Like #GValue, #GWeakRef can be statically allocated, stack- or
|
|
* heap-allocated, or embedded in larger structures.
|
|
*
|
|
* Unlike g_object_weak_ref() and g_object_add_weak_pointer(), this weak
|
|
* reference is thread-safe: converting a weak pointer to a reference is
|
|
* atomic with respect to invalidation of weak pointers to destroyed
|
|
* objects.
|
|
*
|
|
* If the object's #GObjectClass.dispose method results in additional
|
|
* references to the object being held, any #GWeakRefs taken
|
|
* before it was disposed will continue to point to %NULL. If
|
|
* #GWeakRefs are taken after the object is disposed and
|
|
* re-referenced, they will continue to point to it until its refcount
|
|
* goes back to zero, at which point they too will be invalidated.
|
|
*/
|
|
|
|
/**
|
|
* g_weak_ref_init: (skip)
|
|
* @weak_ref: (inout): uninitialized or empty location for a weak
|
|
* reference
|
|
* @object: (type GObject.Object) (nullable): a #GObject or %NULL
|
|
*
|
|
* Initialise a non-statically-allocated #GWeakRef.
|
|
*
|
|
* This function also calls g_weak_ref_set() with @object on the
|
|
* freshly-initialised weak reference.
|
|
*
|
|
* This function should always be matched with a call to
|
|
* g_weak_ref_clear(). It is not necessary to use this function for a
|
|
* #GWeakRef in static storage because it will already be
|
|
* properly initialised. Just use g_weak_ref_set() directly.
|
|
*
|
|
* Since: 2.32
|
|
*/
|
|
void
|
|
g_weak_ref_init (GWeakRef *weak_ref,
|
|
gpointer object)
|
|
{
|
|
weak_ref->priv.p = NULL;
|
|
|
|
g_weak_ref_set (weak_ref, object);
|
|
}
|
|
|
|
/**
|
|
* g_weak_ref_clear: (skip)
|
|
* @weak_ref: (inout): location of a weak reference, which
|
|
* may be empty
|
|
*
|
|
* Frees resources associated with a non-statically-allocated #GWeakRef.
|
|
* After this call, the #GWeakRef is left in an undefined state.
|
|
*
|
|
* You should only call this on a #GWeakRef that previously had
|
|
* g_weak_ref_init() called on it.
|
|
*
|
|
* Since: 2.32
|
|
*/
|
|
void
|
|
g_weak_ref_clear (GWeakRef *weak_ref)
|
|
{
|
|
g_weak_ref_set (weak_ref, NULL);
|
|
|
|
/* be unkind */
|
|
weak_ref->priv.p = (void *) 0xccccccccu;
|
|
}
|
|
|
|
/**
|
|
* g_weak_ref_get: (skip)
|
|
* @weak_ref: (inout): location of a weak reference to a #GObject
|
|
*
|
|
* If @weak_ref is not empty, atomically acquire a strong
|
|
* reference to the object it points to, and return that reference.
|
|
*
|
|
* This function is needed because of the potential race between taking
|
|
* the pointer value and g_object_ref() on it, if the object was losing
|
|
* its last reference at the same time in a different thread.
|
|
*
|
|
* The caller should release the resulting reference in the usual way,
|
|
* by using g_object_unref().
|
|
*
|
|
* Returns: (transfer full) (type GObject.Object): the object pointed to
|
|
* by @weak_ref, or %NULL if it was empty
|
|
*
|
|
* Since: 2.32
|
|
*/
|
|
gpointer
|
|
g_weak_ref_get (GWeakRef *weak_ref)
|
|
{
|
|
gpointer object_or_null;
|
|
|
|
g_return_val_if_fail (weak_ref!= NULL, NULL);
|
|
|
|
g_rw_lock_reader_lock (&weak_locations_lock);
|
|
|
|
object_or_null = weak_ref->priv.p;
|
|
|
|
if (object_or_null != NULL)
|
|
g_object_ref (object_or_null);
|
|
|
|
g_rw_lock_reader_unlock (&weak_locations_lock);
|
|
|
|
return object_or_null;
|
|
}
|
|
|
|
/**
|
|
* g_weak_ref_set: (skip)
|
|
* @weak_ref: location for a weak reference
|
|
* @object: (type GObject.Object) (nullable): a #GObject or %NULL
|
|
*
|
|
* Change the object to which @weak_ref points, or set it to
|
|
* %NULL.
|
|
*
|
|
* You must own a strong reference on @object while calling this
|
|
* function.
|
|
*
|
|
* Since: 2.32
|
|
*/
|
|
void
|
|
g_weak_ref_set (GWeakRef *weak_ref,
|
|
gpointer object)
|
|
{
|
|
GSList **weak_locations;
|
|
GObject *new_object;
|
|
GObject *old_object;
|
|
|
|
g_return_if_fail (weak_ref != NULL);
|
|
g_return_if_fail (object == NULL || G_IS_OBJECT (object));
|
|
|
|
new_object = object;
|
|
|
|
g_rw_lock_writer_lock (&weak_locations_lock);
|
|
|
|
/* We use the extra level of indirection here so that if we have ever
|
|
* had a weak pointer installed at any point in time on this object,
|
|
* we can see that there is a non-NULL value associated with the
|
|
* weak-pointer quark and know that this value will not change at any
|
|
* point in the object's lifetime.
|
|
*
|
|
* Both properties are important for reducing the amount of times we
|
|
* need to acquire locks and for decreasing the duration of time the
|
|
* lock is held while avoiding some rather tricky races.
|
|
*
|
|
* Specifically: we can avoid having to do an extra unconditional lock
|
|
* in g_object_unref() without worrying about some extremely tricky
|
|
* races.
|
|
*/
|
|
|
|
old_object = weak_ref->priv.p;
|
|
if (new_object != old_object)
|
|
{
|
|
weak_ref->priv.p = new_object;
|
|
|
|
/* Remove the weak ref from the old object */
|
|
if (old_object != NULL)
|
|
{
|
|
weak_locations = g_datalist_id_get_data (&old_object->qdata, quark_weak_locations);
|
|
/* for it to point to an object, the object must have had it added once */
|
|
g_assert (weak_locations != NULL);
|
|
|
|
*weak_locations = g_slist_remove (*weak_locations, weak_ref);
|
|
}
|
|
|
|
/* Add the weak ref to the new object */
|
|
if (new_object != NULL)
|
|
{
|
|
weak_locations = g_datalist_id_get_data (&new_object->qdata, quark_weak_locations);
|
|
|
|
if (weak_locations == NULL)
|
|
{
|
|
weak_locations = g_new0 (GSList *, 1);
|
|
g_datalist_id_set_data_full (&new_object->qdata, quark_weak_locations, weak_locations, g_free);
|
|
}
|
|
|
|
*weak_locations = g_slist_prepend (*weak_locations, weak_ref);
|
|
}
|
|
}
|
|
|
|
g_rw_lock_writer_unlock (&weak_locations_lock);
|
|
}
|