Import Upstream version 2.72.4

This commit is contained in:
evinadmin 2023-07-04 11:23:22 +02:00
commit 4ef3ff9793
2003 changed files with 1332420 additions and 0 deletions

172
gobject/gatomicarray.c Normal file
View file

@ -0,0 +1,172 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2009 Benjamin Otte <otte@gnome.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "../glib/gvalgrind.h"
#include <string.h>
#include "gatomicarray.h"
/* A GAtomicArray is a growable, mutable array of data
* generally of the form of a header of a specific size and
* then an array of items of a fixed size.
*
* It is possible to do lock-less read transactions from the
* array without any protection against other reads or writes,
* but such read operation must be aware that the data in the
* atomic array can change at any time during the transaction,
* and only at the end can we verify if the transaction succeeded
* or not. Thus the reading transaction cannot for instance
* dereference a pointer in the array inside the transaction.
*
* The size of an array however cannot change during a read
* transaction.
*
* Writes to the array is done in a copy-update style, but there
* is no real protection against multiple writers overwriting each
* others updates, so writes must be protected by an external lock.
*/
G_LOCK_DEFINE_STATIC (array);
typedef struct _FreeListNode FreeListNode;
struct _FreeListNode {
FreeListNode *next;
};
/* This is really a list of array memory blocks, using the
* first item as the next pointer to chain them together.
* Protected by array lock */
static FreeListNode *freelist = NULL;
/* must hold array lock */
static gpointer
freelist_alloc (gsize size, gboolean reuse)
{
gpointer mem;
FreeListNode *free, **prev;
gsize real_size;
if (reuse)
{
for (free = freelist, prev = &freelist; free != NULL; prev = &free->next, free = free->next)
{
if (G_ATOMIC_ARRAY_DATA_SIZE (free) == size)
{
*prev = free->next;
return (gpointer)free;
}
}
}
real_size = sizeof (gsize) + MAX (size, sizeof (FreeListNode));
mem = g_slice_alloc (real_size);
mem = ((char *) mem) + sizeof (gsize);
G_ATOMIC_ARRAY_DATA_SIZE (mem) = size;
#if ENABLE_VALGRIND
VALGRIND_MALLOCLIKE_BLOCK (mem, real_size - sizeof (gsize), FALSE, FALSE);
#endif
return mem;
}
/* must hold array lock */
static void
freelist_free (gpointer mem)
{
FreeListNode *free;
free = mem;
free->next = freelist;
freelist = free;
}
void
_g_atomic_array_init (GAtomicArray *array)
{
array->data = NULL;
}
/* Get a copy of the data (if non-NULL) that
* can be changed and then re-applied with
* g_atomic_array_update().
*
* If additional_element_size is > 0 then
* then the new memory chunk is that much
* larger, or there were no data we return
* a chunk of header_size + additional_element_size.
* This means you can use this to grow the
* array part and it handles the first element
* being added automatically.
*
* We don't support shrinking arrays, as if
* we then re-grow we may reuse an old pointer
* value and confuse the transaction check.
*/
gpointer
_g_atomic_array_copy (GAtomicArray *array,
gsize header_size,
gsize additional_element_size)
{
guint8 *new, *old;
gsize old_size, new_size;
G_LOCK (array);
old = g_atomic_pointer_get (&array->data);
if (old)
{
old_size = G_ATOMIC_ARRAY_DATA_SIZE (old);
new_size = old_size + additional_element_size;
/* Don't reuse if copying to same size, as this may end
up reusing the same pointer for the same array thus
confusing the transaction check */
new = freelist_alloc (new_size, additional_element_size != 0);
memcpy (new, old, old_size);
}
else if (additional_element_size != 0)
{
new_size = header_size + additional_element_size;
new = freelist_alloc (new_size, TRUE);
}
else
new = NULL;
G_UNLOCK (array);
return new;
}
/* Replace the data in the array with the new data,
* freeing the old data (for reuse). The new data may
* not be smaller than the current data.
*/
void
_g_atomic_array_update (GAtomicArray *array,
gpointer new_data)
{
guint8 *old;
G_LOCK (array);
old = g_atomic_pointer_get (&array->data);
g_assert (old == NULL || G_ATOMIC_ARRAY_DATA_SIZE (old) <= G_ATOMIC_ARRAY_DATA_SIZE (new_data));
g_atomic_pointer_set (&array->data, new_data);
if (old)
freelist_free (old);
G_UNLOCK (array);
}

58
gobject/gatomicarray.h Normal file
View file

@ -0,0 +1,58 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2009 Benjamin Otte <otte@gnome.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __G_ATOMIC_ARRAY_H__
#define __G_ATOMIC_ARRAY_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <glib/glib.h>
G_BEGIN_DECLS
#define G_ATOMIC_ARRAY_DATA_SIZE(mem) (*((gsize *) (mem) - 1))
typedef struct _GAtomicArray GAtomicArray;
struct _GAtomicArray {
gpointer data; /* elements - atomic */
};
void _g_atomic_array_init (GAtomicArray *array);
gpointer _g_atomic_array_copy (GAtomicArray *array,
gsize header_size,
gsize additional_element_size);
void _g_atomic_array_update (GAtomicArray *array,
gpointer new_data);
#define G_ATOMIC_ARRAY_GET_LOCKED(_array, _type) ((_type *)((_array)->data))
#define G_ATOMIC_ARRAY_DO_TRANSACTION(_array, _type, _C_) G_STMT_START { \
gpointer *_datap = &(_array)->data; \
_type *transaction_data, *__check; \
\
__check = g_atomic_pointer_get (_datap); \
do { \
transaction_data = __check; \
{_C_;} \
__check = g_atomic_pointer_get (_datap); \
} while (transaction_data != __check); \
} G_STMT_END
G_END_DECLS
#endif /* __G_ATOMIC_ARRAY_H__ */

1637
gobject/gbinding.c Normal file

File diff suppressed because it is too large Load diff

154
gobject/gbinding.h Normal file
View file

@ -0,0 +1,154 @@
/* gbinding.h: Binding for object properties
*
* Copyright (C) 2010 Intel Corp.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Emmanuele Bassi <ebassi@linux.intel.com>
*/
#ifndef __G_BINDING_H__
#define __G_BINDING_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <glib.h>
#include <gobject/gobject.h>
G_BEGIN_DECLS
#define G_TYPE_BINDING_FLAGS (g_binding_flags_get_type ())
#define G_TYPE_BINDING (g_binding_get_type ())
#define G_BINDING(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_BINDING, GBinding))
#define G_IS_BINDING(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_BINDING))
/**
* GBinding:
*
* GBinding is an opaque structure whose members
* cannot be accessed directly.
*
* Since: 2.26
*/
typedef struct _GBinding GBinding;
/**
* GBindingTransformFunc:
* @binding: a #GBinding
* @from_value: the #GValue containing the value to transform
* @to_value: the #GValue in which to store the transformed value
* @user_data: data passed to the transform function
*
* A function to be called to transform @from_value to @to_value.
*
* If this is the @transform_to function of a binding, then @from_value
* is the @source_property on the @source object, and @to_value is the
* @target_property on the @target object. If this is the
* @transform_from function of a %G_BINDING_BIDIRECTIONAL binding,
* then those roles are reversed.
*
* Returns: %TRUE if the transformation was successful, and %FALSE
* otherwise
*
* Since: 2.26
*/
typedef gboolean (* GBindingTransformFunc) (GBinding *binding,
const GValue *from_value,
GValue *to_value,
gpointer user_data);
/**
* GBindingFlags:
* @G_BINDING_DEFAULT: The default binding; if the source property
* changes, the target property is updated with its value.
* @G_BINDING_BIDIRECTIONAL: Bidirectional binding; if either the
* property of the source or the property of the target changes,
* the other is updated.
* @G_BINDING_SYNC_CREATE: Synchronize the values of the source and
* target properties when creating the binding; the direction of
* the synchronization is always from the source to the target.
* @G_BINDING_INVERT_BOOLEAN: If the two properties being bound are
* booleans, setting one to %TRUE will result in the other being
* set to %FALSE and vice versa. This flag will only work for
* boolean properties, and cannot be used when passing custom
* transformation functions to g_object_bind_property_full().
*
* Flags to be passed to g_object_bind_property() or
* g_object_bind_property_full().
*
* This enumeration can be extended at later date.
*
* Since: 2.26
*/
typedef enum { /*< prefix=G_BINDING >*/
G_BINDING_DEFAULT = 0,
G_BINDING_BIDIRECTIONAL = 1 << 0,
G_BINDING_SYNC_CREATE = 1 << 1,
G_BINDING_INVERT_BOOLEAN = 1 << 2
} GBindingFlags;
GLIB_AVAILABLE_IN_ALL
GType g_binding_flags_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_binding_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GBindingFlags g_binding_get_flags (GBinding *binding);
GLIB_DEPRECATED_IN_2_68_FOR(g_binding_dup_source)
GObject * g_binding_get_source (GBinding *binding);
GLIB_AVAILABLE_IN_2_68
GObject * g_binding_dup_source (GBinding *binding);
GLIB_DEPRECATED_IN_2_68_FOR(g_binding_dup_target)
GObject * g_binding_get_target (GBinding *binding);
GLIB_AVAILABLE_IN_2_68
GObject * g_binding_dup_target (GBinding *binding);
GLIB_AVAILABLE_IN_ALL
const gchar * g_binding_get_source_property (GBinding *binding);
GLIB_AVAILABLE_IN_ALL
const gchar * g_binding_get_target_property (GBinding *binding);
GLIB_AVAILABLE_IN_2_38
void g_binding_unbind (GBinding *binding);
GLIB_AVAILABLE_IN_ALL
GBinding *g_object_bind_property (gpointer source,
const gchar *source_property,
gpointer target,
const gchar *target_property,
GBindingFlags flags);
GLIB_AVAILABLE_IN_ALL
GBinding *g_object_bind_property_full (gpointer source,
const gchar *source_property,
gpointer target,
const gchar *target_property,
GBindingFlags flags,
GBindingTransformFunc transform_to,
GBindingTransformFunc transform_from,
gpointer user_data,
GDestroyNotify notify);
GLIB_AVAILABLE_IN_ALL
GBinding *g_object_bind_property_with_closures (gpointer source,
const gchar *source_property,
gpointer target,
const gchar *target_property,
GBindingFlags flags,
GClosure *transform_to,
GClosure *transform_from);
G_END_DECLS
#endif /* __G_BINDING_H__ */

679
gobject/gbindinggroup.c Normal file
View file

@ -0,0 +1,679 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
*
* Copyright (C) 2015-2022 Christian Hergert <christian@hergert.me>
* Copyright (C) 2015 Garrett Regier <garrettregier@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include "glib.h"
#include "glibintl.h"
#include "gbindinggroup.h"
#include "gparamspecs.h"
/**
* SECTION:gbindinggroup
* @Title: GBindingGroup
* @Short_description: Binding multiple properties as a group
* @include: glib-object.h
*
* The #GBindingGroup can be used to bind multiple properties
* from an object collectively.
*
* Use the various methods to bind properties from a single source
* object to multiple destination objects. Properties can be bound
* bidirectionally and are connected when the source object is set
* with g_binding_group_set_source().
*
* Since: 2.72
*/
#if 0
# define DEBUG_BINDINGS
#endif
struct _GBindingGroup
{
GObject parent_instance;
GMutex mutex;
GObject *source; /* (owned weak) */
GPtrArray *lazy_bindings; /* (owned) (element-type LazyBinding) */
};
typedef struct _GBindingGroupClass
{
GObjectClass parent_class;
} GBindingGroupClass;
typedef struct
{
GBindingGroup *group; /* (unowned) */
const char *source_property; /* (interned) */
const char *target_property; /* (interned) */
GObject *target; /* (owned weak) */
GBinding *binding; /* (unowned) */
gpointer user_data;
GDestroyNotify user_data_destroy;
gpointer transform_to; /* (nullable) (owned) */
gpointer transform_from; /* (nullable) (owned) */
GBindingFlags binding_flags;
guint using_closures : 1;
} LazyBinding;
G_DEFINE_TYPE (GBindingGroup, g_binding_group, G_TYPE_OBJECT)
typedef enum
{
PROP_SOURCE = 1,
N_PROPS
} GBindingGroupProperty;
static void lazy_binding_free (gpointer data);
static GParamSpec *properties[N_PROPS];
static void
g_binding_group_connect (GBindingGroup *self,
LazyBinding *lazy_binding)
{
GBinding *binding;
g_assert (G_IS_BINDING_GROUP (self));
g_assert (self->source != NULL);
g_assert (lazy_binding != NULL);
g_assert (lazy_binding->binding == NULL);
g_assert (lazy_binding->target != NULL);
g_assert (lazy_binding->target_property != NULL);
g_assert (lazy_binding->source_property != NULL);
#ifdef DEBUG_BINDINGS
{
GFlagsClass *flags_class;
g_autofree gchar *flags_str = NULL;
flags_class = g_type_class_ref (G_TYPE_BINDING_FLAGS);
flags_str = g_flags_to_string (flags_class, lazy_binding->binding_flags);
g_print ("Binding %s(%p):%s to %s(%p):%s (flags=%s)\n",
G_OBJECT_TYPE_NAME (self->source),
self->source,
lazy_binding->source_property,
G_OBJECT_TYPE_NAME (lazy_binding->target),
lazy_binding->target,
lazy_binding->target_property,
flags_str);
g_type_class_unref (flags_class);
}
#endif
if (!lazy_binding->using_closures)
binding = g_object_bind_property_full (self->source,
lazy_binding->source_property,
lazy_binding->target,
lazy_binding->target_property,
lazy_binding->binding_flags,
lazy_binding->transform_to,
lazy_binding->transform_from,
lazy_binding->user_data,
NULL);
else
binding = g_object_bind_property_with_closures (self->source,
lazy_binding->source_property,
lazy_binding->target,
lazy_binding->target_property,
lazy_binding->binding_flags,
lazy_binding->transform_to,
lazy_binding->transform_from);
lazy_binding->binding = binding;
}
static void
g_binding_group_disconnect (LazyBinding *lazy_binding)
{
g_assert (lazy_binding != NULL);
if (lazy_binding->binding != NULL)
{
g_binding_unbind (lazy_binding->binding);
lazy_binding->binding = NULL;
}
}
static void
g_binding_group__source_weak_notify (gpointer data,
GObject *where_object_was)
{
GBindingGroup *self = data;
guint i;
g_assert (G_IS_BINDING_GROUP (self));
g_mutex_lock (&self->mutex);
self->source = NULL;
for (i = 0; i < self->lazy_bindings->len; i++)
{
LazyBinding *lazy_binding = g_ptr_array_index (self->lazy_bindings, i);
lazy_binding->binding = NULL;
}
g_mutex_unlock (&self->mutex);
}
static void
g_binding_group__target_weak_notify (gpointer data,
GObject *where_object_was)
{
GBindingGroup *self = data;
LazyBinding *to_free = NULL;
guint i;
g_assert (G_IS_BINDING_GROUP (self));
g_mutex_lock (&self->mutex);
for (i = 0; i < self->lazy_bindings->len; i++)
{
LazyBinding *lazy_binding = g_ptr_array_index (self->lazy_bindings, i);
if (lazy_binding->target == where_object_was)
{
lazy_binding->target = NULL;
lazy_binding->binding = NULL;
to_free = g_ptr_array_steal_index_fast (self->lazy_bindings, i);
break;
}
}
g_mutex_unlock (&self->mutex);
if (to_free != NULL)
lazy_binding_free (to_free);
}
static void
lazy_binding_free (gpointer data)
{
LazyBinding *lazy_binding = data;
if (lazy_binding->target != NULL)
{
g_object_weak_unref (lazy_binding->target,
g_binding_group__target_weak_notify,
lazy_binding->group);
lazy_binding->target = NULL;
}
g_binding_group_disconnect (lazy_binding);
lazy_binding->group = NULL;
lazy_binding->source_property = NULL;
lazy_binding->target_property = NULL;
if (lazy_binding->user_data_destroy)
lazy_binding->user_data_destroy (lazy_binding->user_data);
if (lazy_binding->using_closures)
{
g_clear_pointer (&lazy_binding->transform_to, g_closure_unref);
g_clear_pointer (&lazy_binding->transform_from, g_closure_unref);
}
g_slice_free (LazyBinding, lazy_binding);
}
static void
g_binding_group_dispose (GObject *object)
{
GBindingGroup *self = (GBindingGroup *)object;
LazyBinding **lazy_bindings = NULL;
gsize len = 0;
gsize i;
g_assert (G_IS_BINDING_GROUP (self));
g_mutex_lock (&self->mutex);
if (self->source != NULL)
{
g_object_weak_unref (self->source,
g_binding_group__source_weak_notify,
self);
self->source = NULL;
}
if (self->lazy_bindings->len > 0)
lazy_bindings = (LazyBinding **)g_ptr_array_steal (self->lazy_bindings, &len);
g_mutex_unlock (&self->mutex);
/* Free bindings without holding self->mutex to avoid re-entrancy
* from collateral damage through release of binding closure data,
* GDataList, etc.
*/
for (i = 0; i < len; i++)
lazy_binding_free (lazy_bindings[i]);
g_free (lazy_bindings);
G_OBJECT_CLASS (g_binding_group_parent_class)->dispose (object);
}
static void
g_binding_group_finalize (GObject *object)
{
GBindingGroup *self = (GBindingGroup *)object;
g_assert (self->lazy_bindings != NULL);
g_assert (self->lazy_bindings->len == 0);
g_clear_pointer (&self->lazy_bindings, g_ptr_array_unref);
g_mutex_clear (&self->mutex);
G_OBJECT_CLASS (g_binding_group_parent_class)->finalize (object);
}
static void
g_binding_group_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GBindingGroup *self = G_BINDING_GROUP (object);
switch ((GBindingGroupProperty) prop_id)
{
case PROP_SOURCE:
g_value_take_object (value, g_binding_group_dup_source (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
g_binding_group_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GBindingGroup *self = G_BINDING_GROUP (object);
switch ((GBindingGroupProperty) prop_id)
{
case PROP_SOURCE:
g_binding_group_set_source (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
g_binding_group_class_init (GBindingGroupClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = g_binding_group_dispose;
object_class->finalize = g_binding_group_finalize;
object_class->get_property = g_binding_group_get_property;
object_class->set_property = g_binding_group_set_property;
/**
* GBindingGroup:source: (nullable)
*
* The source object used for binding properties.
*
* Since: 2.72
*/
properties[PROP_SOURCE] =
g_param_spec_object ("source",
"Source",
"The source GObject used for binding properties.",
G_TYPE_OBJECT,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
g_binding_group_init (GBindingGroup *self)
{
g_mutex_init (&self->mutex);
self->lazy_bindings = g_ptr_array_new_with_free_func (lazy_binding_free);
}
/**
* g_binding_group_new:
*
* Creates a new #GBindingGroup.
*
* Returns: (transfer full): a new #GBindingGroup
*
* Since: 2.72
*/
GBindingGroup *
g_binding_group_new (void)
{
return g_object_new (G_TYPE_BINDING_GROUP, NULL);
}
/**
* g_binding_group_dup_source:
* @self: the #GBindingGroup
*
* Gets the source object used for binding properties.
*
* Returns: (transfer none) (nullable) (type GObject): a #GObject or %NULL.
*
* Since: 2.72
*/
gpointer
g_binding_group_dup_source (GBindingGroup *self)
{
GObject *source;
g_return_val_if_fail (G_IS_BINDING_GROUP (self), NULL);
g_mutex_lock (&self->mutex);
source = self->source ? g_object_ref (self->source) : NULL;
g_mutex_unlock (&self->mutex);
return source;
}
static gboolean
g_binding_group_check_source (GBindingGroup *self,
gpointer source)
{
guint i;
g_assert (G_IS_BINDING_GROUP (self));
g_assert (!source || G_IS_OBJECT (source));
for (i = 0; i < self->lazy_bindings->len; i++)
{
LazyBinding *lazy_binding = g_ptr_array_index (self->lazy_bindings, i);
g_return_val_if_fail (g_object_class_find_property (G_OBJECT_GET_CLASS (source),
lazy_binding->source_property) != NULL,
FALSE);
}
return TRUE;
}
/**
* g_binding_group_set_source:
* @self: the #GBindingGroup
* @source: (type GObject) (nullable) (transfer none): the source #GObject,
* or %NULL to clear it
*
* Sets @source as the source object used for creating property
* bindings. If there is already a source object all bindings from it
* will be removed.
*
* Note that all properties that have been bound must exist on @source.
*
* Since: 2.72
*/
void
g_binding_group_set_source (GBindingGroup *self,
gpointer source)
{
gboolean notify = FALSE;
g_return_if_fail (G_IS_BINDING_GROUP (self));
g_return_if_fail (!source || G_IS_OBJECT (source));
g_return_if_fail (source != (gpointer) self);
g_mutex_lock (&self->mutex);
if (source == (gpointer) self->source)
goto unlock;
if (self->source != NULL)
{
guint i;
g_object_weak_unref (self->source,
g_binding_group__source_weak_notify,
self);
self->source = NULL;
for (i = 0; i < self->lazy_bindings->len; i++)
{
LazyBinding *lazy_binding = g_ptr_array_index (self->lazy_bindings, i);
g_binding_group_disconnect (lazy_binding);
}
}
if (source != NULL && g_binding_group_check_source (self, source))
{
guint i;
self->source = source;
g_object_weak_ref (self->source,
g_binding_group__source_weak_notify,
self);
for (i = 0; i < self->lazy_bindings->len; i++)
{
LazyBinding *lazy_binding;
lazy_binding = g_ptr_array_index (self->lazy_bindings, i);
g_binding_group_connect (self, lazy_binding);
}
}
notify = TRUE;
unlock:
g_mutex_unlock (&self->mutex);
if (notify)
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SOURCE]);
}
static void
g_binding_group_bind_helper (GBindingGroup *self,
const gchar *source_property,
gpointer target,
const gchar *target_property,
GBindingFlags flags,
gpointer transform_to,
gpointer transform_from,
gpointer user_data,
GDestroyNotify user_data_destroy,
gboolean using_closures)
{
LazyBinding *lazy_binding;
g_return_if_fail (G_IS_BINDING_GROUP (self));
g_return_if_fail (source_property != NULL);
g_return_if_fail (self->source == NULL ||
g_object_class_find_property (G_OBJECT_GET_CLASS (self->source),
source_property) != NULL);
g_return_if_fail (G_IS_OBJECT (target));
g_return_if_fail (target_property != NULL);
g_return_if_fail (g_object_class_find_property (G_OBJECT_GET_CLASS (target),
target_property) != NULL);
g_return_if_fail (target != (gpointer) self ||
strcmp (source_property, target_property) != 0);
g_mutex_lock (&self->mutex);
lazy_binding = g_slice_new0 (LazyBinding);
lazy_binding->group = self;
lazy_binding->source_property = g_intern_string (source_property);
lazy_binding->target_property = g_intern_string (target_property);
lazy_binding->target = target;
lazy_binding->binding_flags = flags | G_BINDING_SYNC_CREATE;
lazy_binding->user_data = user_data;
lazy_binding->user_data_destroy = user_data_destroy;
lazy_binding->transform_to = transform_to;
lazy_binding->transform_from = transform_from;
if (using_closures)
{
lazy_binding->using_closures = TRUE;
if (transform_to != NULL)
g_closure_sink (g_closure_ref (transform_to));
if (transform_from != NULL)
g_closure_sink (g_closure_ref (transform_from));
}
g_object_weak_ref (target,
g_binding_group__target_weak_notify,
self);
g_ptr_array_add (self->lazy_bindings, lazy_binding);
if (self->source != NULL)
g_binding_group_connect (self, lazy_binding);
g_mutex_unlock (&self->mutex);
}
/**
* g_binding_group_bind:
* @self: the #GBindingGroup
* @source_property: the property on the source to bind
* @target: (type GObject) (transfer none) (not nullable): the target #GObject
* @target_property: the property on @target to bind
* @flags: the flags used to create the #GBinding
*
* Creates a binding between @source_property on the source object
* and @target_property on @target. Whenever the @source_property
* is changed the @target_property is updated using the same value.
* The binding flag %G_BINDING_SYNC_CREATE is automatically specified.
*
* See g_object_bind_property() for more information.
*
* Since: 2.72
*/
void
g_binding_group_bind (GBindingGroup *self,
const gchar *source_property,
gpointer target,
const gchar *target_property,
GBindingFlags flags)
{
g_binding_group_bind_full (self, source_property,
target, target_property,
flags,
NULL, NULL,
NULL, NULL);
}
/**
* g_binding_group_bind_full:
* @self: the #GBindingGroup
* @source_property: the property on the source to bind
* @target: (type GObject) (transfer none) (not nullable): the target #GObject
* @target_property: the property on @target to bind
* @flags: the flags used to create the #GBinding
* @transform_to: (scope notified) (nullable): the transformation function
* from the source object to the @target, or %NULL to use the default
* @transform_from: (scope notified) (nullable): the transformation function
* from the @target to the source object, or %NULL to use the default
* @user_data: custom data to be passed to the transformation
* functions, or %NULL
* @user_data_destroy: function to be called when disposing the binding,
* to free the resources used by the transformation functions
*
* Creates a binding between @source_property on the source object and
* @target_property on @target, allowing you to set the transformation
* functions to be used by the binding. The binding flag
* %G_BINDING_SYNC_CREATE is automatically specified.
*
* See g_object_bind_property_full() for more information.
*
* Since: 2.72
*/
void
g_binding_group_bind_full (GBindingGroup *self,
const gchar *source_property,
gpointer target,
const gchar *target_property,
GBindingFlags flags,
GBindingTransformFunc transform_to,
GBindingTransformFunc transform_from,
gpointer user_data,
GDestroyNotify user_data_destroy)
{
g_binding_group_bind_helper (self, source_property,
target, target_property,
flags,
transform_to, transform_from,
user_data, user_data_destroy,
FALSE);
}
/**
* g_binding_group_bind_with_closures: (rename-to g_binding_group_bind_full)
* @self: the #GBindingGroup
* @source_property: the property on the source to bind
* @target: (type GObject) (transfer none) (not nullable): the target #GObject
* @target_property: the property on @target to bind
* @flags: the flags used to create the #GBinding
* @transform_to: (nullable) (transfer none): a #GClosure wrapping the
* transformation function from the source object to the @target,
* or %NULL to use the default
* @transform_from: (nullable) (transfer none): a #GClosure wrapping the
* transformation function from the @target to the source object,
* or %NULL to use the default
*
* Creates a binding between @source_property on the source object and
* @target_property on @target, allowing you to set the transformation
* functions to be used by the binding. The binding flag
* %G_BINDING_SYNC_CREATE is automatically specified.
*
* This function is the language bindings friendly version of
* g_binding_group_bind_property_full(), using #GClosures
* instead of function pointers.
*
* See g_object_bind_property_with_closures() for more information.
*
* Since: 2.72
*/
void
g_binding_group_bind_with_closures (GBindingGroup *self,
const gchar *source_property,
gpointer target,
const gchar *target_property,
GBindingFlags flags,
GClosure *transform_to,
GClosure *transform_from)
{
g_binding_group_bind_helper (self, source_property,
target, target_property,
flags,
transform_to, transform_from,
NULL, NULL,
TRUE);
}

85
gobject/gbindinggroup.h Normal file
View file

@ -0,0 +1,85 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
*
* Copyright (C) 2015-2022 Christian Hergert <christian@hergert.me>
* Copyright (C) 2015 Garrett Regier <garrettregier@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef __G_BINDING_GROUP_H__
#define __G_BINDING_GROUP_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <glib.h>
#include <gobject/gobject.h>
#include <gobject/gbinding.h>
G_BEGIN_DECLS
#define G_BINDING_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_BINDING_GROUP, GBindingGroup))
#define G_IS_BINDING_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_BINDING_GROUP))
#define G_TYPE_BINDING_GROUP (g_binding_group_get_type())
/**
* GBindingGroup:
*
* GBindingGroup is an opaque structure whose members
* cannot be accessed directly.
*
* Since: 2.72
*/
typedef struct _GBindingGroup GBindingGroup;
GLIB_AVAILABLE_IN_2_72
GType g_binding_group_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_72
GBindingGroup *g_binding_group_new (void);
GLIB_AVAILABLE_IN_2_72
gpointer g_binding_group_dup_source (GBindingGroup *self);
GLIB_AVAILABLE_IN_2_72
void g_binding_group_set_source (GBindingGroup *self,
gpointer source);
GLIB_AVAILABLE_IN_2_72
void g_binding_group_bind (GBindingGroup *self,
const gchar *source_property,
gpointer target,
const gchar *target_property,
GBindingFlags flags);
GLIB_AVAILABLE_IN_2_72
void g_binding_group_bind_full (GBindingGroup *self,
const gchar *source_property,
gpointer target,
const gchar *target_property,
GBindingFlags flags,
GBindingTransformFunc transform_to,
GBindingTransformFunc transform_from,
gpointer user_data,
GDestroyNotify user_data_destroy);
GLIB_AVAILABLE_IN_2_72
void g_binding_group_bind_with_closures (GBindingGroup *self,
const gchar *source_property,
gpointer target,
const gchar *target_property,
GBindingFlags flags,
GClosure *transform_to,
GClosure *transform_from);
G_END_DECLS
#endif /* __G_BINDING_GROUP_H__ */

565
gobject/gboxed.c Normal file
View file

@ -0,0 +1,565 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2000-2001 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
/* for GValueArray */
#ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#endif
#include "gboxed.h"
#include "gclosure.h"
#include "gtype-private.h"
#include "gvalue.h"
#include "gvaluearray.h"
#include "gvaluecollector.h"
/**
* SECTION:gboxed
* @short_description: A mechanism to wrap opaque C structures registered
* by the type system
* @see_also: #GParamSpecBoxed, g_param_spec_boxed()
* @title: Boxed Types
*
* #GBoxed is a generic wrapper mechanism for arbitrary C structures.
*
* The only thing the type system needs to know about the structures is how to
* copy them (a #GBoxedCopyFunc) and how to free them (a #GBoxedFreeFunc);
* beyond that, they are treated as opaque chunks of memory.
*
* Boxed types are useful for simple value-holder structures like rectangles or
* points. They can also be used for wrapping structures defined in non-#GObject
* based libraries. They allow arbitrary structures to be handled in a uniform
* way, allowing uniform copying (or referencing) and freeing (or unreferencing)
* of them, and uniform representation of the type of the contained structure.
* In turn, this allows any type which can be boxed to be set as the data in a
* #GValue, which allows for polymorphic handling of a much wider range of data
* types, and hence usage of such types as #GObject property values.
*
* #GBoxed is designed so that reference counted types can be boxed. Use the
* types ref function as the #GBoxedCopyFunc, and its unref function as the
* #GBoxedFreeFunc. For example, for #GBytes, the #GBoxedCopyFunc is
* g_bytes_ref(), and the #GBoxedFreeFunc is g_bytes_unref().
*/
static inline void /* keep this function in sync with gvalue.c */
value_meminit (GValue *value,
GType value_type)
{
value->g_type = value_type;
memset (value->data, 0, sizeof (value->data));
}
static GValue *
value_copy (GValue *src_value)
{
GValue *dest_value = g_new0 (GValue, 1);
if (G_VALUE_TYPE (src_value))
{
g_value_init (dest_value, G_VALUE_TYPE (src_value));
g_value_copy (src_value, dest_value);
}
return dest_value;
}
static void
value_free (GValue *value)
{
if (G_VALUE_TYPE (value))
g_value_unset (value);
g_free (value);
}
static GPollFD *
pollfd_copy (GPollFD *src)
{
GPollFD *dest = g_new0 (GPollFD, 1);
/* just a couple of integers */
memcpy (dest, src, sizeof (GPollFD));
return dest;
}
void
_g_boxed_type_init (void)
{
const GTypeInfo info = {
0, /* class_size */
NULL, /* base_init */
NULL, /* base_destroy */
NULL, /* class_init */
NULL, /* class_destroy */
NULL, /* class_data */
0, /* instance_size */
0, /* n_preallocs */
NULL, /* instance_init */
NULL, /* value_table */
};
const GTypeFundamentalInfo finfo = { G_TYPE_FLAG_DERIVABLE, };
GType type G_GNUC_UNUSED /* when compiling with G_DISABLE_ASSERT */;
/* G_TYPE_BOXED
*/
type = g_type_register_fundamental (G_TYPE_BOXED, g_intern_static_string ("GBoxed"), &info, &finfo,
G_TYPE_FLAG_ABSTRACT | G_TYPE_FLAG_VALUE_ABSTRACT);
g_assert (type == G_TYPE_BOXED);
}
static GString *
gstring_copy (GString *src_gstring)
{
return g_string_new_len (src_gstring->str, src_gstring->len);
}
static void
gstring_free (GString *gstring)
{
g_string_free (gstring, TRUE);
}
G_DEFINE_BOXED_TYPE (GClosure, g_closure, g_closure_ref, g_closure_unref)
G_DEFINE_BOXED_TYPE (GValue, g_value, value_copy, value_free)
G_DEFINE_BOXED_TYPE (GValueArray, g_value_array, g_value_array_copy, g_value_array_free)
G_DEFINE_BOXED_TYPE (GDate, g_date, g_date_copy, g_date_free)
/* the naming is a bit odd, but GString is obviously not G_TYPE_STRING */
G_DEFINE_BOXED_TYPE (GString, g_gstring, gstring_copy, gstring_free)
G_DEFINE_BOXED_TYPE (GHashTable, g_hash_table, g_hash_table_ref, g_hash_table_unref)
G_DEFINE_BOXED_TYPE (GArray, g_array, g_array_ref, g_array_unref)
G_DEFINE_BOXED_TYPE (GPtrArray, g_ptr_array,g_ptr_array_ref, g_ptr_array_unref)
G_DEFINE_BOXED_TYPE (GByteArray, g_byte_array, g_byte_array_ref, g_byte_array_unref)
G_DEFINE_BOXED_TYPE (GBytes, g_bytes, g_bytes_ref, g_bytes_unref)
G_DEFINE_BOXED_TYPE (GTree, g_tree, g_tree_ref, g_tree_unref)
G_DEFINE_BOXED_TYPE (GRegex, g_regex, g_regex_ref, g_regex_unref)
G_DEFINE_BOXED_TYPE (GMatchInfo, g_match_info, g_match_info_ref, g_match_info_unref)
#define g_variant_type_get_type g_variant_type_get_gtype
G_DEFINE_BOXED_TYPE (GVariantType, g_variant_type, g_variant_type_copy, g_variant_type_free)
#undef g_variant_type_get_type
G_DEFINE_BOXED_TYPE (GVariantBuilder, g_variant_builder, g_variant_builder_ref, g_variant_builder_unref)
G_DEFINE_BOXED_TYPE (GVariantDict, g_variant_dict, g_variant_dict_ref, g_variant_dict_unref)
G_DEFINE_BOXED_TYPE (GError, g_error, g_error_copy, g_error_free)
G_DEFINE_BOXED_TYPE (GDateTime, g_date_time, g_date_time_ref, g_date_time_unref)
G_DEFINE_BOXED_TYPE (GTimeZone, g_time_zone, g_time_zone_ref, g_time_zone_unref)
G_DEFINE_BOXED_TYPE (GKeyFile, g_key_file, g_key_file_ref, g_key_file_unref)
G_DEFINE_BOXED_TYPE (GMappedFile, g_mapped_file, g_mapped_file_ref, g_mapped_file_unref)
G_DEFINE_BOXED_TYPE (GMainLoop, g_main_loop, g_main_loop_ref, g_main_loop_unref)
G_DEFINE_BOXED_TYPE (GMainContext, g_main_context, g_main_context_ref, g_main_context_unref)
G_DEFINE_BOXED_TYPE (GSource, g_source, g_source_ref, g_source_unref)
G_DEFINE_BOXED_TYPE (GPollFD, g_pollfd, pollfd_copy, g_free)
G_DEFINE_BOXED_TYPE (GMarkupParseContext, g_markup_parse_context, g_markup_parse_context_ref, g_markup_parse_context_unref)
G_DEFINE_BOXED_TYPE (GThread, g_thread, g_thread_ref, g_thread_unref)
G_DEFINE_BOXED_TYPE (GChecksum, g_checksum, g_checksum_copy, g_checksum_free)
G_DEFINE_BOXED_TYPE (GUri, g_uri, g_uri_ref, g_uri_unref)
G_DEFINE_BOXED_TYPE (GOptionGroup, g_option_group, g_option_group_ref, g_option_group_unref)
G_DEFINE_BOXED_TYPE (GPatternSpec, g_pattern_spec, g_pattern_spec_copy, g_pattern_spec_free);
/* This one can't use G_DEFINE_BOXED_TYPE (GStrv, g_strv, g_strdupv, g_strfreev) */
GType
g_strv_get_type (void)
{
static gsize static_g_define_type_id = 0;
if (g_once_init_enter (&static_g_define_type_id))
{
GType g_define_type_id =
g_boxed_type_register_static (g_intern_static_string ("GStrv"),
(GBoxedCopyFunc) g_strdupv,
(GBoxedFreeFunc) g_strfreev);
g_once_init_leave (&static_g_define_type_id, g_define_type_id);
}
return static_g_define_type_id;
}
GType
g_variant_get_gtype (void)
{
return G_TYPE_VARIANT;
}
static void
boxed_proxy_value_init (GValue *value)
{
value->data[0].v_pointer = NULL;
}
static void
boxed_proxy_value_free (GValue *value)
{
if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
_g_type_boxed_free (G_VALUE_TYPE (value), value->data[0].v_pointer);
}
static void
boxed_proxy_value_copy (const GValue *src_value,
GValue *dest_value)
{
if (src_value->data[0].v_pointer)
dest_value->data[0].v_pointer = _g_type_boxed_copy (G_VALUE_TYPE (src_value), src_value->data[0].v_pointer);
else
dest_value->data[0].v_pointer = src_value->data[0].v_pointer;
}
static gpointer
boxed_proxy_value_peek_pointer (const GValue *value)
{
return value->data[0].v_pointer;
}
static gchar*
boxed_proxy_collect_value (GValue *value,
guint n_collect_values,
GTypeCValue *collect_values,
guint collect_flags)
{
if (!collect_values[0].v_pointer)
value->data[0].v_pointer = NULL;
else
{
if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
{
value->data[0].v_pointer = collect_values[0].v_pointer;
value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
}
else
value->data[0].v_pointer = _g_type_boxed_copy (G_VALUE_TYPE (value), collect_values[0].v_pointer);
}
return NULL;
}
static gchar*
boxed_proxy_lcopy_value (const GValue *value,
guint n_collect_values,
GTypeCValue *collect_values,
guint collect_flags)
{
gpointer *boxed_p = collect_values[0].v_pointer;
g_return_val_if_fail (boxed_p != NULL, g_strdup_printf ("value location for '%s' passed as NULL", G_VALUE_TYPE_NAME (value)));
if (!value->data[0].v_pointer)
*boxed_p = NULL;
else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
*boxed_p = value->data[0].v_pointer;
else
*boxed_p = _g_type_boxed_copy (G_VALUE_TYPE (value), value->data[0].v_pointer);
return NULL;
}
/**
* g_boxed_type_register_static:
* @name: Name of the new boxed type.
* @boxed_copy: Boxed structure copy function.
* @boxed_free: Boxed structure free function.
*
* This function creates a new %G_TYPE_BOXED derived type id for a new
* boxed type with name @name.
*
* Boxed type handling functions have to be provided to copy and free
* opaque boxed structures of this type.
*
* For the general case, it is recommended to use G_DEFINE_BOXED_TYPE()
* instead of calling g_boxed_type_register_static() directly. The macro
* will create the appropriate `*_get_type()` function for the boxed type.
*
* Returns: New %G_TYPE_BOXED derived type id for @name.
*/
GType
g_boxed_type_register_static (const gchar *name,
GBoxedCopyFunc boxed_copy,
GBoxedFreeFunc boxed_free)
{
static const GTypeValueTable vtable = {
boxed_proxy_value_init,
boxed_proxy_value_free,
boxed_proxy_value_copy,
boxed_proxy_value_peek_pointer,
"p",
boxed_proxy_collect_value,
"p",
boxed_proxy_lcopy_value,
};
GTypeInfo type_info = {
0, /* class_size */
NULL, /* base_init */
NULL, /* base_finalize */
NULL, /* class_init */
NULL, /* class_finalize */
NULL, /* class_data */
0, /* instance_size */
0, /* n_preallocs */
NULL, /* instance_init */
&vtable, /* value_table */
};
GType type;
g_return_val_if_fail (name != NULL, 0);
g_return_val_if_fail (boxed_copy != NULL, 0);
g_return_val_if_fail (boxed_free != NULL, 0);
g_return_val_if_fail (g_type_from_name (name) == 0, 0);
type = g_type_register_static (G_TYPE_BOXED, name, &type_info, 0);
/* install proxy functions upon successful registration */
if (type)
_g_type_boxed_init (type, boxed_copy, boxed_free);
return type;
}
/**
* g_boxed_copy:
* @boxed_type: The type of @src_boxed.
* @src_boxed: (not nullable): The boxed structure to be copied.
*
* Provide a copy of a boxed structure @src_boxed which is of type @boxed_type.
*
* Returns: (transfer full) (not nullable): The newly created copy of the boxed
* structure.
*/
gpointer
g_boxed_copy (GType boxed_type,
gconstpointer src_boxed)
{
GTypeValueTable *value_table;
gpointer dest_boxed;
g_return_val_if_fail (G_TYPE_IS_BOXED (boxed_type), NULL);
g_return_val_if_fail (G_TYPE_IS_ABSTRACT (boxed_type) == FALSE, NULL);
g_return_val_if_fail (src_boxed != NULL, NULL);
value_table = g_type_value_table_peek (boxed_type);
g_assert (value_table != NULL);
/* check if our proxying implementation is used, we can short-cut here */
if (value_table->value_copy == boxed_proxy_value_copy)
dest_boxed = _g_type_boxed_copy (boxed_type, (gpointer) src_boxed);
else
{
GValue src_value, dest_value;
/* we heavily rely on third-party boxed type value vtable
* implementations to follow normal boxed value storage
* (data[0].v_pointer is the boxed struct, and
* data[1].v_uint holds the G_VALUE_NOCOPY_CONTENTS flag,
* rest zero).
* but then, we can expect that since we laid out the
* g_boxed_*() API.
* data[1].v_uint&G_VALUE_NOCOPY_CONTENTS shouldn't be set
* after a copy.
*/
/* equiv. to g_value_set_static_boxed() */
value_meminit (&src_value, boxed_type);
src_value.data[0].v_pointer = (gpointer) src_boxed;
src_value.data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
/* call third-party code copy function, fingers-crossed */
value_meminit (&dest_value, boxed_type);
value_table->value_copy (&src_value, &dest_value);
/* double check and grouse if things went wrong */
if (dest_value.data[1].v_ulong)
g_warning ("the copy_value() implementation of type '%s' seems to make use of reserved GValue fields",
g_type_name (boxed_type));
dest_boxed = dest_value.data[0].v_pointer;
}
return dest_boxed;
}
/**
* g_boxed_free:
* @boxed_type: The type of @boxed.
* @boxed: (not nullable): The boxed structure to be freed.
*
* Free the boxed structure @boxed which is of type @boxed_type.
*/
void
g_boxed_free (GType boxed_type,
gpointer boxed)
{
GTypeValueTable *value_table;
g_return_if_fail (G_TYPE_IS_BOXED (boxed_type));
g_return_if_fail (G_TYPE_IS_ABSTRACT (boxed_type) == FALSE);
g_return_if_fail (boxed != NULL);
value_table = g_type_value_table_peek (boxed_type);
g_assert (value_table != NULL);
/* check if our proxying implementation is used, we can short-cut here */
if (value_table->value_free == boxed_proxy_value_free)
_g_type_boxed_free (boxed_type, boxed);
else
{
GValue value;
/* see g_boxed_copy() on why we think we can do this */
value_meminit (&value, boxed_type);
value.data[0].v_pointer = boxed;
value_table->value_free (&value);
}
}
/**
* g_value_get_boxed:
* @value: a valid #GValue of %G_TYPE_BOXED derived type
*
* Get the contents of a %G_TYPE_BOXED derived #GValue.
*
* Returns: (transfer none): boxed contents of @value
*/
gpointer
g_value_get_boxed (const GValue *value)
{
g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), NULL);
g_return_val_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)), NULL);
return value->data[0].v_pointer;
}
/**
* g_value_dup_boxed: (skip)
* @value: a valid #GValue of %G_TYPE_BOXED derived type
*
* Get the contents of a %G_TYPE_BOXED derived #GValue. Upon getting,
* the boxed value is duplicated and needs to be later freed with
* g_boxed_free(), e.g. like: g_boxed_free (G_VALUE_TYPE (@value),
* return_value);
*
* Returns: boxed contents of @value
*/
gpointer
g_value_dup_boxed (const GValue *value)
{
g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), NULL);
g_return_val_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)), NULL);
return value->data[0].v_pointer ? g_boxed_copy (G_VALUE_TYPE (value), value->data[0].v_pointer) : NULL;
}
static inline void
value_set_boxed_internal (GValue *value,
gconstpointer boxed,
gboolean need_copy,
gboolean need_free)
{
if (!boxed)
{
/* just resetting to NULL might not be desired, need to
* have value reinitialized also (for values defaulting
* to other default value states than a NULL data pointer),
* g_value_reset() will handle this
*/
g_value_reset (value);
return;
}
if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
g_boxed_free (G_VALUE_TYPE (value), value->data[0].v_pointer);
value->data[1].v_uint = need_free ? 0 : G_VALUE_NOCOPY_CONTENTS;
value->data[0].v_pointer = need_copy ? g_boxed_copy (G_VALUE_TYPE (value), boxed) : (gpointer) boxed;
}
/**
* g_value_set_boxed:
* @value: a valid #GValue of %G_TYPE_BOXED derived type
* @v_boxed: (nullable): boxed value to be set
*
* Set the contents of a %G_TYPE_BOXED derived #GValue to @v_boxed.
*/
void
g_value_set_boxed (GValue *value,
gconstpointer boxed)
{
g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
g_return_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)));
value_set_boxed_internal (value, boxed, TRUE, TRUE);
}
/**
* g_value_set_static_boxed:
* @value: a valid #GValue of %G_TYPE_BOXED derived type
* @v_boxed: (nullable): static boxed value to be set
*
* Set the contents of a %G_TYPE_BOXED derived #GValue to @v_boxed.
*
* The boxed value is assumed to be static, and is thus not duplicated
* when setting the #GValue.
*/
void
g_value_set_static_boxed (GValue *value,
gconstpointer boxed)
{
g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
g_return_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)));
value_set_boxed_internal (value, boxed, FALSE, FALSE);
}
/**
* g_value_set_boxed_take_ownership:
* @value: a valid #GValue of %G_TYPE_BOXED derived type
* @v_boxed: (nullable): duplicated unowned boxed value to be set
*
* This is an internal function introduced mainly for C marshallers.
*
* Deprecated: 2.4: Use g_value_take_boxed() instead.
*/
void
g_value_set_boxed_take_ownership (GValue *value,
gconstpointer boxed)
{
g_value_take_boxed (value, boxed);
}
/**
* g_value_take_boxed:
* @value: a valid #GValue of %G_TYPE_BOXED derived type
* @v_boxed: (nullable): duplicated unowned boxed value to be set
*
* Sets the contents of a %G_TYPE_BOXED derived #GValue to @v_boxed
* and takes over the ownership of the callers reference to @v_boxed;
* the caller doesnt have to unref it any more.
*
* Since: 2.4
*/
void
g_value_take_boxed (GValue *value,
gconstpointer boxed)
{
g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
g_return_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)));
value_set_boxed_internal (value, boxed, FALSE, TRUE);
}

122
gobject/gboxed.h Normal file
View file

@ -0,0 +1,122 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2000-2001 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __G_BOXED_H__
#define __G_BOXED_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <gobject/gtype.h>
#ifndef __GI_SCANNER__
#include <gobject/glib-types.h>
#endif
G_BEGIN_DECLS
/* --- type macros --- */
#define G_TYPE_IS_BOXED(type) (G_TYPE_FUNDAMENTAL (type) == G_TYPE_BOXED)
/**
* G_VALUE_HOLDS_BOXED:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values derived
* from type %G_TYPE_BOXED.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_BOXED(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_BOXED))
/* --- typedefs --- */
/**
* GBoxedCopyFunc:
* @boxed: (not nullable): The boxed structure to be copied.
*
* This function is provided by the user and should produce a copy
* of the passed in boxed structure.
*
* Returns: (not nullable): The newly created copy of the boxed structure.
*/
typedef gpointer (*GBoxedCopyFunc) (gpointer boxed);
/**
* GBoxedFreeFunc:
* @boxed: (not nullable): The boxed structure to be freed.
*
* This function is provided by the user and should free the boxed
* structure passed.
*/
typedef void (*GBoxedFreeFunc) (gpointer boxed);
/* --- prototypes --- */
GLIB_AVAILABLE_IN_ALL
gpointer g_boxed_copy (GType boxed_type,
gconstpointer src_boxed);
GLIB_AVAILABLE_IN_ALL
void g_boxed_free (GType boxed_type,
gpointer boxed);
GLIB_AVAILABLE_IN_ALL
void g_value_set_boxed (GValue *value,
gconstpointer v_boxed);
GLIB_AVAILABLE_IN_ALL
void g_value_set_static_boxed (GValue *value,
gconstpointer v_boxed);
GLIB_AVAILABLE_IN_ALL
void g_value_take_boxed (GValue *value,
gconstpointer v_boxed);
GLIB_DEPRECATED_FOR(g_value_take_boxed)
void g_value_set_boxed_take_ownership (GValue *value,
gconstpointer v_boxed);
GLIB_AVAILABLE_IN_ALL
gpointer g_value_get_boxed (const GValue *value);
GLIB_AVAILABLE_IN_ALL
gpointer g_value_dup_boxed (const GValue *value);
/* --- convenience --- */
GLIB_AVAILABLE_IN_ALL
GType g_boxed_type_register_static (const gchar *name,
GBoxedCopyFunc boxed_copy,
GBoxedFreeFunc boxed_free);
/* --- GObject boxed types --- */
/**
* G_TYPE_CLOSURE:
*
* The #GType for #GClosure.
*/
#define G_TYPE_CLOSURE (g_closure_get_type ())
/**
* G_TYPE_VALUE:
*
* The type ID of the "GValue" type which is a boxed type,
* used to pass around pointers to GValues.
*/
#define G_TYPE_VALUE (g_value_get_type ())
GLIB_AVAILABLE_IN_ALL
GType g_closure_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_value_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __G_BOXED_H__ */

1990
gobject/gclosure.c Normal file

File diff suppressed because it is too large Load diff

321
gobject/gclosure.h Normal file
View file

@ -0,0 +1,321 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2000-2001 Red Hat, Inc.
* Copyright (C) 2005 Imendio AB
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __G_CLOSURE_H__
#define __G_CLOSURE_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <gobject/gtype.h>
G_BEGIN_DECLS
/* --- defines --- */
/**
* G_CLOSURE_NEEDS_MARSHAL:
* @closure: a #GClosure
*
* Check if the closure still needs a marshaller. See g_closure_set_marshal().
*
* Returns: %TRUE if a #GClosureMarshal marshaller has not yet been set on
* @closure.
*/
#define G_CLOSURE_NEEDS_MARSHAL(closure) (((GClosure*) (closure))->marshal == NULL)
/**
* G_CLOSURE_N_NOTIFIERS:
* @cl: a #GClosure
*
* Get the total number of notifiers connected with the closure @cl.
*
* The count includes the meta marshaller, the finalize and invalidate notifiers
* and the marshal guards. Note that each guard counts as two notifiers.
* See g_closure_set_meta_marshal(), g_closure_add_finalize_notifier(),
* g_closure_add_invalidate_notifier() and g_closure_add_marshal_guards().
*
* Returns: number of notifiers
*/
#define G_CLOSURE_N_NOTIFIERS(cl) (((cl)->n_guards << 1L) + \
(cl)->n_fnotifiers + (cl)->n_inotifiers)
/**
* G_CCLOSURE_SWAP_DATA:
* @cclosure: a #GCClosure
*
* Checks whether the user data of the #GCClosure should be passed as the
* first parameter to the callback. See g_cclosure_new_swap().
*
* Returns: %TRUE if data has to be swapped.
*/
#define G_CCLOSURE_SWAP_DATA(cclosure) (((GClosure*) (cclosure))->derivative_flag)
/**
* G_CALLBACK:
* @f: a function pointer.
*
* Cast a function pointer to a #GCallback.
*/
#define G_CALLBACK(f) ((GCallback) (f))
/* -- typedefs --- */
typedef struct _GClosure GClosure;
typedef struct _GClosureNotifyData GClosureNotifyData;
/**
* GCallback:
*
* The type used for callback functions in structure definitions and function
* signatures.
*
* This doesn't mean that all callback functions must take no parameters and
* return void. The required signature of a callback function is determined by
* the context in which is used (e.g. the signal to which it is connected).
*
* Use G_CALLBACK() to cast the callback function to a #GCallback.
*/
typedef void (*GCallback) (void);
/**
* GClosureNotify:
* @data: data specified when registering the notification callback
* @closure: the #GClosure on which the notification is emitted
*
* The type used for the various notification callbacks which can be registered
* on closures.
*/
typedef void (*GClosureNotify) (gpointer data,
GClosure *closure);
/**
* GClosureMarshal:
* @closure: the #GClosure to which the marshaller belongs
* @return_value: (nullable): a #GValue to store the return
* value. May be %NULL if the callback of @closure doesn't return a
* value.
* @n_param_values: the length of the @param_values array
* @param_values: (array length=n_param_values): an array of
* #GValues holding the arguments on which to invoke the
* callback of @closure
* @invocation_hint: (nullable): the invocation hint given as the
* last argument to g_closure_invoke()
* @marshal_data: (nullable): additional data specified when
* registering the marshaller, see g_closure_set_marshal() and
* g_closure_set_meta_marshal()
*
* The type used for marshaller functions.
*/
typedef void (*GClosureMarshal) (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
/**
* GVaClosureMarshal:
* @closure: the #GClosure to which the marshaller belongs
* @return_value: (nullable): a #GValue to store the return
* value. May be %NULL if the callback of @closure doesn't return a
* value.
* @instance: (type GObject.TypeInstance): the instance on which the closure is
* invoked.
* @args: va_list of arguments to be passed to the closure.
* @marshal_data: (nullable): additional data specified when
* registering the marshaller, see g_closure_set_marshal() and
* g_closure_set_meta_marshal()
* @n_params: the length of the @param_types array
* @param_types: (array length=n_params): the #GType of each argument from
* @args.
*
* This is the signature of va_list marshaller functions, an optional
* marshaller that can be used in some situations to avoid
* marshalling the signal argument into GValues.
*/
typedef void (* GVaClosureMarshal) (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/**
* GCClosure:
* @closure: the #GClosure
* @callback: the callback function
*
* A #GCClosure is a specialization of #GClosure for C function callbacks.
*/
typedef struct _GCClosure GCClosure;
/* --- structures --- */
struct _GClosureNotifyData
{
gpointer data;
GClosureNotify notify;
};
/**
* GClosure:
* @in_marshal: Indicates whether the closure is currently being invoked with
* g_closure_invoke()
* @is_invalid: Indicates whether the closure has been invalidated by
* g_closure_invalidate()
*
* A #GClosure represents a callback supplied by the programmer.
*/
struct _GClosure
{
/*< private >*/
guint ref_count : 15; /* (atomic) */
/* meta_marshal is not used anymore but must be zero for historical reasons
as it was exposed in the G_CLOSURE_N_NOTIFIERS macro */
guint meta_marshal_nouse : 1; /* (atomic) */
guint n_guards : 1; /* (atomic) */
guint n_fnotifiers : 2; /* finalization notifiers (atomic) */
guint n_inotifiers : 8; /* invalidation notifiers (atomic) */
guint in_inotify : 1; /* (atomic) */
guint floating : 1; /* (atomic) */
/*< protected >*/
guint derivative_flag : 1; /* (atomic) */
/*< public >*/
guint in_marshal : 1; /* (atomic) */
guint is_invalid : 1; /* (atomic) */
/*< private >*/ void (*marshal) (GClosure *closure,
GValue /*out*/ *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
/*< protected >*/ gpointer data;
/*< private >*/ GClosureNotifyData *notifiers;
/* invariants/constraints:
* - ->marshal and ->data are _invalid_ as soon as ->is_invalid==TRUE
* - invocation of all inotifiers occurs prior to fnotifiers
* - order of inotifiers is random
* inotifiers may _not_ free/invalidate parameter values (e.g. ->data)
* - order of fnotifiers is random
* - each notifier may only be removed before or during its invocation
* - reference counting may only happen prior to fnotify invocation
* (in that sense, fnotifiers are really finalization handlers)
*/
};
/* closure for C function calls, callback() is the user function
*/
struct _GCClosure
{
GClosure closure;
gpointer callback;
};
/* --- prototypes --- */
GLIB_AVAILABLE_IN_ALL
GClosure* g_cclosure_new (GCallback callback_func,
gpointer user_data,
GClosureNotify destroy_data);
GLIB_AVAILABLE_IN_ALL
GClosure* g_cclosure_new_swap (GCallback callback_func,
gpointer user_data,
GClosureNotify destroy_data);
GLIB_AVAILABLE_IN_ALL
GClosure* g_signal_type_cclosure_new (GType itype,
guint struct_offset);
/* --- prototypes --- */
GLIB_AVAILABLE_IN_ALL
GClosure* g_closure_ref (GClosure *closure);
GLIB_AVAILABLE_IN_ALL
void g_closure_sink (GClosure *closure);
GLIB_AVAILABLE_IN_ALL
void g_closure_unref (GClosure *closure);
/* intimidating */
GLIB_AVAILABLE_IN_ALL
GClosure* g_closure_new_simple (guint sizeof_closure,
gpointer data);
GLIB_AVAILABLE_IN_ALL
void g_closure_add_finalize_notifier (GClosure *closure,
gpointer notify_data,
GClosureNotify notify_func);
GLIB_AVAILABLE_IN_ALL
void g_closure_remove_finalize_notifier (GClosure *closure,
gpointer notify_data,
GClosureNotify notify_func);
GLIB_AVAILABLE_IN_ALL
void g_closure_add_invalidate_notifier (GClosure *closure,
gpointer notify_data,
GClosureNotify notify_func);
GLIB_AVAILABLE_IN_ALL
void g_closure_remove_invalidate_notifier (GClosure *closure,
gpointer notify_data,
GClosureNotify notify_func);
GLIB_AVAILABLE_IN_ALL
void g_closure_add_marshal_guards (GClosure *closure,
gpointer pre_marshal_data,
GClosureNotify pre_marshal_notify,
gpointer post_marshal_data,
GClosureNotify post_marshal_notify);
GLIB_AVAILABLE_IN_ALL
void g_closure_set_marshal (GClosure *closure,
GClosureMarshal marshal);
GLIB_AVAILABLE_IN_ALL
void g_closure_set_meta_marshal (GClosure *closure,
gpointer marshal_data,
GClosureMarshal meta_marshal);
GLIB_AVAILABLE_IN_ALL
void g_closure_invalidate (GClosure *closure);
GLIB_AVAILABLE_IN_ALL
void g_closure_invoke (GClosure *closure,
GValue /*out*/ *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint);
/* FIXME:
OK: data_object::destroy -> closure_invalidate();
MIS: closure_invalidate() -> disconnect(closure);
MIS: disconnect(closure) -> (unlink) closure_unref();
OK: closure_finalize() -> g_free (data_string);
random remarks:
- need marshaller repo with decent aliasing to base types
- provide marshaller collection, virtually covering anything out there
*/
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_generic (GClosure *closure,
GValue *return_gvalue,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_generic_va (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args_list,
gpointer marshal_data,
int n_params,
GType *param_types);
G_END_DECLS
#endif /* __G_CLOSURE_H__ */

760
gobject/genums.c Normal file
View file

@ -0,0 +1,760 @@
/* 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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* MT safe
*/
#include "config.h"
#include <string.h>
#include "genums.h"
#include "gtype-private.h"
#include "gvalue.h"
#include "gvaluecollector.h"
/**
* SECTION:enumerations_flags
* @short_description: Enumeration and flags types
* @title: Enumeration and Flag Types
* @see_also:#GParamSpecEnum, #GParamSpecFlags, g_param_spec_enum(),
* g_param_spec_flags()
*
* The GLib type system provides fundamental types for enumeration and
* flags types. (Flags types are like enumerations, but allow their
* values to be combined by bitwise or). A registered enumeration or
* flags type associates a name and a nickname with each allowed
* value, and the methods g_enum_get_value_by_name(),
* g_enum_get_value_by_nick(), g_flags_get_value_by_name() and
* g_flags_get_value_by_nick() can look up values by their name or
* nickname. When an enumeration or flags type is registered with the
* GLib type system, it can be used as value type for object
* properties, using g_param_spec_enum() or g_param_spec_flags().
*
* GObject ships with a utility called [glib-mkenums][glib-mkenums],
* that can construct suitable type registration functions from C enumeration
* definitions.
*
* Example of how to get a string representation of an enum value:
* |[<!-- language="C" -->
* GEnumClass *enum_class;
* GEnumValue *enum_value;
*
* enum_class = g_type_class_ref (MAMAN_TYPE_MY_ENUM);
* enum_value = g_enum_get_value (enum_class, MAMAN_MY_ENUM_FOO);
*
* g_print ("Name: %s\n", enum_value->value_name);
*
* g_type_class_unref (enum_class);
* ]|
*/
/* --- prototypes --- */
static void g_enum_class_init (GEnumClass *class,
gpointer class_data);
static void g_flags_class_init (GFlagsClass *class,
gpointer class_data);
static void value_flags_enum_init (GValue *value);
static void value_flags_enum_copy_value (const GValue *src_value,
GValue *dest_value);
static gchar* value_flags_enum_collect_value (GValue *value,
guint n_collect_values,
GTypeCValue *collect_values,
guint collect_flags);
static gchar* value_flags_enum_lcopy_value (const GValue *value,
guint n_collect_values,
GTypeCValue *collect_values,
guint collect_flags);
/* --- functions --- */
void
_g_enum_types_init (void)
{
static gboolean initialized = FALSE;
static const GTypeValueTable flags_enum_value_table = {
value_flags_enum_init, /* value_init */
NULL, /* value_free */
value_flags_enum_copy_value, /* value_copy */
NULL, /* value_peek_pointer */
"i", /* collect_format */
value_flags_enum_collect_value, /* collect_value */
"p", /* lcopy_format */
value_flags_enum_lcopy_value, /* lcopy_value */
};
GTypeInfo info = {
0, /* class_size */
NULL, /* base_init */
NULL, /* base_destroy */
NULL, /* class_init */
NULL, /* class_destroy */
NULL, /* class_data */
0, /* instance_size */
0, /* n_preallocs */
NULL, /* instance_init */
&flags_enum_value_table, /* value_table */
};
static const GTypeFundamentalInfo finfo = {
G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_DERIVABLE,
};
GType type G_GNUC_UNUSED /* when compiling with G_DISABLE_ASSERT */;
g_return_if_fail (initialized == FALSE);
initialized = TRUE;
/* G_TYPE_ENUM
*/
info.class_size = sizeof (GEnumClass);
type = g_type_register_fundamental (G_TYPE_ENUM, g_intern_static_string ("GEnum"), &info, &finfo,
G_TYPE_FLAG_ABSTRACT | G_TYPE_FLAG_VALUE_ABSTRACT);
g_assert (type == G_TYPE_ENUM);
/* G_TYPE_FLAGS
*/
info.class_size = sizeof (GFlagsClass);
type = g_type_register_fundamental (G_TYPE_FLAGS, g_intern_static_string ("GFlags"), &info, &finfo,
G_TYPE_FLAG_ABSTRACT | G_TYPE_FLAG_VALUE_ABSTRACT);
g_assert (type == G_TYPE_FLAGS);
}
static void
value_flags_enum_init (GValue *value)
{
value->data[0].v_long = 0;
}
static void
value_flags_enum_copy_value (const GValue *src_value,
GValue *dest_value)
{
dest_value->data[0].v_long = src_value->data[0].v_long;
}
static gchar*
value_flags_enum_collect_value (GValue *value,
guint n_collect_values,
GTypeCValue *collect_values,
guint collect_flags)
{
if (G_VALUE_HOLDS_ENUM (value))
value->data[0].v_long = collect_values[0].v_int;
else
value->data[0].v_ulong = (guint) collect_values[0].v_int;
return NULL;
}
static gchar*
value_flags_enum_lcopy_value (const GValue *value,
guint n_collect_values,
GTypeCValue *collect_values,
guint collect_flags)
{
gint *int_p = collect_values[0].v_pointer;
g_return_val_if_fail (int_p != NULL, g_strdup_printf ("value location for '%s' passed as NULL", G_VALUE_TYPE_NAME (value)));
*int_p = value->data[0].v_long;
return NULL;
}
/**
* g_enum_register_static:
* @name: A nul-terminated string used as the name of the new type.
* @const_static_values: An array of #GEnumValue structs for the possible
* enumeration values. The array is terminated by a struct with all
* members being 0. GObject keeps a reference to the data, so it cannot
* be stack-allocated.
*
* Registers a new static enumeration type with the name @name.
*
* It is normally more convenient to let [glib-mkenums][glib-mkenums],
* generate a my_enum_get_type() function from a usual C enumeration
* definition than to write one yourself using g_enum_register_static().
*
* Returns: The new type identifier.
*/
GType
g_enum_register_static (const gchar *name,
const GEnumValue *const_static_values)
{
GTypeInfo enum_type_info = {
sizeof (GEnumClass), /* class_size */
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) g_enum_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
0, /* instance_size */
0, /* n_preallocs */
NULL, /* instance_init */
NULL, /* value_table */
};
GType type;
g_return_val_if_fail (name != NULL, 0);
g_return_val_if_fail (const_static_values != NULL, 0);
enum_type_info.class_data = const_static_values;
type = g_type_register_static (G_TYPE_ENUM, name, &enum_type_info, 0);
return type;
}
/**
* g_flags_register_static:
* @name: A nul-terminated string used as the name of the new type.
* @const_static_values: An array of #GFlagsValue structs for the possible
* flags values. The array is terminated by a struct with all members being 0.
* GObject keeps a reference to the data, so it cannot be stack-allocated.
*
* Registers a new static flags type with the name @name.
*
* It is normally more convenient to let [glib-mkenums][glib-mkenums]
* generate a my_flags_get_type() function from a usual C enumeration
* definition than to write one yourself using g_flags_register_static().
*
* Returns: The new type identifier.
*/
GType
g_flags_register_static (const gchar *name,
const GFlagsValue *const_static_values)
{
GTypeInfo flags_type_info = {
sizeof (GFlagsClass), /* class_size */
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) g_flags_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
0, /* instance_size */
0, /* n_preallocs */
NULL, /* instance_init */
NULL, /* value_table */
};
GType type;
g_return_val_if_fail (name != NULL, 0);
g_return_val_if_fail (const_static_values != NULL, 0);
flags_type_info.class_data = const_static_values;
type = g_type_register_static (G_TYPE_FLAGS, name, &flags_type_info, 0);
return type;
}
/**
* g_enum_complete_type_info:
* @g_enum_type: the type identifier of the type being completed
* @info: (out callee-allocates): the #GTypeInfo struct to be filled in
* @const_values: An array of #GEnumValue structs for the possible
* enumeration values. The array is terminated by a struct with all
* members being 0.
*
* This function is meant to be called from the `complete_type_info`
* function of a #GTypePlugin implementation, as in the following
* example:
*
* |[<!-- language="C" -->
* static void
* my_enum_complete_type_info (GTypePlugin *plugin,
* GType g_type,
* GTypeInfo *info,
* GTypeValueTable *value_table)
* {
* static const GEnumValue values[] = {
* { MY_ENUM_FOO, "MY_ENUM_FOO", "foo" },
* { MY_ENUM_BAR, "MY_ENUM_BAR", "bar" },
* { 0, NULL, NULL }
* };
*
* g_enum_complete_type_info (type, info, values);
* }
* ]|
*/
void
g_enum_complete_type_info (GType g_enum_type,
GTypeInfo *info,
const GEnumValue *const_values)
{
g_return_if_fail (G_TYPE_IS_ENUM (g_enum_type));
g_return_if_fail (info != NULL);
g_return_if_fail (const_values != NULL);
info->class_size = sizeof (GEnumClass);
info->base_init = NULL;
info->base_finalize = NULL;
info->class_init = (GClassInitFunc) g_enum_class_init;
info->class_finalize = NULL;
info->class_data = const_values;
}
/**
* g_flags_complete_type_info:
* @g_flags_type: the type identifier of the type being completed
* @info: (out callee-allocates): the #GTypeInfo struct to be filled in
* @const_values: An array of #GFlagsValue structs for the possible
* enumeration values. The array is terminated by a struct with all
* members being 0.
*
* This function is meant to be called from the complete_type_info()
* function of a #GTypePlugin implementation, see the example for
* g_enum_complete_type_info() above.
*/
void
g_flags_complete_type_info (GType g_flags_type,
GTypeInfo *info,
const GFlagsValue *const_values)
{
g_return_if_fail (G_TYPE_IS_FLAGS (g_flags_type));
g_return_if_fail (info != NULL);
g_return_if_fail (const_values != NULL);
info->class_size = sizeof (GFlagsClass);
info->base_init = NULL;
info->base_finalize = NULL;
info->class_init = (GClassInitFunc) g_flags_class_init;
info->class_finalize = NULL;
info->class_data = const_values;
}
static void
g_enum_class_init (GEnumClass *class,
gpointer class_data)
{
g_return_if_fail (G_IS_ENUM_CLASS (class));
class->minimum = 0;
class->maximum = 0;
class->n_values = 0;
class->values = class_data;
if (class->values)
{
GEnumValue *values;
class->minimum = class->values->value;
class->maximum = class->values->value;
for (values = class->values; values->value_name; values++)
{
class->minimum = MIN (class->minimum, values->value);
class->maximum = MAX (class->maximum, values->value);
class->n_values++;
}
}
}
static void
g_flags_class_init (GFlagsClass *class,
gpointer class_data)
{
g_return_if_fail (G_IS_FLAGS_CLASS (class));
class->mask = 0;
class->n_values = 0;
class->values = class_data;
if (class->values)
{
GFlagsValue *values;
for (values = class->values; values->value_name; values++)
{
class->mask |= values->value;
class->n_values++;
}
}
}
/**
* g_enum_get_value_by_name:
* @enum_class: a #GEnumClass
* @name: the name to look up
*
* Looks up a #GEnumValue by name.
*
* Returns: (transfer none) (nullable): the #GEnumValue with name @name,
* or %NULL if the enumeration doesn't have a member
* with that name
*/
GEnumValue*
g_enum_get_value_by_name (GEnumClass *enum_class,
const gchar *name)
{
g_return_val_if_fail (G_IS_ENUM_CLASS (enum_class), NULL);
g_return_val_if_fail (name != NULL, NULL);
if (enum_class->n_values)
{
GEnumValue *enum_value;
for (enum_value = enum_class->values; enum_value->value_name; enum_value++)
if (strcmp (name, enum_value->value_name) == 0)
return enum_value;
}
return NULL;
}
/**
* g_flags_get_value_by_name:
* @flags_class: a #GFlagsClass
* @name: the name to look up
*
* Looks up a #GFlagsValue by name.
*
* Returns: (transfer none) (nullable): the #GFlagsValue with name @name,
* or %NULL if there is no flag with that name
*/
GFlagsValue*
g_flags_get_value_by_name (GFlagsClass *flags_class,
const gchar *name)
{
g_return_val_if_fail (G_IS_FLAGS_CLASS (flags_class), NULL);
g_return_val_if_fail (name != NULL, NULL);
if (flags_class->n_values)
{
GFlagsValue *flags_value;
for (flags_value = flags_class->values; flags_value->value_name; flags_value++)
if (strcmp (name, flags_value->value_name) == 0)
return flags_value;
}
return NULL;
}
/**
* g_enum_get_value_by_nick:
* @enum_class: a #GEnumClass
* @nick: the nickname to look up
*
* Looks up a #GEnumValue by nickname.
*
* Returns: (transfer none) (nullable): the #GEnumValue with nickname @nick,
* or %NULL if the enumeration doesn't have a member
* with that nickname
*/
GEnumValue*
g_enum_get_value_by_nick (GEnumClass *enum_class,
const gchar *nick)
{
g_return_val_if_fail (G_IS_ENUM_CLASS (enum_class), NULL);
g_return_val_if_fail (nick != NULL, NULL);
if (enum_class->n_values)
{
GEnumValue *enum_value;
for (enum_value = enum_class->values; enum_value->value_name; enum_value++)
if (enum_value->value_nick && strcmp (nick, enum_value->value_nick) == 0)
return enum_value;
}
return NULL;
}
/**
* g_flags_get_value_by_nick:
* @flags_class: a #GFlagsClass
* @nick: the nickname to look up
*
* Looks up a #GFlagsValue by nickname.
*
* Returns: (transfer none) (nullable): the #GFlagsValue with nickname @nick,
* or %NULL if there is no flag with that nickname
*/
GFlagsValue*
g_flags_get_value_by_nick (GFlagsClass *flags_class,
const gchar *nick)
{
g_return_val_if_fail (G_IS_FLAGS_CLASS (flags_class), NULL);
g_return_val_if_fail (nick != NULL, NULL);
if (flags_class->n_values)
{
GFlagsValue *flags_value;
for (flags_value = flags_class->values; flags_value->value_nick; flags_value++)
if (flags_value->value_nick && strcmp (nick, flags_value->value_nick) == 0)
return flags_value;
}
return NULL;
}
/**
* g_enum_get_value:
* @enum_class: a #GEnumClass
* @value: the value to look up
*
* Returns the #GEnumValue for a value.
*
* Returns: (transfer none) (nullable): the #GEnumValue for @value, or %NULL
* if @value is not a member of the enumeration
*/
GEnumValue*
g_enum_get_value (GEnumClass *enum_class,
gint value)
{
g_return_val_if_fail (G_IS_ENUM_CLASS (enum_class), NULL);
if (enum_class->n_values)
{
GEnumValue *enum_value;
for (enum_value = enum_class->values; enum_value->value_name; enum_value++)
if (enum_value->value == value)
return enum_value;
}
return NULL;
}
/**
* g_flags_get_first_value:
* @flags_class: a #GFlagsClass
* @value: the value
*
* Returns the first #GFlagsValue which is set in @value.
*
* Returns: (transfer none) (nullable): the first #GFlagsValue which is set in
* @value, or %NULL if none is set
*/
GFlagsValue*
g_flags_get_first_value (GFlagsClass *flags_class,
guint value)
{
g_return_val_if_fail (G_IS_FLAGS_CLASS (flags_class), NULL);
if (flags_class->n_values)
{
GFlagsValue *flags_value;
if (value == 0)
{
for (flags_value = flags_class->values; flags_value->value_name; flags_value++)
if (flags_value->value == 0)
return flags_value;
}
else
{
for (flags_value = flags_class->values; flags_value->value_name; flags_value++)
if (flags_value->value != 0 && (flags_value->value & value) == flags_value->value)
return flags_value;
}
}
return NULL;
}
/**
* g_enum_to_string:
* @g_enum_type: the type identifier of a #GEnumClass type
* @value: the value
*
* Pretty-prints @value in the form of the enums name.
*
* This is intended to be used for debugging purposes. The format of the output
* may change in the future.
*
* Returns: (transfer full): a newly-allocated text string
*
* Since: 2.54
*/
gchar *
g_enum_to_string (GType g_enum_type,
gint value)
{
gchar *result;
GEnumClass *enum_class;
GEnumValue *enum_value;
g_return_val_if_fail (G_TYPE_IS_ENUM (g_enum_type), NULL);
enum_class = g_type_class_ref (g_enum_type);
/* Already warned */
if (enum_class == NULL)
return g_strdup_printf ("%d", value);
enum_value = g_enum_get_value (enum_class, value);
if (enum_value == NULL)
result = g_strdup_printf ("%d", value);
else
result = g_strdup (enum_value->value_name);
g_type_class_unref (enum_class);
return result;
}
/*
* g_flags_get_value_string:
* @flags_class: a #GFlagsClass
* @value: the value
*
* Pretty-prints @value in the form of the flag names separated by ` | ` and
* sorted. Any extra bits will be shown at the end as a hexadecimal number.
*
* This is intended to be used for debugging purposes. The format of the output
* may change in the future.
*
* Returns: (transfer full): a newly-allocated text string
*
* Since: 2.54
*/
static gchar *
g_flags_get_value_string (GFlagsClass *flags_class,
guint value)
{
GString *str;
GFlagsValue *flags_value;
g_return_val_if_fail (G_IS_FLAGS_CLASS (flags_class), NULL);
str = g_string_new (NULL);
while ((str->len == 0 || value != 0) &&
(flags_value = g_flags_get_first_value (flags_class, value)) != NULL)
{
if (str->len > 0)
g_string_append (str, " | ");
g_string_append (str, flags_value->value_name);
value &= ~flags_value->value;
}
/* Show the extra bits */
if (value != 0 || str->len == 0)
{
if (str->len > 0)
g_string_append (str, " | ");
g_string_append_printf (str, "0x%x", value);
}
return g_string_free (str, FALSE);
}
/**
* g_flags_to_string:
* @flags_type: the type identifier of a #GFlagsClass type
* @value: the value
*
* Pretty-prints @value in the form of the flag names separated by ` | ` and
* sorted. Any extra bits will be shown at the end as a hexadecimal number.
*
* This is intended to be used for debugging purposes. The format of the output
* may change in the future.
*
* Returns: (transfer full): a newly-allocated text string
*
* Since: 2.54
*/
gchar *
g_flags_to_string (GType flags_type,
guint value)
{
gchar *result;
GFlagsClass *flags_class;
g_return_val_if_fail (G_TYPE_IS_FLAGS (flags_type), NULL);
flags_class = g_type_class_ref (flags_type);
/* Already warned */
if (flags_class == NULL)
return NULL;
result = g_flags_get_value_string (flags_class, value);
g_type_class_unref (flags_class);
return result;
}
/**
* g_value_set_enum:
* @value: a valid #GValue whose type is derived from %G_TYPE_ENUM
* @v_enum: enum value to be set
*
* Set the contents of a %G_TYPE_ENUM #GValue to @v_enum.
*/
void
g_value_set_enum (GValue *value,
gint v_enum)
{
g_return_if_fail (G_VALUE_HOLDS_ENUM (value));
value->data[0].v_long = v_enum;
}
/**
* g_value_get_enum:
* @value: a valid #GValue whose type is derived from %G_TYPE_ENUM
*
* Get the contents of a %G_TYPE_ENUM #GValue.
*
* Returns: enum contents of @value
*/
gint
g_value_get_enum (const GValue *value)
{
g_return_val_if_fail (G_VALUE_HOLDS_ENUM (value), 0);
return value->data[0].v_long;
}
/**
* g_value_set_flags:
* @value: a valid #GValue whose type is derived from %G_TYPE_FLAGS
* @v_flags: flags value to be set
*
* Set the contents of a %G_TYPE_FLAGS #GValue to @v_flags.
*/
void
g_value_set_flags (GValue *value,
guint v_flags)
{
g_return_if_fail (G_VALUE_HOLDS_FLAGS (value));
value->data[0].v_ulong = v_flags;
}
/**
* g_value_get_flags:
* @value: a valid #GValue whose type is derived from %G_TYPE_FLAGS
*
* Get the contents of a %G_TYPE_FLAGS #GValue.
*
* Returns: flags contents of @value
*/
guint
g_value_get_flags (const GValue *value)
{
g_return_val_if_fail (G_VALUE_HOLDS_FLAGS (value), 0);
return value->data[0].v_ulong;
}

279
gobject/genums.h Normal file
View file

@ -0,0 +1,279 @@
/* 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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __G_ENUMS_H__
#define __G_ENUMS_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <gobject/gtype.h>
G_BEGIN_DECLS
/* --- type macros --- */
/**
* G_TYPE_IS_ENUM:
* @type: a #GType ID.
*
* Checks whether @type "is a" %G_TYPE_ENUM.
*
* Returns: %TRUE if @type "is a" %G_TYPE_ENUM.
*/
#define G_TYPE_IS_ENUM(type) (G_TYPE_FUNDAMENTAL (type) == G_TYPE_ENUM)
/**
* G_ENUM_CLASS:
* @class: a valid #GEnumClass
*
* Casts a derived #GEnumClass structure into a #GEnumClass structure.
*/
#define G_ENUM_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_ENUM, GEnumClass))
/**
* G_IS_ENUM_CLASS:
* @class: a #GEnumClass
*
* Checks whether @class "is a" valid #GEnumClass structure of type %G_TYPE_ENUM
* or derived.
*/
#define G_IS_ENUM_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_ENUM))
/**
* G_ENUM_CLASS_TYPE:
* @class: a #GEnumClass
*
* Get the type identifier from a given #GEnumClass structure.
*
* Returns: the #GType
*/
#define G_ENUM_CLASS_TYPE(class) (G_TYPE_FROM_CLASS (class))
/**
* G_ENUM_CLASS_TYPE_NAME:
* @class: a #GEnumClass
*
* Get the static type name from a given #GEnumClass structure.
*
* Returns: the type name.
*/
#define G_ENUM_CLASS_TYPE_NAME(class) (g_type_name (G_ENUM_CLASS_TYPE (class)))
/**
* G_TYPE_IS_FLAGS:
* @type: a #GType ID.
*
* Checks whether @type "is a" %G_TYPE_FLAGS.
*
* Returns: %TRUE if @type "is a" %G_TYPE_FLAGS.
*/
#define G_TYPE_IS_FLAGS(type) (G_TYPE_FUNDAMENTAL (type) == G_TYPE_FLAGS)
/**
* G_FLAGS_CLASS:
* @class: a valid #GFlagsClass
*
* Casts a derived #GFlagsClass structure into a #GFlagsClass structure.
*/
#define G_FLAGS_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_FLAGS, GFlagsClass))
/**
* G_IS_FLAGS_CLASS:
* @class: a #GFlagsClass
*
* Checks whether @class "is a" valid #GFlagsClass structure of type %G_TYPE_FLAGS
* or derived.
*/
#define G_IS_FLAGS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_FLAGS))
/**
* G_FLAGS_CLASS_TYPE:
* @class: a #GFlagsClass
*
* Get the type identifier from a given #GFlagsClass structure.
*
* Returns: the #GType
*/
#define G_FLAGS_CLASS_TYPE(class) (G_TYPE_FROM_CLASS (class))
/**
* G_FLAGS_CLASS_TYPE_NAME:
* @class: a #GFlagsClass
*
* Get the static type name from a given #GFlagsClass structure.
*
* Returns: the type name.
*/
#define G_FLAGS_CLASS_TYPE_NAME(class) (g_type_name (G_FLAGS_CLASS_TYPE (class)))
/**
* G_VALUE_HOLDS_ENUM:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values derived from type %G_TYPE_ENUM.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_ENUM(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_ENUM))
/**
* G_VALUE_HOLDS_FLAGS:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values derived from type %G_TYPE_FLAGS.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_FLAGS(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_FLAGS))
/* --- enum/flag values & classes --- */
typedef struct _GEnumClass GEnumClass;
typedef struct _GFlagsClass GFlagsClass;
typedef struct _GEnumValue GEnumValue;
typedef struct _GFlagsValue GFlagsValue;
/**
* GEnumClass:
* @g_type_class: the parent class
* @minimum: the smallest possible value.
* @maximum: the largest possible value.
* @n_values: the number of possible values.
* @values: an array of #GEnumValue structs describing the
* individual values.
*
* The class of an enumeration type holds information about its
* possible values.
*/
struct _GEnumClass
{
GTypeClass g_type_class;
/*< public >*/
gint minimum;
gint maximum;
guint n_values;
GEnumValue *values;
};
/**
* GFlagsClass:
* @g_type_class: the parent class
* @mask: a mask covering all possible values.
* @n_values: the number of possible values.
* @values: an array of #GFlagsValue structs describing the
* individual values.
*
* The class of a flags type holds information about its
* possible values.
*/
struct _GFlagsClass
{
GTypeClass g_type_class;
/*< public >*/
guint mask;
guint n_values;
GFlagsValue *values;
};
/**
* GEnumValue:
* @value: the enum value
* @value_name: the name of the value
* @value_nick: the nickname of the value
*
* A structure which contains a single enum value, its name, and its
* nickname.
*/
struct _GEnumValue
{
gint value;
const gchar *value_name;
const gchar *value_nick;
};
/**
* GFlagsValue:
* @value: the flags value
* @value_name: the name of the value
* @value_nick: the nickname of the value
*
* A structure which contains a single flags value, its name, and its
* nickname.
*/
struct _GFlagsValue
{
guint value;
const gchar *value_name;
const gchar *value_nick;
};
/* --- prototypes --- */
GLIB_AVAILABLE_IN_ALL
GEnumValue* g_enum_get_value (GEnumClass *enum_class,
gint value);
GLIB_AVAILABLE_IN_ALL
GEnumValue* g_enum_get_value_by_name (GEnumClass *enum_class,
const gchar *name);
GLIB_AVAILABLE_IN_ALL
GEnumValue* g_enum_get_value_by_nick (GEnumClass *enum_class,
const gchar *nick);
GLIB_AVAILABLE_IN_ALL
GFlagsValue* g_flags_get_first_value (GFlagsClass *flags_class,
guint value);
GLIB_AVAILABLE_IN_ALL
GFlagsValue* g_flags_get_value_by_name (GFlagsClass *flags_class,
const gchar *name);
GLIB_AVAILABLE_IN_ALL
GFlagsValue* g_flags_get_value_by_nick (GFlagsClass *flags_class,
const gchar *nick);
GLIB_AVAILABLE_IN_2_54
gchar *g_enum_to_string (GType g_enum_type,
gint value);
GLIB_AVAILABLE_IN_2_54
gchar *g_flags_to_string (GType flags_type,
guint value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_enum (GValue *value,
gint v_enum);
GLIB_AVAILABLE_IN_ALL
gint g_value_get_enum (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_flags (GValue *value,
guint v_flags);
GLIB_AVAILABLE_IN_ALL
guint g_value_get_flags (const GValue *value);
/* --- registration functions --- */
/* const_static_values is a NULL terminated array of enum/flags
* values that is taken over!
*/
GLIB_AVAILABLE_IN_ALL
GType g_enum_register_static (const gchar *name,
const GEnumValue *const_static_values);
GLIB_AVAILABLE_IN_ALL
GType g_flags_register_static (const gchar *name,
const GFlagsValue *const_static_values);
/* functions to complete the type information
* for enums/flags implemented by plugins
*/
GLIB_AVAILABLE_IN_ALL
void g_enum_complete_type_info (GType g_enum_type,
GTypeInfo *info,
const GEnumValue *const_values);
GLIB_AVAILABLE_IN_ALL
void g_flags_complete_type_info (GType g_flags_type,
GTypeInfo *info,
const GFlagsValue *const_values);
G_END_DECLS
#endif /* __G_ENUMS_H__ */

View file

@ -0,0 +1,47 @@
/*** BEGIN file-header ***/
#include "config.h"
#include "glib-enumtypes.h"
#include <glib-object.h>
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
/*** END file-header ***/
/*** BEGIN file-tail ***/
G_GNUC_END_IGNORE_DEPRECATIONS
/*** END file-tail ***/
/*** BEGIN file-production ***/
/* enumerations from "@filename@" */
/*** END file-production ***/
/*** BEGIN value-header ***/
GType
@enum_name@_get_type (void)
{
static gsize static_g_define_type_id = 0;
if (g_once_init_enter (&static_g_define_type_id))
{
static const G@Type@Value values[] = {
/*** END value-header ***/
/*** BEGIN value-production ***/
{ @VALUENAME@, "@VALUENAME@", "@valuenick@" },
/*** END value-production ***/
/*** BEGIN value-tail ***/
{ 0, NULL, NULL }
};
GType g_define_type_id =
g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
g_once_init_leave (&static_g_define_type_id, g_define_type_id);
}
return static_g_define_type_id;
}
/*** END value-tail ***/

View file

@ -0,0 +1,24 @@
/*** BEGIN file-header ***/
#ifndef __GOBJECT_ENUM_TYPES_H__
#define __GOBJECT_ENUM_TYPES_H__
#include <glib-object.h>
G_BEGIN_DECLS
/*** END file-header ***/
/*** BEGIN file-production ***/
/* enumerations from "@filename@" */
/*** END file-production ***/
/*** BEGIN value-header ***/
GLIB_AVAILABLE_IN_2_60 GType @enum_name@_get_type (void) G_GNUC_CONST;
#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
/*** END value-header ***/
/*** BEGIN file-tail ***/
G_END_DECLS
#endif /* __GOBJECT_ENUM_TYPES_H__ */
/*** END file-tail ***/

1080
gobject/glib-genmarshal.in Executable file

File diff suppressed because it is too large Load diff

806
gobject/glib-mkenums.in Executable file
View file

@ -0,0 +1,806 @@
#!/usr/bin/env @PYTHON@
# If the code below looks horrible and unpythonic, do not panic.
#
# It is.
#
# This is a manual conversion from the original Perl script to
# Python. Improvements are welcome.
#
from __future__ import print_function, unicode_literals
import argparse
import os
import re
import sys
import tempfile
import io
import errno
import codecs
import locale
VERSION_STR = '''glib-mkenums version @VERSION@
glib-mkenums comes with ABSOLUTELY NO WARRANTY.
You may redistribute copies of glib-mkenums under the terms of
the GNU General Public License which can be found in the
GLib source package. Sources, examples and contact
information are available at http://www.gtk.org'''
# pylint: disable=too-few-public-methods
class Color:
'''ANSI Terminal colors'''
GREEN = '\033[1;32m'
BLUE = '\033[1;34m'
YELLOW = '\033[1;33m'
RED = '\033[1;31m'
END = '\033[0m'
def print_color(msg, color=Color.END, prefix='MESSAGE'):
'''Print a string with a color prefix'''
if os.isatty(sys.stderr.fileno()):
real_prefix = '{start}{prefix}{end}'.format(start=color, prefix=prefix, end=Color.END)
else:
real_prefix = prefix
print('{prefix}: {msg}'.format(prefix=real_prefix, msg=msg), file=sys.stderr)
def print_error(msg):
'''Print an error, and terminate'''
print_color(msg, color=Color.RED, prefix='ERROR')
sys.exit(1)
def print_warning(msg, fatal=False):
'''Print a warning, and optionally terminate'''
if fatal:
color = Color.RED
prefix = 'ERROR'
else:
color = Color.YELLOW
prefix = 'WARNING'
print_color(msg, color, prefix)
if fatal:
sys.exit(1)
def print_info(msg):
'''Print a message'''
print_color(msg, color=Color.GREEN, prefix='INFO')
def get_rspfile_args(rspfile):
'''
Response files are useful on Windows where there is a command-line character
limit of 8191 because when passing sources as arguments to glib-mkenums this
limit can be exceeded in large codebases.
There is no specification for response files and each tool that supports it
generally writes them out in slightly different ways, but some sources are:
https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-response-files
https://docs.microsoft.com/en-us/windows/desktop/midl/the-response-file-command
'''
import shlex
if not os.path.isfile(rspfile):
sys.exit('Response file {!r} does not exist'.format(rspfile))
try:
with open(rspfile, 'r') as f:
cmdline = f.read()
except OSError as e:
sys.exit('Response file {!r} could not be read: {}'
.format(rspfile, e.strerror))
return shlex.split(cmdline)
def write_output(output):
global output_stream
print(output, file=output_stream)
# Python 2 defaults to ASCII in case stdout is redirected.
# This should make it match Python 3, which uses the locale encoding.
if sys.stdout.encoding is None:
output_stream = codecs.getwriter(
locale.getpreferredencoding())(sys.stdout)
else:
output_stream = sys.stdout
# Some source files aren't UTF-8 and the old perl version didn't care.
# Replace invalid data with a replacement character to keep things working.
# https://bugzilla.gnome.org/show_bug.cgi?id=785113#c20
def replace_and_warn(err):
# 7 characters of context either side of the offending character
print_warning('UnicodeWarning: {} at {} ({})'.format(
err.reason, err.start,
err.object[err.start - 7:err.end + 7]))
return ('?', err.end)
codecs.register_error('replace_and_warn', replace_and_warn)
# glib-mkenums.py
# Information about the current enumeration
flags = None # Is enumeration a bitmask?
option_underscore_name = '' # Overridden underscore variant of the enum name
# for example to fix the cases we don't get the
# mixed-case -> underscorized transform right.
option_lowercase_name = '' # DEPRECATED. A lower case name to use as part
# of the *_get_type() function, instead of the
# one that we guess. For instance, when an enum
# uses abnormal capitalization and we can not
# guess where to put the underscores.
option_since = '' # User provided version info for the enum.
seenbitshift = 0 # Have we seen bitshift operators?
seenprivate = False # Have we seen a private option?
enum_prefix = None # Prefix for this enumeration
enumname = '' # Name for this enumeration
enumshort = '' # $enumname without prefix
enumname_prefix = '' # prefix of $enumname
enumindex = 0 # Global enum counter
firstenum = 1 # Is this the first enumeration per file?
entries = [] # [ name, val ] for each entry
sandbox = None # sandbox for safe evaluation of expressions
output = '' # Filename to write result into
def parse_trigraph(opts):
result = {}
for opt in re.split(r'\s*,\s*', opts):
opt = re.sub(r'^\s*', '', opt)
opt = re.sub(r'\s*$', '', opt)
m = re.search(r'(\w+)(?:=(.+))?', opt)
assert m is not None
groups = m.groups()
key = groups[0]
if len(groups) > 1:
val = groups[1]
else:
val = 1
result[key] = val
return result
def parse_entries(file, file_name):
global entries, enumindex, enumname, seenbitshift, seenprivate, flags
looking_for_name = False
while True:
line = file.readline()
if not line:
break
line = line.strip()
# read lines until we have no open comments
while re.search(r'/\*([^*]|\*(?!/))*$', line):
line += file.readline()
# strip comments w/o options
line = re.sub(r'''/\*(?!<)
([^*]+|\*(?!/))*
\*/''', '', line, flags=re.X)
line = line.rstrip()
# skip empty lines
if len(line.strip()) == 0:
continue
if looking_for_name:
m = re.match(r'\s*(\w+)', line)
if m:
enumname = m.group(1)
return True
# Handle include files
m = re.match(r'\#include\s*<([^>]*)>', line)
if m:
newfilename = os.path.join("..", m.group(1))
newfile = io.open(newfilename, encoding="utf-8",
errors="replace_and_warn")
if not parse_entries(newfile, newfilename):
return False
else:
continue
m = re.match(r'\s*\}\s*(\w+)', line)
if m:
enumname = m.group(1)
enumindex += 1
return 1
m = re.match(r'\s*\}', line)
if m:
enumindex += 1
looking_for_name = True
continue
m = re.match(r'''\s*
(\w+)\s* # name
(\s+[A-Z]+_(?:AVAILABLE|DEPRECATED)_ENUMERATOR_IN_[0-9_]+(?:_FOR\s*\(\s*\w+\s*\))?\s*)? # availability
(?:=( # value
\s*\w+\s*\(.*\)\s* # macro with multiple args
| # OR
(?:[^,/]|/(?!\*))* # anything but a comma or comment
))?,?\s*
(?:/\*< # options
(([^*]|\*(?!/))*)
>\s*\*/)?,?
\s*$''', line, flags=re.X)
if m:
groups = m.groups()
name = groups[0]
availability = None
value = None
options = None
if len(groups) > 1:
availability = groups[1]
if len(groups) > 2:
value = groups[2]
if len(groups) > 3:
options = groups[3]
if flags is None and value is not None and '<<' in value:
seenbitshift = 1
if seenprivate:
continue
if options is not None:
options = parse_trigraph(options)
if 'skip' not in options:
entries.append((name, value, options.get('nick')))
else:
entries.append((name, value))
else:
m = re.match(r'''\s*
/\*< (([^*]|\*(?!/))*) >\s*\*/
\s*$''', line, flags=re.X)
if m:
options = m.groups()[0]
if options is not None:
options = parse_trigraph(options)
if 'private' in options:
seenprivate = True
continue
if 'public' in options:
seenprivate = False
continue
if re.match(r's*\#', line):
pass
else:
print_warning('Failed to parse "{}" in {}'.format(line, file_name))
return False
help_epilog = '''Production text substitutions:
\u0040EnumName\u0040 PrefixTheXEnum
\u0040enum_name\u0040 prefix_the_xenum
\u0040ENUMNAME\u0040 PREFIX_THE_XENUM
\u0040ENUMSHORT\u0040 THE_XENUM
\u0040ENUMPREFIX\u0040 PREFIX
\u0040enumsince\u0040 the user-provided since value given
\u0040VALUENAME\u0040 PREFIX_THE_XVALUE
\u0040valuenick\u0040 the-xvalue
\u0040valuenum\u0040 the integer value (limited support, Since: 2.26)
\u0040type\u0040 either enum or flags
\u0040Type\u0040 either Enum or Flags
\u0040TYPE\u0040 either ENUM or FLAGS
\u0040filename\u0040 name of current input file
\u0040basename\u0040 base name of the current input file (Since: 2.22)
'''
# production variables:
idprefix = "" # "G", "Gtk", etc
symprefix = "" # "g", "gtk", etc, if not just lc($idprefix)
fhead = "" # output file header
fprod = "" # per input file production
ftail = "" # output file trailer
eprod = "" # per enum text (produced prior to value itarations)
vhead = "" # value header, produced before iterating over enum values
vprod = "" # value text, produced for each enum value
vtail = "" # value tail, produced after iterating over enum values
comment_tmpl = "" # comment template
def read_template_file(file):
global idprefix, symprefix, fhead, fprod, ftail, eprod, vhead, vprod, vtail, comment_tmpl
tmpl = {'file-header': fhead,
'file-production': fprod,
'file-tail': ftail,
'enumeration-production': eprod,
'value-header': vhead,
'value-production': vprod,
'value-tail': vtail,
'comment': comment_tmpl,
}
in_ = 'junk'
ifile = io.open(file, encoding="utf-8", errors="replace_and_warn")
for line in ifile:
m = re.match(r'\/\*\*\*\s+(BEGIN|END)\s+([\w-]+)\s+\*\*\*\/', line)
if m:
if in_ == 'junk' and m.group(1) == 'BEGIN' and m.group(2) in tmpl:
in_ = m.group(2)
continue
elif in_ == m.group(2) and m.group(1) == 'END' and m.group(2) in tmpl:
in_ = 'junk'
continue
else:
sys.exit("Malformed template file " + file)
if in_ != 'junk':
tmpl[in_] += line
if in_ != 'junk':
sys.exit("Malformed template file " + file)
fhead = tmpl['file-header']
fprod = tmpl['file-production']
ftail = tmpl['file-tail']
eprod = tmpl['enumeration-production']
vhead = tmpl['value-header']
vprod = tmpl['value-production']
vtail = tmpl['value-tail']
comment_tmpl = tmpl['comment']
parser = argparse.ArgumentParser(epilog=help_epilog,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('--identifier-prefix', default='', dest='idprefix',
help='Identifier prefix')
parser.add_argument('--symbol-prefix', default='', dest='symprefix',
help='Symbol prefix')
parser.add_argument('--fhead', default=[], dest='fhead', action='append',
help='Output file header')
parser.add_argument('--ftail', default=[], dest='ftail', action='append',
help='Output file footer')
parser.add_argument('--fprod', default=[], dest='fprod', action='append',
help='Put out TEXT every time a new input file is being processed.')
parser.add_argument('--eprod', default=[], dest='eprod', action='append',
help='Per enum text, produced prior to value iterations')
parser.add_argument('--vhead', default=[], dest='vhead', action='append',
help='Value header, produced before iterating over enum values')
parser.add_argument('--vprod', default=[], dest='vprod', action='append',
help='Value text, produced for each enum value.')
parser.add_argument('--vtail', default=[], dest='vtail', action='append',
help='Value tail, produced after iterating over enum values')
parser.add_argument('--comments', default='', dest='comment_tmpl',
help='Comment structure')
parser.add_argument('--template', default='', dest='template',
help='Template file')
parser.add_argument('--output', default=None, dest='output')
parser.add_argument('--version', '-v', default=False, action='store_true', dest='version',
help='Print version information')
parser.add_argument('args', nargs='*',
help='One or more input files, or a single argument @rspfile_path '
'pointing to a file that contains the actual arguments')
# Support reading an rspfile of the form @filename which contains the args
# to be parsed
if len(sys.argv) == 2 and sys.argv[1].startswith('@'):
args = get_rspfile_args(sys.argv[1][1:])
else:
args = sys.argv[1:]
options = parser.parse_args(args)
if options.version:
print(VERSION_STR)
sys.exit(0)
def unescape_cmdline_args(arg):
arg = arg.replace('\\n', '\n')
arg = arg.replace('\\r', '\r')
return arg.replace('\\t', '\t')
if options.template != '':
read_template_file(options.template)
idprefix += options.idprefix
symprefix += options.symprefix
# This is a hack to maintain some semblance of backward compatibility with
# the old, Perl-based glib-mkenums. The old tool had an implicit ordering
# on the arguments and templates; each argument was parsed in order, and
# all the strings appended. This allowed developers to write:
#
# glib-mkenums \
# --fhead ... \
# --template a-template-file.c.in \
# --ftail ...
#
# And have the fhead be prepended to the file-head stanza in the template,
# as well as the ftail be appended to the file-tail stanza in the template.
# Short of throwing away ArgumentParser and going over sys.argv[] element
# by element, we can simulate that behaviour by ensuring some ordering in
# how we build the template strings:
#
# - the head stanzas are always prepended to the template
# - the prod stanzas are always appended to the template
# - the tail stanzas are always appended to the template
#
# Within each instance of the command line argument, we append each value
# to the array in the order in which it appears on the command line.
fhead = ''.join([unescape_cmdline_args(x) for x in options.fhead]) + fhead
vhead = ''.join([unescape_cmdline_args(x) for x in options.vhead]) + vhead
fprod += ''.join([unescape_cmdline_args(x) for x in options.fprod])
eprod += ''.join([unescape_cmdline_args(x) for x in options.eprod])
vprod += ''.join([unescape_cmdline_args(x) for x in options.vprod])
ftail = ftail + ''.join([unescape_cmdline_args(x) for x in options.ftail])
vtail = vtail + ''.join([unescape_cmdline_args(x) for x in options.vtail])
if options.comment_tmpl != '':
comment_tmpl = unescape_cmdline_args(options.comment_tmpl)
elif comment_tmpl == "":
# default to C-style comments
comment_tmpl = "/* \u0040comment\u0040 */"
output = options.output
if output is not None:
(out_dir, out_fn) = os.path.split(options.output)
out_suffix = '_' + os.path.splitext(out_fn)[1]
if out_dir == '':
out_dir = '.'
fd, filename = tempfile.mkstemp(dir=out_dir)
os.close(fd)
tmpfile = io.open(filename, "w", encoding="utf-8")
output_stream = tmpfile
else:
tmpfile = None
# put auto-generation comment
comment = comment_tmpl.replace('\u0040comment\u0040',
'This file is generated by glib-mkenums, do '
'not modify it. This code is licensed under '
'the same license as the containing project. '
'Note that it links to GLib, so must comply '
'with the LGPL linking clauses.')
write_output("\n" + comment + '\n')
def replace_specials(prod):
prod = prod.replace(r'\\a', r'\a')
prod = prod.replace(r'\\b', r'\b')
prod = prod.replace(r'\\t', r'\t')
prod = prod.replace(r'\\n', r'\n')
prod = prod.replace(r'\\f', r'\f')
prod = prod.replace(r'\\r', r'\r')
prod = prod.rstrip()
return prod
def warn_if_filename_basename_used(section, prod):
for substitution in ('\u0040filename\u0040',
'\u0040basename\u0040'):
if substitution in prod:
print_warning('{} used in {} section.'.format(substitution,
section))
if len(fhead) > 0:
prod = fhead
warn_if_filename_basename_used('file-header', prod)
prod = replace_specials(prod)
write_output(prod)
def process_file(curfilename):
global entries, flags, seenbitshift, seenprivate, enum_prefix
firstenum = True
try:
curfile = io.open(curfilename, encoding="utf-8",
errors="replace_and_warn")
except IOError as e:
if e.errno == errno.ENOENT:
print_warning('No file "{}" found.'.format(curfilename))
return
raise
while True:
line = curfile.readline()
if not line:
break
line = line.strip()
# read lines until we have no open comments
while re.search(r'/\*([^*]|\*(?!/))*$', line):
line += curfile.readline()
# strip comments w/o options
line = re.sub(r'''/\*(?!<)
([^*]+|\*(?!/))*
\*/''', '', line)
# ignore forward declarations
if re.match(r'\s*typedef\s+enum.*;', line):
continue
m = re.match(r'''\s*typedef\s+enum\s*[_A-Za-z]*[_A-Za-z0-9]*\s*
({)?\s*
(?:/\*<
(([^*]|\*(?!/))*)
>\s*\*/)?
\s*({)?''', line, flags=re.X)
if m:
groups = m.groups()
if len(groups) >= 2 and groups[1] is not None:
options = parse_trigraph(groups[1])
if 'skip' in options:
continue
enum_prefix = options.get('prefix', None)
flags = options.get('flags', None)
if 'flags' in options:
if flags is None:
flags = 1
else:
flags = int(flags)
option_lowercase_name = options.get('lowercase_name', None)
option_underscore_name = options.get('underscore_name', None)
option_since = options.get('since', None)
else:
enum_prefix = None
flags = None
option_lowercase_name = None
option_underscore_name = None
option_since = None
if option_lowercase_name is not None:
if option_underscore_name is not None:
print_warning("lowercase_name overridden with underscore_name")
option_lowercase_name = None
else:
print_warning("lowercase_name is deprecated, use underscore_name")
# Didn't have trailing '{' look on next lines
if groups[0] is None and (len(groups) < 4 or groups[3] is None):
while True:
line = curfile.readline()
if not line:
print_error("Syntax error when looking for opening { in enum")
if re.match(r'\s*\{', line):
break
seenbitshift = 0
seenprivate = False
entries = []
# Now parse the entries
parse_entries(curfile, curfilename)
# figure out if this was a flags or enums enumeration
if flags is None:
flags = seenbitshift
# Autogenerate a prefix
if enum_prefix is None:
for entry in entries:
if len(entry) < 3 or entry[2] is None:
name = entry[0]
if enum_prefix is not None:
enum_prefix = os.path.commonprefix([name, enum_prefix])
else:
enum_prefix = name
if enum_prefix is None:
enum_prefix = ""
else:
# Trim so that it ends in an underscore
enum_prefix = re.sub(r'_[^_]*$', '_', enum_prefix)
else:
# canonicalize user defined prefixes
enum_prefix = enum_prefix.upper()
enum_prefix = enum_prefix.replace('-', '_')
enum_prefix = re.sub(r'(.*)([^_])$', r'\1\2_', enum_prefix)
fixed_entries = []
for e in entries:
name = e[0]
num = e[1]
if len(e) < 3 or e[2] is None:
nick = re.sub(r'^' + enum_prefix, '', name)
nick = nick.replace('_', '-').lower()
e = (name, num, nick)
fixed_entries.append(e)
entries = fixed_entries
# Spit out the output
if option_underscore_name is not None:
enumlong = option_underscore_name.upper()
enumsym = option_underscore_name.lower()
enumshort = re.sub(r'^[A-Z][A-Z0-9]*_', '', enumlong)
enumname_prefix = re.sub('_' + enumshort + '$', '', enumlong)
elif symprefix == '' and idprefix == '':
# enumname is e.g. GMatchType
enspace = re.sub(r'^([A-Z][a-z]*).*$', r'\1', enumname)
enumshort = re.sub(r'^[A-Z][a-z]*', '', enumname)
enumshort = re.sub(r'([^A-Z])([A-Z])', r'\1_\2', enumshort)
enumshort = re.sub(r'([A-Z][A-Z])([A-Z][0-9a-z])', r'\1_\2', enumshort)
enumshort = enumshort.upper()
enumname_prefix = re.sub(r'^([A-Z][a-z]*).*$', r'\1', enumname).upper()
enumlong = enspace.upper() + "_" + enumshort
enumsym = enspace.lower() + "_" + enumshort.lower()
if option_lowercase_name is not None:
enumsym = option_lowercase_name
else:
enumshort = enumname
if idprefix:
enumshort = re.sub(r'^' + idprefix, '', enumshort)
else:
enumshort = re.sub(r'/^[A-Z][a-z]*', '', enumshort)
enumshort = re.sub(r'([^A-Z])([A-Z])', r'\1_\2', enumshort)
enumshort = re.sub(r'([A-Z][A-Z])([A-Z][0-9a-z])', r'\1_\2', enumshort)
enumshort = enumshort.upper()
if symprefix:
enumname_prefix = symprefix.upper()
else:
enumname_prefix = idprefix.upper()
enumlong = enumname_prefix + "_" + enumshort
enumsym = enumlong.lower()
if option_since is not None:
enumsince = option_since
else:
enumsince = ""
if firstenum:
firstenum = False
if len(fprod) > 0:
prod = fprod
base = os.path.basename(curfilename)
prod = prod.replace('\u0040filename\u0040', curfilename)
prod = prod.replace('\u0040basename\u0040', base)
prod = replace_specials(prod)
write_output(prod)
if len(eprod) > 0:
prod = eprod
prod = prod.replace('\u0040enum_name\u0040', enumsym)
prod = prod.replace('\u0040EnumName\u0040', enumname)
prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort)
prod = prod.replace('\u0040ENUMNAME\u0040', enumlong)
prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix)
prod = prod.replace('\u0040enumsince\u0040', enumsince)
if flags:
prod = prod.replace('\u0040type\u0040', 'flags')
else:
prod = prod.replace('\u0040type\u0040', 'enum')
if flags:
prod = prod.replace('\u0040Type\u0040', 'Flags')
else:
prod = prod.replace('\u0040Type\u0040', 'Enum')
if flags:
prod = prod.replace('\u0040TYPE\u0040', 'FLAGS')
else:
prod = prod.replace('\u0040TYPE\u0040', 'ENUM')
prod = replace_specials(prod)
write_output(prod)
if len(vhead) > 0:
prod = vhead
prod = prod.replace('\u0040enum_name\u0040', enumsym)
prod = prod.replace('\u0040EnumName\u0040', enumname)
prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort)
prod = prod.replace('\u0040ENUMNAME\u0040', enumlong)
prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix)
prod = prod.replace('\u0040enumsince\u0040', enumsince)
if flags:
prod = prod.replace('\u0040type\u0040', 'flags')
else:
prod = prod.replace('\u0040type\u0040', 'enum')
if flags:
prod = prod.replace('\u0040Type\u0040', 'Flags')
else:
prod = prod.replace('\u0040Type\u0040', 'Enum')
if flags:
prod = prod.replace('\u0040TYPE\u0040', 'FLAGS')
else:
prod = prod.replace('\u0040TYPE\u0040', 'ENUM')
prod = replace_specials(prod)
write_output(prod)
if len(vprod) > 0:
prod = vprod
next_num = 0
prod = replace_specials(prod)
for name, num, nick in entries:
tmp_prod = prod
if '\u0040valuenum\u0040' in prod:
# only attempt to eval the value if it is requested
# this prevents us from throwing errors otherwise
if num is not None:
# use sandboxed evaluation as a reasonable
# approximation to C constant folding
inum = eval(num, {}, {})
# make sure it parsed to an integer
if not isinstance(inum, int):
sys.exit("Unable to parse enum value '%s'" % num)
num = inum
else:
num = next_num
tmp_prod = tmp_prod.replace('\u0040valuenum\u0040', str(num))
next_num = int(num) + 1
tmp_prod = tmp_prod.replace('\u0040VALUENAME\u0040', name)
tmp_prod = tmp_prod.replace('\u0040valuenick\u0040', nick)
if flags:
tmp_prod = tmp_prod.replace('\u0040type\u0040', 'flags')
else:
tmp_prod = tmp_prod.replace('\u0040type\u0040', 'enum')
if flags:
tmp_prod = tmp_prod.replace('\u0040Type\u0040', 'Flags')
else:
tmp_prod = tmp_prod.replace('\u0040Type\u0040', 'Enum')
if flags:
tmp_prod = tmp_prod.replace('\u0040TYPE\u0040', 'FLAGS')
else:
tmp_prod = tmp_prod.replace('\u0040TYPE\u0040', 'ENUM')
tmp_prod = tmp_prod.rstrip()
write_output(tmp_prod)
if len(vtail) > 0:
prod = vtail
prod = prod.replace('\u0040enum_name\u0040', enumsym)
prod = prod.replace('\u0040EnumName\u0040', enumname)
prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort)
prod = prod.replace('\u0040ENUMNAME\u0040', enumlong)
prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix)
prod = prod.replace('\u0040enumsince\u0040', enumsince)
if flags:
prod = prod.replace('\u0040type\u0040', 'flags')
else:
prod = prod.replace('\u0040type\u0040', 'enum')
if flags:
prod = prod.replace('\u0040Type\u0040', 'Flags')
else:
prod = prod.replace('\u0040Type\u0040', 'Enum')
if flags:
prod = prod.replace('\u0040TYPE\u0040', 'FLAGS')
else:
prod = prod.replace('\u0040TYPE\u0040', 'ENUM')
prod = replace_specials(prod)
write_output(prod)
for fname in sorted(options.args):
process_file(fname)
if len(ftail) > 0:
prod = ftail
warn_if_filename_basename_used('file-tail', prod)
prod = replace_specials(prod)
write_output(prod)
# put auto-generation comment
comment = comment_tmpl
comment = comment.replace('\u0040comment\u0040', 'Generated data ends here')
write_output("\n" + comment + "\n")
if tmpfile is not None:
tmpfilename = tmpfile.name
tmpfile.close()
try:
os.unlink(options.output)
except OSError as error:
if error.errno != errno.ENOENT:
raise error
os.rename(tmpfilename, options.output)

395
gobject/glib-types.h Normal file
View file

@ -0,0 +1,395 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2000-2001 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GLIB_TYPES_H__
#define __GLIB_TYPES_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION) && !defined(GLIB_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <glib.h>
G_BEGIN_DECLS
/* A hack necesssary to preprocess this file with g-ir-scanner */
#ifdef __GI_SCANNER__
typedef gsize GType;
#endif
/* --- GLib boxed types --- */
/**
* G_TYPE_DATE:
*
* The #GType for #GDate.
*/
#define G_TYPE_DATE (g_date_get_type ())
/**
* G_TYPE_STRV:
*
* The #GType for a boxed type holding a %NULL-terminated array of strings.
*
* The code fragments in the following example show the use of a property of
* type %G_TYPE_STRV with g_object_class_install_property(), g_object_set()
* and g_object_get().
*
* |[
* g_object_class_install_property (object_class,
* PROP_AUTHORS,
* g_param_spec_boxed ("authors",
* _("Authors"),
* _("List of authors"),
* G_TYPE_STRV,
* G_PARAM_READWRITE));
*
* gchar *authors[] = { "Owen", "Tim", NULL };
* g_object_set (obj, "authors", authors, NULL);
*
* gchar *writers[];
* g_object_get (obj, "authors", &writers, NULL);
* /&ast; do something with writers &ast;/
* g_strfreev (writers);
* ]|
*
* Since: 2.4
*/
#define G_TYPE_STRV (g_strv_get_type ())
/**
* G_TYPE_GSTRING:
*
* The #GType for #GString.
*/
#define G_TYPE_GSTRING (g_gstring_get_type ())
/**
* G_TYPE_HASH_TABLE:
*
* The #GType for a boxed type holding a #GHashTable reference.
*
* Since: 2.10
*/
#define G_TYPE_HASH_TABLE (g_hash_table_get_type ())
/**
* G_TYPE_REGEX:
*
* The #GType for a boxed type holding a #GRegex reference.
*
* Since: 2.14
*/
#define G_TYPE_REGEX (g_regex_get_type ())
/**
* G_TYPE_MATCH_INFO:
*
* The #GType for a boxed type holding a #GMatchInfo reference.
*
* Since: 2.30
*/
#define G_TYPE_MATCH_INFO (g_match_info_get_type ())
/**
* G_TYPE_ARRAY:
*
* The #GType for a boxed type holding a #GArray reference.
*
* Since: 2.22
*/
#define G_TYPE_ARRAY (g_array_get_type ())
/**
* G_TYPE_BYTE_ARRAY:
*
* The #GType for a boxed type holding a #GByteArray reference.
*
* Since: 2.22
*/
#define G_TYPE_BYTE_ARRAY (g_byte_array_get_type ())
/**
* G_TYPE_PTR_ARRAY:
*
* The #GType for a boxed type holding a #GPtrArray reference.
*
* Since: 2.22
*/
#define G_TYPE_PTR_ARRAY (g_ptr_array_get_type ())
/**
* G_TYPE_BYTES:
*
* The #GType for #GBytes.
*
* Since: 2.32
*/
#define G_TYPE_BYTES (g_bytes_get_type ())
/**
* G_TYPE_VARIANT_TYPE:
*
* The #GType for a boxed type holding a #GVariantType.
*
* Since: 2.24
*/
#define G_TYPE_VARIANT_TYPE (g_variant_type_get_gtype ())
/**
* G_TYPE_ERROR:
*
* The #GType for a boxed type holding a #GError.
*
* Since: 2.26
*/
#define G_TYPE_ERROR (g_error_get_type ())
/**
* G_TYPE_DATE_TIME:
*
* The #GType for a boxed type holding a #GDateTime.
*
* Since: 2.26
*/
#define G_TYPE_DATE_TIME (g_date_time_get_type ())
/**
* G_TYPE_TIME_ZONE:
*
* The #GType for a boxed type holding a #GTimeZone.
*
* Since: 2.34
*/
#define G_TYPE_TIME_ZONE (g_time_zone_get_type ())
/**
* G_TYPE_IO_CHANNEL:
*
* The #GType for #GIOChannel.
*/
#define G_TYPE_IO_CHANNEL (g_io_channel_get_type ())
/**
* G_TYPE_IO_CONDITION:
*
* The #GType for #GIOCondition.
*/
#define G_TYPE_IO_CONDITION (g_io_condition_get_type ())
/**
* G_TYPE_VARIANT_BUILDER:
*
* The #GType for a boxed type holding a #GVariantBuilder.
*
* Since: 2.30
*/
#define G_TYPE_VARIANT_BUILDER (g_variant_builder_get_type ())
/**
* G_TYPE_VARIANT_DICT:
*
* The #GType for a boxed type holding a #GVariantDict.
*
* Since: 2.40
*/
#define G_TYPE_VARIANT_DICT (g_variant_dict_get_type ())
/**
* G_TYPE_MAIN_LOOP:
*
* The #GType for a boxed type holding a #GMainLoop.
*
* Since: 2.30
*/
#define G_TYPE_MAIN_LOOP (g_main_loop_get_type ())
/**
* G_TYPE_MAIN_CONTEXT:
*
* The #GType for a boxed type holding a #GMainContext.
*
* Since: 2.30
*/
#define G_TYPE_MAIN_CONTEXT (g_main_context_get_type ())
/**
* G_TYPE_SOURCE:
*
* The #GType for a boxed type holding a #GSource.
*
* Since: 2.30
*/
#define G_TYPE_SOURCE (g_source_get_type ())
/**
* G_TYPE_POLLFD:
*
* The #GType for a boxed type holding a #GPollFD.
*
* Since: 2.36
*/
#define G_TYPE_POLLFD (g_pollfd_get_type ())
/**
* G_TYPE_MARKUP_PARSE_CONTEXT:
*
* The #GType for a boxed type holding a #GMarkupParseContext.
*
* Since: 2.36
*/
#define G_TYPE_MARKUP_PARSE_CONTEXT (g_markup_parse_context_get_type ())
/**
* G_TYPE_KEY_FILE:
*
* The #GType for a boxed type holding a #GKeyFile.
*
* Since: 2.32
*/
#define G_TYPE_KEY_FILE (g_key_file_get_type ())
/**
* G_TYPE_MAPPED_FILE:
*
* The #GType for a boxed type holding a #GMappedFile.
*
* Since: 2.40
*/
#define G_TYPE_MAPPED_FILE (g_mapped_file_get_type ())
/**
* G_TYPE_THREAD:
*
* The #GType for a boxed type holding a #GThread.
*
* Since: 2.36
*/
#define G_TYPE_THREAD (g_thread_get_type ())
/**
* G_TYPE_CHECKSUM:
*
* The #GType for a boxed type holding a #GChecksum.
*
* Since: 2.36
*/
#define G_TYPE_CHECKSUM (g_checksum_get_type ())
/**
* G_TYPE_OPTION_GROUP:
*
* The #GType for a boxed type holding a #GOptionGroup.
*
* Since: 2.44
*/
#define G_TYPE_OPTION_GROUP (g_option_group_get_type ())
/**
* G_TYPE_URI:
*
* The #GType for a boxed type holding a #GUri.
*
* Since: 2.66
*/
#define G_TYPE_URI (g_uri_get_type ())
/**
* G_TYPE_TREE:
*
* The #GType for #GTree.
*
* Since: 2.68
*/
#define G_TYPE_TREE (g_tree_get_type ())
/**
* G_TYPE_PATTERN_SPEC:
*
* The #GType for #GPatternSpec.
*
* Since: 2.70
*/
#define G_TYPE_PATTERN_SPEC (g_pattern_spec_get_type ())
GLIB_AVAILABLE_IN_ALL
GType g_date_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_strv_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_gstring_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_hash_table_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_array_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_byte_array_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_ptr_array_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_bytes_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_variant_type_get_gtype (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_regex_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_30
GType g_match_info_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_error_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_date_time_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_time_zone_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_io_channel_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_io_condition_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_variant_builder_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_40
GType g_variant_dict_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GType g_key_file_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_30
GType g_main_loop_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_30
GType g_main_context_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_30
GType g_source_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_36
GType g_pollfd_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_36
GType g_thread_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_36
GType g_checksum_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_36
GType g_markup_parse_context_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_40
GType g_mapped_file_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_44
GType g_option_group_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_66
GType g_uri_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_68
GType g_tree_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_70
GType g_pattern_spec_get_type (void) G_GNUC_CONST;
GLIB_DEPRECATED_FOR('G_TYPE_VARIANT')
GType g_variant_get_gtype (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __GLIB_TYPES_H__ */

2521
gobject/gmarshal.c Normal file

File diff suppressed because it is too large Load diff

434
gobject/gmarshal.h Normal file
View file

@ -0,0 +1,434 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __G_MARSHAL_H__
#define __G_MARSHAL_H__
G_BEGIN_DECLS
/* VOID:VOID */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__VOID (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__VOIDv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:BOOLEAN */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__BOOLEAN (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__BOOLEANv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:CHAR */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__CHAR (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__CHARv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:UCHAR */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__UCHAR (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__UCHARv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:INT */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__INT (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__INTv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:UINT */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__UINT (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__UINTv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:LONG */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__LONG (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__LONGv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:ULONG */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__ULONG (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__ULONGv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:ENUM */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__ENUM (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__ENUMv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:FLAGS */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__FLAGS (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__FLAGSv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:FLOAT */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__FLOAT (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__FLOATv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:DOUBLE */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__DOUBLE (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__DOUBLEv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:STRING */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__STRING (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__STRINGv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:PARAM */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__PARAM (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__PARAMv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:BOXED */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__BOXED (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__BOXEDv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:POINTER */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__POINTER (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__POINTERv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:OBJECT */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__OBJECT (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__OBJECTv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:VARIANT */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__VARIANT (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__VARIANTv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* VOID:UINT,POINTER */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__UINT_POINTER (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_VOID__UINT_POINTERv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* BOOL:FLAGS */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_BOOLEAN__FLAGS (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_BOOLEAN__FLAGSv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/**
* g_cclosure_marshal_BOOL__FLAGS:
* @closure: A #GClosure.
* @return_value: A #GValue to store the return value. May be %NULL
* if the callback of closure doesn't return a value.
* @n_param_values: The length of the @param_values array.
* @param_values: An array of #GValues holding the arguments
* on which to invoke the callback of closure.
* @invocation_hint: The invocation hint given as the last argument to
* g_closure_invoke().
* @marshal_data: Additional data specified when registering the
* marshaller, see g_closure_set_marshal() and
* g_closure_set_meta_marshal()
*
* An old alias for g_cclosure_marshal_BOOLEAN__FLAGS().
*/
#define g_cclosure_marshal_BOOL__FLAGS g_cclosure_marshal_BOOLEAN__FLAGS
/* STRING:OBJECT,POINTER */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_STRING__OBJECT_POINTER (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_STRING__OBJECT_POINTERv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/* BOOL:BOXED,BOXED */
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_BOOLEAN__BOXED_BOXED (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
GLIB_AVAILABLE_IN_ALL
void g_cclosure_marshal_BOOLEAN__BOXED_BOXEDv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/**
* g_cclosure_marshal_BOOL__BOXED_BOXED:
* @closure: A #GClosure.
* @return_value: A #GValue to store the return value. May be %NULL
* if the callback of closure doesn't return a value.
* @n_param_values: The length of the @param_values array.
* @param_values: An array of #GValues holding the arguments
* on which to invoke the callback of closure.
* @invocation_hint: The invocation hint given as the last argument to
* g_closure_invoke().
* @marshal_data: Additional data specified when registering the
* marshaller, see g_closure_set_marshal() and
* g_closure_set_meta_marshal()
*
* An old alias for g_cclosure_marshal_BOOLEAN__BOXED_BOXED().
*/
#define g_cclosure_marshal_BOOL__BOXED_BOXED g_cclosure_marshal_BOOLEAN__BOXED_BOXED
G_END_DECLS
#endif /* __G_MARSHAL_H__ */

View file

@ -0,0 +1,31 @@
/*
* Copyright © 2015 Canonical Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Ryan Lortie <desrt@desrt.ca>
*/
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GClosure, g_closure_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GEnumClass, g_type_class_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFlagsClass, g_type_class_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GObject, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GInitiallyUnowned, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GParamSpec, g_param_spec_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTypeClass, g_type_class_unref)
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GValue, g_value_unset)

223
gobject/gobject-query.c Normal file
View file

@ -0,0 +1,223 @@
/* 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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <glib-object.h>
#include <glib/gprintf.h>
static gchar *indent_inc = NULL;
static guint spacing = 1;
static FILE *f_out = NULL;
static GType root = 0;
static gboolean recursion = TRUE;
#if 0
# define O_SPACE "\\as"
# define O_ESPACE " "
# define O_BRANCH "\\aE"
# define O_VLINE "\\al"
# define O_LLEAF "\\aL"
# define O_KEY_FILL "_"
#else
# define O_SPACE " "
# define O_ESPACE ""
# define O_BRANCH "+"
# define O_VLINE "|"
# define O_LLEAF "`"
# define O_KEY_FILL "_"
#endif
static void
show_nodes (GType type,
GType sibling,
const gchar *indent)
{
GType *children;
guint i;
if (!type)
return;
children = g_type_children (type, NULL);
if (type != root)
for (i = 0; i < spacing; i++)
g_fprintf (f_out, "%s%s\n", indent, O_VLINE);
g_fprintf (f_out, "%s%s%s%s",
indent,
sibling ? O_BRANCH : (type != root ? O_LLEAF : O_SPACE),
O_ESPACE,
g_type_name (type));
for (i = strlen (g_type_name (type)); i <= strlen (indent_inc); i++)
fputs (O_KEY_FILL, f_out);
fputc ('\n', f_out);
if (children && recursion)
{
gchar *new_indent;
GType *child;
if (sibling)
new_indent = g_strconcat (indent, O_VLINE, indent_inc, NULL);
else
new_indent = g_strconcat (indent, O_SPACE, indent_inc, NULL);
for (child = children; *child; child++)
show_nodes (child[0], child[1], new_indent);
g_free (new_indent);
}
g_free (children);
}
static gint
help (gchar *arg)
{
g_fprintf (stderr, "usage: gobject-query <qualifier> [-r <type>] [-{i|b} \"\"] [-s #] [-{h|x|y}]\n");
g_fprintf (stderr, " -r specify root type\n");
g_fprintf (stderr, " -n don't descend type tree\n");
g_fprintf (stderr, " -h guess what ;)\n");
g_fprintf (stderr, " -b specify indent string\n");
g_fprintf (stderr, " -i specify incremental indent string\n");
g_fprintf (stderr, " -s specify line spacing\n");
g_fprintf (stderr, "qualifiers:\n");
g_fprintf (stderr, " froots iterate over fundamental roots\n");
g_fprintf (stderr, " tree print type tree\n");
return arg != NULL;
}
int
main (gint argc,
gchar *argv[])
{
GLogLevelFlags fatal_mask;
gboolean gen_froots = 0;
gboolean gen_tree = 0;
gint i;
const gchar *iindent = "";
f_out = stdout;
fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
g_log_set_always_fatal (fatal_mask);
root = G_TYPE_OBJECT;
for (i = 1; i < argc; i++)
{
if (strcmp ("-s", argv[i]) == 0)
{
i++;
if (i < argc)
spacing = atoi (argv[i]);
}
else if (strcmp ("-i", argv[i]) == 0)
{
i++;
if (i < argc)
{
char *p;
guint n;
p = argv[i];
while (*p)
p++;
n = p - argv[i];
indent_inc = g_new (gchar, n * strlen (O_SPACE) + 1);
*indent_inc = 0;
while (n)
{
n--;
strcpy (indent_inc, O_SPACE);
}
}
}
else if (strcmp ("-b", argv[i]) == 0)
{
i++;
if (i < argc)
iindent = argv[i];
}
else if (strcmp ("-r", argv[i]) == 0)
{
i++;
if (i < argc)
root = g_type_from_name (argv[i]);
}
else if (strcmp ("-n", argv[i]) == 0)
{
recursion = FALSE;
}
else if (strcmp ("froots", argv[i]) == 0)
{
gen_froots = 1;
}
else if (strcmp ("tree", argv[i]) == 0)
{
gen_tree = 1;
}
else if (strcmp ("-h", argv[i]) == 0)
{
return help (NULL);
}
else if (strcmp ("--help", argv[i]) == 0)
{
return help (NULL);
}
else
return help (argv[i]);
}
if (!gen_froots && !gen_tree)
return help ((argc > 0) ? argv[i-1] : NULL);
if (!indent_inc)
{
indent_inc = g_new (gchar, strlen (O_SPACE) + 1);
*indent_inc = 0;
strcpy (indent_inc, O_SPACE);
}
if (gen_tree)
show_nodes (root, 0, iindent);
if (gen_froots)
{
root = ~0;
for (i = 0; i <= G_TYPE_FUNDAMENTAL_MAX; i += G_TYPE_MAKE_FUNDAMENTAL (1))
{
const gchar *name = g_type_name (i);
if (name)
show_nodes (i, 0, iindent);
}
}
return 0;
}

4871
gobject/gobject.c Normal file

File diff suppressed because it is too large Load diff

946
gobject/gobject.h Normal file
View file

@ -0,0 +1,946 @@
/* 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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __G_OBJECT_H__
#define __G_OBJECT_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <gobject/gtype.h>
#include <gobject/gvalue.h>
#include <gobject/gparam.h>
#include <gobject/gclosure.h>
#include <gobject/gsignal.h>
#include <gobject/gboxed.h>
G_BEGIN_DECLS
/* --- type macros --- */
/**
* G_TYPE_IS_OBJECT:
* @type: Type id to check
*
* Check if the passed in type id is a %G_TYPE_OBJECT or derived from it.
*
* Returns: %FALSE or %TRUE, indicating whether @type is a %G_TYPE_OBJECT.
*/
#define G_TYPE_IS_OBJECT(type) (G_TYPE_FUNDAMENTAL (type) == G_TYPE_OBJECT)
/**
* G_OBJECT:
* @object: Object which is subject to casting.
*
* Casts a #GObject or derived pointer into a (GObject*) pointer.
*
* Depending on the current debugging level, this function may invoke
* certain runtime checks to identify invalid casts.
*/
#define G_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_OBJECT, GObject))
/**
* G_OBJECT_CLASS:
* @class: a valid #GObjectClass
*
* Casts a derived #GObjectClass structure into a #GObjectClass structure.
*/
#define G_OBJECT_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_OBJECT, GObjectClass))
/**
* G_IS_OBJECT:
* @object: Instance to check for being a %G_TYPE_OBJECT.
*
* Checks whether a valid #GTypeInstance pointer is of type %G_TYPE_OBJECT.
*/
#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_42
#define G_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_FUNDAMENTAL_TYPE ((object), G_TYPE_OBJECT))
#else
#define G_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_OBJECT))
#endif
/**
* G_IS_OBJECT_CLASS:
* @class: a #GObjectClass
*
* Checks whether @class "is a" valid #GObjectClass structure of type
* %G_TYPE_OBJECT or derived.
*/
#define G_IS_OBJECT_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_OBJECT))
/**
* G_OBJECT_GET_CLASS:
* @object: a #GObject instance.
*
* Get the class structure associated to a #GObject instance.
*
* Returns: pointer to object class structure.
*/
#define G_OBJECT_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), G_TYPE_OBJECT, GObjectClass))
/**
* G_OBJECT_TYPE:
* @object: Object to return the type id for.
*
* Get the type id of an object.
*
* Returns: Type id of @object.
*/
#define G_OBJECT_TYPE(object) (G_TYPE_FROM_INSTANCE (object))
/**
* G_OBJECT_TYPE_NAME:
* @object: Object to return the type name for.
*
* Get the name of an object's type.
*
* Returns: Type name of @object. The string is owned by the type system and
* should not be freed.
*/
#define G_OBJECT_TYPE_NAME(object) (g_type_name (G_OBJECT_TYPE (object)))
/**
* G_OBJECT_CLASS_TYPE:
* @class: a valid #GObjectClass
*
* Get the type id of a class structure.
*
* Returns: Type id of @class.
*/
#define G_OBJECT_CLASS_TYPE(class) (G_TYPE_FROM_CLASS (class))
/**
* G_OBJECT_CLASS_NAME:
* @class: a valid #GObjectClass
*
* Return the name of a class structure's type.
*
* Returns: Type name of @class. The string is owned by the type system and
* should not be freed.
*/
#define G_OBJECT_CLASS_NAME(class) (g_type_name (G_OBJECT_CLASS_TYPE (class)))
/**
* G_VALUE_HOLDS_OBJECT:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values derived from type %G_TYPE_OBJECT.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_OBJECT(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_OBJECT))
/* --- type macros --- */
/**
* G_TYPE_INITIALLY_UNOWNED:
*
* The type for #GInitiallyUnowned.
*/
#define G_TYPE_INITIALLY_UNOWNED (g_initially_unowned_get_type())
/**
* G_INITIALLY_UNOWNED:
* @object: Object which is subject to casting.
*
* Casts a #GInitiallyUnowned or derived pointer into a (GInitiallyUnowned*)
* pointer.
*
* Depending on the current debugging level, this function may invoke
* certain runtime checks to identify invalid casts.
*/
#define G_INITIALLY_UNOWNED(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_INITIALLY_UNOWNED, GInitiallyUnowned))
/**
* G_INITIALLY_UNOWNED_CLASS:
* @class: a valid #GInitiallyUnownedClass
*
* Casts a derived #GInitiallyUnownedClass structure into a
* #GInitiallyUnownedClass structure.
*/
#define G_INITIALLY_UNOWNED_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_INITIALLY_UNOWNED, GInitiallyUnownedClass))
/**
* G_IS_INITIALLY_UNOWNED:
* @object: Instance to check for being a %G_TYPE_INITIALLY_UNOWNED.
*
* Checks whether a valid #GTypeInstance pointer is of type %G_TYPE_INITIALLY_UNOWNED.
*/
#define G_IS_INITIALLY_UNOWNED(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_INITIALLY_UNOWNED))
/**
* G_IS_INITIALLY_UNOWNED_CLASS:
* @class: a #GInitiallyUnownedClass
*
* Checks whether @class "is a" valid #GInitiallyUnownedClass structure of type
* %G_TYPE_INITIALLY_UNOWNED or derived.
*/
#define G_IS_INITIALLY_UNOWNED_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_INITIALLY_UNOWNED))
/**
* G_INITIALLY_UNOWNED_GET_CLASS:
* @object: a #GInitiallyUnowned instance.
*
* Get the class structure associated to a #GInitiallyUnowned instance.
*
* Returns: pointer to object class structure.
*/
#define G_INITIALLY_UNOWNED_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), G_TYPE_INITIALLY_UNOWNED, GInitiallyUnownedClass))
/* GInitiallyUnowned ia a GObject with initially floating reference count */
/* --- typedefs & structures --- */
typedef struct _GObject GObject;
typedef struct _GObjectClass GObjectClass;
typedef struct _GObject GInitiallyUnowned;
typedef struct _GObjectClass GInitiallyUnownedClass;
typedef struct _GObjectConstructParam GObjectConstructParam;
/**
* GObjectGetPropertyFunc:
* @object: a #GObject
* @property_id: the numeric id under which the property was registered with
* g_object_class_install_property().
* @value: a #GValue to return the property value in
* @pspec: the #GParamSpec describing the property
*
* The type of the @get_property function of #GObjectClass.
*/
typedef void (*GObjectGetPropertyFunc) (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
/**
* GObjectSetPropertyFunc:
* @object: a #GObject
* @property_id: the numeric id under which the property was registered with
* g_object_class_install_property().
* @value: the new value for the property
* @pspec: the #GParamSpec describing the property
*
* The type of the @set_property function of #GObjectClass.
*/
typedef void (*GObjectSetPropertyFunc) (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
/**
* GObjectFinalizeFunc:
* @object: the #GObject being finalized
*
* The type of the @finalize function of #GObjectClass.
*/
typedef void (*GObjectFinalizeFunc) (GObject *object);
/**
* GWeakNotify:
* @data: data that was provided when the weak reference was established
* @where_the_object_was: the object being disposed
*
* A #GWeakNotify function can be added to an object as a callback that gets
* triggered when the object is finalized.
*
* Since the object is already being disposed when the #GWeakNotify is called,
* there's not much you could do with the object, apart from e.g. using its
* address as hash-index or the like.
*
* In particular, this means its invalid to call g_object_ref(),
* g_weak_ref_init(), g_weak_ref_set(), g_object_add_toggle_ref(),
* g_object_weak_ref(), g_object_add_weak_pointer() or any function which calls
* them on the object from this callback.
*/
typedef void (*GWeakNotify) (gpointer data,
GObject *where_the_object_was);
/**
* GObject:
*
* The base object type.
*
* All the fields in the `GObject` structure are private to the implementation
* and should never be accessed directly.
*
* Since GLib 2.72, all #GObjects are guaranteed to be aligned to at least the
* alignment of the largest basic GLib type (typically this is #guint64 or
* #gdouble). If you need larger alignment for an element in a #GObject, you
* should allocate it on the heap (aligned), or arrange for your #GObject to be
* appropriately padded. This guarantee applies to the #GObject (or derived)
* struct, the #GObjectClass (or derived) struct, and any private data allocated
* by G_ADD_PRIVATE().
*/
struct _GObject
{
GTypeInstance g_type_instance;
/*< private >*/
guint ref_count; /* (atomic) */
GData *qdata;
};
/**
* GObjectClass:
* @g_type_class: the parent class
* @constructor: the @constructor function is called by g_object_new () to
* complete the object initialization after all the construction properties are
* set. The first thing a @constructor implementation must do is chain up to the
* @constructor of the parent class. Overriding @constructor should be rarely
* needed, e.g. to handle construct properties, or to implement singletons.
* @set_property: the generic setter for all properties of this type. Should be
* overridden for every type with properties. If implementations of
* @set_property don't emit property change notification explicitly, this will
* be done implicitly by the type system. However, if the notify signal is
* emitted explicitly, the type system will not emit it a second time.
* @get_property: the generic getter for all properties of this type. Should be
* overridden for every type with properties.
* @dispose: the @dispose function is supposed to drop all references to other
* objects, but keep the instance otherwise intact, so that client method
* invocations still work. It may be run multiple times (due to reference
* loops). Before returning, @dispose should chain up to the @dispose method
* of the parent class.
* @finalize: instance finalization function, should finish the finalization of
* the instance begun in @dispose and chain up to the @finalize method of the
* parent class.
* @dispatch_properties_changed: emits property change notification for a bunch
* of properties. Overriding @dispatch_properties_changed should be rarely
* needed.
* @notify: the class closure for the notify signal
* @constructed: the @constructed function is called by g_object_new() as the
* final step of the object creation process. At the point of the call, all
* construction properties have been set on the object. The purpose of this
* call is to allow for object initialisation steps that can only be performed
* after construction properties have been set. @constructed implementors
* should chain up to the @constructed call of their parent class to allow it
* to complete its initialisation.
*
* The class structure for the GObject type.
*
* |[<!-- language="C" -->
* // Example of implementing a singleton using a constructor.
* static MySingleton *the_singleton = NULL;
*
* static GObject*
* my_singleton_constructor (GType type,
* guint n_construct_params,
* GObjectConstructParam *construct_params)
* {
* GObject *object;
*
* if (!the_singleton)
* {
* object = G_OBJECT_CLASS (parent_class)->constructor (type,
* n_construct_params,
* construct_params);
* the_singleton = MY_SINGLETON (object);
* }
* else
* object = g_object_ref (G_OBJECT (the_singleton));
*
* return object;
* }
* ]|
*/
struct _GObjectClass
{
GTypeClass g_type_class;
/*< private >*/
GSList *construct_properties;
/*< public >*/
/* seldom overridden */
GObject* (*constructor) (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties);
/* overridable methods */
void (*set_property) (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
void (*get_property) (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
void (*dispose) (GObject *object);
void (*finalize) (GObject *object);
/* seldom overridden */
void (*dispatch_properties_changed) (GObject *object,
guint n_pspecs,
GParamSpec **pspecs);
/* signals */
void (*notify) (GObject *object,
GParamSpec *pspec);
/* called when done constructing */
void (*constructed) (GObject *object);
/*< private >*/
gsize flags;
/* padding */
gpointer pdummy[6];
};
/**
* GObjectConstructParam:
* @pspec: the #GParamSpec of the construct parameter
* @value: the value to set the parameter to
*
* The GObjectConstructParam struct is an auxiliary structure used to hand
* #GParamSpec/#GValue pairs to the @constructor of a #GObjectClass.
*/
struct _GObjectConstructParam
{
GParamSpec *pspec;
GValue *value;
};
/**
* GInitiallyUnowned:
*
* A type for objects that have an initially floating reference.
*
* All the fields in the `GInitiallyUnowned` structure are private to the
* implementation and should never be accessed directly.
*/
/**
* GInitiallyUnownedClass:
*
* The class structure for the GInitiallyUnowned type.
*/
/* --- prototypes --- */
GLIB_AVAILABLE_IN_ALL
GType g_initially_unowned_get_type (void);
GLIB_AVAILABLE_IN_ALL
void g_object_class_install_property (GObjectClass *oclass,
guint property_id,
GParamSpec *pspec);
GLIB_AVAILABLE_IN_ALL
GParamSpec* g_object_class_find_property (GObjectClass *oclass,
const gchar *property_name);
GLIB_AVAILABLE_IN_ALL
GParamSpec**g_object_class_list_properties (GObjectClass *oclass,
guint *n_properties);
GLIB_AVAILABLE_IN_ALL
void g_object_class_override_property (GObjectClass *oclass,
guint property_id,
const gchar *name);
GLIB_AVAILABLE_IN_ALL
void g_object_class_install_properties (GObjectClass *oclass,
guint n_pspecs,
GParamSpec **pspecs);
GLIB_AVAILABLE_IN_ALL
void g_object_interface_install_property (gpointer g_iface,
GParamSpec *pspec);
GLIB_AVAILABLE_IN_ALL
GParamSpec* g_object_interface_find_property (gpointer g_iface,
const gchar *property_name);
GLIB_AVAILABLE_IN_ALL
GParamSpec**g_object_interface_list_properties (gpointer g_iface,
guint *n_properties_p);
GLIB_AVAILABLE_IN_ALL
GType g_object_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
gpointer g_object_new (GType object_type,
const gchar *first_property_name,
...);
GLIB_AVAILABLE_IN_2_54
GObject* g_object_new_with_properties (GType object_type,
guint n_properties,
const char *names[],
const GValue values[]);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
GLIB_DEPRECATED_IN_2_54_FOR(g_object_new_with_properties)
gpointer g_object_newv (GType object_type,
guint n_parameters,
GParameter *parameters);
G_GNUC_END_IGNORE_DEPRECATIONS
GLIB_AVAILABLE_IN_ALL
GObject* g_object_new_valist (GType object_type,
const gchar *first_property_name,
va_list var_args);
GLIB_AVAILABLE_IN_ALL
void g_object_set (gpointer object,
const gchar *first_property_name,
...) G_GNUC_NULL_TERMINATED;
GLIB_AVAILABLE_IN_ALL
void g_object_get (gpointer object,
const gchar *first_property_name,
...) G_GNUC_NULL_TERMINATED;
GLIB_AVAILABLE_IN_ALL
gpointer g_object_connect (gpointer object,
const gchar *signal_spec,
...) G_GNUC_NULL_TERMINATED;
GLIB_AVAILABLE_IN_ALL
void g_object_disconnect (gpointer object,
const gchar *signal_spec,
...) G_GNUC_NULL_TERMINATED;
GLIB_AVAILABLE_IN_2_54
void g_object_setv (GObject *object,
guint n_properties,
const gchar *names[],
const GValue values[]);
GLIB_AVAILABLE_IN_ALL
void g_object_set_valist (GObject *object,
const gchar *first_property_name,
va_list var_args);
GLIB_AVAILABLE_IN_2_54
void g_object_getv (GObject *object,
guint n_properties,
const gchar *names[],
GValue values[]);
GLIB_AVAILABLE_IN_ALL
void g_object_get_valist (GObject *object,
const gchar *first_property_name,
va_list var_args);
GLIB_AVAILABLE_IN_ALL
void g_object_set_property (GObject *object,
const gchar *property_name,
const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_object_get_property (GObject *object,
const gchar *property_name,
GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_object_freeze_notify (GObject *object);
GLIB_AVAILABLE_IN_ALL
void g_object_notify (GObject *object,
const gchar *property_name);
GLIB_AVAILABLE_IN_ALL
void g_object_notify_by_pspec (GObject *object,
GParamSpec *pspec);
GLIB_AVAILABLE_IN_ALL
void g_object_thaw_notify (GObject *object);
GLIB_AVAILABLE_IN_ALL
gboolean g_object_is_floating (gpointer object);
GLIB_AVAILABLE_IN_ALL
gpointer g_object_ref_sink (gpointer object);
GLIB_AVAILABLE_IN_2_70
gpointer g_object_take_ref (gpointer object);
GLIB_AVAILABLE_IN_ALL
gpointer g_object_ref (gpointer object);
GLIB_AVAILABLE_IN_ALL
void g_object_unref (gpointer object);
GLIB_AVAILABLE_IN_ALL
void g_object_weak_ref (GObject *object,
GWeakNotify notify,
gpointer data);
GLIB_AVAILABLE_IN_ALL
void g_object_weak_unref (GObject *object,
GWeakNotify notify,
gpointer data);
GLIB_AVAILABLE_IN_ALL
void g_object_add_weak_pointer (GObject *object,
gpointer *weak_pointer_location);
GLIB_AVAILABLE_IN_ALL
void g_object_remove_weak_pointer (GObject *object,
gpointer *weak_pointer_location);
#if defined(glib_typeof) && GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_56
/* Make reference APIs type safe with macros */
#define g_object_ref(Obj) ((glib_typeof (Obj)) (g_object_ref) (Obj))
#define g_object_ref_sink(Obj) ((glib_typeof (Obj)) (g_object_ref_sink) (Obj))
#endif
/**
* GToggleNotify:
* @data: Callback data passed to g_object_add_toggle_ref()
* @object: The object on which g_object_add_toggle_ref() was called.
* @is_last_ref: %TRUE if the toggle reference is now the
* last reference to the object. %FALSE if the toggle
* reference was the last reference and there are now other
* references.
*
* A callback function used for notification when the state
* of a toggle reference changes.
*
* See also: g_object_add_toggle_ref()
*/
typedef void (*GToggleNotify) (gpointer data,
GObject *object,
gboolean is_last_ref);
GLIB_AVAILABLE_IN_ALL
void g_object_add_toggle_ref (GObject *object,
GToggleNotify notify,
gpointer data);
GLIB_AVAILABLE_IN_ALL
void g_object_remove_toggle_ref (GObject *object,
GToggleNotify notify,
gpointer data);
GLIB_AVAILABLE_IN_ALL
gpointer g_object_get_qdata (GObject *object,
GQuark quark);
GLIB_AVAILABLE_IN_ALL
void g_object_set_qdata (GObject *object,
GQuark quark,
gpointer data);
GLIB_AVAILABLE_IN_ALL
void g_object_set_qdata_full (GObject *object,
GQuark quark,
gpointer data,
GDestroyNotify destroy);
GLIB_AVAILABLE_IN_ALL
gpointer g_object_steal_qdata (GObject *object,
GQuark quark);
GLIB_AVAILABLE_IN_2_34
gpointer g_object_dup_qdata (GObject *object,
GQuark quark,
GDuplicateFunc dup_func,
gpointer user_data);
GLIB_AVAILABLE_IN_2_34
gboolean g_object_replace_qdata (GObject *object,
GQuark quark,
gpointer oldval,
gpointer newval,
GDestroyNotify destroy,
GDestroyNotify *old_destroy);
GLIB_AVAILABLE_IN_ALL
gpointer g_object_get_data (GObject *object,
const gchar *key);
GLIB_AVAILABLE_IN_ALL
void g_object_set_data (GObject *object,
const gchar *key,
gpointer data);
GLIB_AVAILABLE_IN_ALL
void g_object_set_data_full (GObject *object,
const gchar *key,
gpointer data,
GDestroyNotify destroy);
GLIB_AVAILABLE_IN_ALL
gpointer g_object_steal_data (GObject *object,
const gchar *key);
GLIB_AVAILABLE_IN_2_34
gpointer g_object_dup_data (GObject *object,
const gchar *key,
GDuplicateFunc dup_func,
gpointer user_data);
GLIB_AVAILABLE_IN_2_34
gboolean g_object_replace_data (GObject *object,
const gchar *key,
gpointer oldval,
gpointer newval,
GDestroyNotify destroy,
GDestroyNotify *old_destroy);
GLIB_AVAILABLE_IN_ALL
void g_object_watch_closure (GObject *object,
GClosure *closure);
GLIB_AVAILABLE_IN_ALL
GClosure* g_cclosure_new_object (GCallback callback_func,
GObject *object);
GLIB_AVAILABLE_IN_ALL
GClosure* g_cclosure_new_object_swap (GCallback callback_func,
GObject *object);
GLIB_AVAILABLE_IN_ALL
GClosure* g_closure_new_object (guint sizeof_closure,
GObject *object);
GLIB_AVAILABLE_IN_ALL
void g_value_set_object (GValue *value,
gpointer v_object);
GLIB_AVAILABLE_IN_ALL
gpointer g_value_get_object (const GValue *value);
GLIB_AVAILABLE_IN_ALL
gpointer g_value_dup_object (const GValue *value);
GLIB_AVAILABLE_IN_ALL
gulong g_signal_connect_object (gpointer instance,
const gchar *detailed_signal,
GCallback c_handler,
gpointer gobject,
GConnectFlags connect_flags);
/*< protected >*/
GLIB_AVAILABLE_IN_ALL
void g_object_force_floating (GObject *object);
GLIB_AVAILABLE_IN_ALL
void g_object_run_dispose (GObject *object);
GLIB_AVAILABLE_IN_ALL
void g_value_take_object (GValue *value,
gpointer v_object);
GLIB_DEPRECATED_FOR(g_value_take_object)
void g_value_set_object_take_ownership (GValue *value,
gpointer v_object);
GLIB_DEPRECATED
gsize g_object_compat_control (gsize what,
gpointer data);
/* --- implementation macros --- */
#define G_OBJECT_WARN_INVALID_PSPEC(object, pname, property_id, pspec) \
G_STMT_START { \
GObject *_glib__object = (GObject*) (object); \
GParamSpec *_glib__pspec = (GParamSpec*) (pspec); \
guint _glib__property_id = (property_id); \
g_warning ("%s:%d: invalid %s id %u for \"%s\" of type '%s' in '%s'", \
__FILE__, __LINE__, \
(pname), \
_glib__property_id, \
_glib__pspec->name, \
g_type_name (G_PARAM_SPEC_TYPE (_glib__pspec)), \
G_OBJECT_TYPE_NAME (_glib__object)); \
} G_STMT_END
/**
* G_OBJECT_WARN_INVALID_PROPERTY_ID:
* @object: the #GObject on which set_property() or get_property() was called
* @property_id: the numeric id of the property
* @pspec: the #GParamSpec of the property
*
* This macro should be used to emit a standard warning about unexpected
* properties in set_property() and get_property() implementations.
*/
#define G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec) \
G_OBJECT_WARN_INVALID_PSPEC ((object), "property", (property_id), (pspec))
GLIB_AVAILABLE_IN_ALL
void g_clear_object (GObject **object_ptr);
#define g_clear_object(object_ptr) g_clear_pointer ((object_ptr), g_object_unref)
/**
* g_set_object: (skip)
* @object_ptr: (inout) (not optional) (nullable): a pointer to a #GObject reference
* @new_object: (nullable) (transfer none): a pointer to the new #GObject to
* assign to @object_ptr, or %NULL to clear the pointer
*
* Updates a #GObject pointer to refer to @new_object.
*
* It increments the reference count of @new_object (if non-%NULL), decrements
* the reference count of the current value of @object_ptr (if non-%NULL), and
* assigns @new_object to @object_ptr. The assignment is not atomic.
*
* @object_ptr must not be %NULL, but can point to a %NULL value.
*
* A macro is also included that allows this function to be used without
* pointer casts. The function itself is static inline, so its address may vary
* between compilation units.
*
* One convenient usage of this function is in implementing property setters:
* |[
* void
* foo_set_bar (Foo *foo,
* Bar *new_bar)
* {
* g_return_if_fail (IS_FOO (foo));
* g_return_if_fail (new_bar == NULL || IS_BAR (new_bar));
*
* if (g_set_object (&foo->bar, new_bar))
* g_object_notify (foo, "bar");
* }
* ]|
*
* Returns: %TRUE if the value of @object_ptr changed, %FALSE otherwise
*
* Since: 2.44
*/
static inline gboolean
(g_set_object) (GObject **object_ptr,
GObject *new_object)
{
GObject *old_object = *object_ptr;
/* rely on g_object_[un]ref() to check the pointers are actually GObjects;
* elide a (object_ptr != NULL) check because most of the time we will be
* operating on struct members with a constant offset, so a NULL check would
* not catch bugs
*/
if (old_object == new_object)
return FALSE;
if (new_object != NULL)
g_object_ref (new_object);
*object_ptr = new_object;
if (old_object != NULL)
g_object_unref (old_object);
return TRUE;
}
/* We need GCC for __extension__, which we need to sort out strict aliasing of @object_ptr */
#if defined(__GNUC__)
#define g_set_object(object_ptr, new_object) \
(G_GNUC_EXTENSION ({ \
G_STATIC_ASSERT (sizeof *(object_ptr) == sizeof (new_object)); \
/* Only one access, please; work around type aliasing */ \
union { char *in; GObject **out; } _object_ptr; \
_object_ptr.in = (char *) (object_ptr); \
/* Check types match */ \
(void) (0 ? *(object_ptr) = (new_object), FALSE : FALSE); \
(g_set_object) (_object_ptr.out, (GObject *) new_object); \
})) \
GLIB_AVAILABLE_MACRO_IN_2_44
#else /* if !defined(__GNUC__) */
#define g_set_object(object_ptr, new_object) \
(/* Check types match. */ \
0 ? *(object_ptr) = (new_object), FALSE : \
(g_set_object) ((GObject **) (object_ptr), (GObject *) (new_object)) \
)
#endif /* !defined(__GNUC__) */
/**
* g_assert_finalize_object: (skip)
* @object: (transfer full) (type GObject.Object): an object
*
* Assert that @object is non-%NULL, then release one reference to it with
* g_object_unref() and assert that it has been finalized (i.e. that there
* are no more references).
*
* If assertions are disabled via `G_DISABLE_ASSERT`,
* this macro just calls g_object_unref() without any further checks.
*
* This macro should only be used in regression tests.
*
* Since: 2.62
*/
static inline void
(g_assert_finalize_object) (GObject *object)
{
gpointer weak_pointer = object;
g_assert_true (G_IS_OBJECT (weak_pointer));
g_object_add_weak_pointer (object, &weak_pointer);
g_object_unref (weak_pointer);
g_assert_null (weak_pointer);
}
#ifdef G_DISABLE_ASSERT
#define g_assert_finalize_object(object) g_object_unref (object)
#else
#define g_assert_finalize_object(object) (g_assert_finalize_object ((GObject *) object))
#endif
/**
* g_clear_weak_pointer: (skip)
* @weak_pointer_location: The memory address of a pointer
*
* Clears a weak reference to a #GObject.
*
* @weak_pointer_location must not be %NULL.
*
* If the weak reference is %NULL then this function does nothing.
* Otherwise, the weak reference to the object is removed for that location
* and the pointer is set to %NULL.
*
* A macro is also included that allows this function to be used without
* pointer casts. The function itself is static inline, so its address may vary
* between compilation units.
*
* Since: 2.56
*/
static inline void
(g_clear_weak_pointer) (gpointer *weak_pointer_location)
{
GObject *object = (GObject *) *weak_pointer_location;
if (object != NULL)
{
g_object_remove_weak_pointer (object, weak_pointer_location);
*weak_pointer_location = NULL;
}
}
#define g_clear_weak_pointer(weak_pointer_location) \
(/* Check types match. */ \
(g_clear_weak_pointer) ((gpointer *) (weak_pointer_location)) \
)
/**
* g_set_weak_pointer: (skip)
* @weak_pointer_location: the memory address of a pointer
* @new_object: (nullable) (transfer none): a pointer to the new #GObject to
* assign to it, or %NULL to clear the pointer
*
* Updates a pointer to weakly refer to @new_object.
*
* It assigns @new_object to @weak_pointer_location and ensures
* that @weak_pointer_location will automatically be set to %NULL
* if @new_object gets destroyed. The assignment is not atomic.
* The weak reference is not thread-safe, see g_object_add_weak_pointer()
* for details.
*
* The @weak_pointer_location argument must not be %NULL.
*
* A macro is also included that allows this function to be used without
* pointer casts. The function itself is static inline, so its address may vary
* between compilation units.
*
* One convenient usage of this function is in implementing property setters:
* |[
* void
* foo_set_bar (Foo *foo,
* Bar *new_bar)
* {
* g_return_if_fail (IS_FOO (foo));
* g_return_if_fail (new_bar == NULL || IS_BAR (new_bar));
*
* if (g_set_weak_pointer (&foo->bar, new_bar))
* g_object_notify (foo, "bar");
* }
* ]|
*
* Returns: %TRUE if the value of @weak_pointer_location changed, %FALSE otherwise
*
* Since: 2.56
*/
static inline gboolean
(g_set_weak_pointer) (gpointer *weak_pointer_location,
GObject *new_object)
{
GObject *old_object = (GObject *) *weak_pointer_location;
/* elide a (weak_pointer_location != NULL) check because most of the time we
* will be operating on struct members with a constant offset, so a NULL
* check would not catch bugs
*/
if (old_object == new_object)
return FALSE;
if (old_object != NULL)
g_object_remove_weak_pointer (old_object, weak_pointer_location);
*weak_pointer_location = new_object;
if (new_object != NULL)
g_object_add_weak_pointer (new_object, weak_pointer_location);
return TRUE;
}
#define g_set_weak_pointer(weak_pointer_location, new_object) \
(/* Check types match. */ \
0 ? *(weak_pointer_location) = (new_object), FALSE : \
(g_set_weak_pointer) ((gpointer *) (weak_pointer_location), (GObject *) (new_object)) \
)
typedef struct {
/*<private>*/
union { gpointer p; } priv;
} GWeakRef;
GLIB_AVAILABLE_IN_ALL
void g_weak_ref_init (GWeakRef *weak_ref,
gpointer object);
GLIB_AVAILABLE_IN_ALL
void g_weak_ref_clear (GWeakRef *weak_ref);
GLIB_AVAILABLE_IN_ALL
gpointer g_weak_ref_get (GWeakRef *weak_ref);
GLIB_AVAILABLE_IN_ALL
void g_weak_ref_set (GWeakRef *weak_ref,
gpointer object);
G_END_DECLS
#endif /* __G_OBJECT_H__ */

30
gobject/gobject.rc.in Normal file
View file

@ -0,0 +1,30 @@
#include <winver.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION @GLIB_MAJOR_VERSION@,@GLIB_MINOR_VERSION@,@GLIB_MICRO_VERSION@,0
PRODUCTVERSION @GLIB_MAJOR_VERSION@,@GLIB_MINOR_VERSION@,@GLIB_MICRO_VERSION@,0
FILEFLAGSMASK 0
FILEFLAGS 0
FILEOS VOS__WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "The GLib developer community"
VALUE "FileDescription", "GObject"
VALUE "FileVersion", "@GLIB_VERSION@.0"
VALUE "InternalName", "libgobject-2.0-@LT_CURRENT_MINUS_AGE@"
VALUE "LegalCopyright", "Copyright 1998-2011 Tim Janik, Red Hat, Inc. and others"
VALUE "OriginalFilename", "libgobject-2.0-@LT_CURRENT_MINUS_AGE@.dll"
VALUE "ProductName", "GLib"
VALUE "ProductVersion", "@GLIB_VERSION@"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

199
gobject/gobject.stp.in Normal file
View file

@ -0,0 +1,199 @@
global gobject_types_2_0_@LT_CURRENT@_@LT_REVISION@
global gobject_type_names_2_0_@LT_CURRENT@_@LT_REVISION@
global gobject_signal_names_2_0_@LT_CURRENT@_@LT_REVISION@
/* These are needed to keep track of gtype and signal names for the below
* probes.
*/
probe process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("type__new")
{
gobject_types_2_0_@LT_CURRENT@_@LT_REVISION@[pid(),user_string($arg1)] = $arg3;
gobject_type_names_2_0_@LT_CURRENT@_@LT_REVISION@[pid(),$arg3] = user_string($arg1);
}
probe process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("signal__new")
{
gobject_signal_names_2_0_@LT_CURRENT@_@LT_REVISION@[pid(),$arg1] = user_string($arg2);
}
/**
* probe gobject.type_new - Called when any entity registered with the #GType system is created
* @name: String name of type
* @parent_gtype: The parent #GType of this type
* @gtype: The #GType for this type
*/
probe gobject.type_new = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("type__new")
{
name = user_string($arg1);
parent_gtype = $arg2;
gtype = $arg3;
probestr = sprintf("gobject.type_new(%s, %d) -> %d", name, parent_gtype, gtype);
}
/**
* probe gobject.object_new - Called when a #GObject is created
* @object: Raw pointer to object
* @gtype: #GType for this object
* @type: String name of object type
*/
probe gobject.object_new = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("object__new")
{
object = $arg1;
gtype = $arg2;
type = gobject_type_names_2_0_@LT_CURRENT@_@LT_REVISION@[pid(),$arg2];
probestr = sprintf("gobject.object_new(%s) -> %p", type, object);
}
/**
* probe gobject.object_ref - Called when a new reference is added to a #GObject
* @object: Raw pointer to object
* @gtype: #GType for this object
* @type: String name of object type
* @old_refcount: Original value of the reference count
* @refcount: New value of the reference count
*/
probe gobject.object_ref = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("object__ref")
{
object = $arg1;
gtype = $arg2;
type = gobject_type_names_2_0_@LT_CURRENT@_@LT_REVISION@[pid(),gtype];
old_refcount = $arg3;
refcount = old_refcount+1;
probestr = sprintf("gobject.object_ref(%p[%s]) -> %d", object, type, refcount);
}
/**
* probe gobject.object_unref - Called when a reference is removed from a #GObject
* @object: Raw pointer to object
* @gtype: #GType for this object
* @type: String name of object type
* @old_refcount: Original value of the reference count
*/
probe gobject.object_unref = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("object__unref")
{
object = $arg1;
gtype = $arg2;
type = gobject_type_names_2_0_@LT_CURRENT@_@LT_REVISION@[pid(),gtype];
old_refcount = $arg3;
refcount = old_refcount-1;
probestr = sprintf("gobject.object_unref(%p [%s]) -> %d", object, type, refcount);
}
/**
* probe gobject.object_dispose - Called when a g_object_dispose() run for a #GObject is initiated
* @object: Raw pointer to object
* @gtype: #GType for this object
* @type: String name of object type
* @last_unref: FIXME
*/
probe gobject.object_dispose = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("object__dispose")
{
object = $arg1;
gtype = $arg2;
type = gobject_type_names_2_0_@LT_CURRENT@_@LT_REVISION@[pid(),$arg2];
last_unref = $arg3;
probestr = sprintf("gobject.object_dispose(%p[%s])", object, type);
}
/**
* probe gobject.object_dispose_end - Called when a g_object_dispose() run for a #GObject is completed
* @object: Raw pointer to object
* @gtype: #GType for this object
* @type: String name of object type
* @last_unref: FIXME
*/
probe gobject.object_dispose_end = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("object__dispose__end")
{
object = $arg1;
gtype = $arg2;
type = gobject_type_names_2_0_@LT_CURRENT@_@LT_REVISION@[pid(),$arg2];
last_unref = $arg3;
probestr = sprintf("gobject.object_dispose_end(%p[%s])", object, type);
}
/**
* probe gobject.object_finalize - Called when finalization for a #GObject is started
* @object: Raw pointer to object
* @gtype: #GType for this object
* @type: String name of object type
*/
probe gobject.object_finalize = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("object__finalize")
{
object = $arg1;
gtype = $arg2;
type = gobject_type_names_2_0_@LT_CURRENT@_@LT_REVISION@[pid(),$arg2];
probestr = sprintf("gobject.object_finalize(%p[%s])", object, type);
}
/**
* probe gobject.object_finalize - Called when finalization for a #GObject is completed
* @object: Raw pointer to object
* @gtype: #GType for this object
* @type: String name of object type
*/
probe gobject.object_finalize_end = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("object__finalize__end")
{
object = $arg1;
gtype = $arg2;
type = gobject_type_names_2_0_@LT_CURRENT@_@LT_REVISION@[pid(),$arg2];
probestr = sprintf("gobject.object_finalize_end(%p[%s])", object, type);
}
/**
* probe gobject.signal_new - Called when a new signal is registered for a #GObject
* @gsignal: Integer value for this signal
* @name: String name for this signal
* @gtype: #GType for the type which will gain the new signal
* @type: String name of the type which will gain the new signal
*/
probe gobject.signal_new = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("signal__new")
{
gsignal = $arg1;
name = user_string($arg2);
gtype = $arg3;
type = gobject_type_names_2_0_@LT_CURRENT@_@LT_REVISION@[pid(),$arg3];
probestr = sprintf("gobject.signal_new(%s, %s) -> %d", name, type, gsignal);
}
/**
* probe gobject.signal_emit - Called when a signal emission for a #GObject is started
* @gsignal: Integer value for this signal
* @detail: String containing signal "detail"
* @signal: String name of the signal
* @object: Raw pointer for object emitting signal
* @gtype: #GType for the type emitting the signal
* @type: String name of the type emitting the signal
*/
probe gobject.signal_emit = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("signal__emit")
{
gsignal = $arg1;
detail = $arg2;
signal = gobject_signal_names_2_0_@LT_CURRENT@_@LT_REVISION@[pid(),$arg1];
if (detail != 0)
signal = signal . "::" . gquarks[pid(), detail]
object = $arg3;
gtype = $arg4;
type = gobject_type_names_2_0_@LT_CURRENT@_@LT_REVISION@[pid(),$arg4];
probestr = sprintf("gobject.signal_emit(%p[%s], %s)", object, type, signal);
}
/**
* probe gobject.signal_emit_end - Called when a signal emission for a #GObject is completed
* @gsignal: Integer value for this signal
* @detail: String containing signal "detail"
* @signal: String name of the signal
* @object: Raw pointer for object emitting signal
* @gtype: #GType for the type emitting the signal
* @type: String name of the type emitting the signal
*/
probe gobject.signal_emit_end = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("signal__emit__end")
{
gsignal = $arg1;
detail = $arg2;
signal = gobject_signal_names_2_0_@LT_CURRENT@_@LT_REVISION@[pid(),$arg1];
if (detail != 0)
signal = signal . "::" . gquarks[pid(), detail]
object = $arg3;
gtype = $arg4;
type = gobject_type_names_2_0_@LT_CURRENT@_@LT_REVISION@[pid(),$arg4];
probestr = sprintf("gobject.signal_emit_end(%p[%s], %s)", object, type, signal);
}

352
gobject/gobject_gdb.py Normal file
View file

@ -0,0 +1,352 @@
import gdb
import glib_gdb
import sys
if sys.version_info[0] >= 3:
long = int
else:
import itertools
map = itertools.imap
# FrameDecorator is new in gdb 7.7, so we adapt to its absence.
try:
import gdb.FrameDecorator
HAVE_GDB_FRAMEDECORATOR = True
FrameDecorator = gdb.FrameDecorator.FrameDecorator
except ImportError:
HAVE_GDB_FRAMEDECORATOR = False
# This is not quite right, as local vars may override symname
def read_global_var(symname):
return gdb.selected_frame().read_var(symname)
def g_type_to_typenode(gtype):
def lookup_fundamental_type(typenode):
if typenode == 0:
return None
val = read_global_var("static_fundamental_type_nodes")
if val is None:
return None
return val[typenode >> 2].address
gtype = long(gtype)
typenode = gtype - gtype % 4
if typenode > (255 << 2):
typenode = gdb.Value(typenode).cast(gdb.lookup_type("TypeNode").pointer())
else:
typenode = lookup_fundamental_type(typenode)
return typenode
def g_type_to_name(gtype):
typenode = g_type_to_typenode(gtype)
if typenode is not None:
return glib_gdb.g_quark_to_string(typenode["qname"])
return None
def is_g_type_instance(val):
def is_g_type_instance_helper(type):
if str(type) == "GTypeInstance":
return True
while type.code == gdb.TYPE_CODE_TYPEDEF:
type = type.target()
if type.code != gdb.TYPE_CODE_STRUCT:
return False
fields = type.fields()
if len(fields) < 1:
return False
first_field = fields[0]
return is_g_type_instance_helper(first_field.type)
type = val.type
if type.code != gdb.TYPE_CODE_PTR:
return False
type = type.target()
return is_g_type_instance_helper(type)
def g_type_name_from_instance(instance):
if long(instance) != 0:
try:
inst = instance.cast(gdb.lookup_type("GTypeInstance").pointer())
klass = inst["g_class"]
gtype = klass["g_type"]
name = g_type_to_name(gtype)
return name
except RuntimeError:
pass
return None
class GTypePrettyPrinter:
"Prints a GType instance pointer"
def __init__(self, val):
self.val = val
def to_string(self):
name = g_type_name_from_instance(self.val)
if name:
return ("0x%x [%s]") % (long(self.val), name)
return ("0x%x") % (long(self.val))
def is_g_type_class_instance(val):
type = val.type
if type.code != gdb.TYPE_CODE_PTR:
return False
return str(type.target()) == "GTypeClass"
class GTypeHandlePrettyPrinter:
"Prints a GType instance"
def __init__(self, val, hint=""):
self.val = val
self.hint = hint
def to_string(self):
typenode = g_type_to_typenode(self.val)
if typenode is not None:
name = glib_gdb.g_quark_to_string(typenode["qname"])
s = ("0x%x [%s%s") % (long(self.val), self.hint, name)
for i in range(1, int(typenode["n_supers"])):
node = g_type_to_typenode(typenode["supers"][i])
if node:
name = glib_gdb.g_quark_to_string(node["qname"])
else:
name = "???"
s += "/" + name
return s + "]"
else:
return ("0x%x") % (long(self.val))
def pretty_printer_lookup(val):
if is_g_type_instance(val):
return GTypePrettyPrinter(val)
if str(val.type) == "GType":
return GTypeHandlePrettyPrinter(val)
if is_g_type_class_instance(val):
return GTypeHandlePrettyPrinter(val["g_type"], "g_type: ")
return None
def get_signal_name(id):
if id is None:
return None
id = long(id)
if id == 0:
return None
val = read_global_var("g_signal_nodes")
max_s = read_global_var("g_n_signal_nodes")
max_s = long(max_s)
if id < max_s:
return val[id]["name"].string()
return None
def frame_name(frame):
return str(frame.function())
def frame_var(frame, var):
return frame.inferior_frame().read_var(var)
class SignalFrame(FrameDecorator):
def __init__(self, frames):
FrameDecorator.__init__(self, frames[-1])
self.frame = frames[-1]
self.frames = frames
def name(self):
return "signal-emission"
def read_var(self, frame, name, array=None):
try:
v = frame_var(frame, name)
if v is None or v.is_optimized_out:
return None
if array is not None:
array.append(v)
return v
except ValueError:
return None
def read_object(self, frame, name, array=None):
try:
v = frame_var(frame, name)
if v is None or v.is_optimized_out:
return None
v = v.cast(gdb.lookup_type("GObject").pointer())
# Ensure this is a somewhat correct object pointer
if v is not None and g_type_name_from_instance(v):
if array is not None:
array.append(v)
return v
return None
except ValueError:
return None
def append(self, array, obj):
if obj is not None:
array.append(obj)
def or_join_array(self, array):
if len(array) == 0:
return "???"
else:
return " or ".join(set(map(str, array)))
def get_detailed_signal_from_frame(self, frame, signal):
detail = self.read_var(frame, "detail")
detail = glib_gdb.g_quark_to_string(detail)
if detail is not None:
return signal + ":" + detail
else:
return detail
def function(self):
instances = []
signals = []
for frame in self.frames:
name = frame_name(frame)
if name == "signal_emit_unlocked_R":
self.read_object(frame, "instance", instances)
node = self.read_var(frame, "node")
if node:
signal = node["name"].string()
signal = self.get_detailed_signal_from_frame(frame, signal)
self.append(signals, signal)
if name == "g_signal_emitv":
instance_and_params = self.read_var(frame, "instance_and_params")
if instance_and_params:
instance = instance_and_params[0]["v_pointer"].cast(
gdb.Type("GObject").pointer()
)
self.append(instances, instance)
id = self.read_var(frame, "signal_id")
signal = get_signal_name(id)
if signal:
signal = self.get_detailed_signal_from_frame(frame, signal)
self.append(signals, signal)
if name == "g_signal_emit_valist" or name == "g_signal_emit":
self.read_object(frame, "instance", instances)
id = self.read_var(frame, "signal_id")
signal = get_signal_name(id)
if signal:
signal = self.get_detailed_signal_from_frame(frame, signal)
self.append(signals, signal)
if name == "g_signal_emit_by_name":
self.read_object(frame, "instance", instances)
self.read_var(frame, "detailed_signal", signals)
break
instance = self.or_join_array(instances)
signal = self.or_join_array(signals)
return "<emit signal %s on instance %s>" % (signal, instance)
def elided(self):
return self.frames[0:-1]
def describe(self, stream, full):
stream.write(" " + self.function() + "\n")
class GFrameDecorator:
def __init__(self, iter):
self.queue = []
self.iter = iter
def __iter__(self):
return self
def fill(self):
while len(self.queue) <= 8:
try:
f = next(self.iter)
self.queue.append(f)
except StopIteration:
return
def find_signal_emission(self):
for i in range(min(len(self.queue), 3)):
if frame_name(self.queue[i]) == "signal_emit_unlocked_R":
return i
return -1
def next(self):
# Ensure we have enough frames for a full signal emission
self.fill()
# Are we at the end?
if len(self.queue) == 0:
raise StopIteration
emission = self.find_signal_emission()
if emission > 0:
start = emission
while True:
if start == 0:
break
prev_name = frame_name(self.queue[start - 1])
if prev_name.find("_marshal_") >= 0 or prev_name == "g_closure_invoke":
start = start - 1
else:
break
end = emission + 1
while end < len(self.queue):
if frame_name(self.queue[end]) in [
"g_signal_emitv",
"g_signal_emit_valist",
"g_signal_emit",
"g_signal_emit_by_name",
"_g_closure_invoke_va",
]:
end = end + 1
else:
break
signal_frames = self.queue[start:end]
new_frames = [SignalFrame(signal_frames)]
self.queue[start:end] = new_frames
return self.queue.pop(0)
def __next__(self):
return self.next()
class GFrameFilter(object):
name = "glib"
enabled = True
priority = 100
def filter(self, iterator):
return GFrameDecorator(iterator)
def register(obj):
if obj is None:
obj = gdb
if HAVE_GDB_FRAMEDECORATOR:
filter = GFrameFilter()
obj.frame_filters[filter.name] = filter
obj.pretty_printers.append(pretty_printer_lookup)

13
gobject/gobject_probes.d Normal file
View file

@ -0,0 +1,13 @@
provider gobject {
probe type__new(char *, unsigned long, unsigned long);
probe object__new(void*, unsigned long);
probe object__ref(void*, unsigned long, unsigned int);
probe object__unref(void*, unsigned long, unsigned int);
probe object__dispose(void*, unsigned long, unsigned int);
probe object__dispose__end(void*, unsigned long, unsigned int);
probe object__finalize(void*, unsigned long);
probe object__finalize__end(void*, unsigned long);
probe signal__new(unsigned int, char *, unsigned long);
probe signal__emit(unsigned int, unsigned int, void *, unsigned long);
probe signal__emit__end(unsigned int, unsigned int, void *, unsigned long);
};

43
gobject/gobject_trace.h Normal file
View file

@ -0,0 +1,43 @@
/* GLIB - Library of useful routines for C programming
*
* Copyright (C) 2009,2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#ifndef __GOBJECTTRACE_H__
#define __GOBJECTTRACE_H__
#ifndef SIZEOF_CHAR
#error "config.h must be included prior to gobject_trace.h"
#endif
/* Ignore probes when doing static analysis, as they do weird things which
* confuses the analyser. */
#if defined(HAVE_DTRACE) && !defined(__clang_analyzer__)
/* include the generated probes header and put markers in code */
#include "gobject_probes.h"
#define TRACE(probe) probe
#else
/* Wrap the probe to allow it to be removed when no systemtap available */
#define TRACE(probe)
#endif
#endif /* __GOBJECTTRACE_H__ */

View file

@ -0,0 +1,197 @@
/* 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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/* WARNING:
*
* This file is INSTALLED and other projects (outside of glib)
* #include its contents.
*/
#ifndef __G_OBJECT_NOTIFY_QUEUE_H__
#define __G_OBJECT_NOTIFY_QUEUE_H__
#include <string.h> /* memset */
#include <glib-object.h>
G_BEGIN_DECLS
/* --- typedefs --- */
typedef struct _GObjectNotifyContext GObjectNotifyContext;
typedef struct _GObjectNotifyQueue GObjectNotifyQueue;
typedef void (*GObjectNotifyQueueDispatcher) (GObject *object,
guint n_pspecs,
GParamSpec **pspecs);
/* --- structures --- */
struct _GObjectNotifyContext
{
GQuark quark_notify_queue;
GObjectNotifyQueueDispatcher dispatcher;
GTrashStack *_nqueue_trash; /* unused */
};
struct _GObjectNotifyQueue
{
GObjectNotifyContext *context;
GSList *pspecs;
guint16 n_pspecs;
guint16 freeze_count;
};
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 inline GObjectNotifyQueue*
g_object_notify_queue_freeze (GObject *object,
GObjectNotifyContext *context)
{
GObjectNotifyQueue *nqueue;
G_LOCK(notify_lock);
nqueue = g_datalist_id_get_data (&object->qdata, context->quark_notify_queue);
if (!nqueue)
{
nqueue = g_slice_new0 (GObjectNotifyQueue);
nqueue->context = context;
g_datalist_id_set_data_full (&object->qdata, context->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 inline void
g_object_notify_queue_thaw (GObject *object,
GObjectNotifyQueue *nqueue)
{
GObjectNotifyContext *context = nqueue->context;
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, context->quark_notify_queue, NULL);
G_UNLOCK(notify_lock);
if (n_pspecs)
context->dispatcher (object, n_pspecs, pspecs);
g_free (free_me);
}
static inline void
g_object_notify_queue_clear (GObject *object,
GObjectNotifyQueue *nqueue)
{
g_return_if_fail (nqueue->freeze_count > 0);
G_LOCK(notify_lock);
g_slist_free (nqueue->pspecs);
nqueue->pspecs = NULL;
nqueue->n_pspecs = 0;
G_UNLOCK(notify_lock);
}
static inline void
g_object_notify_queue_add (GObject *object,
GObjectNotifyQueue *nqueue,
GParamSpec *pspec)
{
if (pspec->flags & G_PARAM_READABLE)
{
GParamSpec *redirect;
G_LOCK(notify_lock);
g_return_if_fail (nqueue->n_pspecs < 65535);
redirect = g_param_spec_get_redirect_target (pspec);
if (redirect)
pspec = redirect;
/* we do the deduping in _thaw */
if (g_slist_find (nqueue->pspecs, pspec) == NULL)
{
nqueue->pspecs = g_slist_prepend (nqueue->pspecs, pspec);
nqueue->n_pspecs++;
}
G_UNLOCK(notify_lock);
}
}
/* NB: This function is not threadsafe, do not ever use it if
* you need a threadsafe notify queue.
* Use g_object_notify_queue_freeze() to acquire the queue and
* g_object_notify_queue_thaw() after you are done instead.
*/
static inline GObjectNotifyQueue*
g_object_notify_queue_from_object (GObject *object,
GObjectNotifyContext *context)
{
return g_datalist_id_get_data (&object->qdata, context->quark_notify_queue);
}
G_END_DECLS
#endif /* __G_OBJECT_NOTIFY_QUEUE_H__ */

1619
gobject/gparam.c Normal file

File diff suppressed because it is too large Load diff

458
gobject/gparam.h Normal file
View file

@ -0,0 +1,458 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 1997-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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* gparam.h: GParamSpec base class implementation
*/
#ifndef __G_PARAM_H__
#define __G_PARAM_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <gobject/gvalue.h>
G_BEGIN_DECLS
/* --- standard type macros --- */
/**
* G_TYPE_IS_PARAM:
* @type: a #GType ID
*
* Checks whether @type "is a" %G_TYPE_PARAM.
*/
#define G_TYPE_IS_PARAM(type) (G_TYPE_FUNDAMENTAL (type) == G_TYPE_PARAM)
/**
* G_PARAM_SPEC:
* @pspec: a valid #GParamSpec
*
* Casts a derived #GParamSpec object (e.g. of type #GParamSpecInt) into
* a #GParamSpec object.
*/
#define G_PARAM_SPEC(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), G_TYPE_PARAM, GParamSpec))
/**
* G_IS_PARAM_SPEC:
* @pspec: a #GParamSpec
*
* Checks whether @pspec "is a" valid #GParamSpec structure of type %G_TYPE_PARAM
* or derived.
*/
#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_42
#define G_IS_PARAM_SPEC(pspec) (G_TYPE_CHECK_INSTANCE_FUNDAMENTAL_TYPE ((pspec), G_TYPE_PARAM))
#else
#define G_IS_PARAM_SPEC(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), G_TYPE_PARAM))
#endif
/**
* G_PARAM_SPEC_CLASS:
* @pclass: a valid #GParamSpecClass
*
* Casts a derived #GParamSpecClass structure into a #GParamSpecClass structure.
*/
#define G_PARAM_SPEC_CLASS(pclass) (G_TYPE_CHECK_CLASS_CAST ((pclass), G_TYPE_PARAM, GParamSpecClass))
/**
* G_IS_PARAM_SPEC_CLASS:
* @pclass: a #GParamSpecClass
*
* Checks whether @pclass "is a" valid #GParamSpecClass structure of type
* %G_TYPE_PARAM or derived.
*/
#define G_IS_PARAM_SPEC_CLASS(pclass) (G_TYPE_CHECK_CLASS_TYPE ((pclass), G_TYPE_PARAM))
/**
* G_PARAM_SPEC_GET_CLASS:
* @pspec: a valid #GParamSpec
*
* Retrieves the #GParamSpecClass of a #GParamSpec.
*/
#define G_PARAM_SPEC_GET_CLASS(pspec) (G_TYPE_INSTANCE_GET_CLASS ((pspec), G_TYPE_PARAM, GParamSpecClass))
/* --- convenience macros --- */
/**
* G_PARAM_SPEC_TYPE:
* @pspec: a valid #GParamSpec
*
* Retrieves the #GType of this @pspec.
*/
#define G_PARAM_SPEC_TYPE(pspec) (G_TYPE_FROM_INSTANCE (pspec))
/**
* G_PARAM_SPEC_TYPE_NAME:
* @pspec: a valid #GParamSpec
*
* Retrieves the #GType name of this @pspec.
*/
#define G_PARAM_SPEC_TYPE_NAME(pspec) (g_type_name (G_PARAM_SPEC_TYPE (pspec)))
/**
* G_PARAM_SPEC_VALUE_TYPE:
* @pspec: a valid #GParamSpec
*
* Retrieves the #GType to initialize a #GValue for this parameter.
*/
#define G_PARAM_SPEC_VALUE_TYPE(pspec) (G_PARAM_SPEC (pspec)->value_type)
/**
* G_VALUE_HOLDS_PARAM:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values derived from type %G_TYPE_PARAM.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_PARAM(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_PARAM))
/* --- flags --- */
/**
* GParamFlags:
* @G_PARAM_READABLE: the parameter is readable
* @G_PARAM_WRITABLE: the parameter is writable
* @G_PARAM_READWRITE: alias for %G_PARAM_READABLE | %G_PARAM_WRITABLE
* @G_PARAM_CONSTRUCT: the parameter will be set upon object construction
* @G_PARAM_CONSTRUCT_ONLY: the parameter can only be set upon object construction
* @G_PARAM_LAX_VALIDATION: upon parameter conversion (see g_param_value_convert())
* strict validation is not required
* @G_PARAM_STATIC_NAME: the string used as name when constructing the
* parameter is guaranteed to remain valid and
* unmodified for the lifetime of the parameter.
* Since 2.8
* @G_PARAM_STATIC_NICK: the string used as nick when constructing the
* parameter is guaranteed to remain valid and
* unmmodified for the lifetime of the parameter.
* Since 2.8
* @G_PARAM_STATIC_BLURB: the string used as blurb when constructing the
* parameter is guaranteed to remain valid and
* unmodified for the lifetime of the parameter.
* Since 2.8
* @G_PARAM_EXPLICIT_NOTIFY: calls to g_object_set_property() for this
* property will not automatically result in a "notify" signal being
* emitted: the implementation must call g_object_notify() themselves
* in case the property actually changes. Since: 2.42.
* @G_PARAM_PRIVATE: internal
* @G_PARAM_DEPRECATED: the parameter is deprecated and will be removed
* in a future version. A warning will be generated if it is used
* while running with G_ENABLE_DIAGNOSTIC=1.
* Since 2.26
*
* Through the #GParamFlags flag values, certain aspects of parameters
* can be configured.
*
* See also: %G_PARAM_STATIC_STRINGS
*/
typedef enum
{
G_PARAM_READABLE = 1 << 0,
G_PARAM_WRITABLE = 1 << 1,
G_PARAM_READWRITE = (G_PARAM_READABLE | G_PARAM_WRITABLE),
G_PARAM_CONSTRUCT = 1 << 2,
G_PARAM_CONSTRUCT_ONLY = 1 << 3,
G_PARAM_LAX_VALIDATION = 1 << 4,
G_PARAM_STATIC_NAME = 1 << 5,
G_PARAM_PRIVATE GLIB_DEPRECATED_ENUMERATOR_IN_2_26 = G_PARAM_STATIC_NAME,
G_PARAM_STATIC_NICK = 1 << 6,
G_PARAM_STATIC_BLURB = 1 << 7,
/* User defined flags go here */
G_PARAM_EXPLICIT_NOTIFY = 1 << 30,
/* Avoid warning with -Wpedantic for gcc6 */
G_PARAM_DEPRECATED = (gint)(1u << 31)
} GParamFlags;
/**
* G_PARAM_STATIC_STRINGS:
*
* #GParamFlags value alias for %G_PARAM_STATIC_NAME | %G_PARAM_STATIC_NICK | %G_PARAM_STATIC_BLURB.
*
* Since 2.13.0
*/
#define G_PARAM_STATIC_STRINGS (G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)
/* bits in the range 0xffffff00 are reserved for 3rd party usage */
/**
* G_PARAM_MASK:
*
* Mask containing the bits of #GParamSpec.flags which are reserved for GLib.
*/
#define G_PARAM_MASK (0x000000ff)
/**
* G_PARAM_USER_SHIFT:
*
* Minimum shift count to be used for user defined flags, to be stored in
* #GParamSpec.flags. The maximum allowed is 10.
*/
#define G_PARAM_USER_SHIFT (8)
/* --- typedefs & structures --- */
typedef struct _GParamSpec GParamSpec;
typedef struct _GParamSpecClass GParamSpecClass;
typedef struct _GParameter GParameter GLIB_DEPRECATED_TYPE_IN_2_54;
typedef struct _GParamSpecPool GParamSpecPool;
/**
* GParamSpec: (ref-func g_param_spec_ref_sink) (unref-func g_param_spec_unref) (set-value-func g_value_set_param) (get-value-func g_value_get_param)
* @g_type_instance: private #GTypeInstance portion
* @name: name of this parameter: always an interned string
* @flags: #GParamFlags flags for this parameter
* @value_type: the #GValue type for this parameter
* @owner_type: #GType type that uses (introduces) this parameter
*
* All other fields of the GParamSpec struct are private and
* should not be used directly.
*/
struct _GParamSpec
{
GTypeInstance g_type_instance;
const gchar *name; /* interned string */
GParamFlags flags;
GType value_type;
GType owner_type; /* class or interface using this property */
/*< private >*/
gchar *_nick;
gchar *_blurb;
GData *qdata;
guint ref_count;
guint param_id; /* sort-criteria */
};
/**
* GParamSpecClass:
* @g_type_class: the parent class
* @value_type: the #GValue type for this parameter
* @finalize: The instance finalization function (optional), should chain
* up to the finalize method of the parent class.
* @value_set_default: Resets a @value to the default value for this type
* (recommended, the default is g_value_reset()), see
* g_param_value_set_default().
* @value_validate: Ensures that the contents of @value comply with the
* specifications set out by this type (optional), see
* g_param_value_validate().
* @values_cmp: Compares @value1 with @value2 according to this type
* (recommended, the default is memcmp()), see g_param_values_cmp().
*
* The class structure for the GParamSpec type.
* Normally, GParamSpec classes are filled by
* g_param_type_register_static().
*/
struct _GParamSpecClass
{
GTypeClass g_type_class;
GType value_type;
void (*finalize) (GParamSpec *pspec);
/* GParam methods */
void (*value_set_default) (GParamSpec *pspec,
GValue *value);
gboolean (*value_validate) (GParamSpec *pspec,
GValue *value);
gint (*values_cmp) (GParamSpec *pspec,
const GValue *value1,
const GValue *value2);
/*< private >*/
gpointer dummy[4];
};
/**
* GParameter:
* @name: the parameter name
* @value: the parameter value
*
* The GParameter struct is an auxiliary structure used
* to hand parameter name/value pairs to g_object_newv().
*
* Deprecated: 2.54: This type is not introspectable.
*/
struct _GParameter /* auxiliary structure for _setv() variants */
{
const gchar *name;
GValue value;
} GLIB_DEPRECATED_TYPE_IN_2_54;
/* --- prototypes --- */
GLIB_AVAILABLE_IN_ALL
GParamSpec* g_param_spec_ref (GParamSpec *pspec);
GLIB_AVAILABLE_IN_ALL
void g_param_spec_unref (GParamSpec *pspec);
GLIB_AVAILABLE_IN_ALL
void g_param_spec_sink (GParamSpec *pspec);
GLIB_AVAILABLE_IN_ALL
GParamSpec* g_param_spec_ref_sink (GParamSpec *pspec);
GLIB_AVAILABLE_IN_ALL
gpointer g_param_spec_get_qdata (GParamSpec *pspec,
GQuark quark);
GLIB_AVAILABLE_IN_ALL
void g_param_spec_set_qdata (GParamSpec *pspec,
GQuark quark,
gpointer data);
GLIB_AVAILABLE_IN_ALL
void g_param_spec_set_qdata_full (GParamSpec *pspec,
GQuark quark,
gpointer data,
GDestroyNotify destroy);
GLIB_AVAILABLE_IN_ALL
gpointer g_param_spec_steal_qdata (GParamSpec *pspec,
GQuark quark);
GLIB_AVAILABLE_IN_ALL
GParamSpec* g_param_spec_get_redirect_target (GParamSpec *pspec);
GLIB_AVAILABLE_IN_ALL
void g_param_value_set_default (GParamSpec *pspec,
GValue *value);
GLIB_AVAILABLE_IN_ALL
gboolean g_param_value_defaults (GParamSpec *pspec,
const GValue *value);
GLIB_AVAILABLE_IN_ALL
gboolean g_param_value_validate (GParamSpec *pspec,
GValue *value);
GLIB_AVAILABLE_IN_ALL
gboolean g_param_value_convert (GParamSpec *pspec,
const GValue *src_value,
GValue *dest_value,
gboolean strict_validation);
GLIB_AVAILABLE_IN_ALL
gint g_param_values_cmp (GParamSpec *pspec,
const GValue *value1,
const GValue *value2);
GLIB_AVAILABLE_IN_ALL
const gchar * g_param_spec_get_name (GParamSpec *pspec);
GLIB_AVAILABLE_IN_ALL
const gchar * g_param_spec_get_nick (GParamSpec *pspec);
GLIB_AVAILABLE_IN_ALL
const gchar * g_param_spec_get_blurb (GParamSpec *pspec);
GLIB_AVAILABLE_IN_ALL
void g_value_set_param (GValue *value,
GParamSpec *param);
GLIB_AVAILABLE_IN_ALL
GParamSpec* g_value_get_param (const GValue *value);
GLIB_AVAILABLE_IN_ALL
GParamSpec* g_value_dup_param (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_take_param (GValue *value,
GParamSpec *param);
GLIB_DEPRECATED_FOR(g_value_take_param)
void g_value_set_param_take_ownership (GValue *value,
GParamSpec *param);
GLIB_AVAILABLE_IN_2_36
const GValue * g_param_spec_get_default_value (GParamSpec *pspec);
GLIB_AVAILABLE_IN_2_46
GQuark g_param_spec_get_name_quark (GParamSpec *pspec);
/* --- convenience functions --- */
typedef struct _GParamSpecTypeInfo GParamSpecTypeInfo;
/**
* GParamSpecTypeInfo:
* @instance_size: Size of the instance (object) structure.
* @n_preallocs: Prior to GLib 2.10, it specified the number of pre-allocated (cached) instances to reserve memory for (0 indicates no caching). Since GLib 2.10, it is ignored, since instances are allocated with the [slice allocator][glib-Memory-Slices] now.
* @instance_init: Location of the instance initialization function (optional).
* @value_type: The #GType of values conforming to this #GParamSpec
* @finalize: The instance finalization function (optional).
* @value_set_default: Resets a @value to the default value for @pspec
* (recommended, the default is g_value_reset()), see
* g_param_value_set_default().
* @value_validate: Ensures that the contents of @value comply with the
* specifications set out by @pspec (optional), see
* g_param_value_validate().
* @values_cmp: Compares @value1 with @value2 according to @pspec
* (recommended, the default is memcmp()), see g_param_values_cmp().
*
* This structure is used to provide the type system with the information
* required to initialize and destruct (finalize) a parameter's class and
* instances thereof.
*
* The initialized structure is passed to the g_param_type_register_static()
* The type system will perform a deep copy of this structure, so its memory
* does not need to be persistent across invocation of
* g_param_type_register_static().
*/
struct _GParamSpecTypeInfo
{
/* type system portion */
guint16 instance_size; /* obligatory */
guint16 n_preallocs; /* optional */
void (*instance_init) (GParamSpec *pspec); /* optional */
/* class portion */
GType value_type; /* obligatory */
void (*finalize) (GParamSpec *pspec); /* optional */
void (*value_set_default) (GParamSpec *pspec, /* recommended */
GValue *value);
gboolean (*value_validate) (GParamSpec *pspec, /* optional */
GValue *value);
gint (*values_cmp) (GParamSpec *pspec, /* recommended */
const GValue *value1,
const GValue *value2);
};
GLIB_AVAILABLE_IN_ALL
GType g_param_type_register_static (const gchar *name,
const GParamSpecTypeInfo *pspec_info);
GLIB_AVAILABLE_IN_2_66
gboolean g_param_spec_is_valid_name (const gchar *name);
/* For registering builting types */
GType _g_param_type_register_static_constant (const gchar *name,
const GParamSpecTypeInfo *pspec_info,
GType opt_type);
/* --- protected --- */
GLIB_AVAILABLE_IN_ALL
gpointer g_param_spec_internal (GType param_type,
const gchar *name,
const gchar *nick,
const gchar *blurb,
GParamFlags flags);
GLIB_AVAILABLE_IN_ALL
GParamSpecPool* g_param_spec_pool_new (gboolean type_prefixing);
GLIB_AVAILABLE_IN_ALL
void g_param_spec_pool_insert (GParamSpecPool *pool,
GParamSpec *pspec,
GType owner_type);
GLIB_AVAILABLE_IN_ALL
void g_param_spec_pool_remove (GParamSpecPool *pool,
GParamSpec *pspec);
GLIB_AVAILABLE_IN_ALL
GParamSpec* g_param_spec_pool_lookup (GParamSpecPool *pool,
const gchar *param_name,
GType owner_type,
gboolean walk_ancestors);
GLIB_AVAILABLE_IN_ALL
GList* g_param_spec_pool_list_owned (GParamSpecPool *pool,
GType owner_type);
GLIB_AVAILABLE_IN_ALL
GParamSpec** g_param_spec_pool_list (GParamSpecPool *pool,
GType owner_type,
guint *n_pspecs_p);
/* contracts:
*
* gboolean value_validate (GParamSpec *pspec,
* GValue *value):
* modify value contents in the least destructive way, so
* that it complies with pspec's requirements (i.e.
* according to minimum/maximum ranges etc...). return
* whether modification was necessary.
*
* gint values_cmp (GParamSpec *pspec,
* const GValue *value1,
* const GValue *value2):
* return value1 - value2, i.e. (-1) if value1 < value2,
* (+1) if value1 > value2, and (0) otherwise (equality)
*/
G_END_DECLS
#endif /* __G_PARAM_H__ */

2622
gobject/gparamspecs.c Normal file

File diff suppressed because it is too large Load diff

1173
gobject/gparamspecs.h Normal file

File diff suppressed because it is too large Load diff

4075
gobject/gsignal.c Normal file

File diff suppressed because it is too large Load diff

640
gobject/gsignal.h Normal file
View file

@ -0,0 +1,640 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2000-2001 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __G_SIGNAL_H__
#define __G_SIGNAL_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <gobject/gclosure.h>
#include <gobject/gvalue.h>
#include <gobject/gparam.h>
#include <gobject/gmarshal.h>
G_BEGIN_DECLS
/* --- typedefs --- */
typedef struct _GSignalQuery GSignalQuery;
typedef struct _GSignalInvocationHint GSignalInvocationHint;
/**
* GSignalCMarshaller:
*
* This is the signature of marshaller functions, required to marshall
* arrays of parameter values to signal emissions into C language callback
* invocations.
*
* It is merely an alias to #GClosureMarshal since the #GClosure mechanism
* takes over responsibility of actual function invocation for the signal
* system.
*/
typedef GClosureMarshal GSignalCMarshaller;
/**
* GSignalCVaMarshaller:
*
* This is the signature of va_list marshaller functions, an optional
* marshaller that can be used in some situations to avoid
* marshalling the signal argument into GValues.
*/
typedef GVaClosureMarshal GSignalCVaMarshaller;
/**
* GSignalEmissionHook:
* @ihint: Signal invocation hint, see #GSignalInvocationHint.
* @n_param_values: the number of parameters to the function, including
* the instance on which the signal was emitted.
* @param_values: (array length=n_param_values): the instance on which
* the signal was emitted, followed by the parameters of the emission.
* @data: user data associated with the hook.
*
* A simple function pointer to get invoked when the signal is emitted.
*
* Emission hooks allow you to tie a hook to the signal type, so that it will
* trap all emissions of that signal, from any object.
*
* You may not attach these to signals created with the %G_SIGNAL_NO_HOOKS flag.
*
* Returns: whether it wants to stay connected. If it returns %FALSE, the signal
* hook is disconnected (and destroyed).
*/
typedef gboolean (*GSignalEmissionHook) (GSignalInvocationHint *ihint,
guint n_param_values,
const GValue *param_values,
gpointer data);
/**
* GSignalAccumulator:
* @ihint: Signal invocation hint, see #GSignalInvocationHint.
* @return_accu: Accumulator to collect callback return values in, this
* is the return value of the current signal emission.
* @handler_return: A #GValue holding the return value of the signal handler.
* @data: Callback data that was specified when creating the signal.
*
* The signal accumulator is a special callback function that can be used
* to collect return values of the various callbacks that are called
* during a signal emission.
*
* The signal accumulator is specified at signal creation time, if it is
* left %NULL, no accumulation of callback return values is performed.
* The return value of signal emissions is then the value returned by the
* last callback.
*
* Returns: The accumulator function returns whether the signal emission
* should be aborted. Returning %TRUE will continue with
* the signal emission. Returning %FALSE will abort the current emission.
* Since 2.62, returning %FALSE will skip to the CLEANUP stage. In this case,
* emission will occur as normal in the CLEANUP stage and the handler's
* return value will be accumulated.
*/
typedef gboolean (*GSignalAccumulator) (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer data);
/* --- run, match and connect types --- */
/**
* GSignalFlags:
* @G_SIGNAL_RUN_FIRST: Invoke the object method handler in the first emission stage.
* @G_SIGNAL_RUN_LAST: Invoke the object method handler in the third emission stage.
* @G_SIGNAL_RUN_CLEANUP: Invoke the object method handler in the last emission stage.
* @G_SIGNAL_NO_RECURSE: Signals being emitted for an object while currently being in
* emission for this very object will not be emitted recursively,
* but instead cause the first emission to be restarted.
* @G_SIGNAL_DETAILED: This signal supports "::detail" appendices to the signal name
* upon handler connections and emissions.
* @G_SIGNAL_ACTION: Action signals are signals that may freely be emitted on alive
* objects from user code via g_signal_emit() and friends, without
* the need of being embedded into extra code that performs pre or
* post emission adjustments on the object. They can also be thought
* of as object methods which can be called generically by
* third-party code.
* @G_SIGNAL_NO_HOOKS: No emissions hooks are supported for this signal.
* @G_SIGNAL_MUST_COLLECT: Varargs signal emission will always collect the
* arguments, even if there are no signal handlers connected. Since 2.30.
* @G_SIGNAL_DEPRECATED: The signal is deprecated and will be removed
* in a future version. A warning will be generated if it is connected while
* running with G_ENABLE_DIAGNOSTIC=1. Since 2.32.
* @G_SIGNAL_ACCUMULATOR_FIRST_RUN: Only used in #GSignalAccumulator accumulator
* functions for the #GSignalInvocationHint::run_type field to mark the first
* call to the accumulator function for a signal emission. Since 2.68.
*
* The signal flags are used to specify a signal's behaviour.
*/
typedef enum
{
G_SIGNAL_RUN_FIRST = 1 << 0,
G_SIGNAL_RUN_LAST = 1 << 1,
G_SIGNAL_RUN_CLEANUP = 1 << 2,
G_SIGNAL_NO_RECURSE = 1 << 3,
G_SIGNAL_DETAILED = 1 << 4,
G_SIGNAL_ACTION = 1 << 5,
G_SIGNAL_NO_HOOKS = 1 << 6,
G_SIGNAL_MUST_COLLECT = 1 << 7,
G_SIGNAL_DEPRECATED = 1 << 8,
/* normal signal flags until 1 << 16 */
G_SIGNAL_ACCUMULATOR_FIRST_RUN = 1 << 17,
} GSignalFlags;
/**
* G_SIGNAL_FLAGS_MASK:
*
* A mask for all #GSignalFlags bits.
*/
#define G_SIGNAL_FLAGS_MASK 0x1ff
/**
* GConnectFlags:
* @G_CONNECT_AFTER: whether the handler should be called before or after the
* default handler of the signal.
* @G_CONNECT_SWAPPED: whether the instance and data should be swapped when
* calling the handler; see g_signal_connect_swapped() for an example.
*
* The connection flags are used to specify the behaviour of a signal's
* connection.
*/
typedef enum
{
G_CONNECT_AFTER = 1 << 0,
G_CONNECT_SWAPPED = 1 << 1
} GConnectFlags;
/**
* GSignalMatchType:
* @G_SIGNAL_MATCH_ID: The signal id must be equal.
* @G_SIGNAL_MATCH_DETAIL: The signal detail must be equal.
* @G_SIGNAL_MATCH_CLOSURE: The closure must be the same.
* @G_SIGNAL_MATCH_FUNC: The C closure callback must be the same.
* @G_SIGNAL_MATCH_DATA: The closure data must be the same.
* @G_SIGNAL_MATCH_UNBLOCKED: Only unblocked signals may be matched.
*
* The match types specify what g_signal_handlers_block_matched(),
* g_signal_handlers_unblock_matched() and g_signal_handlers_disconnect_matched()
* match signals by.
*/
typedef enum
{
G_SIGNAL_MATCH_ID = 1 << 0,
G_SIGNAL_MATCH_DETAIL = 1 << 1,
G_SIGNAL_MATCH_CLOSURE = 1 << 2,
G_SIGNAL_MATCH_FUNC = 1 << 3,
G_SIGNAL_MATCH_DATA = 1 << 4,
G_SIGNAL_MATCH_UNBLOCKED = 1 << 5
} GSignalMatchType;
/**
* G_SIGNAL_MATCH_MASK:
*
* A mask for all #GSignalMatchType bits.
*/
#define G_SIGNAL_MATCH_MASK 0x3f
/**
* G_SIGNAL_TYPE_STATIC_SCOPE:
*
* This macro flags signal argument types for which the signal system may
* assume that instances thereof remain persistent across all signal emissions
* they are used in. This is only useful for non ref-counted, value-copy types.
*
* To flag a signal argument in this way, add `| G_SIGNAL_TYPE_STATIC_SCOPE`
* to the corresponding argument of g_signal_new().
* |[
* g_signal_new ("size_request",
* G_TYPE_FROM_CLASS (gobject_class),
* G_SIGNAL_RUN_FIRST,
* G_STRUCT_OFFSET (GtkWidgetClass, size_request),
* NULL, NULL,
* _gtk_marshal_VOID__BOXED,
* G_TYPE_NONE, 1,
* GTK_TYPE_REQUISITION | G_SIGNAL_TYPE_STATIC_SCOPE);
* ]|
*/
#define G_SIGNAL_TYPE_STATIC_SCOPE (G_TYPE_FLAG_RESERVED_ID_BIT)
/* --- signal information --- */
/**
* GSignalInvocationHint:
* @signal_id: The signal id of the signal invoking the callback
* @detail: The detail passed on for this emission
* @run_type: The stage the signal emission is currently in, this
* field will contain one of %G_SIGNAL_RUN_FIRST,
* %G_SIGNAL_RUN_LAST or %G_SIGNAL_RUN_CLEANUP and %G_SIGNAL_ACCUMULATOR_FIRST_RUN.
* %G_SIGNAL_ACCUMULATOR_FIRST_RUN is only set for the first run of the accumulator
* function for a signal emission.
*
* The #GSignalInvocationHint structure is used to pass on additional information
* to callbacks during a signal emission.
*/
struct _GSignalInvocationHint
{
guint signal_id;
GQuark detail;
GSignalFlags run_type;
};
/**
* GSignalQuery:
* @signal_id: The signal id of the signal being queried, or 0 if the
* signal to be queried was unknown.
* @signal_name: The signal name.
* @itype: The interface/instance type that this signal can be emitted for.
* @signal_flags: The signal flags as passed in to g_signal_new().
* @return_type: The return type for user callbacks.
* @n_params: The number of parameters that user callbacks take.
* @param_types: (array length=n_params): The individual parameter types for
* user callbacks, note that the effective callback signature is:
* |[<!-- language="C" -->
* @return_type callback (#gpointer data1,
* [param_types param_names,]
* gpointer data2);
* ]|
*
* A structure holding in-depth information for a specific signal.
*
* See also: g_signal_query()
*/
struct _GSignalQuery
{
guint signal_id;
const gchar *signal_name;
GType itype;
GSignalFlags signal_flags;
GType return_type; /* mangled with G_SIGNAL_TYPE_STATIC_SCOPE flag */
guint n_params;
const GType *param_types; /* mangled with G_SIGNAL_TYPE_STATIC_SCOPE flag */
};
/* --- signals --- */
GLIB_AVAILABLE_IN_ALL
guint g_signal_newv (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
GClosure *class_closure,
GSignalAccumulator accumulator,
gpointer accu_data,
GSignalCMarshaller c_marshaller,
GType return_type,
guint n_params,
GType *param_types);
GLIB_AVAILABLE_IN_ALL
guint g_signal_new_valist (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
GClosure *class_closure,
GSignalAccumulator accumulator,
gpointer accu_data,
GSignalCMarshaller c_marshaller,
GType return_type,
guint n_params,
va_list args);
GLIB_AVAILABLE_IN_ALL
guint g_signal_new (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
guint class_offset,
GSignalAccumulator accumulator,
gpointer accu_data,
GSignalCMarshaller c_marshaller,
GType return_type,
guint n_params,
...);
GLIB_AVAILABLE_IN_ALL
guint g_signal_new_class_handler (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
GCallback class_handler,
GSignalAccumulator accumulator,
gpointer accu_data,
GSignalCMarshaller c_marshaller,
GType return_type,
guint n_params,
...);
GLIB_AVAILABLE_IN_ALL
void g_signal_set_va_marshaller (guint signal_id,
GType instance_type,
GSignalCVaMarshaller va_marshaller);
GLIB_AVAILABLE_IN_ALL
void g_signal_emitv (const GValue *instance_and_params,
guint signal_id,
GQuark detail,
GValue *return_value);
GLIB_AVAILABLE_IN_ALL
void g_signal_emit_valist (gpointer instance,
guint signal_id,
GQuark detail,
va_list var_args);
GLIB_AVAILABLE_IN_ALL
void g_signal_emit (gpointer instance,
guint signal_id,
GQuark detail,
...);
GLIB_AVAILABLE_IN_ALL
void g_signal_emit_by_name (gpointer instance,
const gchar *detailed_signal,
...);
GLIB_AVAILABLE_IN_ALL
guint g_signal_lookup (const gchar *name,
GType itype);
GLIB_AVAILABLE_IN_ALL
const gchar * g_signal_name (guint signal_id);
GLIB_AVAILABLE_IN_ALL
void g_signal_query (guint signal_id,
GSignalQuery *query);
GLIB_AVAILABLE_IN_ALL
guint* g_signal_list_ids (GType itype,
guint *n_ids);
GLIB_AVAILABLE_IN_2_66
gboolean g_signal_is_valid_name (const gchar *name);
GLIB_AVAILABLE_IN_ALL
gboolean g_signal_parse_name (const gchar *detailed_signal,
GType itype,
guint *signal_id_p,
GQuark *detail_p,
gboolean force_detail_quark);
GLIB_AVAILABLE_IN_ALL
GSignalInvocationHint* g_signal_get_invocation_hint (gpointer instance);
/* --- signal emissions --- */
GLIB_AVAILABLE_IN_ALL
void g_signal_stop_emission (gpointer instance,
guint signal_id,
GQuark detail);
GLIB_AVAILABLE_IN_ALL
void g_signal_stop_emission_by_name (gpointer instance,
const gchar *detailed_signal);
GLIB_AVAILABLE_IN_ALL
gulong g_signal_add_emission_hook (guint signal_id,
GQuark detail,
GSignalEmissionHook hook_func,
gpointer hook_data,
GDestroyNotify data_destroy);
GLIB_AVAILABLE_IN_ALL
void g_signal_remove_emission_hook (guint signal_id,
gulong hook_id);
/* --- signal handlers --- */
GLIB_AVAILABLE_IN_ALL
gboolean g_signal_has_handler_pending (gpointer instance,
guint signal_id,
GQuark detail,
gboolean may_be_blocked);
GLIB_AVAILABLE_IN_ALL
gulong g_signal_connect_closure_by_id (gpointer instance,
guint signal_id,
GQuark detail,
GClosure *closure,
gboolean after);
GLIB_AVAILABLE_IN_ALL
gulong g_signal_connect_closure (gpointer instance,
const gchar *detailed_signal,
GClosure *closure,
gboolean after);
GLIB_AVAILABLE_IN_ALL
gulong g_signal_connect_data (gpointer instance,
const gchar *detailed_signal,
GCallback c_handler,
gpointer data,
GClosureNotify destroy_data,
GConnectFlags connect_flags);
GLIB_AVAILABLE_IN_ALL
void g_signal_handler_block (gpointer instance,
gulong handler_id);
GLIB_AVAILABLE_IN_ALL
void g_signal_handler_unblock (gpointer instance,
gulong handler_id);
GLIB_AVAILABLE_IN_ALL
void g_signal_handler_disconnect (gpointer instance,
gulong handler_id);
GLIB_AVAILABLE_IN_ALL
gboolean g_signal_handler_is_connected (gpointer instance,
gulong handler_id);
GLIB_AVAILABLE_IN_ALL
gulong g_signal_handler_find (gpointer instance,
GSignalMatchType mask,
guint signal_id,
GQuark detail,
GClosure *closure,
gpointer func,
gpointer data);
GLIB_AVAILABLE_IN_ALL
guint g_signal_handlers_block_matched (gpointer instance,
GSignalMatchType mask,
guint signal_id,
GQuark detail,
GClosure *closure,
gpointer func,
gpointer data);
GLIB_AVAILABLE_IN_ALL
guint g_signal_handlers_unblock_matched (gpointer instance,
GSignalMatchType mask,
guint signal_id,
GQuark detail,
GClosure *closure,
gpointer func,
gpointer data);
GLIB_AVAILABLE_IN_ALL
guint g_signal_handlers_disconnect_matched (gpointer instance,
GSignalMatchType mask,
guint signal_id,
GQuark detail,
GClosure *closure,
gpointer func,
gpointer data);
GLIB_AVAILABLE_IN_2_62
void g_clear_signal_handler (gulong *handler_id_ptr,
gpointer instance);
#define g_clear_signal_handler(handler_id_ptr, instance) \
G_STMT_START { \
gpointer const _instance = (instance); \
gulong *const _handler_id_ptr = (handler_id_ptr); \
const gulong _handler_id = *_handler_id_ptr; \
\
if (_handler_id > 0) \
{ \
*_handler_id_ptr = 0; \
g_signal_handler_disconnect (_instance, _handler_id); \
} \
} G_STMT_END \
GLIB_AVAILABLE_MACRO_IN_2_62
/* --- overriding and chaining --- */
GLIB_AVAILABLE_IN_ALL
void g_signal_override_class_closure (guint signal_id,
GType instance_type,
GClosure *class_closure);
GLIB_AVAILABLE_IN_ALL
void g_signal_override_class_handler (const gchar *signal_name,
GType instance_type,
GCallback class_handler);
GLIB_AVAILABLE_IN_ALL
void g_signal_chain_from_overridden (const GValue *instance_and_params,
GValue *return_value);
GLIB_AVAILABLE_IN_ALL
void g_signal_chain_from_overridden_handler (gpointer instance,
...);
/* --- convenience --- */
/**
* g_signal_connect:
* @instance: the instance to connect to.
* @detailed_signal: a string of the form "signal-name::detail".
* @c_handler: the #GCallback to connect.
* @data: data to pass to @c_handler calls.
*
* Connects a #GCallback function to a signal for a particular object.
*
* The handler will be called synchronously, before the default handler of the signal. g_signal_emit() will not return control until all handlers are called.
*
* See [memory management of signal handlers][signal-memory-management] for
* details on how to handle the return value and memory management of @data.
*
* Returns: the handler ID, of type #gulong (always greater than 0 for successful connections)
*/
#define g_signal_connect(instance, detailed_signal, c_handler, data) \
g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0)
/**
* g_signal_connect_after:
* @instance: the instance to connect to.
* @detailed_signal: a string of the form "signal-name::detail".
* @c_handler: the #GCallback to connect.
* @data: data to pass to @c_handler calls.
*
* Connects a #GCallback function to a signal for a particular object.
*
* The handler will be called synchronously, after the default handler of the signal.
*
* Returns: the handler ID, of type #gulong (always greater than 0 for successful connections)
*/
#define g_signal_connect_after(instance, detailed_signal, c_handler, data) \
g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, G_CONNECT_AFTER)
/**
* g_signal_connect_swapped:
* @instance: the instance to connect to.
* @detailed_signal: a string of the form "signal-name::detail".
* @c_handler: the #GCallback to connect.
* @data: data to pass to @c_handler calls.
*
* Connects a #GCallback function to a signal for a particular object.
*
* The instance on which the signal is emitted and @data will be swapped when
* calling the handler. This is useful when calling pre-existing functions to
* operate purely on the @data, rather than the @instance: swapping the
* parameters avoids the need to write a wrapper function.
*
* For example, this allows the shorter code:
* |[<!-- language="C" -->
* g_signal_connect_swapped (button, "clicked",
* (GCallback) gtk_widget_hide, other_widget);
* ]|
*
* Rather than the cumbersome:
* |[<!-- language="C" -->
* static void
* button_clicked_cb (GtkButton *button, GtkWidget *other_widget)
* {
* gtk_widget_hide (other_widget);
* }
*
* ...
*
* g_signal_connect (button, "clicked",
* (GCallback) button_clicked_cb, other_widget);
* ]|
*
* Returns: the handler ID, of type #gulong (always greater than 0 for successful connections)
*/
#define g_signal_connect_swapped(instance, detailed_signal, c_handler, data) \
g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, G_CONNECT_SWAPPED)
/**
* g_signal_handlers_disconnect_by_func:
* @instance: The instance to remove handlers from.
* @func: The C closure callback of the handlers (useless for non-C closures).
* @data: The closure data of the handlers' closures.
*
* Disconnects all handlers on an instance that match @func and @data.
*
* Returns: The number of handlers that matched.
*/
#define g_signal_handlers_disconnect_by_func(instance, func, data) \
g_signal_handlers_disconnect_matched ((instance), \
(GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), \
0, 0, NULL, (func), (data))
/**
* g_signal_handlers_disconnect_by_data:
* @instance: The instance to remove handlers from
* @data: the closure data of the handlers' closures
*
* Disconnects all handlers on an instance that match @data.
*
* Returns: The number of handlers that matched.
*
* Since: 2.32
*/
#define g_signal_handlers_disconnect_by_data(instance, data) \
g_signal_handlers_disconnect_matched ((instance), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, (data))
/**
* g_signal_handlers_block_by_func:
* @instance: The instance to block handlers from.
* @func: The C closure callback of the handlers (useless for non-C closures).
* @data: The closure data of the handlers' closures.
*
* Blocks all handlers on an instance that match @func and @data.
*
* Returns: The number of handlers that matched.
*/
#define g_signal_handlers_block_by_func(instance, func, data) \
g_signal_handlers_block_matched ((instance), \
(GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), \
0, 0, NULL, (func), (data))
/**
* g_signal_handlers_unblock_by_func:
* @instance: The instance to unblock handlers from.
* @func: The C closure callback of the handlers (useless for non-C closures).
* @data: The closure data of the handlers' closures.
*
* Unblocks all handlers on an instance that match @func and @data.
*
* Returns: The number of handlers that matched.
*/
#define g_signal_handlers_unblock_by_func(instance, func, data) \
g_signal_handlers_unblock_matched ((instance), \
(GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), \
0, 0, NULL, (func), (data))
GLIB_AVAILABLE_IN_ALL
gboolean g_signal_accumulator_true_handled (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer dummy);
GLIB_AVAILABLE_IN_ALL
gboolean g_signal_accumulator_first_wins (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer dummy);
/*< private >*/
GLIB_AVAILABLE_IN_ALL
void g_signal_handlers_destroy (gpointer instance);
void _g_signals_destroy (GType itype);
G_END_DECLS
#endif /* __G_SIGNAL_H__ */

912
gobject/gsignalgroup.c Normal file
View file

@ -0,0 +1,912 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
*
* Copyright (C) 2015-2022 Christian Hergert <christian@hergert.me>
* Copyright (C) 2015 Garrett Regier <garrettregier@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include "glib.h"
#include "glibintl.h"
#include "gparamspecs.h"
#include "gsignalgroup.h"
#include "gvaluetypes.h"
/**
* SECTION:gsignalgroup
* @Title: GSignalGroup
* @Short_description: Manage a collection of signals on a GObject
*
* #GSignalGroup manages to simplify the process of connecting
* many signals to a #GObject as a group. As such there is no API
* to disconnect a signal from the group.
*
* In particular, this allows you to:
*
* - Change the target instance, which automatically causes disconnection
* of the signals from the old instance and connecting to the new instance.
* - Block and unblock signals as a group
* - Ensuring that blocked state transfers across target instances.
*
* One place you might want to use such a structure is with #GtkTextView and
* #GtkTextBuffer. Often times, you'll need to connect to many signals on
* #GtkTextBuffer from a #GtkTextView subclass. This allows you to create a
* signal group during instance construction, simply bind the
* #GtkTextView:buffer property to #GSignalGroup:target and connect
* all the signals you need. When the #GtkTextView:buffer property changes
* all of the signals will be transitioned correctly.
*
* Since: 2.72
*/
struct _GSignalGroup
{
GObject parent_instance;
GWeakRef target_ref;
GRecMutex mutex;
GPtrArray *handlers;
GType target_type;
gssize block_count;
guint has_bound_at_least_once : 1;
};
typedef struct _GSignalGroupClass
{
GObjectClass parent_class;
void (*bind) (GSignalGroup *self,
GObject *target);
} GSignalGroupClass;
typedef struct
{
GSignalGroup *group;
gulong handler_id;
GClosure *closure;
guint signal_id;
GQuark signal_detail;
guint connect_after : 1;
} SignalHandler;
G_DEFINE_TYPE (GSignalGroup, g_signal_group, G_TYPE_OBJECT)
typedef enum
{
PROP_TARGET = 1,
PROP_TARGET_TYPE,
LAST_PROP
} GSignalGroupProperty;
enum
{
BIND,
UNBIND,
LAST_SIGNAL
};
static GParamSpec *properties[LAST_PROP];
static guint signals[LAST_SIGNAL];
static void
g_signal_group_set_target_type (GSignalGroup *self,
GType target_type)
{
g_assert (G_IS_SIGNAL_GROUP (self));
g_assert (g_type_is_a (target_type, G_TYPE_OBJECT));
self->target_type = target_type;
/* The class must be created at least once for the signals
* to be registered, otherwise g_signal_parse_name() will fail
*/
if (G_TYPE_IS_INTERFACE (target_type))
{
if (g_type_default_interface_peek (target_type) == NULL)
g_type_default_interface_unref (g_type_default_interface_ref (target_type));
}
else
{
if (g_type_class_peek (target_type) == NULL)
g_type_class_unref (g_type_class_ref (target_type));
}
}
static void
g_signal_group_gc_handlers (GSignalGroup *self)
{
guint i;
g_assert (G_IS_SIGNAL_GROUP (self));
/*
* Remove any handlers for which the closures have become invalid. We do
* this cleanup lazily to avoid situations where we could have disposal
* active on both the signal group and the peer object.
*/
for (i = self->handlers->len; i > 0; i--)
{
const SignalHandler *handler = g_ptr_array_index (self->handlers, i - 1);
g_assert (handler != NULL);
g_assert (handler->closure != NULL);
if (handler->closure->is_invalid)
g_ptr_array_remove_index (self->handlers, i - 1);
}
}
static void
g_signal_group__target_weak_notify (gpointer data,
GObject *where_object_was)
{
GSignalGroup *self = data;
guint i;
g_assert (G_IS_SIGNAL_GROUP (self));
g_assert (where_object_was != NULL);
g_rec_mutex_lock (&self->mutex);
g_weak_ref_set (&self->target_ref, NULL);
for (i = 0; i < self->handlers->len; i++)
{
SignalHandler *handler = g_ptr_array_index (self->handlers, i);
handler->handler_id = 0;
}
g_signal_emit (self, signals[UNBIND], 0);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TARGET]);
g_rec_mutex_unlock (&self->mutex);
}
static void
g_signal_group_bind_handler (GSignalGroup *self,
SignalHandler *handler,
GObject *target)
{
gssize i;
g_assert (self != NULL);
g_assert (G_IS_OBJECT (target));
g_assert (handler != NULL);
g_assert (handler->signal_id != 0);
g_assert (handler->closure != NULL);
g_assert (handler->closure->is_invalid == 0);
g_assert (handler->handler_id == 0);
handler->handler_id = g_signal_connect_closure_by_id (target,
handler->signal_id,
handler->signal_detail,
handler->closure,
handler->connect_after);
g_assert (handler->handler_id != 0);
for (i = 0; i < self->block_count; i++)
g_signal_handler_block (target, handler->handler_id);
}
static void
g_signal_group_bind (GSignalGroup *self,
GObject *target)
{
GObject *hold;
guint i;
g_assert (G_IS_SIGNAL_GROUP (self));
g_assert (!target || G_IS_OBJECT (target));
if (target == NULL)
return;
self->has_bound_at_least_once = TRUE;
hold = g_object_ref (target);
g_weak_ref_set (&self->target_ref, hold);
g_object_weak_ref (hold, g_signal_group__target_weak_notify, self);
g_signal_group_gc_handlers (self);
for (i = 0; i < self->handlers->len; i++)
{
SignalHandler *handler = g_ptr_array_index (self->handlers, i);
g_signal_group_bind_handler (self, handler, hold);
}
g_signal_emit (self, signals [BIND], 0, hold);
g_object_unref (hold);
}
static void
g_signal_group_unbind (GSignalGroup *self)
{
GObject *target;
guint i;
g_return_if_fail (G_IS_SIGNAL_GROUP (self));
target = g_weak_ref_get (&self->target_ref);
/*
* Target may be NULL by this point, as we got notified of its destruction.
* However, if we're early enough, we may get a full reference back and can
* cleanly disconnect our connections.
*/
if (target != NULL)
{
g_weak_ref_set (&self->target_ref, NULL);
/*
* Let go of our weak reference now that we have a full reference
* for the life of this function.
*/
g_object_weak_unref (target,
g_signal_group__target_weak_notify,
self);
}
g_signal_group_gc_handlers (self);
for (i = 0; i < self->handlers->len; i++)
{
SignalHandler *handler;
gulong handler_id;
handler = g_ptr_array_index (self->handlers, i);
g_assert (handler != NULL);
g_assert (handler->signal_id != 0);
g_assert (handler->closure != NULL);
handler_id = handler->handler_id;
handler->handler_id = 0;
/*
* If @target is NULL, we lost a race to cleanup the weak
* instance and the signal connections have already been
* finalized and therefore nothing to do.
*/
if (target != NULL && handler_id != 0)
g_signal_handler_disconnect (target, handler_id);
}
g_signal_emit (self, signals [UNBIND], 0);
g_clear_object (&target);
}
static gboolean
g_signal_group_check_target_type (GSignalGroup *self,
gpointer target)
{
if ((target != NULL) &&
!g_type_is_a (G_OBJECT_TYPE (target), self->target_type))
{
g_critical ("Failed to set GSignalGroup of target type %s "
"using target %p of type %s",
g_type_name (self->target_type),
target, G_OBJECT_TYPE_NAME (target));
return FALSE;
}
return TRUE;
}
/**
* g_signal_group_block:
* @self: the #GSignalGroup
*
* Blocks all signal handlers managed by @self so they will not
* be called during any signal emissions. Must be unblocked exactly
* the same number of times it has been blocked to become active again.
*
* This blocked state will be kept across changes of the target instance.
*
* Since: 2.72
*/
void
g_signal_group_block (GSignalGroup *self)
{
GObject *target;
guint i;
g_return_if_fail (G_IS_SIGNAL_GROUP (self));
g_return_if_fail (self->block_count >= 0);
g_rec_mutex_lock (&self->mutex);
self->block_count++;
target = g_weak_ref_get (&self->target_ref);
if (target == NULL)
goto unlock;
for (i = 0; i < self->handlers->len; i++)
{
const SignalHandler *handler = g_ptr_array_index (self->handlers, i);
g_assert (handler != NULL);
g_assert (handler->signal_id != 0);
g_assert (handler->closure != NULL);
g_assert (handler->handler_id != 0);
g_signal_handler_block (target, handler->handler_id);
}
g_object_unref (target);
unlock:
g_rec_mutex_unlock (&self->mutex);
}
/**
* g_signal_group_unblock:
* @self: the #GSignalGroup
*
* Unblocks all signal handlers managed by @self so they will be
* called again during any signal emissions unless it is blocked
* again. Must be unblocked exactly the same number of times it
* has been blocked to become active again.
*
* Since: 2.72
*/
void
g_signal_group_unblock (GSignalGroup *self)
{
GObject *target;
guint i;
g_return_if_fail (G_IS_SIGNAL_GROUP (self));
g_return_if_fail (self->block_count > 0);
g_rec_mutex_lock (&self->mutex);
self->block_count--;
target = g_weak_ref_get (&self->target_ref);
if (target == NULL)
goto unlock;
for (i = 0; i < self->handlers->len; i++)
{
const SignalHandler *handler = g_ptr_array_index (self->handlers, i);
g_assert (handler != NULL);
g_assert (handler->signal_id != 0);
g_assert (handler->closure != NULL);
g_assert (handler->handler_id != 0);
g_signal_handler_unblock (target, handler->handler_id);
}
g_object_unref (target);
unlock:
g_rec_mutex_unlock (&self->mutex);
}
/**
* g_signal_group_dup_target:
* @self: the #GSignalGroup
*
* Gets the target instance used when connecting signals.
*
* Returns: (nullable) (transfer full) (type GObject): The target instance
*
* Since: 2.72
*/
gpointer
g_signal_group_dup_target (GSignalGroup *self)
{
GObject *target;
g_return_val_if_fail (G_IS_SIGNAL_GROUP (self), NULL);
g_rec_mutex_lock (&self->mutex);
target = g_weak_ref_get (&self->target_ref);
g_rec_mutex_unlock (&self->mutex);
return target;
}
/**
* g_signal_group_set_target:
* @self: the #GSignalGroup.
* @target: (nullable) (type GObject) (transfer none): The target instance used
* when connecting signals.
*
* Sets the target instance used when connecting signals. Any signal
* that has been registered with g_signal_group_connect_object() or
* similar functions will be connected to this object.
*
* If the target instance was previously set, signals will be
* disconnected from that object prior to connecting to @target.
*
* Since: 2.72
*/
void
g_signal_group_set_target (GSignalGroup *self,
gpointer target)
{
GObject *object;
g_return_if_fail (G_IS_SIGNAL_GROUP (self));
g_rec_mutex_lock (&self->mutex);
object = g_weak_ref_get (&self->target_ref);
if (object == (GObject *)target)
goto cleanup;
if (!g_signal_group_check_target_type (self, target))
goto cleanup;
/* Only emit unbind if we've ever called bind */
if (self->has_bound_at_least_once)
g_signal_group_unbind (self);
g_signal_group_bind (self, target);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TARGET]);
cleanup:
g_clear_object (&object);
g_rec_mutex_unlock (&self->mutex);
}
static void
signal_handler_free (gpointer data)
{
SignalHandler *handler = data;
if (handler->closure != NULL)
g_closure_invalidate (handler->closure);
handler->handler_id = 0;
handler->signal_id = 0;
handler->signal_detail = 0;
g_clear_pointer (&handler->closure, g_closure_unref);
g_slice_free (SignalHandler, handler);
}
static void
g_signal_group_constructed (GObject *object)
{
GSignalGroup *self = (GSignalGroup *)object;
GObject *target;
g_rec_mutex_lock (&self->mutex);
target = g_weak_ref_get (&self->target_ref);
if (!g_signal_group_check_target_type (self, target))
g_signal_group_set_target (self, NULL);
G_OBJECT_CLASS (g_signal_group_parent_class)->constructed (object);
g_clear_object (&target);
g_rec_mutex_unlock (&self->mutex);
}
static void
g_signal_group_dispose (GObject *object)
{
GSignalGroup *self = (GSignalGroup *)object;
g_rec_mutex_lock (&self->mutex);
g_signal_group_gc_handlers (self);
if (self->has_bound_at_least_once)
g_signal_group_unbind (self);
g_clear_pointer (&self->handlers, g_ptr_array_unref);
g_rec_mutex_unlock (&self->mutex);
G_OBJECT_CLASS (g_signal_group_parent_class)->dispose (object);
}
static void
g_signal_group_finalize (GObject *object)
{
GSignalGroup *self = (GSignalGroup *)object;
g_weak_ref_clear (&self->target_ref);
g_rec_mutex_clear (&self->mutex);
G_OBJECT_CLASS (g_signal_group_parent_class)->finalize (object);
}
static void
g_signal_group_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GSignalGroup *self = G_SIGNAL_GROUP (object);
switch ((GSignalGroupProperty) prop_id)
{
case PROP_TARGET:
g_value_take_object (value, g_signal_group_dup_target (self));
break;
case PROP_TARGET_TYPE:
g_value_set_gtype (value, self->target_type);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
g_signal_group_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GSignalGroup *self = G_SIGNAL_GROUP (object);
switch ((GSignalGroupProperty) prop_id)
{
case PROP_TARGET:
g_signal_group_set_target (self, g_value_get_object (value));
break;
case PROP_TARGET_TYPE:
g_signal_group_set_target_type (self, g_value_get_gtype (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
g_signal_group_class_init (GSignalGroupClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = g_signal_group_constructed;
object_class->dispose = g_signal_group_dispose;
object_class->finalize = g_signal_group_finalize;
object_class->get_property = g_signal_group_get_property;
object_class->set_property = g_signal_group_set_property;
/**
* GSignalGroup:target
*
* The target instance used when connecting signals.
*
* Since: 2.72
*/
properties[PROP_TARGET] =
g_param_spec_object ("target",
"Target",
"The target instance used when connecting signals.",
G_TYPE_OBJECT,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
/**
* GSignalGroup:target-type
*
* The #GType of the target property.
*
* Since: 2.72
*/
properties[PROP_TARGET_TYPE] =
g_param_spec_gtype ("target-type",
"Target Type",
"The GType of the target property.",
G_TYPE_OBJECT,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, LAST_PROP, properties);
/**
* GSignalGroup::bind:
* @self: the #GSignalGroup
* @instance: a #GObject containing the new value for #GSignalGroup:target
*
* This signal is emitted when #GSignalGroup:target is set to a new value
* other than %NULL. It is similar to #GObject::notify on `target` except it
* will not emit when #GSignalGroup:target is %NULL and also allows for
* receiving the #GObject without a data-race.
*
* Since: 2.72
*/
signals[BIND] =
g_signal_new ("bind",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE,
1,
G_TYPE_OBJECT);
/**
* GSignalGroup::unbind:
* @self: a #GSignalGroup
*
* This signal is emitted when the target instance of @self is set to a
* new #GObject.
*
* This signal will only be emitted if the previous target of @self is
* non-%NULL.
*
* Since: 2.72
*/
signals[UNBIND] =
g_signal_new ("unbind",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE,
0);
}
static void
g_signal_group_init (GSignalGroup *self)
{
g_rec_mutex_init (&self->mutex);
self->handlers = g_ptr_array_new_with_free_func (signal_handler_free);
self->target_type = G_TYPE_OBJECT;
}
/**
* g_signal_group_new:
* @target_type: the #GType of the target instance.
*
* Creates a new #GSignalGroup for target instances of @target_type.
*
* Returns: (transfer full): a new #GSignalGroup
*
* Since: 2.72
*/
GSignalGroup *
g_signal_group_new (GType target_type)
{
g_return_val_if_fail (g_type_is_a (target_type, G_TYPE_OBJECT), NULL);
return g_object_new (G_TYPE_SIGNAL_GROUP,
"target-type", target_type,
NULL);
}
static void
g_signal_group_connect_full (GSignalGroup *self,
const gchar *detailed_signal,
GCallback c_handler,
gpointer data,
GClosureNotify notify,
GConnectFlags flags,
gboolean is_object)
{
GObject *target;
SignalHandler *handler;
GClosure *closure;
guint signal_id;
GQuark signal_detail;
g_return_if_fail (G_IS_SIGNAL_GROUP (self));
g_return_if_fail (detailed_signal != NULL);
g_return_if_fail (g_signal_parse_name (detailed_signal, self->target_type,
&signal_id, &signal_detail, TRUE) != 0);
g_return_if_fail (c_handler != NULL);
g_return_if_fail (!is_object || G_IS_OBJECT (data));
g_rec_mutex_lock (&self->mutex);
if (self->has_bound_at_least_once)
{
g_critical ("Cannot add signals after setting target");
g_rec_mutex_unlock (&self->mutex);
return;
}
if ((flags & G_CONNECT_SWAPPED) != 0)
closure = g_cclosure_new_swap (c_handler, data, notify);
else
closure = g_cclosure_new (c_handler, data, notify);
handler = g_slice_new0 (SignalHandler);
handler->group = self;
handler->signal_id = signal_id;
handler->signal_detail = signal_detail;
handler->closure = g_closure_ref (closure);
handler->connect_after = ((flags & G_CONNECT_AFTER) != 0);
g_closure_sink (closure);
if (is_object)
{
/* Set closure->is_invalid when data is disposed. We only track this to avoid
* reconnecting in the future. However, we do a round of cleanup when ever we
* connect a new object or the target changes to GC the old handlers.
*/
g_object_watch_closure (data, closure);
}
g_ptr_array_add (self->handlers, handler);
target = g_weak_ref_get (&self->target_ref);
if (target != NULL)
{
g_signal_group_bind_handler (self, handler, target);
g_object_unref (target);
}
/* Lazily remove any old handlers on connect */
g_signal_group_gc_handlers (self);
g_rec_mutex_unlock (&self->mutex);
}
/**
* g_signal_group_connect_object: (skip)
* @self: a #GSignalGroup
* @detailed_signal: a string of the form `signal-name` with optional `::signal-detail`
* @c_handler: (scope notified): the #GCallback to connect
* @object: (not nullable) (transfer none): the #GObject to pass as data to @c_handler calls
* @flags: #GConnectFlags for the signal connection
*
* Connects @c_handler to the signal @detailed_signal on #GSignalGroup:target.
*
* Ensures that the @object stays alive during the call to @c_handler
* by temporarily adding a reference count. When the @object is destroyed
* the signal handler will automatically be removed.
*
* You cannot connect a signal handler after #GSignalGroup:target has been set.
*
* Since: 2.72
*/
void
g_signal_group_connect_object (GSignalGroup *self,
const gchar *detailed_signal,
GCallback c_handler,
gpointer object,
GConnectFlags flags)
{
g_return_if_fail (G_IS_OBJECT (object));
g_signal_group_connect_full (self, detailed_signal, c_handler, object, NULL,
flags, TRUE);
}
/**
* g_signal_group_connect_data:
* @self: a #GSignalGroup
* @detailed_signal: a string of the form "signal-name::detail"
* @c_handler: (scope notified) (closure data) (destroy notify): the #GCallback to connect
* @data: the data to pass to @c_handler calls
* @notify: function to be called when disposing of @self
* @flags: the flags used to create the signal connection
*
* Connects @c_handler to the signal @detailed_signal
* on the target instance of @self.
*
* You cannot connect a signal handler after #GSignalGroup:target has been set.
*
* Since: 2.72
*/
void
g_signal_group_connect_data (GSignalGroup *self,
const gchar *detailed_signal,
GCallback c_handler,
gpointer data,
GClosureNotify notify,
GConnectFlags flags)
{
g_signal_group_connect_full (self, detailed_signal, c_handler, data, notify,
flags, FALSE);
}
/**
* g_signal_group_connect: (skip)
* @self: a #GSignalGroup
* @detailed_signal: a string of the form "signal-name::detail"
* @c_handler: (scope notified): the #GCallback to connect
* @data: the data to pass to @c_handler calls
*
* Connects @c_handler to the signal @detailed_signal
* on the target instance of @self.
*
* You cannot connect a signal handler after #GSignalGroup:target has been set.
*
* Since: 2.72
*/
void
g_signal_group_connect (GSignalGroup *self,
const gchar *detailed_signal,
GCallback c_handler,
gpointer data)
{
g_signal_group_connect_full (self, detailed_signal, c_handler, data, NULL,
0, FALSE);
}
/**
* g_signal_group_connect_after: (skip)
* @self: a #GSignalGroup
* @detailed_signal: a string of the form "signal-name::detail"
* @c_handler: (scope notified): the #GCallback to connect
* @data: the data to pass to @c_handler calls
*
* Connects @c_handler to the signal @detailed_signal
* on the target instance of @self.
*
* The @c_handler will be called after the default handler of the signal.
*
* You cannot connect a signal handler after #GSignalGroup:target has been set.
*
* Since: 2.72
*/
void
g_signal_group_connect_after (GSignalGroup *self,
const gchar *detailed_signal,
GCallback c_handler,
gpointer data)
{
g_signal_group_connect_full (self, detailed_signal, c_handler,
data, NULL, G_CONNECT_AFTER, FALSE);
}
/**
* g_signal_group_connect_swapped:
* @self: a #GSignalGroup
* @detailed_signal: a string of the form "signal-name::detail"
* @c_handler: (scope async): the #GCallback to connect
* @data: the data to pass to @c_handler calls
*
* Connects @c_handler to the signal @detailed_signal
* on the target instance of @self.
*
* The instance on which the signal is emitted and @data
* will be swapped when calling @c_handler.
*
* You cannot connect a signal handler after #GSignalGroup:target has been set.
*
* Since: 2.72
*/
void
g_signal_group_connect_swapped (GSignalGroup *self,
const gchar *detailed_signal,
GCallback c_handler,
gpointer data)
{
g_signal_group_connect_full (self, detailed_signal, c_handler, data, NULL,
G_CONNECT_SWAPPED, FALSE);
}

93
gobject/gsignalgroup.h Normal file
View file

@ -0,0 +1,93 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
*
* Copyright (C) 2015-2022 Christian Hergert <christian@hergert.me>
* Copyright (C) 2015 Garrett Regier <garrettregier@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef __G_SIGNAL_GROUP_H__
#define __G_SIGNAL_GROUP_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <glib.h>
#include <gobject/gobject.h>
#include <gobject/gsignal.h>
G_BEGIN_DECLS
#define G_SIGNAL_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_SIGNAL_GROUP, GSignalGroup))
#define G_IS_SIGNAL_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_SIGNAL_GROUP))
#define G_TYPE_SIGNAL_GROUP (g_signal_group_get_type())
/**
* GSignalGroup:
*
* #GSignalGroup is an opaque structure whose members
* cannot be accessed directly.
*
* Since: 2.72
*/
typedef struct _GSignalGroup GSignalGroup;
GLIB_AVAILABLE_IN_2_72
GType g_signal_group_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_72
GSignalGroup *g_signal_group_new (GType target_type);
GLIB_AVAILABLE_IN_2_72
void g_signal_group_set_target (GSignalGroup *self,
gpointer target);
GLIB_AVAILABLE_IN_2_72
gpointer g_signal_group_dup_target (GSignalGroup *self);
GLIB_AVAILABLE_IN_2_72
void g_signal_group_block (GSignalGroup *self);
GLIB_AVAILABLE_IN_2_72
void g_signal_group_unblock (GSignalGroup *self);
GLIB_AVAILABLE_IN_2_72
void g_signal_group_connect_object (GSignalGroup *self,
const gchar *detailed_signal,
GCallback c_handler,
gpointer object,
GConnectFlags flags);
GLIB_AVAILABLE_IN_2_72
void g_signal_group_connect_data (GSignalGroup *self,
const gchar *detailed_signal,
GCallback c_handler,
gpointer data,
GClosureNotify notify,
GConnectFlags flags);
GLIB_AVAILABLE_IN_2_72
void g_signal_group_connect (GSignalGroup *self,
const gchar *detailed_signal,
GCallback c_handler,
gpointer data);
GLIB_AVAILABLE_IN_2_72
void g_signal_group_connect_after (GSignalGroup *self,
const gchar *detailed_signal,
GCallback c_handler,
gpointer data);
GLIB_AVAILABLE_IN_2_72
void g_signal_group_connect_swapped (GSignalGroup *self,
const gchar *detailed_signal,
GCallback c_handler,
gpointer data);
G_END_DECLS
#endif /* __G_SIGNAL_GROUP_H__ */

322
gobject/gsourceclosure.c Normal file
View file

@ -0,0 +1,322 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2001 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gsourceclosure.h"
#include "gboxed.h"
#include "genums.h"
#include "gmarshal.h"
#include "gvalue.h"
#include "gvaluetypes.h"
#ifdef G_OS_UNIX
#include "glib-unix.h"
#endif
G_DEFINE_BOXED_TYPE (GIOChannel, g_io_channel, g_io_channel_ref, g_io_channel_unref)
GType
g_io_condition_get_type (void)
{
static GType etype = 0;
if (g_once_init_enter (&etype))
{
static const GFlagsValue values[] = {
{ G_IO_IN, "G_IO_IN", "in" },
{ G_IO_OUT, "G_IO_OUT", "out" },
{ G_IO_PRI, "G_IO_PRI", "pri" },
{ G_IO_ERR, "G_IO_ERR", "err" },
{ G_IO_HUP, "G_IO_HUP", "hup" },
{ G_IO_NVAL, "G_IO_NVAL", "nval" },
{ 0, NULL, NULL }
};
GType type_id = g_flags_register_static ("GIOCondition", values);
g_once_init_leave (&etype, type_id);
}
return etype;
}
/* We need to hand-write this marshaler, since it doesn't have an
* instance object.
*/
static void
source_closure_marshal_BOOLEAN__VOID (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data)
{
GSourceFunc callback;
GCClosure *cc = (GCClosure*) closure;
gboolean v_return;
g_return_if_fail (return_value != NULL);
g_return_if_fail (n_param_values == 0);
callback = (GSourceFunc) (marshal_data ? marshal_data : cc->callback);
v_return = callback (closure->data);
g_value_set_boolean (return_value, v_return);
}
static gboolean
io_watch_closure_callback (GIOChannel *channel,
GIOCondition condition,
gpointer data)
{
GClosure *closure = data;
GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT };
GValue result_value = G_VALUE_INIT;
gboolean result;
g_value_init (&result_value, G_TYPE_BOOLEAN);
g_value_init (&params[0], G_TYPE_IO_CHANNEL);
g_value_set_boxed (&params[0], channel);
g_value_init (&params[1], G_TYPE_IO_CONDITION);
g_value_set_flags (&params[1], condition);
g_closure_invoke (closure, &result_value, 2, params, NULL);
result = g_value_get_boolean (&result_value);
g_value_unset (&result_value);
g_value_unset (&params[0]);
g_value_unset (&params[1]);
return result;
}
static gboolean
g_child_watch_closure_callback (GPid pid,
gint status,
gpointer data)
{
GClosure *closure = data;
GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT };
GValue result_value = G_VALUE_INIT;
gboolean result;
g_value_init (&result_value, G_TYPE_BOOLEAN);
#ifdef G_OS_UNIX
g_value_init (&params[0], G_TYPE_ULONG);
g_value_set_ulong (&params[0], pid);
#endif
#ifdef G_OS_WIN32
g_value_init (&params[0], G_TYPE_POINTER);
g_value_set_pointer (&params[0], pid);
#endif
g_value_init (&params[1], G_TYPE_INT);
g_value_set_int (&params[1], status);
g_closure_invoke (closure, &result_value, 2, params, NULL);
result = g_value_get_boolean (&result_value);
g_value_unset (&result_value);
g_value_unset (&params[0]);
g_value_unset (&params[1]);
return result;
}
#ifdef G_OS_UNIX
static gboolean
g_unix_fd_source_closure_callback (int fd,
GIOCondition condition,
gpointer data)
{
GClosure *closure = data;
GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT };
GValue result_value = G_VALUE_INIT;
gboolean result;
g_value_init (&result_value, G_TYPE_BOOLEAN);
g_value_init (&params[0], G_TYPE_INT);
g_value_set_int (&params[0], fd);
g_value_init (&params[1], G_TYPE_IO_CONDITION);
g_value_set_flags (&params[1], condition);
g_closure_invoke (closure, &result_value, 2, params, NULL);
result = g_value_get_boolean (&result_value);
g_value_unset (&result_value);
g_value_unset (&params[0]);
g_value_unset (&params[1]);
return result;
}
#endif
static gboolean
source_closure_callback (gpointer data)
{
GClosure *closure = data;
GValue result_value = G_VALUE_INIT;
gboolean result;
g_value_init (&result_value, G_TYPE_BOOLEAN);
g_closure_invoke (closure, &result_value, 0, NULL, NULL);
result = g_value_get_boolean (&result_value);
g_value_unset (&result_value);
return result;
}
static void
closure_callback_get (gpointer cb_data,
GSource *source,
GSourceFunc *func,
gpointer *data)
{
GSourceFunc closure_callback = source->source_funcs->closure_callback;
if (!closure_callback)
{
if (source->source_funcs == &g_io_watch_funcs)
closure_callback = (GSourceFunc)io_watch_closure_callback;
else if (source->source_funcs == &g_child_watch_funcs)
closure_callback = (GSourceFunc)g_child_watch_closure_callback;
#ifdef G_OS_UNIX
else if (source->source_funcs == &g_unix_fd_source_funcs)
closure_callback = (GSourceFunc)g_unix_fd_source_closure_callback;
#endif
else if (source->source_funcs == &g_timeout_funcs ||
#ifdef G_OS_UNIX
source->source_funcs == &g_unix_signal_funcs ||
#endif
source->source_funcs == &g_idle_funcs)
closure_callback = source_closure_callback;
}
*func = closure_callback;
*data = cb_data;
}
static GSourceCallbackFuncs closure_callback_funcs = {
(void (*) (gpointer)) g_closure_ref,
(void (*) (gpointer)) g_closure_unref,
closure_callback_get
};
static void
closure_invalidated (gpointer user_data,
GClosure *closure)
{
g_source_destroy (user_data);
}
/**
* g_source_set_closure:
* @source: the source
* @closure: a #GClosure
*
* Set the callback for a source as a #GClosure.
*
* If the source is not one of the standard GLib types, the @closure_callback
* and @closure_marshal fields of the #GSourceFuncs structure must have been
* filled in with pointers to appropriate functions.
*/
void
g_source_set_closure (GSource *source,
GClosure *closure)
{
g_return_if_fail (source != NULL);
g_return_if_fail (closure != NULL);
if (!source->source_funcs->closure_callback &&
#ifdef G_OS_UNIX
source->source_funcs != &g_unix_fd_source_funcs &&
source->source_funcs != &g_unix_signal_funcs &&
#endif
source->source_funcs != &g_child_watch_funcs &&
source->source_funcs != &g_io_watch_funcs &&
source->source_funcs != &g_timeout_funcs &&
source->source_funcs != &g_idle_funcs)
{
g_critical (G_STRLOC ": closure cannot be set on GSource without GSourceFuncs::closure_callback");
return;
}
g_closure_ref (closure);
g_closure_sink (closure);
g_source_set_callback_indirect (source, closure, &closure_callback_funcs);
g_closure_add_invalidate_notifier (closure, source, closure_invalidated);
if (G_CLOSURE_NEEDS_MARSHAL (closure))
{
GClosureMarshal marshal = (GClosureMarshal)source->source_funcs->closure_marshal;
if (marshal)
g_closure_set_marshal (closure, marshal);
else if (source->source_funcs == &g_idle_funcs ||
#ifdef G_OS_UNIX
source->source_funcs == &g_unix_signal_funcs ||
#endif
source->source_funcs == &g_timeout_funcs)
g_closure_set_marshal (closure, source_closure_marshal_BOOLEAN__VOID);
else
g_closure_set_marshal (closure, g_cclosure_marshal_generic);
}
}
static void
dummy_closure_marshal (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data)
{
if (G_VALUE_HOLDS_BOOLEAN (return_value))
g_value_set_boolean (return_value, TRUE);
}
/**
* g_source_set_dummy_callback:
* @source: the source
*
* Sets a dummy callback for @source. The callback will do nothing, and
* if the source expects a #gboolean return value, it will return %TRUE.
* (If the source expects any other type of return value, it will return
* a 0/%NULL value; whatever g_value_init() initializes a #GValue to for
* that type.)
*
* If the source is not one of the standard GLib types, the
* @closure_callback and @closure_marshal fields of the #GSourceFuncs
* structure must have been filled in with pointers to appropriate
* functions.
*/
void
g_source_set_dummy_callback (GSource *source)
{
GClosure *closure;
closure = g_closure_new_simple (sizeof (GClosure), NULL);
g_closure_set_meta_marshal (closure, NULL, dummy_closure_marshal);
g_source_set_closure (source, closure);
}

38
gobject/gsourceclosure.h Normal file
View file

@ -0,0 +1,38 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2001 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __G_SOURCECLOSURE_H__
#define __G_SOURCECLOSURE_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <gobject/gclosure.h>
#include <gobject/glib-types.h>
G_BEGIN_DECLS
GLIB_AVAILABLE_IN_ALL
void g_source_set_closure (GSource *source,
GClosure *closure);
GLIB_AVAILABLE_IN_ALL
void g_source_set_dummy_callback (GSource *source);
G_END_DECLS
#endif /* __G_SOURCECLOSURE_H__ */

113
gobject/gtype-private.h Normal file
View file

@ -0,0 +1,113 @@
/* 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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __G_TYPE_PRIVATE_H__
#define __G_TYPE_PRIVATE_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include "gboxed.h"
#include "gclosure.h"
#include "gobject.h"
/*< private >
* GOBJECT_IF_DEBUG:
* @debug_type: Currently only OBJECTS and SIGNALS are supported.
* @code_block: Custom debug code.
*
* A convenience macro for debugging GObject.
* This macro is only used internally.
*/
#ifdef G_ENABLE_DEBUG
#define GOBJECT_IF_DEBUG(debug_type, code_block) \
G_STMT_START { \
if (_g_type_debug_flags & G_TYPE_DEBUG_ ## debug_type) \
{ code_block; } \
} G_STMT_END
#else /* !G_ENABLE_DEBUG */
#define GOBJECT_IF_DEBUG(debug_type, code_block)
#endif /* G_ENABLE_DEBUG */
G_BEGIN_DECLS
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
extern GTypeDebugFlags _g_type_debug_flags;
G_GNUC_END_IGNORE_DEPRECATIONS
typedef struct _GRealClosure GRealClosure;
struct _GRealClosure
{
GClosureMarshal meta_marshal;
gpointer meta_marshal_data;
GVaClosureMarshal va_meta_marshal;
GVaClosureMarshal va_marshal;
GClosure closure;
};
#define G_REAL_CLOSURE(_c) \
((GRealClosure *)G_STRUCT_MEMBER_P ((_c), -G_STRUCT_OFFSET (GRealClosure, closure)))
void _g_value_c_init (void); /* sync with gvalue.c */
void _g_value_types_init (void); /* sync with gvaluetypes.c */
void _g_enum_types_init (void); /* sync with genums.c */
void _g_param_type_init (void); /* sync with gparam.c */
void _g_boxed_type_init (void); /* sync with gboxed.c */
void _g_object_type_init (void); /* sync with gobject.c */
void _g_param_spec_types_init (void); /* sync with gparamspecs.c */
void _g_value_transforms_init (void); /* sync with gvaluetransform.c */
void _g_signal_init (void); /* sync with gsignal.c */
/* for gboxed.c */
gpointer _g_type_boxed_copy (GType type,
gpointer value);
void _g_type_boxed_free (GType type,
gpointer value);
void _g_type_boxed_init (GType type,
GBoxedCopyFunc copy_func,
GBoxedFreeFunc free_func);
gboolean _g_closure_is_void (GClosure *closure,
gpointer instance);
gboolean _g_closure_supports_invoke_va (GClosure *closure);
void _g_closure_set_va_marshal (GClosure *closure,
GVaClosureMarshal marshal);
void _g_closure_invoke_va (GClosure *closure,
GValue /*out*/ *return_value,
gpointer instance,
va_list args,
int n_params,
GType *param_types);
gboolean _g_object_has_signal_handler (GObject *object);
void _g_object_set_has_signal_handler (GObject *object);
/**
* _G_DEFINE_TYPE_EXTENDED_WITH_PRELUDE:
*
* See also G_DEFINE_TYPE_EXTENDED(). This macro is generally only
* necessary as a workaround for classes which have properties of
* object types that may be initialized in distinct threads. See:
* https://bugzilla.gnome.org/show_bug.cgi?id=674885
*
* Currently private.
*/
#define _G_DEFINE_TYPE_EXTENDED_WITH_PRELUDE(TN, t_n, T_P, _f_, _P_, _C_) _G_DEFINE_TYPE_EXTENDED_BEGIN_PRE (TN, t_n, T_P) {_P_;} _G_DEFINE_TYPE_EXTENDED_BEGIN_REGISTER (TN, t_n, T_P, _f_){_C_;} _G_DEFINE_TYPE_EXTENDED_END()
G_END_DECLS
#endif /* __G_TYPE_PRIVATE_H__ */

5024
gobject/gtype.c Normal file

File diff suppressed because it is too large Load diff

2515
gobject/gtype.h Normal file

File diff suppressed because it is too large Load diff

602
gobject/gtypemodule.c Normal file
View file

@ -0,0 +1,602 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2000 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdlib.h>
#include "gtypeplugin.h"
#include "gtypemodule.h"
/**
* SECTION:gtypemodule
* @short_description: Type loading modules
* @see_also: #GTypePlugin, #GModule
* @title: GTypeModule
*
* #GTypeModule provides a simple implementation of the #GTypePlugin
* interface.
*
* The model of #GTypeModule is a dynamically loaded module which
* implements some number of types and interface implementations.
*
* When the module is loaded, it registers its types and interfaces
* using g_type_module_register_type() and g_type_module_add_interface().
* As long as any instances of these types and interface implementations
* are in use, the module is kept loaded. When the types and interfaces
* are gone, the module may be unloaded. If the types and interfaces
* become used again, the module will be reloaded. Note that the last
* reference cannot be released from within the module code, since that
* would lead to the caller's code being unloaded before g_object_unref()
* returns to it.
*
* Keeping track of whether the module should be loaded or not is done by
* using a use count - it starts at zero, and whenever it is greater than
* zero, the module is loaded. The use count is maintained internally by
* the type system, but also can be explicitly controlled by
* g_type_module_use() and g_type_module_unuse(). Typically, when loading
* a module for the first type, g_type_module_use() will be used to load
* it so that it can initialize its types. At some later point, when the
* module no longer needs to be loaded except for the type
* implementations it contains, g_type_module_unuse() is called.
*
* #GTypeModule does not actually provide any implementation of module
* loading and unloading. To create a particular module type you must
* derive from #GTypeModule and implement the load and unload functions
* in #GTypeModuleClass.
*/
typedef struct _ModuleTypeInfo ModuleTypeInfo;
typedef struct _ModuleInterfaceInfo ModuleInterfaceInfo;
struct _ModuleTypeInfo
{
gboolean loaded;
GType type;
GType parent_type;
GTypeInfo info;
};
struct _ModuleInterfaceInfo
{
gboolean loaded;
GType instance_type;
GType interface_type;
GInterfaceInfo info;
};
static void g_type_module_use_plugin (GTypePlugin *plugin);
static void g_type_module_complete_type_info (GTypePlugin *plugin,
GType g_type,
GTypeInfo *info,
GTypeValueTable *value_table);
static void g_type_module_complete_interface_info (GTypePlugin *plugin,
GType instance_type,
GType interface_type,
GInterfaceInfo *info);
static gpointer parent_class = NULL;
static void
g_type_module_dispose (GObject *object)
{
GTypeModule *module = G_TYPE_MODULE (object);
if (module->type_infos || module->interface_infos)
{
g_warning (G_STRLOC ": unsolicitated invocation of g_object_run_dispose() on GTypeModule");
g_object_ref (object);
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
g_type_module_finalize (GObject *object)
{
GTypeModule *module = G_TYPE_MODULE (object);
g_free (module->name);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
g_type_module_class_init (GTypeModuleClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (class));
gobject_class->dispose = g_type_module_dispose;
gobject_class->finalize = g_type_module_finalize;
}
static void
g_type_module_iface_init (GTypePluginClass *iface)
{
iface->use_plugin = g_type_module_use_plugin;
iface->unuse_plugin = (void (*) (GTypePlugin *))g_type_module_unuse;
iface->complete_type_info = g_type_module_complete_type_info;
iface->complete_interface_info = g_type_module_complete_interface_info;
}
GType
g_type_module_get_type (void)
{
static GType type_module_type = 0;
if (!type_module_type)
{
const GTypeInfo type_module_info = {
sizeof (GTypeModuleClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) g_type_module_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GTypeModule),
0, /* n_preallocs */
NULL, /* instance_init */
NULL, /* value_table */
};
const GInterfaceInfo iface_info = {
(GInterfaceInitFunc) g_type_module_iface_init,
NULL, /* interface_finalize */
NULL, /* interface_data */
};
type_module_type = g_type_register_static (G_TYPE_OBJECT, g_intern_static_string ("GTypeModule"), &type_module_info, G_TYPE_FLAG_ABSTRACT);
g_type_add_interface_static (type_module_type, G_TYPE_TYPE_PLUGIN, &iface_info);
}
return type_module_type;
}
/**
* g_type_module_set_name:
* @module: a #GTypeModule.
* @name: a human-readable name to use in error messages.
*
* Sets the name for a #GTypeModule
*/
void
g_type_module_set_name (GTypeModule *module,
const gchar *name)
{
g_return_if_fail (G_IS_TYPE_MODULE (module));
g_free (module->name);
module->name = g_strdup (name);
}
static ModuleTypeInfo *
g_type_module_find_type_info (GTypeModule *module,
GType type)
{
GSList *tmp_list = module->type_infos;
while (tmp_list)
{
ModuleTypeInfo *type_info = tmp_list->data;
if (type_info->type == type)
return type_info;
tmp_list = tmp_list->next;
}
return NULL;
}
static ModuleInterfaceInfo *
g_type_module_find_interface_info (GTypeModule *module,
GType instance_type,
GType interface_type)
{
GSList *tmp_list = module->interface_infos;
while (tmp_list)
{
ModuleInterfaceInfo *interface_info = tmp_list->data;
if (interface_info->instance_type == instance_type &&
interface_info->interface_type == interface_type)
return interface_info;
tmp_list = tmp_list->next;
}
return NULL;
}
/**
* g_type_module_use:
* @module: a #GTypeModule
*
* Increases the use count of a #GTypeModule by one. If the
* use count was zero before, the plugin will be loaded.
* If loading the plugin fails, the use count is reset to
* its prior value.
*
* Returns: %FALSE if the plugin needed to be loaded and
* loading the plugin failed.
*/
gboolean
g_type_module_use (GTypeModule *module)
{
g_return_val_if_fail (G_IS_TYPE_MODULE (module), FALSE);
module->use_count++;
if (module->use_count == 1)
{
GSList *tmp_list;
if (!G_TYPE_MODULE_GET_CLASS (module)->load (module))
{
module->use_count--;
return FALSE;
}
tmp_list = module->type_infos;
while (tmp_list)
{
ModuleTypeInfo *type_info = tmp_list->data;
if (!type_info->loaded)
{
g_warning ("plugin '%s' failed to register type '%s'",
module->name ? module->name : "(unknown)",
g_type_name (type_info->type));
module->use_count--;
return FALSE;
}
tmp_list = tmp_list->next;
}
}
return TRUE;
}
/**
* g_type_module_unuse:
* @module: a #GTypeModule
*
* Decreases the use count of a #GTypeModule by one. If the
* result is zero, the module will be unloaded. (However, the
* #GTypeModule will not be freed, and types associated with the
* #GTypeModule are not unregistered. Once a #GTypeModule is
* initialized, it must exist forever.)
*/
void
g_type_module_unuse (GTypeModule *module)
{
g_return_if_fail (G_IS_TYPE_MODULE (module));
g_return_if_fail (module->use_count > 0);
module->use_count--;
if (module->use_count == 0)
{
GSList *tmp_list;
G_TYPE_MODULE_GET_CLASS (module)->unload (module);
tmp_list = module->type_infos;
while (tmp_list)
{
ModuleTypeInfo *type_info = tmp_list->data;
type_info->loaded = FALSE;
tmp_list = tmp_list->next;
}
}
}
static void
g_type_module_use_plugin (GTypePlugin *plugin)
{
GTypeModule *module = G_TYPE_MODULE (plugin);
if (!g_type_module_use (module))
{
g_warning ("Fatal error - Could not reload previously loaded plugin '%s'",
module->name ? module->name : "(unknown)");
exit (1);
}
}
static void
g_type_module_complete_type_info (GTypePlugin *plugin,
GType g_type,
GTypeInfo *info,
GTypeValueTable *value_table)
{
GTypeModule *module = G_TYPE_MODULE (plugin);
ModuleTypeInfo *module_type_info = g_type_module_find_type_info (module, g_type);
*info = module_type_info->info;
if (module_type_info->info.value_table)
*value_table = *module_type_info->info.value_table;
}
static void
g_type_module_complete_interface_info (GTypePlugin *plugin,
GType instance_type,
GType interface_type,
GInterfaceInfo *info)
{
GTypeModule *module = G_TYPE_MODULE (plugin);
ModuleInterfaceInfo *module_interface_info = g_type_module_find_interface_info (module, instance_type, interface_type);
*info = module_interface_info->info;
}
/**
* g_type_module_register_type:
* @module: (nullable): a #GTypeModule
* @parent_type: the type for the parent class
* @type_name: name for the type
* @type_info: type information structure
* @flags: flags field providing details about the type
*
* Looks up or registers a type that is implemented with a particular
* type plugin. If a type with name @type_name was previously registered,
* the #GType identifier for the type is returned, otherwise the type
* is newly registered, and the resulting #GType identifier returned.
*
* When reregistering a type (typically because a module is unloaded
* then reloaded, and reinitialized), @module and @parent_type must
* be the same as they were previously.
*
* As long as any instances of the type exist, the type plugin will
* not be unloaded.
*
* Since 2.56 if @module is %NULL this will call g_type_register_static()
* instead. This can be used when making a static build of the module.
*
* Returns: the new or existing type ID
*/
GType
g_type_module_register_type (GTypeModule *module,
GType parent_type,
const gchar *type_name,
const GTypeInfo *type_info,
GTypeFlags flags)
{
ModuleTypeInfo *module_type_info = NULL;
GType type;
g_return_val_if_fail (type_name != NULL, 0);
g_return_val_if_fail (type_info != NULL, 0);
if (module == NULL)
{
/* Cannot pass type_info directly to g_type_register_static() here because
* it has class_finalize != NULL and that's forbidden for static types */
return g_type_register_static_simple (parent_type,
type_name,
type_info->class_size,
type_info->class_init,
type_info->instance_size,
type_info->instance_init,
flags);
}
type = g_type_from_name (type_name);
if (type)
{
GTypePlugin *old_plugin = g_type_get_plugin (type);
if (old_plugin != G_TYPE_PLUGIN (module))
{
g_warning ("Two different plugins tried to register '%s'.", type_name);
return 0;
}
}
if (type)
{
module_type_info = g_type_module_find_type_info (module, type);
if (module_type_info->parent_type != parent_type)
{
const gchar *parent_type_name = g_type_name (parent_type);
g_warning ("Type '%s' recreated with different parent type."
"(was '%s', now '%s')", type_name,
g_type_name (module_type_info->parent_type),
parent_type_name ? parent_type_name : "(unknown)");
return 0;
}
if (module_type_info->info.value_table)
g_free ((GTypeValueTable *) module_type_info->info.value_table);
}
else
{
module_type_info = g_new (ModuleTypeInfo, 1);
module_type_info->parent_type = parent_type;
module_type_info->type = g_type_register_dynamic (parent_type, type_name, G_TYPE_PLUGIN (module), flags);
module->type_infos = g_slist_prepend (module->type_infos, module_type_info);
}
module_type_info->loaded = TRUE;
module_type_info->info = *type_info;
if (type_info->value_table)
module_type_info->info.value_table = g_memdup2 (type_info->value_table,
sizeof (GTypeValueTable));
return module_type_info->type;
}
/**
* g_type_module_add_interface:
* @module: (nullable): a #GTypeModule
* @instance_type: type to which to add the interface.
* @interface_type: interface type to add
* @interface_info: type information structure
*
* Registers an additional interface for a type, whose interface lives
* in the given type plugin. If the interface was already registered
* for the type in this plugin, nothing will be done.
*
* As long as any instances of the type exist, the type plugin will
* not be unloaded.
*
* Since 2.56 if @module is %NULL this will call g_type_add_interface_static()
* instead. This can be used when making a static build of the module.
*/
void
g_type_module_add_interface (GTypeModule *module,
GType instance_type,
GType interface_type,
const GInterfaceInfo *interface_info)
{
ModuleInterfaceInfo *module_interface_info = NULL;
g_return_if_fail (interface_info != NULL);
if (module == NULL)
{
g_type_add_interface_static (instance_type, interface_type, interface_info);
return;
}
if (g_type_is_a (instance_type, interface_type))
{
GTypePlugin *old_plugin = g_type_interface_get_plugin (instance_type,
interface_type);
if (!old_plugin)
{
g_warning ("Interface '%s' for '%s' was previously registered statically or for a parent type.",
g_type_name (interface_type), g_type_name (instance_type));
return;
}
else if (old_plugin != G_TYPE_PLUGIN (module))
{
g_warning ("Two different plugins tried to register interface '%s' for '%s'.",
g_type_name (interface_type), g_type_name (instance_type));
return;
}
module_interface_info = g_type_module_find_interface_info (module, instance_type, interface_type);
g_assert (module_interface_info);
}
else
{
module_interface_info = g_new (ModuleInterfaceInfo, 1);
module_interface_info->instance_type = instance_type;
module_interface_info->interface_type = interface_type;
g_type_add_interface_dynamic (instance_type, interface_type, G_TYPE_PLUGIN (module));
module->interface_infos = g_slist_prepend (module->interface_infos, module_interface_info);
}
module_interface_info->loaded = TRUE;
module_interface_info->info = *interface_info;
}
/**
* g_type_module_register_enum:
* @module: (nullable): a #GTypeModule
* @name: name for the type
* @const_static_values: an array of #GEnumValue structs for the
* possible enumeration values. The array is
* terminated by a struct with all members being
* 0.
*
* Looks up or registers an enumeration that is implemented with a particular
* type plugin. If a type with name @type_name was previously registered,
* the #GType identifier for the type is returned, otherwise the type
* is newly registered, and the resulting #GType identifier returned.
*
* As long as any instances of the type exist, the type plugin will
* not be unloaded.
*
* Since 2.56 if @module is %NULL this will call g_type_register_static()
* instead. This can be used when making a static build of the module.
*
* Since: 2.6
*
* Returns: the new or existing type ID
*/
GType
g_type_module_register_enum (GTypeModule *module,
const gchar *name,
const GEnumValue *const_static_values)
{
GTypeInfo enum_type_info = { 0, };
g_return_val_if_fail (module == NULL || G_IS_TYPE_MODULE (module), 0);
g_return_val_if_fail (name != NULL, 0);
g_return_val_if_fail (const_static_values != NULL, 0);
g_enum_complete_type_info (G_TYPE_ENUM,
&enum_type_info, const_static_values);
return g_type_module_register_type (G_TYPE_MODULE (module),
G_TYPE_ENUM, name, &enum_type_info, 0);
}
/**
* g_type_module_register_flags:
* @module: (nullable): a #GTypeModule
* @name: name for the type
* @const_static_values: an array of #GFlagsValue structs for the
* possible flags values. The array is
* terminated by a struct with all members being
* 0.
*
* Looks up or registers a flags type that is implemented with a particular
* type plugin. If a type with name @type_name was previously registered,
* the #GType identifier for the type is returned, otherwise the type
* is newly registered, and the resulting #GType identifier returned.
*
* As long as any instances of the type exist, the type plugin will
* not be unloaded.
*
* Since 2.56 if @module is %NULL this will call g_type_register_static()
* instead. This can be used when making a static build of the module.
*
* Since: 2.6
*
* Returns: the new or existing type ID
*/
GType
g_type_module_register_flags (GTypeModule *module,
const gchar *name,
const GFlagsValue *const_static_values)
{
GTypeInfo flags_type_info = { 0, };
g_return_val_if_fail (module == NULL || G_IS_TYPE_MODULE (module), 0);
g_return_val_if_fail (name != NULL, 0);
g_return_val_if_fail (const_static_values != NULL, 0);
g_flags_complete_type_info (G_TYPE_FLAGS,
&flags_type_info, const_static_values);
return g_type_module_register_type (G_TYPE_MODULE (module),
G_TYPE_FLAGS, name, &flags_type_info, 0);
}

300
gobject/gtypemodule.h Normal file
View file

@ -0,0 +1,300 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2000 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __G_TYPE_MODULE_H__
#define __G_TYPE_MODULE_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <gobject/gobject.h>
#include <gobject/genums.h>
G_BEGIN_DECLS
typedef struct _GTypeModule GTypeModule;
typedef struct _GTypeModuleClass GTypeModuleClass;
#define G_TYPE_TYPE_MODULE (g_type_module_get_type ())
#define G_TYPE_MODULE(module) (G_TYPE_CHECK_INSTANCE_CAST ((module), G_TYPE_TYPE_MODULE, GTypeModule))
#define G_TYPE_MODULE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TYPE_MODULE, GTypeModuleClass))
#define G_IS_TYPE_MODULE(module) (G_TYPE_CHECK_INSTANCE_TYPE ((module), G_TYPE_TYPE_MODULE))
#define G_IS_TYPE_MODULE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TYPE_MODULE))
#define G_TYPE_MODULE_GET_CLASS(module) (G_TYPE_INSTANCE_GET_CLASS ((module), G_TYPE_TYPE_MODULE, GTypeModuleClass))
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTypeModule, g_object_unref)
/**
* GTypeModule:
* @name: the name of the module
*
* The members of the GTypeModule structure should not
* be accessed directly, except for the @name field.
*/
struct _GTypeModule
{
GObject parent_instance;
guint use_count;
GSList *type_infos;
GSList *interface_infos;
/*< public >*/
gchar *name;
};
/**
* GTypeModuleClass:
* @parent_class: the parent class
* @load: loads the module and registers one or more types using
* g_type_module_register_type().
* @unload: unloads the module
*
* In order to implement dynamic loading of types based on #GTypeModule,
* the @load and @unload functions in #GTypeModuleClass must be implemented.
*/
struct _GTypeModuleClass
{
GObjectClass parent_class;
/*< public >*/
gboolean (* load) (GTypeModule *module);
void (* unload) (GTypeModule *module);
/*< private >*/
/* Padding for future expansion */
void (*reserved1) (void);
void (*reserved2) (void);
void (*reserved3) (void);
void (*reserved4) (void);
};
/**
* G_DEFINE_DYNAMIC_TYPE:
* @TN: The name of the new type, in Camel case.
* @t_n: The name of the new type, in lowercase, with words
* separated by '_'.
* @T_P: The #GType of the parent type.
*
* A convenience macro for dynamic type implementations, which declares a
* class initialization function, an instance initialization function (see
* #GTypeInfo for information about these) and a static variable named
* `t_n`_parent_class pointing to the parent class.
*
* Furthermore, it defines a `*_get_type()` and a static `*_register_type()`
* functions for use in your `module_init()`.
*
* See G_DEFINE_DYNAMIC_TYPE_EXTENDED() for an example.
*
* Since: 2.14
*/
#define G_DEFINE_DYNAMIC_TYPE(TN, t_n, T_P) G_DEFINE_DYNAMIC_TYPE_EXTENDED (TN, t_n, T_P, 0, {})
/**
* G_DEFINE_DYNAMIC_TYPE_EXTENDED:
* @TypeName: The name of the new type, in Camel case.
* @type_name: The name of the new type, in lowercase, with words
* separated by '_'.
* @TYPE_PARENT: The #GType of the parent type.
* @flags: #GTypeFlags to pass to g_type_module_register_type()
* @CODE: Custom code that gets inserted in the *_get_type() function.
*
* A more general version of G_DEFINE_DYNAMIC_TYPE() which
* allows to specify #GTypeFlags and custom code.
*
* |[<!-- language="C" -->
* G_DEFINE_DYNAMIC_TYPE_EXTENDED (GtkGadget,
* gtk_gadget,
* GTK_TYPE_THING,
* 0,
* G_IMPLEMENT_INTERFACE_DYNAMIC (TYPE_GIZMO,
* gtk_gadget_gizmo_init));
* ]|
*
* expands to
*
* |[<!-- language="C" -->
* static void gtk_gadget_init (GtkGadget *self);
* static void gtk_gadget_class_init (GtkGadgetClass *klass);
* static void gtk_gadget_class_finalize (GtkGadgetClass *klass);
*
* static gpointer gtk_gadget_parent_class = NULL;
* static GType gtk_gadget_type_id = 0;
*
* static void gtk_gadget_class_intern_init (gpointer klass)
* {
* gtk_gadget_parent_class = g_type_class_peek_parent (klass);
* gtk_gadget_class_init ((GtkGadgetClass*) klass);
* }
*
* GType
* gtk_gadget_get_type (void)
* {
* return gtk_gadget_type_id;
* }
*
* static void
* gtk_gadget_register_type (GTypeModule *type_module)
* {
* const GTypeInfo g_define_type_info = {
* sizeof (GtkGadgetClass),
* (GBaseInitFunc) NULL,
* (GBaseFinalizeFunc) NULL,
* (GClassInitFunc) gtk_gadget_class_intern_init,
* (GClassFinalizeFunc) gtk_gadget_class_finalize,
* NULL, // class_data
* sizeof (GtkGadget),
* 0, // n_preallocs
* (GInstanceInitFunc) gtk_gadget_init,
* NULL // value_table
* };
* gtk_gadget_type_id = g_type_module_register_type (type_module,
* GTK_TYPE_THING,
* "GtkGadget",
* &g_define_type_info,
* (GTypeFlags) flags);
* {
* const GInterfaceInfo g_implement_interface_info = {
* (GInterfaceInitFunc) gtk_gadget_gizmo_init
* };
* g_type_module_add_interface (type_module, g_define_type_id, TYPE_GIZMO, &g_implement_interface_info);
* }
* }
* ]|
*
* Since: 2.14
*/
#define G_DEFINE_DYNAMIC_TYPE_EXTENDED(TypeName, type_name, TYPE_PARENT, flags, CODE) \
static void type_name##_init (TypeName *self); \
static void type_name##_class_init (TypeName##Class *klass); \
static void type_name##_class_finalize (TypeName##Class *klass); \
static gpointer type_name##_parent_class = NULL; \
static GType type_name##_type_id = 0; \
static gint TypeName##_private_offset; \
\
_G_DEFINE_TYPE_EXTENDED_CLASS_INIT(TypeName, type_name) \
\
G_GNUC_UNUSED \
static inline gpointer \
type_name##_get_instance_private (TypeName *self) \
{ \
return (G_STRUCT_MEMBER_P (self, TypeName##_private_offset)); \
} \
\
GType \
type_name##_get_type (void) \
{ \
return type_name##_type_id; \
} \
static void \
type_name##_register_type (GTypeModule *type_module) \
{ \
GType g_define_type_id G_GNUC_UNUSED; \
const GTypeInfo g_define_type_info = { \
sizeof (TypeName##Class), \
(GBaseInitFunc) NULL, \
(GBaseFinalizeFunc) NULL, \
(GClassInitFunc)(void (*)(void)) type_name##_class_intern_init, \
(GClassFinalizeFunc)(void (*)(void)) type_name##_class_finalize, \
NULL, /* class_data */ \
sizeof (TypeName), \
0, /* n_preallocs */ \
(GInstanceInitFunc)(void (*)(void)) type_name##_init, \
NULL /* value_table */ \
}; \
type_name##_type_id = g_type_module_register_type (type_module, \
TYPE_PARENT, \
#TypeName, \
&g_define_type_info, \
(GTypeFlags) flags); \
g_define_type_id = type_name##_type_id; \
{ CODE ; } \
}
/**
* G_IMPLEMENT_INTERFACE_DYNAMIC:
* @TYPE_IFACE: The #GType of the interface to add
* @iface_init: The interface init function
*
* A convenience macro to ease interface addition in the @_C_ section
* of G_DEFINE_DYNAMIC_TYPE_EXTENDED().
*
* See G_DEFINE_DYNAMIC_TYPE_EXTENDED() for an example.
*
* Note that this macro can only be used together with the
* G_DEFINE_DYNAMIC_TYPE_EXTENDED macros, since it depends on variable
* names from that macro.
*
* Since: 2.24
*/
#define G_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init) { \
const GInterfaceInfo g_implement_interface_info = { \
(GInterfaceInitFunc)(void (*)(void)) iface_init, NULL, NULL \
}; \
g_type_module_add_interface (type_module, g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \
}
/**
* G_ADD_PRIVATE_DYNAMIC:
* @TypeName: the name of the type in CamelCase
*
* A convenience macro to ease adding private data to instances of a new dynamic
* type in the @_C_ section of G_DEFINE_DYNAMIC_TYPE_EXTENDED().
*
* See G_ADD_PRIVATE() for details, it is similar but for static types.
*
* Note that this macro can only be used together with the
* G_DEFINE_DYNAMIC_TYPE_EXTENDED macros, since it depends on variable
* names from that macro.
*
* Since: 2.38
*/
#define G_ADD_PRIVATE_DYNAMIC(TypeName) { \
TypeName##_private_offset = sizeof (TypeName##Private); \
}
GLIB_AVAILABLE_IN_ALL
GType g_type_module_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
gboolean g_type_module_use (GTypeModule *module);
GLIB_AVAILABLE_IN_ALL
void g_type_module_unuse (GTypeModule *module);
GLIB_AVAILABLE_IN_ALL
void g_type_module_set_name (GTypeModule *module,
const gchar *name);
GLIB_AVAILABLE_IN_ALL
GType g_type_module_register_type (GTypeModule *module,
GType parent_type,
const gchar *type_name,
const GTypeInfo *type_info,
GTypeFlags flags);
GLIB_AVAILABLE_IN_ALL
void g_type_module_add_interface (GTypeModule *module,
GType instance_type,
GType interface_type,
const GInterfaceInfo *interface_info);
GLIB_AVAILABLE_IN_ALL
GType g_type_module_register_enum (GTypeModule *module,
const gchar *name,
const GEnumValue *const_static_values);
GLIB_AVAILABLE_IN_ALL
GType g_type_module_register_flags (GTypeModule *module,
const gchar *name,
const GFlagsValue *const_static_values);
G_END_DECLS
#endif /* __G_TYPE_MODULE_H__ */

203
gobject/gtypeplugin.c Normal file
View file

@ -0,0 +1,203 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2000 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtypeplugin.h"
/**
* SECTION:gtypeplugin
* @short_description: An interface for dynamically loadable types
* @see_also: #GTypeModule and g_type_register_dynamic().
* @title: GTypePlugin
*
* An interface that handles the lifecycle of dynamically loaded types.
*
* The GObject type system supports dynamic loading of types.
* It goes as follows:
*
* 1. The type is initially introduced (usually upon loading the module
* the first time, or by your main application that knows what modules
* introduces what types), like this:
* |[<!-- language="C" -->
* new_type_id = g_type_register_dynamic (parent_type_id,
* "TypeName",
* new_type_plugin,
* type_flags);
* ]|
* where @new_type_plugin is an implementation of the
* #GTypePlugin interface.
*
* 2. The type's implementation is referenced, e.g. through
* g_type_class_ref() or through g_type_create_instance() (this is
* being called by g_object_new()) or through one of the above done on
* a type derived from @new_type_id.
*
* 3. This causes the type system to load the type's implementation by
* calling g_type_plugin_use() and g_type_plugin_complete_type_info()
* on @new_type_plugin.
*
* 4. At some point the type's implementation isn't required anymore,
* e.g. after g_type_class_unref() or g_type_free_instance() (called
* when the reference count of an instance drops to zero).
*
* 5. This causes the type system to throw away the information retrieved
* from g_type_plugin_complete_type_info() and then it calls
* g_type_plugin_unuse() on @new_type_plugin.
*
* 6. Things may repeat from the second step.
*
* So basically, you need to implement a #GTypePlugin type that
* carries a use_count, once use_count goes from zero to one, you need
* to load the implementation to successfully handle the upcoming
* g_type_plugin_complete_type_info() call. Later, maybe after
* succeeding use/unuse calls, once use_count drops to zero, you can
* unload the implementation again. The type system makes sure to call
* g_type_plugin_use() and g_type_plugin_complete_type_info() again
* when the type is needed again.
*
* #GTypeModule is an implementation of #GTypePlugin that already
* implements most of this except for the actual module loading and
* unloading. It even handles multiple registered types per module.
*/
/* --- functions --- */
GType
g_type_plugin_get_type (void)
{
static GType type_plugin_type = 0;
if (!type_plugin_type)
{
const GTypeInfo type_plugin_info = {
sizeof (GTypePluginClass),
NULL, /* base_init */
NULL, /* base_finalize */
0, /* class_init */
NULL, /* class_destroy */
NULL, /* class_data */
0, /* instance_size */
0, /* n_preallocs */
NULL, /* instance_init */
NULL, /* value_table */
};
type_plugin_type = g_type_register_static (G_TYPE_INTERFACE, g_intern_static_string ("GTypePlugin"), &type_plugin_info, 0);
}
return type_plugin_type;
}
/**
* g_type_plugin_use:
* @plugin: a #GTypePlugin
*
* Calls the @use_plugin function from the #GTypePluginClass of
* @plugin. There should be no need to use this function outside of
* the GObject type system itself.
*/
void
g_type_plugin_use (GTypePlugin *plugin)
{
GTypePluginClass *iface;
g_return_if_fail (G_IS_TYPE_PLUGIN (plugin));
iface = G_TYPE_PLUGIN_GET_CLASS (plugin);
iface->use_plugin (plugin);
}
/**
* g_type_plugin_unuse:
* @plugin: a #GTypePlugin
*
* Calls the @unuse_plugin function from the #GTypePluginClass of
* @plugin. There should be no need to use this function outside of
* the GObject type system itself.
*/
void
g_type_plugin_unuse (GTypePlugin *plugin)
{
GTypePluginClass *iface;
g_return_if_fail (G_IS_TYPE_PLUGIN (plugin));
iface = G_TYPE_PLUGIN_GET_CLASS (plugin);
iface->unuse_plugin (plugin);
}
/**
* g_type_plugin_complete_type_info:
* @plugin: a #GTypePlugin
* @g_type: the #GType whose info is completed
* @info: the #GTypeInfo struct to fill in
* @value_table: the #GTypeValueTable to fill in
*
* Calls the @complete_type_info function from the #GTypePluginClass of @plugin.
* There should be no need to use this function outside of the GObject
* type system itself.
*/
void
g_type_plugin_complete_type_info (GTypePlugin *plugin,
GType g_type,
GTypeInfo *info,
GTypeValueTable *value_table)
{
GTypePluginClass *iface;
g_return_if_fail (G_IS_TYPE_PLUGIN (plugin));
g_return_if_fail (info != NULL);
g_return_if_fail (value_table != NULL);
iface = G_TYPE_PLUGIN_GET_CLASS (plugin);
iface->complete_type_info (plugin,
g_type,
info,
value_table);
}
/**
* g_type_plugin_complete_interface_info:
* @plugin: the #GTypePlugin
* @instance_type: the #GType of an instantiatable type to which the interface
* is added
* @interface_type: the #GType of the interface whose info is completed
* @info: the #GInterfaceInfo to fill in
*
* Calls the @complete_interface_info function from the
* #GTypePluginClass of @plugin. There should be no need to use this
* function outside of the GObject type system itself.
*/
void
g_type_plugin_complete_interface_info (GTypePlugin *plugin,
GType instance_type,
GType interface_type,
GInterfaceInfo *info)
{
GTypePluginClass *iface;
g_return_if_fail (G_IS_TYPE_PLUGIN (plugin));
g_return_if_fail (info != NULL);
iface = G_TYPE_PLUGIN_GET_CLASS (plugin);
iface->complete_interface_info (plugin,
instance_type,
interface_type,
info);
}

134
gobject/gtypeplugin.h Normal file
View file

@ -0,0 +1,134 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2000 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __G_TYPE_PLUGIN_H__
#define __G_TYPE_PLUGIN_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <gobject/gtype.h>
G_BEGIN_DECLS
/* --- type macros --- */
#define G_TYPE_TYPE_PLUGIN (g_type_plugin_get_type ())
#define G_TYPE_PLUGIN(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TYPE_PLUGIN, GTypePlugin))
#define G_TYPE_PLUGIN_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), G_TYPE_TYPE_PLUGIN, GTypePluginClass))
#define G_IS_TYPE_PLUGIN(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TYPE_PLUGIN))
#define G_IS_TYPE_PLUGIN_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), G_TYPE_TYPE_PLUGIN))
#define G_TYPE_PLUGIN_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), G_TYPE_TYPE_PLUGIN, GTypePluginClass))
/* --- typedefs & structures --- */
typedef struct _GTypePluginClass GTypePluginClass;
/**
* GTypePluginUse:
* @plugin: the #GTypePlugin whose use count should be increased
*
* The type of the @use_plugin function of #GTypePluginClass, which gets called
* to increase the use count of @plugin.
*/
typedef void (*GTypePluginUse) (GTypePlugin *plugin);
/**
* GTypePluginUnuse:
* @plugin: the #GTypePlugin whose use count should be decreased
*
* The type of the @unuse_plugin function of #GTypePluginClass.
*/
typedef void (*GTypePluginUnuse) (GTypePlugin *plugin);
/**
* GTypePluginCompleteTypeInfo:
* @plugin: the #GTypePlugin
* @g_type: the #GType whose info is completed
* @info: the #GTypeInfo struct to fill in
* @value_table: the #GTypeValueTable to fill in
*
* The type of the @complete_type_info function of #GTypePluginClass.
*/
typedef void (*GTypePluginCompleteTypeInfo) (GTypePlugin *plugin,
GType g_type,
GTypeInfo *info,
GTypeValueTable *value_table);
/**
* GTypePluginCompleteInterfaceInfo:
* @plugin: the #GTypePlugin
* @instance_type: the #GType of an instantiatable type to which the interface
* is added
* @interface_type: the #GType of the interface whose info is completed
* @info: the #GInterfaceInfo to fill in
*
* The type of the @complete_interface_info function of #GTypePluginClass.
*/
typedef void (*GTypePluginCompleteInterfaceInfo) (GTypePlugin *plugin,
GType instance_type,
GType interface_type,
GInterfaceInfo *info);
/**
* GTypePlugin:
*
* The GTypePlugin typedef is used as a placeholder
* for objects that implement the GTypePlugin interface.
*/
/**
* GTypePluginClass:
* @use_plugin: Increases the use count of the plugin.
* @unuse_plugin: Decreases the use count of the plugin.
* @complete_type_info: Fills in the #GTypeInfo and
* #GTypeValueTable structs for the type. The structs are initialized
* with `memset(s, 0, sizeof (s))` before calling this function.
* @complete_interface_info: Fills in missing parts of the #GInterfaceInfo
* for the interface. The structs is initialized with
* `memset(s, 0, sizeof (s))` before calling this function.
*
* The #GTypePlugin interface is used by the type system in order to handle
* the lifecycle of dynamically loaded types.
*/
struct _GTypePluginClass
{
/*< private >*/
GTypeInterface base_iface;
/*< public >*/
GTypePluginUse use_plugin;
GTypePluginUnuse unuse_plugin;
GTypePluginCompleteTypeInfo complete_type_info;
GTypePluginCompleteInterfaceInfo complete_interface_info;
};
/* --- prototypes --- */
GLIB_AVAILABLE_IN_ALL
GType g_type_plugin_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
void g_type_plugin_use (GTypePlugin *plugin);
GLIB_AVAILABLE_IN_ALL
void g_type_plugin_unuse (GTypePlugin *plugin);
GLIB_AVAILABLE_IN_ALL
void g_type_plugin_complete_type_info (GTypePlugin *plugin,
GType g_type,
GTypeInfo *info,
GTypeValueTable *value_table);
GLIB_AVAILABLE_IN_ALL
void g_type_plugin_complete_interface_info (GTypePlugin *plugin,
GType instance_type,
GType interface_type,
GInterfaceInfo *info);
G_END_DECLS
#endif /* __G_TYPE_PLUGIN_H__ */

675
gobject/gvalue.c Normal file
View file

@ -0,0 +1,675 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 1997-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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* FIXME: MT-safety
*/
#include "config.h"
#include <string.h>
#include "gvalue.h"
#include "gvaluecollector.h"
#include "gbsearcharray.h"
#include "gtype-private.h"
/**
* SECTION:generic_values
* @short_description: A polymorphic type that can hold values of any
* other type
* @see_also: The fundamental types which all support #GValue
* operations and thus can be used as a type initializer for
* g_value_init() are defined by a separate interface. See the
* [standard values API][gobject-Standard-Parameter-and-Value-Types]
* for details
* @title: Generic values
*
* The #GValue structure is basically a variable container that consists
* of a type identifier and a specific value of that type.
*
* The type identifier within a #GValue structure always determines the
* type of the associated value.
*
* To create an undefined #GValue structure, simply create a zero-filled
* #GValue structure. To initialize the #GValue, use the g_value_init()
* function. A #GValue cannot be used until it is initialized. Before
* destruction you must always use g_value_unset() to make sure allocated
* memory is freed.
*
* The basic type operations (such as freeing and copying) are determined
* by the #GTypeValueTable associated with the type ID stored in the #GValue.
* Other #GValue operations (such as converting values between types) are
* provided by this interface.
*
* The code in the example program below demonstrates #GValue's
* features.
*
* |[<!-- language="C" -->
* #include <glib-object.h>
*
* static void
* int2string (const GValue *src_value,
* GValue *dest_value)
* {
* if (g_value_get_int (src_value) == 42)
* g_value_set_static_string (dest_value, "An important number");
* else
* g_value_set_static_string (dest_value, "What's that?");
* }
*
* int
* main (int argc,
* char *argv[])
* {
* // GValues must be initialized
* GValue a = G_VALUE_INIT;
* GValue b = G_VALUE_INIT;
* const gchar *message;
*
* // The GValue starts empty
* g_assert (!G_VALUE_HOLDS_STRING (&a));
*
* // Put a string in it
* g_value_init (&a, G_TYPE_STRING);
* g_assert (G_VALUE_HOLDS_STRING (&a));
* g_value_set_static_string (&a, "Hello, world!");
* g_printf ("%s\n", g_value_get_string (&a));
*
* // Reset it to its pristine state
* g_value_unset (&a);
*
* // It can then be reused for another type
* g_value_init (&a, G_TYPE_INT);
* g_value_set_int (&a, 42);
*
* // Attempt to transform it into a GValue of type STRING
* g_value_init (&b, G_TYPE_STRING);
*
* // An INT is transformable to a STRING
* g_assert (g_value_type_transformable (G_TYPE_INT, G_TYPE_STRING));
*
* g_value_transform (&a, &b);
* g_printf ("%s\n", g_value_get_string (&b));
*
* // Attempt to transform it again using a custom transform function
* g_value_register_transform_func (G_TYPE_INT, G_TYPE_STRING, int2string);
* g_value_transform (&a, &b);
* g_printf ("%s\n", g_value_get_string (&b));
* return 0;
* }
* ]|
*
* See also [gobject-Standard-Parameter-and-Value-Types] for more information on
* validation of #GValue.
*
* For letting a #GValue own (and memory manage) arbitrary types or pointers,
* they need to become a [boxed type][gboxed]. The example below shows how
* the pointer `mystruct` of type `MyStruct` is used as a [boxed type][gboxed].
*
* |[<!-- language="C" -->
* typedef struct { ... } MyStruct;
* G_DEFINE_BOXED_TYPE (MyStruct, my_struct, my_struct_copy, my_struct_free)
*
* // These two lines normally go in a public header. By GObject convention,
* // the naming scheme is NAMESPACE_TYPE_NAME:
* #define MY_TYPE_STRUCT (my_struct_get_type ())
* GType my_struct_get_type (void);
*
* void
* foo ()
* {
* GValue *value = g_new0 (GValue, 1);
* g_value_init (value, MY_TYPE_STRUCT);
* g_value_set_boxed (value, mystruct);
* // [... your code ....]
* g_value_unset (value);
* g_value_free (value);
* }
* ]|
*/
/* --- typedefs & structures --- */
typedef struct {
GType src_type;
GType dest_type;
GValueTransform func;
} TransformEntry;
/* --- prototypes --- */
static gint transform_entries_cmp (gconstpointer bsearch_node1,
gconstpointer bsearch_node2);
/* --- variables --- */
static GBSearchArray *transform_array = NULL;
static GBSearchConfig transform_bconfig = {
sizeof (TransformEntry),
transform_entries_cmp,
G_BSEARCH_ARRAY_ALIGN_POWER2,
};
/* --- functions --- */
void
_g_value_c_init (void)
{
transform_array = g_bsearch_array_create (&transform_bconfig);
}
static inline void /* keep this function in sync with gvaluecollector.h and gboxed.c */
value_meminit (GValue *value,
GType value_type)
{
value->g_type = value_type;
memset (value->data, 0, sizeof (value->data));
}
/**
* g_value_init:
* @value: A zero-filled (uninitialized) #GValue structure.
* @g_type: Type the #GValue should hold values of.
*
* Initializes @value with the default value of @type.
*
* Returns: (transfer none): the #GValue structure that has been passed in
*/
GValue*
g_value_init (GValue *value,
GType g_type)
{
GTypeValueTable *value_table;
/* g_return_val_if_fail (G_TYPE_IS_VALUE (g_type), NULL); be more elaborate below */
g_return_val_if_fail (value != NULL, NULL);
/* g_return_val_if_fail (G_VALUE_TYPE (value) == 0, NULL); be more elaborate below */
value_table = g_type_value_table_peek (g_type);
if (value_table && G_VALUE_TYPE (value) == 0)
{
/* setup and init */
value_meminit (value, g_type);
value_table->value_init (value);
}
else if (G_VALUE_TYPE (value))
g_warning ("%s: cannot initialize GValue with type '%s', the value has already been initialized as '%s'",
G_STRLOC,
g_type_name (g_type),
g_type_name (G_VALUE_TYPE (value)));
else /* !G_TYPE_IS_VALUE (g_type) */
g_warning ("%s: cannot initialize GValue with type '%s', %s",
G_STRLOC,
g_type_name (g_type),
value_table ? "this type is abstract with regards to GValue use, use a more specific (derived) type" : "this type has no GTypeValueTable implementation");
return value;
}
/**
* g_value_copy:
* @src_value: An initialized #GValue structure.
* @dest_value: An initialized #GValue structure of the same type as @src_value.
*
* Copies the value of @src_value into @dest_value.
*/
void
g_value_copy (const GValue *src_value,
GValue *dest_value)
{
g_return_if_fail (src_value);
g_return_if_fail (dest_value);
g_return_if_fail (g_value_type_compatible (G_VALUE_TYPE (src_value), G_VALUE_TYPE (dest_value)));
if (src_value != dest_value)
{
GType dest_type = G_VALUE_TYPE (dest_value);
GTypeValueTable *value_table = g_type_value_table_peek (dest_type);
g_return_if_fail (value_table);
/* make sure dest_value's value is free()d */
if (value_table->value_free)
value_table->value_free (dest_value);
/* setup and copy */
value_meminit (dest_value, dest_type);
value_table->value_copy (src_value, dest_value);
}
}
/**
* g_value_reset:
* @value: An initialized #GValue structure.
*
* Clears the current value in @value and resets it to the default value
* (as if the value had just been initialized).
*
* Returns: the #GValue structure that has been passed in
*/
GValue*
g_value_reset (GValue *value)
{
GTypeValueTable *value_table;
GType g_type;
g_return_val_if_fail (value, NULL);
g_type = G_VALUE_TYPE (value);
value_table = g_type_value_table_peek (g_type);
g_return_val_if_fail (value_table, NULL);
/* make sure value's value is free()d */
if (value_table->value_free)
value_table->value_free (value);
/* setup and init */
value_meminit (value, g_type);
value_table->value_init (value);
return value;
}
/**
* g_value_unset:
* @value: An initialized #GValue structure.
*
* Clears the current value in @value (if any) and "unsets" the type,
* this releases all resources associated with this GValue. An unset
* value is the same as an uninitialized (zero-filled) #GValue
* structure.
*/
void
g_value_unset (GValue *value)
{
GTypeValueTable *value_table;
if (value->g_type == 0)
return;
g_return_if_fail (value);
value_table = g_type_value_table_peek (G_VALUE_TYPE (value));
g_return_if_fail (value_table);
if (value_table->value_free)
value_table->value_free (value);
memset (value, 0, sizeof (*value));
}
/**
* g_value_fits_pointer:
* @value: An initialized #GValue structure.
*
* Determines if @value will fit inside the size of a pointer value.
* This is an internal function introduced mainly for C marshallers.
*
* Returns: %TRUE if @value will fit inside a pointer value.
*/
gboolean
g_value_fits_pointer (const GValue *value)
{
GTypeValueTable *value_table;
g_return_val_if_fail (value, FALSE);
value_table = g_type_value_table_peek (G_VALUE_TYPE (value));
g_return_val_if_fail (value_table, FALSE);
return value_table->value_peek_pointer != NULL;
}
/**
* g_value_peek_pointer:
* @value: An initialized #GValue structure
*
* Returns the value contents as pointer. This function asserts that
* g_value_fits_pointer() returned %TRUE for the passed in value.
* This is an internal function introduced mainly for C marshallers.
*
* Returns: (transfer none): the value contents as pointer
*/
gpointer
g_value_peek_pointer (const GValue *value)
{
GTypeValueTable *value_table;
g_return_val_if_fail (value, NULL);
value_table = g_type_value_table_peek (G_VALUE_TYPE (value));
g_return_val_if_fail (value_table, NULL);
if (!value_table->value_peek_pointer)
{
g_return_val_if_fail (g_value_fits_pointer (value) == TRUE, NULL);
return NULL;
}
return value_table->value_peek_pointer (value);
}
/**
* g_value_set_instance:
* @value: An initialized #GValue structure.
* @instance: (nullable): the instance
*
* Sets @value from an instantiatable type via the
* value_table's collect_value() function.
*/
void
g_value_set_instance (GValue *value,
gpointer instance)
{
GType g_type;
GTypeValueTable *value_table;
GTypeCValue cvalue;
gchar *error_msg;
g_return_if_fail (value);
g_type = G_VALUE_TYPE (value);
value_table = g_type_value_table_peek (g_type);
g_return_if_fail (value_table);
if (instance)
{
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (instance), G_VALUE_TYPE (value)));
}
g_return_if_fail (strcmp (value_table->collect_format, "p") == 0);
memset (&cvalue, 0, sizeof (cvalue));
cvalue.v_pointer = instance;
/* make sure value's value is free()d */
if (value_table->value_free)
value_table->value_free (value);
/* setup and collect */
value_meminit (value, g_type);
error_msg = value_table->collect_value (value, 1, &cvalue, 0);
if (error_msg)
{
g_warning ("%s: %s", G_STRLOC, error_msg);
g_free (error_msg);
/* we purposely leak the value here, it might not be
* in a correct state if an error condition occurred
*/
value_meminit (value, g_type);
value_table->value_init (value);
}
}
/**
* g_value_init_from_instance:
* @value: An uninitialized #GValue structure.
* @instance: (type GObject.TypeInstance): the instance
*
* Initializes and sets @value from an instantiatable type via the
* value_table's collect_value() function.
*
* Note: The @value will be initialised with the exact type of
* @instance. If you wish to set the @value's type to a different GType
* (such as a parent class GType), you need to manually call
* g_value_init() and g_value_set_instance().
*
* Since: 2.42
*/
void
g_value_init_from_instance (GValue *value,
gpointer instance)
{
g_return_if_fail (value != NULL && G_VALUE_TYPE(value) == 0);
if (G_IS_OBJECT (instance))
{
/* Fast-path.
* If G_IS_OBJECT() succeeds we know:
* * that instance is present and valid
* * that it is a GObject, and therefore we can directly
* use the collect implementation (g_object_ref) */
value_meminit (value, G_TYPE_FROM_INSTANCE (instance));
value->data[0].v_pointer = g_object_ref (instance);
}
else
{
GType g_type;
GTypeValueTable *value_table;
GTypeCValue cvalue;
gchar *error_msg;
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
g_type = G_TYPE_FROM_INSTANCE (instance);
value_table = g_type_value_table_peek (g_type);
g_return_if_fail (strcmp (value_table->collect_format, "p") == 0);
memset (&cvalue, 0, sizeof (cvalue));
cvalue.v_pointer = instance;
/* setup and collect */
value_meminit (value, g_type);
value_table->value_init (value);
error_msg = value_table->collect_value (value, 1, &cvalue, 0);
if (error_msg)
{
g_warning ("%s: %s", G_STRLOC, error_msg);
g_free (error_msg);
/* we purposely leak the value here, it might not be
* in a correct state if an error condition occurred
*/
value_meminit (value, g_type);
value_table->value_init (value);
}
}
}
static GType
transform_lookup_get_parent_type (GType type)
{
if (g_type_fundamental (type) == G_TYPE_INTERFACE)
return g_type_interface_instantiatable_prerequisite (type);
return g_type_parent (type);
}
static GValueTransform
transform_func_lookup (GType src_type,
GType dest_type)
{
TransformEntry entry;
entry.src_type = src_type;
do
{
entry.dest_type = dest_type;
do
{
TransformEntry *e;
e = g_bsearch_array_lookup (transform_array, &transform_bconfig, &entry);
if (e)
{
/* need to check that there hasn't been a change in value handling */
if (g_type_value_table_peek (entry.dest_type) == g_type_value_table_peek (dest_type) &&
g_type_value_table_peek (entry.src_type) == g_type_value_table_peek (src_type))
return e->func;
}
entry.dest_type = transform_lookup_get_parent_type (entry.dest_type);
}
while (entry.dest_type);
entry.src_type = transform_lookup_get_parent_type (entry.src_type);
}
while (entry.src_type);
return NULL;
}
static gint
transform_entries_cmp (gconstpointer bsearch_node1,
gconstpointer bsearch_node2)
{
const TransformEntry *e1 = bsearch_node1;
const TransformEntry *e2 = bsearch_node2;
gint cmp = G_BSEARCH_ARRAY_CMP (e1->src_type, e2->src_type);
if (cmp)
return cmp;
else
return G_BSEARCH_ARRAY_CMP (e1->dest_type, e2->dest_type);
}
/**
* g_value_register_transform_func: (skip)
* @src_type: Source type.
* @dest_type: Target type.
* @transform_func: a function which transforms values of type @src_type
* into value of type @dest_type
*
* Registers a value transformation function for use in g_value_transform().
* A previously registered transformation function for @src_type and @dest_type
* will be replaced.
*/
void
g_value_register_transform_func (GType src_type,
GType dest_type,
GValueTransform transform_func)
{
TransformEntry entry;
/* these checks won't pass for dynamic types.
* g_return_if_fail (G_TYPE_HAS_VALUE_TABLE (src_type));
* g_return_if_fail (G_TYPE_HAS_VALUE_TABLE (dest_type));
*/
g_return_if_fail (transform_func != NULL);
entry.src_type = src_type;
entry.dest_type = dest_type;
#if 0 /* let transform function replacement be a valid operation */
if (g_bsearch_array_lookup (transform_array, &transform_bconfig, &entry))
g_warning ("reregistering value transformation function (%p) for '%s' to '%s'",
transform_func,
g_type_name (src_type),
g_type_name (dest_type));
#endif
entry.func = transform_func;
transform_array = g_bsearch_array_replace (transform_array, &transform_bconfig, &entry);
}
/**
* g_value_type_transformable:
* @src_type: Source type.
* @dest_type: Target type.
*
* Check whether g_value_transform() is able to transform values
* of type @src_type into values of type @dest_type. Note that for
* the types to be transformable, they must be compatible or a
* transformation function must be registered.
*
* Returns: %TRUE if the transformation is possible, %FALSE otherwise.
*/
gboolean
g_value_type_transformable (GType src_type,
GType dest_type)
{
g_return_val_if_fail (src_type, FALSE);
g_return_val_if_fail (dest_type, FALSE);
return (g_value_type_compatible (src_type, dest_type) ||
transform_func_lookup (src_type, dest_type) != NULL);
}
/**
* g_value_type_compatible:
* @src_type: source type to be copied.
* @dest_type: destination type for copying.
*
* Returns whether a #GValue of type @src_type can be copied into
* a #GValue of type @dest_type.
*
* Returns: %TRUE if g_value_copy() is possible with @src_type and @dest_type.
*/
gboolean
g_value_type_compatible (GType src_type,
GType dest_type)
{
g_return_val_if_fail (src_type, FALSE);
g_return_val_if_fail (dest_type, FALSE);
/* Fast path */
if (src_type == dest_type)
return TRUE;
return (g_type_is_a (src_type, dest_type) &&
g_type_value_table_peek (dest_type) == g_type_value_table_peek (src_type));
}
/**
* g_value_transform:
* @src_value: Source value.
* @dest_value: Target value.
*
* Tries to cast the contents of @src_value into a type appropriate
* to store in @dest_value, e.g. to transform a %G_TYPE_INT value
* into a %G_TYPE_FLOAT value. Performing transformations between
* value types might incur precision lossage. Especially
* transformations into strings might reveal seemingly arbitrary
* results and shouldn't be relied upon for production code (such
* as rcfile value or object property serialization).
*
* Returns: Whether a transformation rule was found and could be applied.
* Upon failing transformations, @dest_value is left untouched.
*/
gboolean
g_value_transform (const GValue *src_value,
GValue *dest_value)
{
GType dest_type;
g_return_val_if_fail (src_value, FALSE);
g_return_val_if_fail (dest_value, FALSE);
dest_type = G_VALUE_TYPE (dest_value);
if (g_value_type_compatible (G_VALUE_TYPE (src_value), dest_type))
{
g_value_copy (src_value, dest_value);
return TRUE;
}
else
{
GValueTransform transform = transform_func_lookup (G_VALUE_TYPE (src_value), dest_type);
if (transform)
{
g_value_unset (dest_value);
/* setup and transform */
value_meminit (dest_value, dest_type);
transform (src_value, dest_value);
return TRUE;
}
}
return FALSE;
}

210
gobject/gvalue.h Normal file
View file

@ -0,0 +1,210 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 1997-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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* gvalue.h: generic GValue functions
*/
#ifndef __G_VALUE_H__
#define __G_VALUE_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <gobject/gtype.h>
G_BEGIN_DECLS
/* --- type macros --- */
/**
* G_TYPE_IS_VALUE:
* @type: A #GType value.
*
* Checks whether the passed in type ID can be used for g_value_init().
*
* That is, this macro checks whether this type provides an implementation
* of the #GTypeValueTable functions required for a type to create a #GValue of.
*
* Returns: Whether @type is suitable as a #GValue type.
*/
#define G_TYPE_IS_VALUE(type) (g_type_check_is_value_type (type))
/**
* G_IS_VALUE:
* @value: A #GValue structure.
*
* Checks if @value is a valid and initialized #GValue structure.
*
* Returns: %TRUE on success.
*/
#define G_IS_VALUE(value) (G_TYPE_CHECK_VALUE (value))
/**
* G_VALUE_TYPE:
* @value: A #GValue structure.
*
* Get the type identifier of @value.
*
* Returns: the #GType.
*/
#define G_VALUE_TYPE(value) (((GValue*) (value))->g_type)
/**
* G_VALUE_TYPE_NAME:
* @value: A #GValue structure.
*
* Gets the type name of @value.
*
* Returns: the type name.
*/
#define G_VALUE_TYPE_NAME(value) (g_type_name (G_VALUE_TYPE (value)))
/**
* G_VALUE_HOLDS:
* @value: A #GValue structure.
* @type: A #GType value.
*
* Checks if @value holds (or contains) a value of @type.
* This macro will also check for @value != %NULL and issue a
* warning if the check fails.
*
* Returns: %TRUE if @value holds the @type.
*/
#define G_VALUE_HOLDS(value,type) (G_TYPE_CHECK_VALUE_TYPE ((value), (type)))
/* --- typedefs & structures --- */
/**
* GValueTransform:
* @src_value: Source value.
* @dest_value: Target value.
*
* The type of value transformation functions which can be registered with
* g_value_register_transform_func().
*
* @dest_value will be initialized to the correct destination type.
*/
typedef void (*GValueTransform) (const GValue *src_value,
GValue *dest_value);
/**
* GValue:
*
* An opaque structure used to hold different types of values.
*
* The data within the structure has protected scope: it is accessible only
* to functions within a #GTypeValueTable structure, or implementations of
* the g_value_*() API. That is, code portions which implement new fundamental
* types.
*
* #GValue users cannot make any assumptions about how data is stored
* within the 2 element @data union, and the @g_type member should
* only be accessed through the G_VALUE_TYPE() macro.
*/
struct _GValue
{
/*< private >*/
GType g_type;
/* public for GTypeValueTable methods */
union {
gint v_int;
guint v_uint;
glong v_long;
gulong v_ulong;
gint64 v_int64;
guint64 v_uint64;
gfloat v_float;
gdouble v_double;
gpointer v_pointer;
} data[2];
};
/* --- prototypes --- */
GLIB_AVAILABLE_IN_ALL
GValue* g_value_init (GValue *value,
GType g_type);
GLIB_AVAILABLE_IN_ALL
void g_value_copy (const GValue *src_value,
GValue *dest_value);
GLIB_AVAILABLE_IN_ALL
GValue* g_value_reset (GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_unset (GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_instance (GValue *value,
gpointer instance);
GLIB_AVAILABLE_IN_2_42
void g_value_init_from_instance (GValue *value,
gpointer instance);
/* --- private --- */
GLIB_AVAILABLE_IN_ALL
gboolean g_value_fits_pointer (const GValue *value);
GLIB_AVAILABLE_IN_ALL
gpointer g_value_peek_pointer (const GValue *value);
/* --- implementation details --- */
GLIB_AVAILABLE_IN_ALL
gboolean g_value_type_compatible (GType src_type,
GType dest_type);
GLIB_AVAILABLE_IN_ALL
gboolean g_value_type_transformable (GType src_type,
GType dest_type);
GLIB_AVAILABLE_IN_ALL
gboolean g_value_transform (const GValue *src_value,
GValue *dest_value);
GLIB_AVAILABLE_IN_ALL
void g_value_register_transform_func (GType src_type,
GType dest_type,
GValueTransform transform_func);
/**
* G_VALUE_NOCOPY_CONTENTS:
*
* If passed to G_VALUE_COLLECT(), allocated data won't be copied
* but used verbatim. This does not affect ref-counted types like
* objects. This does not affect usage of g_value_copy(), the data will
* be copied if it is not ref-counted.
*/
#define G_VALUE_NOCOPY_CONTENTS (1 << 27)
/**
* G_VALUE_INTERNED_STRING:
*
* For string values, indicates that the string contained is canonical and will
* exist for the duration of the process. See g_value_set_interned_string().
*
* Since: 2.66
*/
#define G_VALUE_INTERNED_STRING (1 << 28) GLIB_AVAILABLE_MACRO_IN_2_66
/**
* G_VALUE_INIT:
*
* A #GValue must be initialized before it can be used. This macro can
* be used as initializer instead of an explicit `{ 0 }` when declaring
* a variable, but it cannot be assigned to a variable.
*
* |[<!-- language="C" -->
* GValue value = G_VALUE_INIT;
* ]|
*
* Since: 2.30
*/
#define G_VALUE_INIT { 0, { { 0 } } }
G_END_DECLS
#endif /* __G_VALUE_H__ */

370
gobject/gvaluearray.c Normal file
View file

@ -0,0 +1,370 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2001 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* MT safe
*/
#include "config.h"
#include <string.h>
#include <stdlib.h> /* qsort() */
#include "gvaluearray.h"
/**
* SECTION:value_arrays
* @short_description: A container structure to maintain an array of
* generic values
* @see_also: #GValue, #GParamSpecValueArray, g_param_spec_value_array()
* @title: Value arrays
*
* The prime purpose of a #GValueArray is for it to be used as an
* object property that holds an array of values. A #GValueArray wraps
* an array of #GValue elements in order for it to be used as a boxed
* type through %G_TYPE_VALUE_ARRAY.
*
* #GValueArray is deprecated in favour of #GArray since GLib 2.32. It
* is possible to create a #GArray that behaves like a #GValueArray by
* using the size of #GValue as the element size, and by setting
* g_value_unset() as the clear function using g_array_set_clear_func(),
* for instance, the following code:
*
* |[<!-- language="C" -->
* GValueArray *array = g_value_array_new (10);
* ]|
*
* can be replaced by:
*
* |[<!-- language="C" -->
* GArray *array = g_array_sized_new (FALSE, TRUE, sizeof (GValue), 10);
* g_array_set_clear_func (array, (GDestroyNotify) g_value_unset);
* ]|
*
* Deprecated: 2.32: Use #GArray instead, if possible for the given use case,
* as described above.
*/
#define GROUP_N_VALUES (8) /* power of 2 !! */
/* --- functions --- */
/**
* g_value_array_get_nth:
* @value_array: #GValueArray to get a value from
* @index_: index of the value of interest
*
* Return a pointer to the value at @index_ containd in @value_array.
*
* Returns: (transfer none): pointer to a value at @index_ in @value_array
*
* Deprecated: 2.32: Use g_array_index() instead.
*/
GValue*
g_value_array_get_nth (GValueArray *value_array,
guint index)
{
g_return_val_if_fail (value_array != NULL, NULL);
g_return_val_if_fail (index < value_array->n_values, NULL);
return value_array->values + index;
}
static inline void
value_array_grow (GValueArray *value_array,
guint n_values,
gboolean zero_init)
{
g_return_if_fail (n_values >= value_array->n_values);
value_array->n_values = n_values;
if (value_array->n_values > value_array->n_prealloced)
{
guint i = value_array->n_prealloced;
value_array->n_prealloced = (value_array->n_values + GROUP_N_VALUES - 1) & ~(GROUP_N_VALUES - 1);
value_array->values = g_renew (GValue, value_array->values, value_array->n_prealloced);
if (!zero_init)
i = value_array->n_values;
memset (value_array->values + i, 0,
(value_array->n_prealloced - i) * sizeof (value_array->values[0]));
}
}
/**
* g_value_array_new:
* @n_prealloced: number of values to preallocate space for
*
* Allocate and initialize a new #GValueArray, optionally preserve space
* for @n_prealloced elements. New arrays always contain 0 elements,
* regardless of the value of @n_prealloced.
*
* Returns: a newly allocated #GValueArray with 0 values
*
* Deprecated: 2.32: Use #GArray and g_array_sized_new() instead.
*/
GValueArray*
g_value_array_new (guint n_prealloced)
{
GValueArray *value_array = g_slice_new (GValueArray);
value_array->n_values = 0;
value_array->n_prealloced = 0;
value_array->values = NULL;
value_array_grow (value_array, n_prealloced, TRUE);
value_array->n_values = 0;
return value_array;
}
/**
* g_value_array_free: (skip)
* @value_array: #GValueArray to free
*
* Free a #GValueArray including its contents.
*
* Deprecated: 2.32: Use #GArray and g_array_unref() instead.
*/
void
g_value_array_free (GValueArray *value_array)
{
guint i;
g_return_if_fail (value_array != NULL);
for (i = 0; i < value_array->n_values; i++)
{
GValue *value = value_array->values + i;
if (G_VALUE_TYPE (value) != 0) /* we allow unset values in the array */
g_value_unset (value);
}
g_free (value_array->values);
g_slice_free (GValueArray, value_array);
}
/**
* g_value_array_copy:
* @value_array: #GValueArray to copy
*
* Construct an exact copy of a #GValueArray by duplicating all its
* contents.
*
* Returns: (transfer full): Newly allocated copy of #GValueArray
*
* Deprecated: 2.32: Use #GArray and g_array_ref() instead.
*/
GValueArray*
g_value_array_copy (const GValueArray *value_array)
{
GValueArray *new_array;
guint i;
g_return_val_if_fail (value_array != NULL, NULL);
new_array = g_slice_new (GValueArray);
new_array->n_values = 0;
new_array->values = NULL;
new_array->n_prealloced = 0;
value_array_grow (new_array, value_array->n_values, TRUE);
for (i = 0; i < new_array->n_values; i++)
if (G_VALUE_TYPE (value_array->values + i) != 0)
{
GValue *value = new_array->values + i;
g_value_init (value, G_VALUE_TYPE (value_array->values + i));
g_value_copy (value_array->values + i, value);
}
return new_array;
}
/**
* g_value_array_prepend:
* @value_array: #GValueArray to add an element to
* @value: (nullable): #GValue to copy into #GValueArray, or %NULL
*
* Insert a copy of @value as first element of @value_array. If @value is
* %NULL, an uninitialized value is prepended.
*
*
* Returns: (transfer none): the #GValueArray passed in as @value_array
*
* Deprecated: 2.32: Use #GArray and g_array_prepend_val() instead.
*/
GValueArray*
g_value_array_prepend (GValueArray *value_array,
const GValue *value)
{
g_return_val_if_fail (value_array != NULL, NULL);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
return g_value_array_insert (value_array, 0, value);
G_GNUC_END_IGNORE_DEPRECATIONS
}
/**
* g_value_array_append:
* @value_array: #GValueArray to add an element to
* @value: (nullable): #GValue to copy into #GValueArray, or %NULL
*
* Insert a copy of @value as last element of @value_array. If @value is
* %NULL, an uninitialized value is appended.
*
* Returns: (transfer none): the #GValueArray passed in as @value_array
*
* Deprecated: 2.32: Use #GArray and g_array_append_val() instead.
*/
GValueArray*
g_value_array_append (GValueArray *value_array,
const GValue *value)
{
g_return_val_if_fail (value_array != NULL, NULL);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
return g_value_array_insert (value_array, value_array->n_values, value);
G_GNUC_END_IGNORE_DEPRECATIONS
}
/**
* g_value_array_insert:
* @value_array: #GValueArray to add an element to
* @index_: insertion position, must be <= value_array->;n_values
* @value: (nullable): #GValue to copy into #GValueArray, or %NULL
*
* Insert a copy of @value at specified position into @value_array. If @value
* is %NULL, an uninitialized value is inserted.
*
* Returns: (transfer none): the #GValueArray passed in as @value_array
*
* Deprecated: 2.32: Use #GArray and g_array_insert_val() instead.
*/
GValueArray*
g_value_array_insert (GValueArray *value_array,
guint index,
const GValue *value)
{
guint i;
g_return_val_if_fail (value_array != NULL, NULL);
g_return_val_if_fail (index <= value_array->n_values, value_array);
i = value_array->n_values;
value_array_grow (value_array, value_array->n_values + 1, FALSE);
if (index + 1 < value_array->n_values)
memmove (value_array->values + index + 1, value_array->values + index,
(i - index) * sizeof (value_array->values[0]));
memset (value_array->values + index, 0, sizeof (value_array->values[0]));
if (value)
{
g_value_init (value_array->values + index, G_VALUE_TYPE (value));
g_value_copy (value, value_array->values + index);
}
return value_array;
}
/**
* g_value_array_remove:
* @value_array: #GValueArray to remove an element from
* @index_: position of value to remove, which must be less than
* @value_array->n_values
*
* Remove the value at position @index_ from @value_array.
*
* Returns: (transfer none): the #GValueArray passed in as @value_array
*
* Deprecated: 2.32: Use #GArray and g_array_remove_index() instead.
*/
GValueArray*
g_value_array_remove (GValueArray *value_array,
guint index)
{
g_return_val_if_fail (value_array != NULL, NULL);
g_return_val_if_fail (index < value_array->n_values, value_array);
if (G_VALUE_TYPE (value_array->values + index) != 0)
g_value_unset (value_array->values + index);
value_array->n_values--;
if (index < value_array->n_values)
memmove (value_array->values + index, value_array->values + index + 1,
(value_array->n_values - index) * sizeof (value_array->values[0]));
if (value_array->n_prealloced > value_array->n_values)
memset (value_array->values + value_array->n_values, 0, sizeof (value_array->values[0]));
return value_array;
}
/**
* g_value_array_sort:
* @value_array: #GValueArray to sort
* @compare_func: (scope call): function to compare elements
*
* Sort @value_array using @compare_func to compare the elements according to
* the semantics of #GCompareFunc.
*
* The current implementation uses the same sorting algorithm as standard
* C qsort() function.
*
* Returns: (transfer none): the #GValueArray passed in as @value_array
*
* Deprecated: 2.32: Use #GArray and g_array_sort().
*/
GValueArray*
g_value_array_sort (GValueArray *value_array,
GCompareFunc compare_func)
{
g_return_val_if_fail (compare_func != NULL, NULL);
if (value_array->n_values)
qsort (value_array->values,
value_array->n_values,
sizeof (value_array->values[0]),
compare_func);
return value_array;
}
/**
* g_value_array_sort_with_data: (rename-to g_value_array_sort)
* @value_array: #GValueArray to sort
* @compare_func: (scope call): function to compare elements
* @user_data: (closure): extra data argument provided for @compare_func
*
* Sort @value_array using @compare_func to compare the elements according
* to the semantics of #GCompareDataFunc.
*
* The current implementation uses the same sorting algorithm as standard
* C qsort() function.
*
* Returns: (transfer none): the #GValueArray passed in as @value_array
*
* Deprecated: 2.32: Use #GArray and g_array_sort_with_data().
*/
GValueArray*
g_value_array_sort_with_data (GValueArray *value_array,
GCompareDataFunc compare_func,
gpointer user_data)
{
g_return_val_if_fail (value_array != NULL, NULL);
g_return_val_if_fail (compare_func != NULL, NULL);
if (value_array->n_values)
g_qsort_with_data (value_array->values,
value_array->n_values,
sizeof (value_array->values[0]),
compare_func, user_data);
return value_array;
}

104
gobject/gvaluearray.h Normal file
View file

@ -0,0 +1,104 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2001 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* gvaluearray.h: GLib array type holding GValues
*/
#ifndef __G_VALUE_ARRAY_H__
#define __G_VALUE_ARRAY_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <gobject/gvalue.h>
G_BEGIN_DECLS
/**
* G_TYPE_VALUE_ARRAY:
*
* The type ID of the "GValueArray" type which is a boxed type,
* used to pass around pointers to GValueArrays.
*
* Deprecated: 2.32: Use #GArray instead of #GValueArray
*/
#define G_TYPE_VALUE_ARRAY (g_value_array_get_type ()) GLIB_DEPRECATED_MACRO_IN_2_32_FOR(G_TYPE_ARRAY)
/* --- typedefs & structs --- */
typedef struct _GValueArray GValueArray;
/**
* GValueArray:
* @n_values: number of values contained in the array
* @values: array of values
*
* A #GValueArray contains an array of #GValue elements.
*/
struct _GValueArray
{
guint n_values;
GValue *values;
/*< private >*/
guint n_prealloced;
};
/* --- prototypes --- */
GLIB_DEPRECATED_IN_2_32_FOR(GArray)
GType g_value_array_get_type (void) G_GNUC_CONST;
GLIB_DEPRECATED_IN_2_32_FOR(GArray)
GValue* g_value_array_get_nth (GValueArray *value_array,
guint index_);
GLIB_DEPRECATED_IN_2_32_FOR(GArray)
GValueArray* g_value_array_new (guint n_prealloced);
GLIB_DEPRECATED_IN_2_32_FOR(GArray)
void g_value_array_free (GValueArray *value_array);
GLIB_DEPRECATED_IN_2_32_FOR(GArray)
GValueArray* g_value_array_copy (const GValueArray *value_array);
GLIB_DEPRECATED_IN_2_32_FOR(GArray)
GValueArray* g_value_array_prepend (GValueArray *value_array,
const GValue *value);
GLIB_DEPRECATED_IN_2_32_FOR(GArray)
GValueArray* g_value_array_append (GValueArray *value_array,
const GValue *value);
GLIB_DEPRECATED_IN_2_32_FOR(GArray)
GValueArray* g_value_array_insert (GValueArray *value_array,
guint index_,
const GValue *value);
GLIB_DEPRECATED_IN_2_32_FOR(GArray)
GValueArray* g_value_array_remove (GValueArray *value_array,
guint index_);
GLIB_DEPRECATED_IN_2_32_FOR(GArray)
GValueArray* g_value_array_sort (GValueArray *value_array,
GCompareFunc compare_func);
GLIB_DEPRECATED_IN_2_32_FOR(GArray)
GValueArray* g_value_array_sort_with_data (GValueArray *value_array,
GCompareDataFunc compare_func,
gpointer user_data);
G_END_DECLS
#endif /* __G_VALUE_ARRAY_H__ */

265
gobject/gvaluecollector.h Normal file
View file

@ -0,0 +1,265 @@
/* 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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* gvaluecollector.h: GValue varargs stubs
*/
/**
* SECTION:value_collection
* @Short_description: Converting varargs to generic values
* @Title: Varargs Value Collection
*
* The macros in this section provide the varargs parsing support needed
* in variadic GObject functions such as g_object_new() or g_object_set().
*
* They currently support the collection of integral types, floating point
* types and pointers.
*/
#ifndef __G_VALUE_COLLECTOR_H__
#define __G_VALUE_COLLECTOR_H__
#include <glib-object.h>
G_BEGIN_DECLS
/* we may want to add aggregate types here some day, if requested
* by users. the basic C types are covered already, everything
* smaller than an int is promoted to an integer and floats are
* always promoted to doubles for varargs call constructions.
*/
enum /*< skip >*/
{
G_VALUE_COLLECT_INT = 'i',
G_VALUE_COLLECT_LONG = 'l',
G_VALUE_COLLECT_INT64 = 'q',
G_VALUE_COLLECT_DOUBLE = 'd',
G_VALUE_COLLECT_POINTER = 'p'
};
/* vararg union holding actual values collected
*/
/**
* GTypeCValue:
* @v_int: the field for holding integer values
* @v_long: the field for holding long integer values
* @v_int64: the field for holding 64 bit integer values
* @v_double: the field for holding floating point values
* @v_pointer: the field for holding pointers
*
* A union holding one collected value.
*/
union _GTypeCValue
{
gint v_int;
glong v_long;
gint64 v_int64;
gdouble v_double;
gpointer v_pointer;
};
/**
* G_VALUE_COLLECT_INIT:
* @value: a #GValue return location. @value must contain only 0 bytes.
* @_value_type: the #GType to use for @value.
* @var_args: the va_list variable; it may be evaluated multiple times
* @flags: flags which are passed on to the collect_value() function of
* the #GTypeValueTable of @value.
* @__error: a #gchar** variable that will be modified to hold a g_new()
* allocated error messages if something fails
*
* Collects a variable argument value from a `va_list`.
*
* We have to implement the varargs collection as a macro, because on some
* systems `va_list` variables cannot be passed by reference.
*
* Since: 2.24
*/
#define G_VALUE_COLLECT_INIT(value, _value_type, var_args, flags, __error) \
G_STMT_START { \
GValue *g_vci_val = (value); \
guint g_vci_flags = (flags); \
GTypeValueTable *g_vci_vtab = g_type_value_table_peek (_value_type); \
const gchar *g_vci_collect_format = g_vci_vtab->collect_format; \
GTypeCValue g_vci_cvalues[G_VALUE_COLLECT_FORMAT_MAX_LENGTH] = { { 0, }, }; \
guint g_vci_n_values = 0; \
\
g_vci_val->g_type = _value_type; /* value_meminit() from gvalue.c */ \
while (*g_vci_collect_format) \
{ \
GTypeCValue *g_vci_cvalue = g_vci_cvalues + g_vci_n_values++; \
\
switch (*g_vci_collect_format++) \
{ \
case G_VALUE_COLLECT_INT: \
g_vci_cvalue->v_int = va_arg ((var_args), gint); \
break; \
case G_VALUE_COLLECT_LONG: \
g_vci_cvalue->v_long = va_arg ((var_args), glong); \
break; \
case G_VALUE_COLLECT_INT64: \
g_vci_cvalue->v_int64 = va_arg ((var_args), gint64); \
break; \
case G_VALUE_COLLECT_DOUBLE: \
g_vci_cvalue->v_double = va_arg ((var_args), gdouble); \
break; \
case G_VALUE_COLLECT_POINTER: \
g_vci_cvalue->v_pointer = va_arg ((var_args), gpointer); \
break; \
default: \
g_assert_not_reached (); \
} \
} \
*(__error) = g_vci_vtab->collect_value (g_vci_val, \
g_vci_n_values, \
g_vci_cvalues, \
g_vci_flags); \
} G_STMT_END
/**
* G_VALUE_COLLECT:
* @value: a #GValue return location. @value is supposed to be initialized
* according to the value type to be collected
* @var_args: the va_list variable; it may be evaluated multiple times
* @flags: flags which are passed on to the collect_value() function of
* the #GTypeValueTable of @value.
* @__error: a #gchar** variable that will be modified to hold a g_new()
* allocated error messages if something fails
*
* Collects a variable argument value from a `va_list`.
*
* We have to implement the varargs collection as a macro, because on some systems
* `va_list` variables cannot be passed by reference.
*
* Note: If you are creating the @value argument just before calling this macro,
* you should use the G_VALUE_COLLECT_INIT() variant and pass the uninitialized
* #GValue. That variant is faster than G_VALUE_COLLECT().
*/
#define G_VALUE_COLLECT(value, var_args, flags, __error) G_STMT_START { \
GValue *g_vc_value = (value); \
GType g_vc_value_type = G_VALUE_TYPE (g_vc_value); \
GTypeValueTable *g_vc_vtable = g_type_value_table_peek (g_vc_value_type); \
\
if (g_vc_vtable->value_free) \
g_vc_vtable->value_free (g_vc_value); \
memset (g_vc_value->data, 0, sizeof (g_vc_value->data)); \
\
G_VALUE_COLLECT_INIT(value, g_vc_value_type, var_args, flags, __error); \
} G_STMT_END
/**
* G_VALUE_COLLECT_SKIP:
* @_value_type: the #GType of the value to skip
* @var_args: the va_list variable; it may be evaluated multiple times
*
* Skip an argument of type @_value_type from @var_args.
*/
#define G_VALUE_COLLECT_SKIP(_value_type, var_args) \
G_STMT_START { \
GTypeValueTable *g_vcs_vtable = g_type_value_table_peek (_value_type); \
const gchar *g_vcs_collect_format = g_vcs_vtable->collect_format; \
\
while (*g_vcs_collect_format) \
{ \
switch (*g_vcs_collect_format++) \
{ \
case G_VALUE_COLLECT_INT: \
va_arg ((var_args), gint); \
break; \
case G_VALUE_COLLECT_LONG: \
va_arg ((var_args), glong); \
break; \
case G_VALUE_COLLECT_INT64: \
va_arg ((var_args), gint64); \
break; \
case G_VALUE_COLLECT_DOUBLE: \
va_arg ((var_args), gdouble); \
break; \
case G_VALUE_COLLECT_POINTER: \
va_arg ((var_args), gpointer); \
break; \
default: \
g_assert_not_reached (); \
} \
} \
} G_STMT_END
/**
* G_VALUE_LCOPY:
* @value: a #GValue to store into the @var_args; this must be initialized
* and set
* @var_args: the va_list variable; it may be evaluated multiple times
* @flags: flags which are passed on to the lcopy_value() function of
* the #GTypeValueTable of @value.
* @__error: a #gchar** variable that will be modified to hold a g_new()
* allocated error message if something fails
*
* Stores a values value into one or more argument locations from a `va_list`.
*
* This is the inverse of G_VALUE_COLLECT().
*/
#define G_VALUE_LCOPY(value, var_args, flags, __error) \
G_STMT_START { \
const GValue *g_vl_value = (value); \
guint g_vl_flags = (flags); \
GType g_vl_value_type = G_VALUE_TYPE (g_vl_value); \
GTypeValueTable *g_vl_vtable = g_type_value_table_peek (g_vl_value_type); \
const gchar *g_vl_lcopy_format = g_vl_vtable->lcopy_format; \
GTypeCValue g_vl_cvalues[G_VALUE_COLLECT_FORMAT_MAX_LENGTH] = { { 0, }, }; \
guint g_vl_n_values = 0; \
\
while (*g_vl_lcopy_format) \
{ \
GTypeCValue *g_vl_cvalue = g_vl_cvalues + g_vl_n_values++; \
\
switch (*g_vl_lcopy_format++) \
{ \
case G_VALUE_COLLECT_INT: \
g_vl_cvalue->v_int = va_arg ((var_args), gint); \
break; \
case G_VALUE_COLLECT_LONG: \
g_vl_cvalue->v_long = va_arg ((var_args), glong); \
break; \
case G_VALUE_COLLECT_INT64: \
g_vl_cvalue->v_int64 = va_arg ((var_args), gint64); \
break; \
case G_VALUE_COLLECT_DOUBLE: \
g_vl_cvalue->v_double = va_arg ((var_args), gdouble); \
break; \
case G_VALUE_COLLECT_POINTER: \
g_vl_cvalue->v_pointer = va_arg ((var_args), gpointer); \
break; \
default: \
g_assert_not_reached (); \
} \
} \
*(__error) = g_vl_vtable->lcopy_value (g_vl_value, \
g_vl_n_values, \
g_vl_cvalues, \
g_vl_flags); \
} G_STMT_END
/**
* G_VALUE_COLLECT_FORMAT_MAX_LENGTH:
*
* The maximal number of #GTypeCValues which can be collected for a
* single #GValue.
*/
#define G_VALUE_COLLECT_FORMAT_MAX_LENGTH (8)
G_END_DECLS
#endif /* __G_VALUE_COLLECTOR_H__ */

428
gobject/gvaluetransform.c Normal file
View file

@ -0,0 +1,428 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2001 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include "gvalue.h"
#include "gtype-private.h"
#include "genums.h"
/* same type transforms
*/
static void
value_transform_memcpy_data0 (const GValue *src_value,
GValue *dest_value)
{
memcpy (&dest_value->data[0], &src_value->data[0], sizeof (src_value->data[0]));
}
#define value_transform_int_int value_transform_memcpy_data0
#define value_transform_uint_uint value_transform_memcpy_data0
#define value_transform_long_long value_transform_memcpy_data0
#define value_transform_ulong_ulong value_transform_memcpy_data0
#define value_transform_int64_int64 value_transform_memcpy_data0
#define value_transform_uint64_uint64 value_transform_memcpy_data0
#define value_transform_float_float value_transform_memcpy_data0
#define value_transform_double_double value_transform_memcpy_data0
/* numeric casts
*/
#define DEFINE_CAST(func_name, from_member, ctype, to_member) \
static void \
value_transform_##func_name (const GValue *src_value, \
GValue *dest_value) \
{ \
ctype c_value = src_value->data[0].from_member; \
dest_value->data[0].to_member = c_value; \
} extern void glib_dummy_decl (void)
DEFINE_CAST (int_s8, v_int, gint8, v_int);
DEFINE_CAST (int_u8, v_int, guint8, v_uint);
DEFINE_CAST (int_uint, v_int, guint, v_uint);
DEFINE_CAST (int_long, v_int, glong, v_long);
DEFINE_CAST (int_ulong, v_int, gulong, v_ulong);
DEFINE_CAST (int_int64, v_int, gint64, v_int64);
DEFINE_CAST (int_uint64, v_int, guint64, v_uint64);
DEFINE_CAST (int_float, v_int, gfloat, v_float);
DEFINE_CAST (int_double, v_int, gdouble, v_double);
DEFINE_CAST (uint_s8, v_uint, gint8, v_int);
DEFINE_CAST (uint_u8, v_uint, guint8, v_uint);
DEFINE_CAST (uint_int, v_uint, gint, v_int);
DEFINE_CAST (uint_long, v_uint, glong, v_long);
DEFINE_CAST (uint_ulong, v_uint, gulong, v_ulong);
DEFINE_CAST (uint_int64, v_uint, gint64, v_int64);
DEFINE_CAST (uint_uint64, v_uint, guint64, v_uint64);
DEFINE_CAST (uint_float, v_uint, gfloat, v_float);
DEFINE_CAST (uint_double, v_uint, gdouble, v_double);
DEFINE_CAST (long_s8, v_long, gint8, v_int);
DEFINE_CAST (long_u8, v_long, guint8, v_uint);
DEFINE_CAST (long_int, v_long, gint, v_int);
DEFINE_CAST (long_uint, v_long, guint, v_uint);
DEFINE_CAST (long_ulong, v_long, gulong, v_ulong);
DEFINE_CAST (long_int64, v_long, gint64, v_int64);
DEFINE_CAST (long_uint64, v_long, guint64, v_uint64);
DEFINE_CAST (long_float, v_long, gfloat, v_float);
DEFINE_CAST (long_double, v_long, gdouble, v_double);
DEFINE_CAST (ulong_s8, v_ulong, gint8, v_int);
DEFINE_CAST (ulong_u8, v_ulong, guint8, v_uint);
DEFINE_CAST (ulong_int, v_ulong, gint, v_int);
DEFINE_CAST (ulong_uint, v_ulong, guint, v_uint);
DEFINE_CAST (ulong_int64, v_ulong, gint64, v_int64);
DEFINE_CAST (ulong_uint64, v_ulong, guint64, v_uint64);
DEFINE_CAST (ulong_long, v_ulong, glong, v_long);
DEFINE_CAST (ulong_float, v_ulong, gfloat, v_float);
DEFINE_CAST (ulong_double, v_ulong, gdouble, v_double);
DEFINE_CAST (int64_s8, v_int64, gint8, v_int);
DEFINE_CAST (int64_u8, v_int64, guint8, v_uint);
DEFINE_CAST (int64_int, v_int64, gint, v_int);
DEFINE_CAST (int64_uint, v_int64, guint, v_uint);
DEFINE_CAST (int64_long, v_int64, glong, v_long);
DEFINE_CAST (int64_uint64, v_int64, guint64, v_uint64);
DEFINE_CAST (int64_ulong, v_int64, gulong, v_ulong);
DEFINE_CAST (int64_float, v_int64, gfloat, v_float);
DEFINE_CAST (int64_double, v_int64, gdouble, v_double);
DEFINE_CAST (uint64_s8, v_uint64, gint8, v_int);
DEFINE_CAST (uint64_u8, v_uint64, guint8, v_uint);
DEFINE_CAST (uint64_int, v_uint64, gint, v_int);
DEFINE_CAST (uint64_uint, v_uint64, guint, v_uint);
DEFINE_CAST (uint64_long, v_uint64, glong, v_long);
DEFINE_CAST (uint64_ulong, v_uint64, gulong, v_ulong);
DEFINE_CAST (uint64_int64, v_uint64, gint64, v_int64);
DEFINE_CAST (uint64_float, v_uint64, gfloat, v_float);
DEFINE_CAST (uint64_double, v_uint64, gdouble, v_double);
DEFINE_CAST (float_s8, v_float, gint8, v_int);
DEFINE_CAST (float_u8, v_float, guint8, v_uint);
DEFINE_CAST (float_int, v_float, gint, v_int);
DEFINE_CAST (float_uint, v_float, guint, v_uint);
DEFINE_CAST (float_long, v_float, glong, v_long);
DEFINE_CAST (float_ulong, v_float, gulong, v_ulong);
DEFINE_CAST (float_int64, v_float, gint64, v_int64);
DEFINE_CAST (float_uint64, v_float, guint64, v_uint64);
DEFINE_CAST (float_double, v_float, gdouble, v_double);
DEFINE_CAST (double_s8, v_double, gint8, v_int);
DEFINE_CAST (double_u8, v_double, guint8, v_uint);
DEFINE_CAST (double_int, v_double, gint, v_int);
DEFINE_CAST (double_uint, v_double, guint, v_uint);
DEFINE_CAST (double_long, v_double, glong, v_long);
DEFINE_CAST (double_ulong, v_double, gulong, v_ulong);
DEFINE_CAST (double_int64, v_double, gint64, v_int64);
DEFINE_CAST (double_uint64, v_double, guint64, v_uint64);
DEFINE_CAST (double_float, v_double, gfloat, v_float);
/* boolean assignments
*/
#define DEFINE_BOOL_CHECK(func_name, from_member) \
static void \
value_transform_##func_name (const GValue *src_value, \
GValue *dest_value) \
{ \
dest_value->data[0].v_int = src_value->data[0].from_member != 0; \
} extern void glib_dummy_decl (void)
DEFINE_BOOL_CHECK (int_bool, v_int);
DEFINE_BOOL_CHECK (uint_bool, v_uint);
DEFINE_BOOL_CHECK (long_bool, v_long);
DEFINE_BOOL_CHECK (ulong_bool, v_ulong);
DEFINE_BOOL_CHECK (int64_bool, v_int64);
DEFINE_BOOL_CHECK (uint64_bool, v_uint64);
/* string printouts
*/
#define DEFINE_SPRINTF(func_name, from_member, format) \
static void \
value_transform_##func_name (const GValue *src_value, \
GValue *dest_value) \
{ \
dest_value->data[0].v_pointer = g_strdup_printf ((format), \
src_value->data[0].from_member); \
} extern void glib_dummy_decl (void)
DEFINE_SPRINTF (int_string, v_int, "%d");
DEFINE_SPRINTF (uint_string, v_uint, "%u");
DEFINE_SPRINTF (long_string, v_long, "%ld");
DEFINE_SPRINTF (ulong_string, v_ulong, "%lu");
DEFINE_SPRINTF (int64_string, v_int64, "%" G_GINT64_FORMAT);
DEFINE_SPRINTF (uint64_string, v_uint64, "%" G_GUINT64_FORMAT);
DEFINE_SPRINTF (float_string, v_float, "%f");
DEFINE_SPRINTF (double_string, v_double, "%f");
/* special cases
*/
static void
value_transform_bool_string (const GValue *src_value,
GValue *dest_value)
{
dest_value->data[0].v_pointer = g_strdup_printf ("%s",
src_value->data[0].v_int ?
"TRUE" : "FALSE");
}
static void
value_transform_string_string (const GValue *src_value,
GValue *dest_value)
{
dest_value->data[0].v_pointer = g_strdup (src_value->data[0].v_pointer);
}
static void
value_transform_enum_string (const GValue *src_value,
GValue *dest_value)
{
gint v_enum = src_value->data[0].v_long;
gchar *str = g_enum_to_string (G_VALUE_TYPE (src_value), v_enum);
dest_value->data[0].v_pointer = str;
}
static void
value_transform_flags_string (const GValue *src_value,
GValue *dest_value)
{
GFlagsClass *class = g_type_class_ref (G_VALUE_TYPE (src_value));
GFlagsValue *flags_value = g_flags_get_first_value (class, src_value->data[0].v_ulong);
/* Note: this does not use g_flags_to_string()
* to keep backwards compatibility.
*/
if (flags_value)
{
GString *gstring = g_string_new (NULL);
guint v_flags = src_value->data[0].v_ulong;
do
{
v_flags &= ~flags_value->value;
if (gstring->str[0])
g_string_append (gstring, " | ");
g_string_append (gstring, flags_value->value_name);
flags_value = g_flags_get_first_value (class, v_flags);
}
while (flags_value && v_flags);
if (v_flags)
dest_value->data[0].v_pointer = g_strdup_printf ("%s | %u",
gstring->str,
v_flags);
else
dest_value->data[0].v_pointer = g_strdup (gstring->str);
g_string_free (gstring, TRUE);
}
else
dest_value->data[0].v_pointer = g_strdup_printf ("%lu", src_value->data[0].v_ulong);
g_type_class_unref (class);
}
/* registration
*/
void
_g_value_transforms_init (void)
{
/* some transformations are a bit questionable,
* we currently skip those
*/
#define SKIP____register_transform_func(type1,type2,transform_func) /* skip questionable transforms */ \
(void)0
/* numeric types (plus to string) */
g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_CHAR, value_transform_int_int);
g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_UCHAR, value_transform_int_u8);
g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_BOOLEAN, value_transform_int_bool);
g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_INT, value_transform_int_int);
g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_UINT, value_transform_int_uint);
g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_LONG, value_transform_int_long);
g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_ULONG, value_transform_int_ulong);
g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_INT64, value_transform_int_int64);
g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_UINT64, value_transform_int_uint64);
g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_ENUM, value_transform_int_long);
g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_FLAGS, value_transform_int_ulong);
g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_FLOAT, value_transform_int_float);
g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_DOUBLE, value_transform_int_double);
g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_STRING, value_transform_int_string);
g_value_register_transform_func (G_TYPE_UCHAR, G_TYPE_CHAR, value_transform_uint_s8);
g_value_register_transform_func (G_TYPE_UCHAR, G_TYPE_UCHAR, value_transform_uint_uint);
g_value_register_transform_func (G_TYPE_UCHAR, G_TYPE_BOOLEAN, value_transform_uint_bool);
g_value_register_transform_func (G_TYPE_UCHAR, G_TYPE_INT, value_transform_uint_int);
g_value_register_transform_func (G_TYPE_UCHAR, G_TYPE_UINT, value_transform_uint_uint);
g_value_register_transform_func (G_TYPE_UCHAR, G_TYPE_LONG, value_transform_uint_long);
g_value_register_transform_func (G_TYPE_UCHAR, G_TYPE_ULONG, value_transform_uint_ulong);
g_value_register_transform_func (G_TYPE_UCHAR, G_TYPE_INT64, value_transform_uint_int64);
g_value_register_transform_func (G_TYPE_UCHAR, G_TYPE_UINT64, value_transform_uint_uint64);
g_value_register_transform_func (G_TYPE_UCHAR, G_TYPE_ENUM, value_transform_uint_long);
g_value_register_transform_func (G_TYPE_UCHAR, G_TYPE_FLAGS, value_transform_uint_ulong);
g_value_register_transform_func (G_TYPE_UCHAR, G_TYPE_FLOAT, value_transform_uint_float);
g_value_register_transform_func (G_TYPE_UCHAR, G_TYPE_DOUBLE, value_transform_uint_double);
g_value_register_transform_func (G_TYPE_UCHAR, G_TYPE_STRING, value_transform_uint_string);
g_value_register_transform_func (G_TYPE_BOOLEAN, G_TYPE_CHAR, value_transform_int_s8);
g_value_register_transform_func (G_TYPE_BOOLEAN, G_TYPE_UCHAR, value_transform_int_u8);
g_value_register_transform_func (G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, value_transform_int_int);
g_value_register_transform_func (G_TYPE_BOOLEAN, G_TYPE_INT, value_transform_int_int);
g_value_register_transform_func (G_TYPE_BOOLEAN, G_TYPE_UINT, value_transform_int_uint);
g_value_register_transform_func (G_TYPE_BOOLEAN, G_TYPE_LONG, value_transform_int_long);
g_value_register_transform_func (G_TYPE_BOOLEAN, G_TYPE_ULONG, value_transform_int_ulong);
g_value_register_transform_func (G_TYPE_BOOLEAN, G_TYPE_INT64, value_transform_int_int64);
g_value_register_transform_func (G_TYPE_BOOLEAN, G_TYPE_UINT64, value_transform_int_uint64);
g_value_register_transform_func (G_TYPE_BOOLEAN, G_TYPE_ENUM, value_transform_int_long);
g_value_register_transform_func (G_TYPE_BOOLEAN, G_TYPE_FLAGS, value_transform_int_ulong);
SKIP____register_transform_func (G_TYPE_BOOLEAN, G_TYPE_FLOAT, value_transform_int_float);
SKIP____register_transform_func (G_TYPE_BOOLEAN, G_TYPE_DOUBLE, value_transform_int_double);
g_value_register_transform_func (G_TYPE_BOOLEAN, G_TYPE_STRING, value_transform_bool_string);
g_value_register_transform_func (G_TYPE_INT, G_TYPE_CHAR, value_transform_int_s8);
g_value_register_transform_func (G_TYPE_INT, G_TYPE_UCHAR, value_transform_int_u8);
g_value_register_transform_func (G_TYPE_INT, G_TYPE_BOOLEAN, value_transform_int_bool);
g_value_register_transform_func (G_TYPE_INT, G_TYPE_INT, value_transform_int_int);
g_value_register_transform_func (G_TYPE_INT, G_TYPE_UINT, value_transform_int_uint);
g_value_register_transform_func (G_TYPE_INT, G_TYPE_LONG, value_transform_int_long);
g_value_register_transform_func (G_TYPE_INT, G_TYPE_ULONG, value_transform_int_ulong);
g_value_register_transform_func (G_TYPE_INT, G_TYPE_INT64, value_transform_int_int64);
g_value_register_transform_func (G_TYPE_INT, G_TYPE_UINT64, value_transform_int_uint64);
g_value_register_transform_func (G_TYPE_INT, G_TYPE_ENUM, value_transform_int_long);
g_value_register_transform_func (G_TYPE_INT, G_TYPE_FLAGS, value_transform_int_ulong);
g_value_register_transform_func (G_TYPE_INT, G_TYPE_FLOAT, value_transform_int_float);
g_value_register_transform_func (G_TYPE_INT, G_TYPE_DOUBLE, value_transform_int_double);
g_value_register_transform_func (G_TYPE_INT, G_TYPE_STRING, value_transform_int_string);
g_value_register_transform_func (G_TYPE_UINT, G_TYPE_CHAR, value_transform_uint_s8);
g_value_register_transform_func (G_TYPE_UINT, G_TYPE_UCHAR, value_transform_uint_u8);
g_value_register_transform_func (G_TYPE_UINT, G_TYPE_BOOLEAN, value_transform_uint_bool);
g_value_register_transform_func (G_TYPE_UINT, G_TYPE_INT, value_transform_uint_int);
g_value_register_transform_func (G_TYPE_UINT, G_TYPE_UINT, value_transform_uint_uint);
g_value_register_transform_func (G_TYPE_UINT, G_TYPE_LONG, value_transform_uint_long);
g_value_register_transform_func (G_TYPE_UINT, G_TYPE_ULONG, value_transform_uint_ulong);
g_value_register_transform_func (G_TYPE_UINT, G_TYPE_INT64, value_transform_uint_int64);
g_value_register_transform_func (G_TYPE_UINT, G_TYPE_UINT64, value_transform_uint_uint64);
g_value_register_transform_func (G_TYPE_UINT, G_TYPE_ENUM, value_transform_uint_long);
g_value_register_transform_func (G_TYPE_UINT, G_TYPE_FLAGS, value_transform_uint_ulong);
g_value_register_transform_func (G_TYPE_UINT, G_TYPE_FLOAT, value_transform_uint_float);
g_value_register_transform_func (G_TYPE_UINT, G_TYPE_DOUBLE, value_transform_uint_double);
g_value_register_transform_func (G_TYPE_UINT, G_TYPE_STRING, value_transform_uint_string);
g_value_register_transform_func (G_TYPE_LONG, G_TYPE_CHAR, value_transform_long_s8);
g_value_register_transform_func (G_TYPE_LONG, G_TYPE_UCHAR, value_transform_long_u8);
g_value_register_transform_func (G_TYPE_LONG, G_TYPE_BOOLEAN, value_transform_long_bool);
g_value_register_transform_func (G_TYPE_LONG, G_TYPE_INT, value_transform_long_int);
g_value_register_transform_func (G_TYPE_LONG, G_TYPE_UINT, value_transform_long_uint);
g_value_register_transform_func (G_TYPE_LONG, G_TYPE_LONG, value_transform_long_long);
g_value_register_transform_func (G_TYPE_LONG, G_TYPE_ULONG, value_transform_long_ulong);
g_value_register_transform_func (G_TYPE_LONG, G_TYPE_INT64, value_transform_long_int64);
g_value_register_transform_func (G_TYPE_LONG, G_TYPE_UINT64, value_transform_long_uint64);
g_value_register_transform_func (G_TYPE_LONG, G_TYPE_ENUM, value_transform_long_long);
g_value_register_transform_func (G_TYPE_LONG, G_TYPE_FLAGS, value_transform_long_ulong);
g_value_register_transform_func (G_TYPE_LONG, G_TYPE_FLOAT, value_transform_long_float);
g_value_register_transform_func (G_TYPE_LONG, G_TYPE_DOUBLE, value_transform_long_double);
g_value_register_transform_func (G_TYPE_LONG, G_TYPE_STRING, value_transform_long_string);
g_value_register_transform_func (G_TYPE_ULONG, G_TYPE_CHAR, value_transform_ulong_s8);
g_value_register_transform_func (G_TYPE_ULONG, G_TYPE_UCHAR, value_transform_ulong_u8);
g_value_register_transform_func (G_TYPE_ULONG, G_TYPE_BOOLEAN, value_transform_ulong_bool);
g_value_register_transform_func (G_TYPE_ULONG, G_TYPE_INT, value_transform_ulong_int);
g_value_register_transform_func (G_TYPE_ULONG, G_TYPE_UINT, value_transform_ulong_uint);
g_value_register_transform_func (G_TYPE_ULONG, G_TYPE_LONG, value_transform_ulong_long);
g_value_register_transform_func (G_TYPE_ULONG, G_TYPE_ULONG, value_transform_ulong_ulong);
g_value_register_transform_func (G_TYPE_ULONG, G_TYPE_INT64, value_transform_ulong_int64);
g_value_register_transform_func (G_TYPE_ULONG, G_TYPE_UINT64, value_transform_ulong_uint64);
g_value_register_transform_func (G_TYPE_ULONG, G_TYPE_ENUM, value_transform_ulong_long);
g_value_register_transform_func (G_TYPE_ULONG, G_TYPE_FLAGS, value_transform_ulong_ulong);
g_value_register_transform_func (G_TYPE_ULONG, G_TYPE_FLOAT, value_transform_ulong_float);
g_value_register_transform_func (G_TYPE_ULONG, G_TYPE_DOUBLE, value_transform_ulong_double);
g_value_register_transform_func (G_TYPE_ULONG, G_TYPE_STRING, value_transform_ulong_string);
g_value_register_transform_func (G_TYPE_INT64, G_TYPE_CHAR, value_transform_int64_s8);
g_value_register_transform_func (G_TYPE_INT64, G_TYPE_UCHAR, value_transform_int64_u8);
g_value_register_transform_func (G_TYPE_INT64, G_TYPE_BOOLEAN, value_transform_int64_bool);
g_value_register_transform_func (G_TYPE_INT64, G_TYPE_INT, value_transform_int64_int);
g_value_register_transform_func (G_TYPE_INT64, G_TYPE_UINT, value_transform_int64_uint);
g_value_register_transform_func (G_TYPE_INT64, G_TYPE_LONG, value_transform_int64_long);
g_value_register_transform_func (G_TYPE_INT64, G_TYPE_ULONG, value_transform_int64_ulong);
g_value_register_transform_func (G_TYPE_INT64, G_TYPE_INT64, value_transform_int64_int64);
g_value_register_transform_func (G_TYPE_INT64, G_TYPE_UINT64, value_transform_int64_uint64);
g_value_register_transform_func (G_TYPE_INT64, G_TYPE_ENUM, value_transform_int64_long);
g_value_register_transform_func (G_TYPE_INT64, G_TYPE_FLAGS, value_transform_int64_ulong);
g_value_register_transform_func (G_TYPE_INT64, G_TYPE_FLOAT, value_transform_int64_float);
g_value_register_transform_func (G_TYPE_INT64, G_TYPE_DOUBLE, value_transform_int64_double);
g_value_register_transform_func (G_TYPE_INT64, G_TYPE_STRING, value_transform_int64_string);
g_value_register_transform_func (G_TYPE_UINT64, G_TYPE_CHAR, value_transform_uint64_s8);
g_value_register_transform_func (G_TYPE_UINT64, G_TYPE_UCHAR, value_transform_uint64_u8);
g_value_register_transform_func (G_TYPE_UINT64, G_TYPE_BOOLEAN, value_transform_uint64_bool);
g_value_register_transform_func (G_TYPE_UINT64, G_TYPE_INT, value_transform_uint64_int);
g_value_register_transform_func (G_TYPE_UINT64, G_TYPE_UINT, value_transform_uint64_uint);
g_value_register_transform_func (G_TYPE_UINT64, G_TYPE_LONG, value_transform_uint64_long);
g_value_register_transform_func (G_TYPE_UINT64, G_TYPE_ULONG, value_transform_uint64_ulong);
g_value_register_transform_func (G_TYPE_UINT64, G_TYPE_INT64, value_transform_uint64_int64);
g_value_register_transform_func (G_TYPE_UINT64, G_TYPE_UINT64, value_transform_uint64_uint64);
g_value_register_transform_func (G_TYPE_UINT64, G_TYPE_ENUM, value_transform_uint64_long);
g_value_register_transform_func (G_TYPE_UINT64, G_TYPE_FLAGS, value_transform_uint64_ulong);
g_value_register_transform_func (G_TYPE_UINT64, G_TYPE_FLOAT, value_transform_uint64_float);
g_value_register_transform_func (G_TYPE_UINT64, G_TYPE_DOUBLE, value_transform_uint64_double);
g_value_register_transform_func (G_TYPE_UINT64, G_TYPE_STRING, value_transform_uint64_string);
g_value_register_transform_func (G_TYPE_ENUM, G_TYPE_CHAR, value_transform_long_s8);
g_value_register_transform_func (G_TYPE_ENUM, G_TYPE_UCHAR, value_transform_long_u8);
SKIP____register_transform_func (G_TYPE_ENUM, G_TYPE_BOOLEAN, value_transform_long_bool);
g_value_register_transform_func (G_TYPE_ENUM, G_TYPE_INT, value_transform_long_int);
g_value_register_transform_func (G_TYPE_ENUM, G_TYPE_UINT, value_transform_long_uint);
g_value_register_transform_func (G_TYPE_ENUM, G_TYPE_LONG, value_transform_long_long);
g_value_register_transform_func (G_TYPE_ENUM, G_TYPE_ULONG, value_transform_long_ulong);
g_value_register_transform_func (G_TYPE_ENUM, G_TYPE_INT64, value_transform_long_int64);
g_value_register_transform_func (G_TYPE_ENUM, G_TYPE_UINT64, value_transform_long_uint64);
g_value_register_transform_func (G_TYPE_ENUM, G_TYPE_ENUM, value_transform_long_long);
g_value_register_transform_func (G_TYPE_ENUM, G_TYPE_FLAGS, value_transform_long_ulong);
SKIP____register_transform_func (G_TYPE_ENUM, G_TYPE_FLOAT, value_transform_long_float);
SKIP____register_transform_func (G_TYPE_ENUM, G_TYPE_DOUBLE, value_transform_long_double);
g_value_register_transform_func (G_TYPE_ENUM, G_TYPE_STRING, value_transform_enum_string);
g_value_register_transform_func (G_TYPE_FLAGS, G_TYPE_CHAR, value_transform_ulong_s8);
g_value_register_transform_func (G_TYPE_FLAGS, G_TYPE_UCHAR, value_transform_ulong_u8);
SKIP____register_transform_func (G_TYPE_FLAGS, G_TYPE_BOOLEAN, value_transform_ulong_bool);
g_value_register_transform_func (G_TYPE_FLAGS, G_TYPE_INT, value_transform_ulong_int);
g_value_register_transform_func (G_TYPE_FLAGS, G_TYPE_UINT, value_transform_ulong_uint);
g_value_register_transform_func (G_TYPE_FLAGS, G_TYPE_LONG, value_transform_ulong_long);
g_value_register_transform_func (G_TYPE_FLAGS, G_TYPE_ULONG, value_transform_ulong_ulong);
g_value_register_transform_func (G_TYPE_FLAGS, G_TYPE_INT64, value_transform_ulong_int64);
g_value_register_transform_func (G_TYPE_FLAGS, G_TYPE_UINT64, value_transform_ulong_uint64);
SKIP____register_transform_func (G_TYPE_FLAGS, G_TYPE_ENUM, value_transform_ulong_long);
g_value_register_transform_func (G_TYPE_FLAGS, G_TYPE_FLAGS, value_transform_ulong_ulong);
SKIP____register_transform_func (G_TYPE_FLAGS, G_TYPE_FLOAT, value_transform_ulong_float);
SKIP____register_transform_func (G_TYPE_FLAGS, G_TYPE_DOUBLE, value_transform_ulong_double);
g_value_register_transform_func (G_TYPE_FLAGS, G_TYPE_STRING, value_transform_flags_string);
g_value_register_transform_func (G_TYPE_FLOAT, G_TYPE_CHAR, value_transform_float_s8);
g_value_register_transform_func (G_TYPE_FLOAT, G_TYPE_UCHAR, value_transform_float_u8);
SKIP____register_transform_func (G_TYPE_FLOAT, G_TYPE_BOOLEAN, value_transform_float_bool);
g_value_register_transform_func (G_TYPE_FLOAT, G_TYPE_INT, value_transform_float_int);
g_value_register_transform_func (G_TYPE_FLOAT, G_TYPE_UINT, value_transform_float_uint);
g_value_register_transform_func (G_TYPE_FLOAT, G_TYPE_LONG, value_transform_float_long);
g_value_register_transform_func (G_TYPE_FLOAT, G_TYPE_ULONG, value_transform_float_ulong);
g_value_register_transform_func (G_TYPE_FLOAT, G_TYPE_INT64, value_transform_float_int64);
g_value_register_transform_func (G_TYPE_FLOAT, G_TYPE_UINT64, value_transform_float_uint64);
SKIP____register_transform_func (G_TYPE_FLOAT, G_TYPE_ENUM, value_transform_float_long);
SKIP____register_transform_func (G_TYPE_FLOAT, G_TYPE_FLAGS, value_transform_float_ulong);
g_value_register_transform_func (G_TYPE_FLOAT, G_TYPE_FLOAT, value_transform_float_float);
g_value_register_transform_func (G_TYPE_FLOAT, G_TYPE_DOUBLE, value_transform_float_double);
g_value_register_transform_func (G_TYPE_FLOAT, G_TYPE_STRING, value_transform_float_string);
g_value_register_transform_func (G_TYPE_DOUBLE, G_TYPE_CHAR, value_transform_double_s8);
g_value_register_transform_func (G_TYPE_DOUBLE, G_TYPE_UCHAR, value_transform_double_u8);
SKIP____register_transform_func (G_TYPE_DOUBLE, G_TYPE_BOOLEAN, value_transform_double_bool);
g_value_register_transform_func (G_TYPE_DOUBLE, G_TYPE_INT, value_transform_double_int);
g_value_register_transform_func (G_TYPE_DOUBLE, G_TYPE_UINT, value_transform_double_uint);
g_value_register_transform_func (G_TYPE_DOUBLE, G_TYPE_LONG, value_transform_double_long);
g_value_register_transform_func (G_TYPE_DOUBLE, G_TYPE_ULONG, value_transform_double_ulong);
g_value_register_transform_func (G_TYPE_DOUBLE, G_TYPE_INT64, value_transform_double_int64);
g_value_register_transform_func (G_TYPE_DOUBLE, G_TYPE_UINT64, value_transform_double_uint64);
SKIP____register_transform_func (G_TYPE_DOUBLE, G_TYPE_ENUM, value_transform_double_long);
SKIP____register_transform_func (G_TYPE_DOUBLE, G_TYPE_FLAGS, value_transform_double_ulong);
g_value_register_transform_func (G_TYPE_DOUBLE, G_TYPE_FLOAT, value_transform_double_float);
g_value_register_transform_func (G_TYPE_DOUBLE, G_TYPE_DOUBLE, value_transform_double_double);
g_value_register_transform_func (G_TYPE_DOUBLE, G_TYPE_STRING, value_transform_double_string);
/* string types */
g_value_register_transform_func (G_TYPE_STRING, G_TYPE_STRING, value_transform_string_string);
}

1471
gobject/gvaluetypes.c Normal file

File diff suppressed because it is too large Load diff

316
gobject/gvaluetypes.h Normal file
View file

@ -0,0 +1,316 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 1997-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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* gvaluetypes.h: GLib default values
*/
#ifndef __G_VALUETYPES_H__
#define __G_VALUETYPES_H__
#if !defined (__GLIB_GOBJECT_H_INSIDE__) && !defined (GOBJECT_COMPILATION)
#error "Only <glib-object.h> can be included directly."
#endif
#include <gobject/gvalue.h>
G_BEGIN_DECLS
/* --- type macros --- */
/**
* G_VALUE_HOLDS_CHAR:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values of type %G_TYPE_CHAR.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_CHAR(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_CHAR))
/**
* G_VALUE_HOLDS_UCHAR:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values of type %G_TYPE_UCHAR.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_UCHAR(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_UCHAR))
/**
* G_VALUE_HOLDS_BOOLEAN:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values of type %G_TYPE_BOOLEAN.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_BOOLEAN(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_BOOLEAN))
/**
* G_VALUE_HOLDS_INT:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values of type %G_TYPE_INT.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_INT(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_INT))
/**
* G_VALUE_HOLDS_UINT:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values of type %G_TYPE_UINT.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_UINT(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_UINT))
/**
* G_VALUE_HOLDS_LONG:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values of type %G_TYPE_LONG.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_LONG(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_LONG))
/**
* G_VALUE_HOLDS_ULONG:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values of type %G_TYPE_ULONG.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_ULONG(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_ULONG))
/**
* G_VALUE_HOLDS_INT64:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values of type %G_TYPE_INT64.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_INT64(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_INT64))
/**
* G_VALUE_HOLDS_UINT64:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values of type %G_TYPE_UINT64.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_UINT64(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_UINT64))
/**
* G_VALUE_HOLDS_FLOAT:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values of type %G_TYPE_FLOAT.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_FLOAT(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_FLOAT))
/**
* G_VALUE_HOLDS_DOUBLE:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values of type %G_TYPE_DOUBLE.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_DOUBLE(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_DOUBLE))
/**
* G_VALUE_HOLDS_STRING:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values of type %G_TYPE_STRING.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_STRING(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_STRING))
/**
* G_VALUE_IS_INTERNED_STRING:
* @value: a valid #GValue structure
*
* Checks whether @value contains a string which is canonical.
*
* Returns: %TRUE if the value contains a string in its canonical
* representation, as returned by g_intern_string(). See also
* g_value_set_interned_string().
*
* Since: 2.66
*/
#define G_VALUE_IS_INTERNED_STRING(value) (G_VALUE_HOLDS_STRING (value) && ((value)->data[1].v_uint & G_VALUE_INTERNED_STRING)) GLIB_AVAILABLE_MACRO_IN_2_66
/**
* G_VALUE_HOLDS_POINTER:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values of type %G_TYPE_POINTER.
*
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_POINTER(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_POINTER))
/**
* G_TYPE_GTYPE:
*
* The type for #GType.
*/
#define G_TYPE_GTYPE (g_gtype_get_type())
/**
* G_VALUE_HOLDS_GTYPE:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values of type %G_TYPE_GTYPE.
*
* Since: 2.12
* Returns: %TRUE on success.
*/
#define G_VALUE_HOLDS_GTYPE(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_GTYPE))
/**
* G_VALUE_HOLDS_VARIANT:
* @value: a valid #GValue structure
*
* Checks whether the given #GValue can hold values of type %G_TYPE_VARIANT.
*
* Returns: %TRUE on success.
*
* Since: 2.26
*/
#define G_VALUE_HOLDS_VARIANT(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_VARIANT))
/* --- prototypes --- */
GLIB_DEPRECATED_IN_2_32_FOR(g_value_set_schar)
void g_value_set_char (GValue *value,
gchar v_char);
GLIB_DEPRECATED_IN_2_32_FOR(g_value_get_schar)
gchar g_value_get_char (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_schar (GValue *value,
gint8 v_char);
GLIB_AVAILABLE_IN_ALL
gint8 g_value_get_schar (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_uchar (GValue *value,
guchar v_uchar);
GLIB_AVAILABLE_IN_ALL
guchar g_value_get_uchar (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_boolean (GValue *value,
gboolean v_boolean);
GLIB_AVAILABLE_IN_ALL
gboolean g_value_get_boolean (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_int (GValue *value,
gint v_int);
GLIB_AVAILABLE_IN_ALL
gint g_value_get_int (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_uint (GValue *value,
guint v_uint);
GLIB_AVAILABLE_IN_ALL
guint g_value_get_uint (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_long (GValue *value,
glong v_long);
GLIB_AVAILABLE_IN_ALL
glong g_value_get_long (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_ulong (GValue *value,
gulong v_ulong);
GLIB_AVAILABLE_IN_ALL
gulong g_value_get_ulong (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_int64 (GValue *value,
gint64 v_int64);
GLIB_AVAILABLE_IN_ALL
gint64 g_value_get_int64 (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_uint64 (GValue *value,
guint64 v_uint64);
GLIB_AVAILABLE_IN_ALL
guint64 g_value_get_uint64 (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_float (GValue *value,
gfloat v_float);
GLIB_AVAILABLE_IN_ALL
gfloat g_value_get_float (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_double (GValue *value,
gdouble v_double);
GLIB_AVAILABLE_IN_ALL
gdouble g_value_get_double (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_string (GValue *value,
const gchar *v_string);
GLIB_AVAILABLE_IN_ALL
void g_value_set_static_string (GValue *value,
const gchar *v_string);
GLIB_AVAILABLE_IN_2_66
void g_value_set_interned_string (GValue *value,
const gchar *v_string);
GLIB_AVAILABLE_IN_ALL
const gchar * g_value_get_string (const GValue *value);
GLIB_AVAILABLE_IN_ALL
gchar* g_value_dup_string (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_pointer (GValue *value,
gpointer v_pointer);
GLIB_AVAILABLE_IN_ALL
gpointer g_value_get_pointer (const GValue *value);
GLIB_AVAILABLE_IN_ALL
GType g_gtype_get_type (void);
GLIB_AVAILABLE_IN_ALL
void g_value_set_gtype (GValue *value,
GType v_gtype);
GLIB_AVAILABLE_IN_ALL
GType g_value_get_gtype (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_set_variant (GValue *value,
GVariant *variant);
GLIB_AVAILABLE_IN_ALL
void g_value_take_variant (GValue *value,
GVariant *variant);
GLIB_AVAILABLE_IN_ALL
GVariant* g_value_get_variant (const GValue *value);
GLIB_AVAILABLE_IN_ALL
GVariant* g_value_dup_variant (const GValue *value);
/* Convenience for registering new pointer types */
GLIB_AVAILABLE_IN_ALL
GType g_pointer_type_register_static (const gchar *name);
/* debugging aid, describe value contents as string */
GLIB_AVAILABLE_IN_ALL
gchar* g_strdup_value_contents (const GValue *value);
GLIB_AVAILABLE_IN_ALL
void g_value_take_string (GValue *value,
gchar *v_string);
GLIB_DEPRECATED_FOR(g_value_take_string)
void g_value_set_string_take_ownership (GValue *value,
gchar *v_string);
/* humpf, need a C representable type name for G_TYPE_STRING */
/**
* gchararray:
*
* A C representable type name for %G_TYPE_STRING.
*/
typedef gchar* gchararray;
G_END_DECLS
#endif /* __G_VALUETYPES_H__ */

View file

@ -0,0 +1,10 @@
import sys
import gdb
# Update module path.
dir_ = '@datadir@/glib-2.0/gdb'
if not dir_ in sys.path:
sys.path.insert(0, dir_)
from gobject_gdb import register
register (gdb.current_objfile ())

190
gobject/meson.build Normal file
View file

@ -0,0 +1,190 @@
gobject_install_headers = files(
'gobject-autocleanups.h',
'glib-types.h',
'gbinding.h',
'gbindinggroup.h',
'gboxed.h',
'gclosure.h',
'genums.h',
'gmarshal.h',
'gobject.h',
'gparam.h',
'gparamspecs.h',
'gsignal.h',
'gsignalgroup.h',
'gsourceclosure.h',
'gtype.h',
'gtypemodule.h',
'gtypeplugin.h',
'gvalue.h',
'gvaluearray.h',
'gvaluecollector.h',
'gvaluetypes.h',
'gobjectnotifyqueue.c', # sic
)
install_headers(gobject_install_headers, subdir : 'glib-2.0/gobject')
gobject_sources = files(
'gatomicarray.c',
'gbinding.c',
'gbindinggroup.c',
'gboxed.c',
'gclosure.c',
'genums.c',
'gmarshal.c',
'gobject.c',
'gparam.c',
'gparamspecs.c',
'gsignal.c',
'gsignalgroup.c',
'gsourceclosure.c',
'gtype.c',
'gtypemodule.c',
'gtypeplugin.c',
'gvalue.c',
'gvaluearray.c',
'gvaluetransform.c',
'gvaluetypes.c',
)
if host_system == 'windows' and get_option('default_library') == 'shared'
gobject_win_rc = configure_file(
input: 'gobject.rc.in',
output: 'gobject.rc',
configuration: glibconfig_conf,
)
gobject_win_res = windows.compile_resources(gobject_win_rc)
gobject_sources += [gobject_win_res]
endif
if enable_dtrace
gobject_dtrace_obj = dtrace_obj_gen.process('gobject_probes.d')
gobject_dtrace_hdr = dtrace_hdr_gen.process('gobject_probes.d')
else
gobject_dtrace_obj = []
gobject_dtrace_hdr = []
endif
python_tools = [
'glib-genmarshal',
'glib-mkenums',
]
python_tools_conf = configuration_data()
python_tools_conf.set('VERSION', glib_version)
python_tools_conf.set('PYTHON', python_name)
foreach tool: python_tools
tool_bin = configure_file(
input : tool + '.in',
output : tool,
configuration : python_tools_conf,
install_dir : glib_bindir,
)
# Set variables for later use
set_variable(tool.underscorify(), tool_bin)
# Provide tools for others when we're a subproject and they use the Meson GNOME module
meson.override_find_program(tool, tool_bin)
endforeach
# Generate a header file containing the GObject enum types for the enums defined
# in libglib.
#
# For now, we only include gunicode.h here, since GScriptType is needed for
# Pango. More headers can be added as needed in future.
#
# We can't use gnome.mkenums() because the GNOME module looks for glib-mkenums
# in PATH, which means you can't bootstrap glib with its own glib-mkenums.
glib_enumtypes_input_headers = files(
'../glib/gunicode.h',
)
glib_enumtypes_h = custom_target('glib_enumtypes_h',
output : 'glib-enumtypes.h',
capture : true,
input : glib_enumtypes_input_headers,
install : true,
install_dir : join_paths(get_option('includedir'), 'glib-2.0/gobject'),
command : [python, glib_mkenums,
'--template', files('glib-enumtypes.h.template'),
'@INPUT@'])
glib_enumtypes_c = custom_target('glib_enumtypes_c',
output : 'glib-enumtypes.c',
capture : true,
input : glib_enumtypes_input_headers,
depends : [glib_enumtypes_h],
command : [python, glib_mkenums,
'--template', files('glib-enumtypes.c.template'),
'@INPUT@'])
glib_enumtypes_dep = declare_dependency(sources : [glib_enumtypes_h])
# Expose as variable to be used by gobject-introspection
# when it includes GLib as a subproject
glib_types_h = files('glib-types.h')
libgobject = library('gobject-2.0',
gobject_dtrace_obj, gobject_dtrace_hdr, glib_enumtypes_h, glib_enumtypes_c,
sources : gobject_sources,
version : library_version,
soversion : soversion,
darwin_versions : darwin_versions,
install : true,
include_directories : [configinc],
dependencies : [libffi_dep, libglib_dep],
c_args : ['-DG_LOG_DOMAIN="GLib-GObject"', '-DGOBJECT_COMPILATION'] + glib_hidden_visibility_args,
link_args : glib_link_flags,
)
pkg.generate(libgobject,
requires : ['glib-2.0'],
version : glib_version,
install_dir : glib_pkgconfigreldir,
filebase : 'gobject-2.0',
name : 'GObject',
description : 'GLib Type, Object, Parameter and Signal Library',
)
libgobject_dep = declare_dependency(link_with : libgobject,
include_directories : [gobjectinc],
dependencies : [libglib_dep, glib_enumtypes_dep])
if meson.version().version_compare('>=0.54.0')
meson.override_dependency('gobject-2.0', libgobject_dep)
endif
executable('gobject-query', 'gobject-query.c',
install : true,
dependencies : [libglib_dep, libgobject_dep])
install_data('gobject_gdb.py', install_dir : join_paths(glib_pkgdatadir, 'gdb'))
gdb_conf = configuration_data()
gdb_conf.set('datadir', glib_datadir)
configure_file(
input: 'libgobject-gdb.py.in',
output: 'libgobject-2.0.so.@0@-gdb.py'.format(library_version),
configuration: gdb_conf,
install_dir: gdb_install_dir,
install: gdb_install,
)
# This is needed to make gdb find gobject_gdb.py
if meson.version().version_compare('>=0.58')
env = environment()
env.prepend('PYTHONPATH', meson.current_source_dir())
meson.add_devenv(env)
endif
if enable_systemtap
gobject_stp = configure_file(input : 'gobject.stp.in',
output : '@0@.stp'.format(libgobject.full_path().split('/').get(-1)),
configuration : stp_cdata,
install_dir : tapset_install_dir,
)
endif
if build_tests
subdir('tests')
endif

18
gobject/tests/.gitignore vendored Normal file
View file

@ -0,0 +1,18 @@
binding
boxed
closure
dynamictests
enums
ifaceproperties
object
param
properties
qdata
reference
signal-handler
signals
threadtests
type
value
private
marshalers.[ch]

237
gobject/tests/autoptr.c Normal file
View file

@ -0,0 +1,237 @@
/* GLib testing framework examples and tests
* Copyright (C) 2018 Canonical Ltd
* Authors: Marco Trevisan <marco@ubuntu.com>
*
* This work is provided "as is"; redistribution and modification
* in whole or in part, in any medium, physical or electronic is
* permitted without restriction.
*
* This work 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.
*
* In no event shall the authors or contributors be liable for any
* direct, indirect, incidental, special, exemplary, or consequential
* damages (including, but not limited to, procurement of substitute
* goods or services; loss of use, data, or profits; or business
* interruption) however caused and on any theory of liability, whether
* in contract, strict liability, or tort (including negligence or
* otherwise) arising in any way out of the use of this software, even
* if advised of the possibility of such damage.
*/
#include <glib-object.h>
#include <string.h>
G_DECLARE_DERIVABLE_TYPE (TestAutoCleanupBase, test_base_auto_cleanup, TEST, BASE_AUTO_CLEANUP, GObject)
struct _TestAutoCleanupBaseClass {
GObjectClass parent_class;
};
G_DEFINE_TYPE (TestAutoCleanupBase, test_base_auto_cleanup, G_TYPE_OBJECT)
static void
test_base_auto_cleanup_class_init (TestAutoCleanupBaseClass *class)
{
}
static void
test_base_auto_cleanup_init (TestAutoCleanupBase *tac)
{
}
G_DECLARE_FINAL_TYPE (TestAutoCleanup, test_auto_cleanup, TEST, AUTO_CLEANUP, TestAutoCleanupBase)
struct _TestAutoCleanup
{
TestAutoCleanupBase parent_instance;
};
G_DEFINE_TYPE (TestAutoCleanup, test_auto_cleanup, G_TYPE_OBJECT)
static void
test_auto_cleanup_class_init (TestAutoCleanupClass *class)
{
}
static void
test_auto_cleanup_init (TestAutoCleanup *tac)
{
}
static TestAutoCleanup *
test_auto_cleanup_new (void)
{
return g_object_new (test_auto_cleanup_get_type (), NULL);
}
/* Verify that an object declared with G_DECLARE_FINAL_TYPE provides by default
* autocleanup functions, defined using the ones of the base type (defined with
* G_DECLARE_DERIVABLE_TYPE) and so that it can be used with g_autoptr */
static void
test_autoptr (void)
{
TestAutoCleanup *tac_ptr = test_auto_cleanup_new ();
g_object_add_weak_pointer (G_OBJECT (tac_ptr), (gpointer *) &tac_ptr);
{
g_autoptr (TestAutoCleanup) tac = tac_ptr;
g_assert_nonnull (tac);
}
#ifdef __GNUC__
g_assert_null (tac_ptr);
#endif
}
/* Verify that an object declared with G_DECLARE_FINAL_TYPE provides by default
* autocleanup functions, defined using the ones of the base type (defined with
* G_DECLARE_DERIVABLE_TYPE) and that stealing an autopointer works properly */
static void
test_autoptr_steal (void)
{
g_autoptr (TestAutoCleanup) tac1 = test_auto_cleanup_new ();
TestAutoCleanup *tac_ptr = tac1;
g_object_add_weak_pointer (G_OBJECT (tac_ptr), (gpointer *) &tac_ptr);
{
g_autoptr (TestAutoCleanup) tac2 = g_steal_pointer (&tac1);
g_assert_nonnull (tac_ptr);
g_assert_null (tac1);
g_assert_true (tac2 == tac_ptr);
}
#ifdef __GNUC__
g_assert_null (tac_ptr);
#endif
}
/* Verify that an object declared with G_DECLARE_FINAL_TYPE provides by default
* autolist cleanup functions defined using the ones of the parent type
* and so that can be used with g_autolist, and that freeing the list correctly
* unrefs the object too */
static void
test_autolist (void)
{
TestAutoCleanup *tac1 = test_auto_cleanup_new ();
TestAutoCleanup *tac2 = test_auto_cleanup_new ();
g_autoptr (TestAutoCleanup) tac3 = test_auto_cleanup_new ();
g_object_add_weak_pointer (G_OBJECT (tac1), (gpointer *) &tac1);
g_object_add_weak_pointer (G_OBJECT (tac2), (gpointer *) &tac2);
g_object_add_weak_pointer (G_OBJECT (tac3), (gpointer *) &tac3);
{
g_autolist (TestAutoCleanup) l = NULL;
l = g_list_prepend (l, tac1);
l = g_list_prepend (l, tac2);
/* Squash warnings about dead stores */
(void) l;
}
/* Only assert if autoptr works */
#ifdef __GNUC__
g_assert_null (tac1);
g_assert_null (tac2);
#endif
g_assert_nonnull (tac3);
g_clear_object (&tac3);
g_assert_null (tac3);
}
/* Verify that an object declared with G_DECLARE_FINAL_TYPE provides by default
* autoslist cleanup functions (defined using the ones of the base type declared
* with G_DECLARE_DERIVABLE_TYPE) and so that can be used with g_autoslist, and
* that freeing the slist correctly unrefs the object too */
static void
test_autoslist (void)
{
TestAutoCleanup *tac1 = test_auto_cleanup_new ();
TestAutoCleanup *tac2 = test_auto_cleanup_new ();
g_autoptr (TestAutoCleanup) tac3 = test_auto_cleanup_new ();
g_object_add_weak_pointer (G_OBJECT (tac1), (gpointer *) &tac1);
g_object_add_weak_pointer (G_OBJECT (tac2), (gpointer *) &tac2);
g_object_add_weak_pointer (G_OBJECT (tac3), (gpointer *) &tac3);
{
g_autoslist (TestAutoCleanup) l = NULL;
l = g_slist_prepend (l, tac1);
l = g_slist_prepend (l, tac2);
}
/* Only assert if autoptr works */
#ifdef __GNUC__
g_assert_null (tac1);
g_assert_null (tac2);
#endif
g_assert_nonnull (tac3);
g_clear_object (&tac3);
g_assert_null (tac3);
}
/* Verify that an object declared with G_DECLARE_FINAL_TYPE provides by default
* autoqueue cleanup functions (defined using the ones of the base type declared
* with G_DECLARE_DERIVABLE_TYPE) and so that can be used with g_autoqueue, and
* that freeing the queue correctly unrefs the object too */
static void
test_autoqueue (void)
{
TestAutoCleanup *tac1 = test_auto_cleanup_new ();
TestAutoCleanup *tac2 = test_auto_cleanup_new ();
g_autoptr (TestAutoCleanup) tac3 = test_auto_cleanup_new ();
g_object_add_weak_pointer (G_OBJECT (tac1), (gpointer *) &tac1);
g_object_add_weak_pointer (G_OBJECT (tac2), (gpointer *) &tac2);
g_object_add_weak_pointer (G_OBJECT (tac3), (gpointer *) &tac3);
{
g_autoqueue (TestAutoCleanup) q = g_queue_new ();
g_queue_push_head (q, tac1);
g_queue_push_tail (q, tac2);
}
/* Only assert if autoptr works */
#ifdef __GNUC__
g_assert_null (tac1);
g_assert_null (tac2);
#endif
g_assert_nonnull (tac3);
g_clear_object (&tac3);
g_assert_null (tac3);
}
static void
test_autoclass (void)
{
g_autoptr (TestAutoCleanupBaseClass) base_class_ptr = NULL;
g_autoptr (TestAutoCleanupClass) class_ptr = NULL;
base_class_ptr = g_type_class_ref (test_base_auto_cleanup_get_type ());
class_ptr = g_type_class_ref (test_auto_cleanup_get_type ());
g_assert_nonnull (base_class_ptr);
g_assert_nonnull (class_ptr);
}
int
main (int argc, gchar *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/autoptr/autoptr", test_autoptr);
g_test_add_func ("/autoptr/autoptr_steal", test_autoptr_steal);
g_test_add_func ("/autoptr/autolist", test_autolist);
g_test_add_func ("/autoptr/autoslist", test_autoslist);
g_test_add_func ("/autoptr/autoqueue", test_autoqueue);
g_test_add_func ("/autoptr/autoclass", test_autoclass);
return g_test_run ();
}

1102
gobject/tests/binding.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,694 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
*
* Copyright (C) 2015-2022 Christian Hergert <christian@hergert.me>
* Copyright (C) 2015 Garrett Regier <garrettregier@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include <glib-object.h>
/* Copied from glib */
typedef struct _BindingSource
{
GObject parent_instance;
gint foo;
gint bar;
gdouble value;
gboolean toggle;
} BindingSource;
typedef struct _BindingSourceClass
{
GObjectClass parent_class;
} BindingSourceClass;
enum
{
PROP_SOURCE_FOO = 1,
PROP_SOURCE_BAR,
PROP_SOURCE_VALUE,
PROP_SOURCE_TOGGLE
};
static GType binding_source_get_type (void);
G_DEFINE_TYPE (BindingSource, binding_source, G_TYPE_OBJECT);
static void
binding_source_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BindingSource *source = (BindingSource *) gobject;
switch (prop_id)
{
case PROP_SOURCE_FOO:
source->foo = g_value_get_int (value);
break;
case PROP_SOURCE_BAR:
source->bar = g_value_get_int (value);
break;
case PROP_SOURCE_VALUE:
source->value = g_value_get_double (value);
break;
case PROP_SOURCE_TOGGLE:
source->toggle = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
binding_source_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BindingSource *source = (BindingSource *) gobject;
switch (prop_id)
{
case PROP_SOURCE_FOO:
g_value_set_int (value, source->foo);
break;
case PROP_SOURCE_BAR:
g_value_set_int (value, source->bar);
break;
case PROP_SOURCE_VALUE:
g_value_set_double (value, source->value);
break;
case PROP_SOURCE_TOGGLE:
g_value_set_boolean (value, source->toggle);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
binding_source_class_init (BindingSourceClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = binding_source_set_property;
gobject_class->get_property = binding_source_get_property;
g_object_class_install_property (gobject_class, PROP_SOURCE_FOO,
g_param_spec_int ("foo", "Foo", "Foo",
-1, 100,
0,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_SOURCE_BAR,
g_param_spec_int ("bar", "Bar", "Bar",
-1, 100,
0,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_SOURCE_VALUE,
g_param_spec_double ("value", "Value", "Value",
-100.0, 200.0,
0.0,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_SOURCE_TOGGLE,
g_param_spec_boolean ("toggle", "Toggle", "Toggle",
FALSE,
G_PARAM_READWRITE));
}
static void
binding_source_init (BindingSource *self)
{
}
typedef struct _BindingTarget
{
GObject parent_instance;
gint bar;
gdouble value;
gboolean toggle;
} BindingTarget;
typedef struct _BindingTargetClass
{
GObjectClass parent_class;
} BindingTargetClass;
enum
{
PROP_TARGET_BAR = 1,
PROP_TARGET_VALUE,
PROP_TARGET_TOGGLE
};
static GType binding_target_get_type (void);
G_DEFINE_TYPE (BindingTarget, binding_target, G_TYPE_OBJECT);
static void
binding_target_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BindingTarget *target = (BindingTarget *) gobject;
switch (prop_id)
{
case PROP_TARGET_BAR:
target->bar = g_value_get_int (value);
break;
case PROP_TARGET_VALUE:
target->value = g_value_get_double (value);
break;
case PROP_TARGET_TOGGLE:
target->toggle = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
binding_target_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BindingTarget *target = (BindingTarget *) gobject;
switch (prop_id)
{
case PROP_TARGET_BAR:
g_value_set_int (value, target->bar);
break;
case PROP_TARGET_VALUE:
g_value_set_double (value, target->value);
break;
case PROP_TARGET_TOGGLE:
g_value_set_boolean (value, target->toggle);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
binding_target_class_init (BindingTargetClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = binding_target_set_property;
gobject_class->get_property = binding_target_get_property;
g_object_class_install_property (gobject_class, PROP_TARGET_BAR,
g_param_spec_int ("bar", "Bar", "Bar",
-1, 100,
0,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_TARGET_VALUE,
g_param_spec_double ("value", "Value", "Value",
-100.0, 200.0,
0.0,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_TARGET_TOGGLE,
g_param_spec_boolean ("toggle", "Toggle", "Toggle",
FALSE,
G_PARAM_READWRITE));
}
static void
binding_target_init (BindingTarget *self)
{
}
static gboolean
celsius_to_fahrenheit (GBinding *binding,
const GValue *from_value,
GValue *to_value,
gpointer user_data G_GNUC_UNUSED)
{
gdouble celsius, fahrenheit;
g_assert_true (G_VALUE_HOLDS (from_value, G_TYPE_DOUBLE));
g_assert_true (G_VALUE_HOLDS (to_value, G_TYPE_DOUBLE));
celsius = g_value_get_double (from_value);
fahrenheit = (9 * celsius / 5) + 32.0;
if (g_test_verbose ())
g_printerr ("Converting %.2fC to %.2fF\n", celsius, fahrenheit);
g_value_set_double (to_value, fahrenheit);
return TRUE;
}
static gboolean
fahrenheit_to_celsius (GBinding *binding,
const GValue *from_value,
GValue *to_value,
gpointer user_data G_GNUC_UNUSED)
{
gdouble celsius, fahrenheit;
g_assert_true (G_VALUE_HOLDS (from_value, G_TYPE_DOUBLE));
g_assert_true (G_VALUE_HOLDS (to_value, G_TYPE_DOUBLE));
fahrenheit = g_value_get_double (from_value);
celsius = 5 * (fahrenheit - 32.0) / 9;
if (g_test_verbose ())
g_printerr ("Converting %.2fF to %.2fC\n", fahrenheit, celsius);
g_value_set_double (to_value, celsius);
return TRUE;
}
static void
test_binding_group_invalid (void)
{
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
/* Invalid Target Property */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*find_property*target_property*!=*NULL*");
g_binding_group_bind (group, "value",
target, "does-not-exist",
G_BINDING_DEFAULT);
g_test_assert_expected_messages ();
g_binding_group_set_source (group, NULL);
/* Invalid Source Property */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*find_property*source_property*!=*NULL*");
g_binding_group_set_source (group, source);
g_binding_group_bind (group, "does-not-exist",
target, "value",
G_BINDING_DEFAULT);
g_test_assert_expected_messages ();
g_binding_group_set_source (group, NULL);
/* Invalid Source */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*find_property*->source_property*!=*NULL*");
g_binding_group_bind (group, "does-not-exist",
target, "value",
G_BINDING_DEFAULT);
g_binding_group_set_source (group, source);
g_test_assert_expected_messages ();
g_object_unref (target);
g_object_unref (source);
g_object_unref (group);
}
static void
test_binding_group_default (void)
{
gsize i, j;
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *targets[5];
BindingSource *readback;
for (i = 0; i < G_N_ELEMENTS (targets); ++i)
{
targets[i] = g_object_new (binding_target_get_type (), NULL);
g_binding_group_bind (group, "foo",
targets[i], "bar",
G_BINDING_DEFAULT);
}
g_assert_null (g_binding_group_dup_source (group));
g_binding_group_set_source (group, source);
readback = g_binding_group_dup_source (group);
g_assert_true (readback == source);
g_object_unref (readback);
for (i = 0; i < 2; ++i)
{
g_object_set (source, "foo", 42, NULL);
for (j = 0; j < G_N_ELEMENTS (targets); ++j)
g_assert_cmpint (source->foo, ==, targets[j]->bar);
g_object_set (targets[0], "bar", 47, NULL);
g_assert_cmpint (source->foo, !=, targets[0]->bar);
/* Check that we transition the source correctly */
g_binding_group_set_source (group, NULL);
g_assert_null (g_binding_group_dup_source (group));
g_binding_group_set_source (group, source);
readback = g_binding_group_dup_source (group);
g_assert_true (readback == source);
g_object_unref (readback);
}
g_object_unref (group);
g_object_set (source, "foo", 0, NULL);
for (i = 0; i < G_N_ELEMENTS (targets); ++i)
g_assert_cmpint (source->foo, !=, targets[i]->bar);
g_object_unref (source);
for (i = 0; i < G_N_ELEMENTS (targets); ++i)
g_object_unref (targets[i]);
}
static void
test_binding_group_bidirectional (void)
{
gsize i, j;
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *targets[5];
BindingSource *readback;
for (i = 0; i < G_N_ELEMENTS (targets); ++i)
{
targets[i] = g_object_new (binding_target_get_type (), NULL);
g_binding_group_bind (group, "value",
targets[i], "value",
G_BINDING_BIDIRECTIONAL);
}
g_assert_null (g_binding_group_dup_source (group));
g_binding_group_set_source (group, source);
readback = g_binding_group_dup_source (group);
g_assert_true (readback == source);
g_object_unref (readback);
for (i = 0; i < 2; ++i)
{
g_object_set (source, "value", 42.0, NULL);
for (j = 0; j < G_N_ELEMENTS (targets); ++j)
g_assert_cmpfloat (source->value, ==, targets[j]->value);
g_object_set (targets[0], "value", 47.0, NULL);
g_assert_cmpfloat (source->value, ==, targets[0]->value);
/* Check that we transition the source correctly */
g_binding_group_set_source (group, NULL);
g_assert_null (g_binding_group_dup_source (group));
g_binding_group_set_source (group, source);
readback = g_binding_group_dup_source (group);
g_assert_true (readback == source);
g_object_unref (readback);
}
g_object_unref (group);
g_object_set (targets[0], "value", 0.0, NULL);
g_assert_cmpfloat (source->value, !=, targets[0]->value);
g_object_unref (source);
for (i = 0; i < G_N_ELEMENTS (targets); ++i)
g_object_unref (targets[i]);
}
static void
transform_destroy_notify (gpointer data)
{
gboolean *transform_destroy_called = data;
*transform_destroy_called = TRUE;
}
static void
test_binding_group_transform (void)
{
gboolean transform_destroy_called = FALSE;
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
g_binding_group_set_source (group, source);
g_binding_group_bind_full (group, "value",
target, "value",
G_BINDING_BIDIRECTIONAL,
celsius_to_fahrenheit,
fahrenheit_to_celsius,
&transform_destroy_called,
transform_destroy_notify);
g_object_set (source, "value", 24.0, NULL);
g_assert_cmpfloat (target->value, ==, ((9 * 24.0 / 5) + 32.0));
g_object_set (target, "value", 69.0, NULL);
g_assert_cmpfloat (source->value, ==, (5 * (69.0 - 32.0) / 9));
/* The GDestroyNotify should only be called when the
* set is freed, not when the various GBindings are freed
*/
g_binding_group_set_source (group, NULL);
g_assert_false (transform_destroy_called);
g_object_unref (group);
g_assert_true (transform_destroy_called);
g_object_unref (source);
g_object_unref (target);
}
static void
test_binding_group_transform_closures (void)
{
gboolean transform_destroy_called_1 = FALSE;
gboolean transform_destroy_called_2 = FALSE;
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
GClosure *c2f_closure, *f2c_closure;
c2f_closure = g_cclosure_new (G_CALLBACK (celsius_to_fahrenheit),
&transform_destroy_called_1,
(GClosureNotify) transform_destroy_notify);
f2c_closure = g_cclosure_new (G_CALLBACK (fahrenheit_to_celsius),
&transform_destroy_called_2,
(GClosureNotify) transform_destroy_notify);
g_binding_group_set_source (group, source);
g_binding_group_bind_with_closures (group, "value",
target, "value",
G_BINDING_BIDIRECTIONAL,
c2f_closure,
f2c_closure);
g_object_set (source, "value", 24.0, NULL);
g_assert_cmpfloat (target->value, ==, ((9 * 24.0 / 5) + 32.0));
g_object_set (target, "value", 69.0, NULL);
g_assert_cmpfloat (source->value, ==, (5 * (69.0 - 32.0) / 9));
/* The GClsoureNotify should only be called when the
* set is freed, not when the various GBindings are freed
*/
g_binding_group_set_source (group, NULL);
g_assert_false (transform_destroy_called_1);
g_assert_false (transform_destroy_called_2);
g_object_unref (group);
g_assert_true (transform_destroy_called_1);
g_assert_true (transform_destroy_called_2);
g_object_unref (source);
g_object_unref (target);
}
static void
test_binding_group_same_object (void)
{
gsize i;
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (),
"foo", 100,
"bar", 50,
NULL);
g_binding_group_set_source (group, source);
g_binding_group_bind (group, "foo",
source, "bar",
G_BINDING_BIDIRECTIONAL);
for (i = 0; i < 2; ++i)
{
g_object_set (source, "foo", 10, NULL);
g_assert_cmpint (source->foo, ==, 10);
g_assert_cmpint (source->bar, ==, 10);
g_object_set (source, "bar", 30, NULL);
g_assert_cmpint (source->foo, ==, 30);
g_assert_cmpint (source->bar, ==, 30);
/* Check that it is possible both when initially
* adding the binding and when changing the source
*/
g_binding_group_set_source (group, NULL);
g_binding_group_set_source (group, source);
}
g_object_unref (source);
g_object_unref (group);
}
static void
test_binding_group_weak_ref_source (void)
{
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
BindingSource *readback;
g_binding_group_set_source (group, source);
g_binding_group_bind (group, "value",
target, "value",
G_BINDING_BIDIRECTIONAL);
g_object_add_weak_pointer (G_OBJECT (source), (gpointer)&source);
readback = g_binding_group_dup_source (group);
g_assert_true (readback == source);
g_object_unref (readback);
g_object_unref (source);
g_assert_null (source);
g_assert_null (g_binding_group_dup_source (group));
/* Hopefully this would explode if the binding was still alive */
g_object_set (target, "value", 0.0, NULL);
g_object_unref (target);
g_object_unref (group);
}
static void
test_binding_group_weak_ref_target (void)
{
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
g_binding_group_set_source (group, source);
g_binding_group_bind (group, "value",
target, "value",
G_BINDING_BIDIRECTIONAL);
g_object_set (source, "value", 47.0, NULL);
g_assert_cmpfloat (target->value, ==, 47.0);
g_object_add_weak_pointer (G_OBJECT (target), (gpointer)&target);
g_object_unref (target);
g_assert_null (target);
/* Hopefully this would explode if the binding was still alive */
g_object_set (source, "value", 0.0, NULL);
g_object_unref (source);
g_object_unref (group);
}
static void
test_binding_group_properties (void)
{
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
BindingSource *other;
g_binding_group_set_source (group, source);
g_binding_group_bind (group, "value",
target, "value",
G_BINDING_BIDIRECTIONAL);
g_object_get (group, "source", &other, NULL);
g_assert_true (other == source);
g_object_unref (other);
g_object_set (group, "source", NULL, NULL);
g_object_get (group, "source", &other, NULL);
g_assert_null (other);
g_object_add_weak_pointer (G_OBJECT (target), (gpointer)&target);
g_object_unref (target);
g_assert_null (target);
g_object_unref (source);
g_object_unref (group);
}
static void
test_binding_group_weak_notify_no_bindings (void)
{
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
g_binding_group_set_source (group, source);
g_assert_finalize_object (source);
g_assert_finalize_object (group);
}
static void
test_binding_group_empty_closures (void)
{
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
g_binding_group_bind_full (group, "value", target, "value", 0,
NULL, NULL, NULL, NULL);
g_assert_finalize_object (group);
g_assert_finalize_object (target);
g_assert_finalize_object (source);
}
gint
main (gint argc,
gchar *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/GObject/BindingGroup/invalid", test_binding_group_invalid);
g_test_add_func ("/GObject/BindingGroup/default", test_binding_group_default);
g_test_add_func ("/GObject/BindingGroup/bidirectional", test_binding_group_bidirectional);
g_test_add_func ("/GObject/BindingGroup/transform", test_binding_group_transform);
g_test_add_func ("/GObject/BindingGroup/transform-closures", test_binding_group_transform_closures);
g_test_add_func ("/GObject/BindingGroup/same-object", test_binding_group_same_object);
g_test_add_func ("/GObject/BindingGroup/weak-ref-source", test_binding_group_weak_ref_source);
g_test_add_func ("/GObject/BindingGroup/weak-ref-target", test_binding_group_weak_ref_target);
g_test_add_func ("/GObject/BindingGroup/properties", test_binding_group_properties);
g_test_add_func ("/GObject/BindingGroup/weak-notify-no-bindings", test_binding_group_weak_notify_no_bindings);
g_test_add_func ("/GObject/BindingGroup/empty-closures", test_binding_group_empty_closures);
return g_test_run ();
}

704
gobject/tests/boxed.c Normal file
View file

@ -0,0 +1,704 @@
#ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#endif
#include <glib-object.h>
typedef struct _MyBoxed MyBoxed;
struct _MyBoxed
{
gint ivalue;
gchar *bla;
};
static gpointer
my_boxed_copy (gpointer orig)
{
MyBoxed *a = orig;
MyBoxed *b;
b = g_slice_new (MyBoxed);
b->ivalue = a->ivalue;
b->bla = g_strdup (a->bla);
return b;
}
static gint my_boxed_free_count;
static void
my_boxed_free (gpointer orig)
{
MyBoxed *a = orig;
g_free (a->bla);
g_slice_free (MyBoxed, a);
my_boxed_free_count++;
}
static GType my_boxed_get_type (void);
#define MY_TYPE_BOXED (my_boxed_get_type ())
G_DEFINE_BOXED_TYPE (MyBoxed, my_boxed, my_boxed_copy, my_boxed_free)
static void
test_define_boxed (void)
{
MyBoxed a;
MyBoxed *b;
a.ivalue = 20;
a.bla = g_strdup ("bla");
b = g_boxed_copy (MY_TYPE_BOXED, &a);
g_assert_cmpint (b->ivalue, ==, 20);
g_assert_cmpstr (b->bla, ==, "bla");
g_boxed_free (MY_TYPE_BOXED, b);
g_free (a.bla);
}
static void
test_boxed_ownership (void)
{
GValue value = G_VALUE_INIT;
static MyBoxed boxed = { 10, "bla" };
g_value_init (&value, MY_TYPE_BOXED);
my_boxed_free_count = 0;
g_value_set_static_boxed (&value, &boxed);
g_value_reset (&value);
g_assert_cmpint (my_boxed_free_count, ==, 0);
g_value_set_boxed_take_ownership (&value, g_boxed_copy (MY_TYPE_BOXED, &boxed));
g_value_reset (&value);
g_assert_cmpint (my_boxed_free_count, ==, 1);
g_value_take_boxed (&value, g_boxed_copy (MY_TYPE_BOXED, &boxed));
g_value_reset (&value);
g_assert_cmpint (my_boxed_free_count, ==, 2);
g_value_set_boxed (&value, &boxed);
g_value_reset (&value);
g_assert_cmpint (my_boxed_free_count, ==, 3);
}
static void
my_callback (gpointer user_data)
{
}
static gint destroy_count;
static void
my_closure_notify (gpointer user_data, GClosure *closure)
{
destroy_count++;
}
static void
test_boxed_closure (void)
{
GClosure *closure;
GClosure *closure2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_CLOSURE);
g_assert (G_VALUE_HOLDS_BOXED (&value));
closure = g_cclosure_new (G_CALLBACK (my_callback), "bla", my_closure_notify);
g_value_take_boxed (&value, closure);
closure2 = g_value_get_boxed (&value);
g_assert (closure2 == closure);
closure2 = g_value_dup_boxed (&value);
g_assert (closure2 == closure); /* closures use ref/unref for copy/free */
g_closure_unref (closure2);
g_value_unset (&value);
g_assert_cmpint (destroy_count, ==, 1);
}
static void
test_boxed_date (void)
{
GDate *date;
GDate *date2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_DATE);
g_assert (G_VALUE_HOLDS_BOXED (&value));
date = g_date_new_dmy (1, 3, 1970);
g_value_take_boxed (&value, date);
date2 = g_value_get_boxed (&value);
g_assert (date2 == date);
date2 = g_value_dup_boxed (&value);
g_assert (date2 != date);
g_assert (g_date_compare (date, date2) == 0);
g_date_free (date2);
g_value_unset (&value);
}
static void
test_boxed_value (void)
{
GValue value1 = G_VALUE_INIT;
GValue *value2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_VALUE);
g_assert (G_VALUE_HOLDS_BOXED (&value));
g_value_init (&value1, G_TYPE_INT);
g_value_set_int (&value1, 26);
g_value_set_static_boxed (&value, &value1);
value2 = g_value_get_boxed (&value);
g_assert (value2 == &value1);
value2 = g_value_dup_boxed (&value);
g_assert (value2 != &value1);
g_assert (G_VALUE_HOLDS_INT (value2));
g_assert_cmpint (g_value_get_int (value2), ==, 26);
g_boxed_free (G_TYPE_VALUE, value2);
g_value_unset (&value);
}
static void
test_boxed_string (void)
{
GString *v;
GString *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_GSTRING);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_string_new ("bla");
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v2 == v);
v2 = g_value_dup_boxed (&value);
g_assert (v2 != v);
g_assert (g_string_equal (v, v2));
g_string_free (v2, TRUE);
g_value_unset (&value);
}
static void
test_boxed_hashtable (void)
{
GHashTable *v;
GHashTable *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_HASH_TABLE);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_hash_table_new (g_str_hash, g_str_equal);
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v2 == v);
v2 = g_value_dup_boxed (&value);
g_assert (v2 == v); /* hash tables use ref/unref for copy/free */
g_hash_table_unref (v2);
g_value_unset (&value);
}
static void
test_boxed_array (void)
{
GArray *v;
GArray *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_ARRAY);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_array_new (TRUE, FALSE, 1);
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v2 == v);
v2 = g_value_dup_boxed (&value);
g_assert (v2 == v); /* arrays use ref/unref for copy/free */
g_array_unref (v2);
g_value_unset (&value);
}
static void
test_boxed_ptrarray (void)
{
GPtrArray *v;
GPtrArray *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_PTR_ARRAY);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_ptr_array_new ();
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v2 == v);
v2 = g_value_dup_boxed (&value);
g_assert (v2 == v); /* ptr arrays use ref/unref for copy/free */
g_ptr_array_unref (v2);
g_value_unset (&value);
}
static void
test_boxed_regex (void)
{
GRegex *v;
GRegex *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_REGEX);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_regex_new ("a+b+", 0, 0, NULL);
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v2 == v);
v2 = g_value_dup_boxed (&value);
g_assert (v2 == v); /* regexes use ref/unref for copy/free */
g_regex_unref (v2);
g_value_unset (&value);
}
static void
test_boxed_matchinfo (void)
{
GRegex *r;
GMatchInfo *info, *info2;
gboolean ret;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_MATCH_INFO);
g_assert (G_VALUE_HOLDS_BOXED (&value));
r = g_regex_new ("ab", 0, 0, NULL);
ret = g_regex_match (r, "blabla abab bla", 0, &info);
g_assert (ret);
g_value_take_boxed (&value, info);
info2 = g_value_get_boxed (&value);
g_assert (info == info2);
info2 = g_value_dup_boxed (&value);
g_assert (info == info2); /* matchinfo uses ref/unref for copy/free */
g_match_info_unref (info2);
g_value_unset (&value);
g_regex_unref (r);
}
static void
test_boxed_varianttype (void)
{
GVariantType *v;
GVariantType *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_VARIANT_TYPE);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_variant_type_new ("mas");
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v2 == v);
v2 = g_value_dup_boxed (&value);
g_assert (v2 != v);
g_assert_cmpstr (g_variant_type_peek_string (v), ==, g_variant_type_peek_string (v2));
g_variant_type_free (v2);
g_value_unset (&value);
}
static void
test_boxed_datetime (void)
{
GDateTime *v;
GDateTime *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_DATE_TIME);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_date_time_new_now_local ();
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v2 == v);
v2 = g_value_dup_boxed (&value);
g_assert (v2 == v); /* datetime uses ref/unref for copy/free */
g_date_time_unref (v2);
g_value_unset (&value);
}
static void
test_boxed_error (void)
{
GError *v;
GError *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_ERROR);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_error_new_literal (G_VARIANT_PARSE_ERROR,
G_VARIANT_PARSE_ERROR_NUMBER_TOO_BIG,
"Too damn big");
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v2 == v);
v2 = g_value_dup_boxed (&value);
g_assert (v2 != v);
g_assert_cmpint (v->domain, ==, v2->domain);
g_assert_cmpint (v->code, ==, v2->code);
g_assert_cmpstr (v->message, ==, v2->message);
g_error_free (v2);
g_value_unset (&value);
}
static void
test_boxed_keyfile (void)
{
GKeyFile *k, *k2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_KEY_FILE);
g_assert (G_VALUE_HOLDS_BOXED (&value));
k = g_key_file_new ();
g_value_take_boxed (&value, k);
k2 = g_value_get_boxed (&value);
g_assert (k == k2);
k2 = g_value_dup_boxed (&value);
g_assert (k == k2); /* keyfile uses ref/unref for copy/free */
g_key_file_unref (k2);
g_value_unset (&value);
}
static void
test_boxed_mainloop (void)
{
GMainLoop *l, *l2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_MAIN_LOOP);
g_assert (G_VALUE_HOLDS_BOXED (&value));
l = g_main_loop_new (NULL, FALSE);
g_value_take_boxed (&value, l);
l2 = g_value_get_boxed (&value);
g_assert (l == l2);
l2 = g_value_dup_boxed (&value);
g_assert (l == l2); /* mainloop uses ref/unref for copy/free */
g_main_loop_unref (l2);
g_value_unset (&value);
}
static void
test_boxed_maincontext (void)
{
GMainContext *c, *c2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_MAIN_CONTEXT);
g_assert (G_VALUE_HOLDS_BOXED (&value));
c = g_main_context_new ();
g_value_take_boxed (&value, c);
c2 = g_value_get_boxed (&value);
g_assert (c == c2);
c2 = g_value_dup_boxed (&value);
g_assert (c == c2); /* maincontext uses ref/unref for copy/free */
g_main_context_unref (c2);
g_value_unset (&value);
}
static void
test_boxed_source (void)
{
GSource *s, *s2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_SOURCE);
g_assert (G_VALUE_HOLDS_BOXED (&value));
s = g_idle_source_new ();
g_value_take_boxed (&value, s);
s2 = g_value_get_boxed (&value);
g_assert (s == s2);
s2 = g_value_dup_boxed (&value);
g_assert (s == s2); /* source uses ref/unref for copy/free */
g_source_unref (s2);
g_value_unset (&value);
}
static void
test_boxed_variantbuilder (void)
{
GVariantBuilder *v, *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_VARIANT_BUILDER);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_variant_builder_new (G_VARIANT_TYPE_OBJECT_PATH_ARRAY);
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v == v2);
v2 = g_value_dup_boxed (&value);
g_assert (v == v2); /* variantbuilder uses ref/unref for copy/free */
g_variant_builder_unref (v2);
g_value_unset (&value);
}
static void
test_boxed_timezone (void)
{
GTimeZone *z, *z2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_TIME_ZONE);
g_assert (G_VALUE_HOLDS_BOXED (&value));
z = g_time_zone_new_utc ();
g_value_take_boxed (&value, z);
z2 = g_value_get_boxed (&value);
g_assert (z == z2);
z2 = g_value_dup_boxed (&value);
g_assert (z == z2); /* timezone uses ref/unref for copy/free */
g_time_zone_unref (z2);
g_value_unset (&value);
}
static void
test_boxed_pollfd (void)
{
GPollFD *p, *p2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_POLLFD);
g_assert (G_VALUE_HOLDS_BOXED (&value));
p = g_new (GPollFD, 1);
g_value_take_boxed (&value, p);
p2 = g_value_get_boxed (&value);
g_assert (p == p2);
p2 = g_value_dup_boxed (&value);
g_assert (p != p2);
g_free (p2);
g_value_unset (&value);
}
static void
test_boxed_markup (void)
{
GMarkupParseContext *c, *c2;
const GMarkupParser parser = { 0 };
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_MARKUP_PARSE_CONTEXT);
g_assert (G_VALUE_HOLDS_BOXED (&value));
c = g_markup_parse_context_new (&parser, 0, NULL, NULL);
g_value_take_boxed (&value, c);
c2 = g_value_get_boxed (&value);
g_assert (c == c2);
c2 = g_value_dup_boxed (&value);
g_assert (c == c2);
g_markup_parse_context_unref (c2);
g_value_unset (&value);
}
static void
test_boxed_thread (void)
{
GThread *t, *t2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_THREAD);
g_assert (G_VALUE_HOLDS_BOXED (&value));
t = g_thread_self ();
g_value_set_boxed (&value, t);
t2 = g_value_get_boxed (&value);
g_assert (t == t2);
t2 = g_value_dup_boxed (&value);
g_assert (t == t2);
g_thread_unref (t2);
g_value_unset (&value);
}
static void
test_boxed_checksum (void)
{
GChecksum *c, *c2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_CHECKSUM);
g_assert (G_VALUE_HOLDS_BOXED (&value));
c = g_checksum_new (G_CHECKSUM_SHA512);
g_value_take_boxed (&value, c);
c2 = g_value_get_boxed (&value);
g_assert (c == c2);
c2 = g_value_dup_boxed (&value);
g_assert (c != c2);
g_checksum_free (c2);
g_value_unset (&value);
}
static gint
treecmp (gconstpointer a, gconstpointer b)
{
return (a < b) ? -1 : (a > b);
}
static void
test_boxed_tree (void)
{
GTree *t, *t2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_TREE);
g_assert_true (G_VALUE_HOLDS_BOXED (&value));
t = g_tree_new (treecmp);
g_value_take_boxed (&value, t);
t2 = g_value_get_boxed (&value);
g_assert_true (t == t2);
t2 = g_value_dup_boxed (&value);
g_assert_true (t == t2); /* trees use ref/unref for copy/free */
g_tree_unref (t2);
g_value_unset (&value);
}
static void
test_boxed_pattern_spec (void)
{
GPatternSpec *ps, *ps2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_PATTERN_SPEC);
g_assert_true (G_VALUE_HOLDS_BOXED (&value));
ps = g_pattern_spec_new ("*abc*?cde");
g_value_take_boxed (&value, ps);
ps2 = g_value_get_boxed (&value);
g_assert_true (ps == ps2);
ps2 = g_value_dup_boxed (&value);
g_assert_true (ps != ps2);
g_assert_true (g_pattern_spec_equal (ps, ps2));
g_pattern_spec_free (ps2);
g_value_unset (&value);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/boxed/define", test_define_boxed);
g_test_add_func ("/boxed/ownership", test_boxed_ownership);
g_test_add_func ("/boxed/closure", test_boxed_closure);
g_test_add_func ("/boxed/date", test_boxed_date);
g_test_add_func ("/boxed/value", test_boxed_value);
g_test_add_func ("/boxed/string", test_boxed_string);
g_test_add_func ("/boxed/hashtable", test_boxed_hashtable);
g_test_add_func ("/boxed/array", test_boxed_array);
g_test_add_func ("/boxed/ptrarray", test_boxed_ptrarray);
g_test_add_func ("/boxed/regex", test_boxed_regex);
g_test_add_func ("/boxed/varianttype", test_boxed_varianttype);
g_test_add_func ("/boxed/error", test_boxed_error);
g_test_add_func ("/boxed/datetime", test_boxed_datetime);
g_test_add_func ("/boxed/matchinfo", test_boxed_matchinfo);
g_test_add_func ("/boxed/keyfile", test_boxed_keyfile);
g_test_add_func ("/boxed/mainloop", test_boxed_mainloop);
g_test_add_func ("/boxed/maincontext", test_boxed_maincontext);
g_test_add_func ("/boxed/source", test_boxed_source);
g_test_add_func ("/boxed/variantbuilder", test_boxed_variantbuilder);
g_test_add_func ("/boxed/timezone", test_boxed_timezone);
g_test_add_func ("/boxed/pollfd", test_boxed_pollfd);
g_test_add_func ("/boxed/markup", test_boxed_markup);
g_test_add_func ("/boxed/thread", test_boxed_thread);
g_test_add_func ("/boxed/checksum", test_boxed_checksum);
g_test_add_func ("/boxed/tree", test_boxed_tree);
g_test_add_func ("/boxed/patternspec", test_boxed_pattern_spec);
return g_test_run ();
}

View file

@ -0,0 +1,337 @@
/* Copyright (C) 2005 Imendio AB
*
* This software is provided "as is"; redistribution and modification
* is permitted, provided that the following disclaimer is retained.
*
* This software 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.
* In no event shall the authors or contributors be liable for any
* direct, indirect, incidental, special, exemplary, or consequential
* damages (including, but not limited to, procurement of substitute
* goods or services; loss of use, data, or profits; or business
* interruption) however caused and on any theory of liability, whether
* in contract, strict liability, or tort (including negligence or
* otherwise) arising in any way out of the use of this software, even
* if advised of the possibility of such damage.
*/
#include <glib-object.h>
#ifdef G_OS_UNIX
#include <unistd.h>
#endif
#define TEST_POINTER1 ((gpointer) 47)
#define TEST_POINTER2 ((gpointer) 49)
#define TEST_INT1 (-77)
#define TEST_INT2 (78)
/* --- GTest class --- */
typedef struct {
GObject object;
gint value;
gpointer test_pointer1;
gpointer test_pointer2;
} GTest;
typedef struct {
GObjectClass parent_class;
void (*test_signal1) (GTest * test, gint an_int);
void (*test_signal2) (GTest * test, gint an_int);
} GTestClass;
#define G_TYPE_TEST (my_test_get_type ())
#define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
#define MY_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
#define MY_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
#define MY_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
#define MY_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
static GType my_test_get_type (void);
G_DEFINE_TYPE (GTest, my_test, G_TYPE_OBJECT)
/* Test state */
typedef struct
{
GClosure *closure; /* (unowned) */
gboolean stopping;
gboolean seen_signal_handler;
gboolean seen_cleanup;
gboolean seen_test_int1;
gboolean seen_test_int2;
gboolean seen_thread1;
gboolean seen_thread2;
} TestClosureRefcountData;
/* --- functions --- */
static void
my_test_init (GTest * test)
{
g_test_message ("Init %p", test);
test->value = 0;
test->test_pointer1 = TEST_POINTER1;
test->test_pointer2 = TEST_POINTER2;
}
typedef enum
{
PROP_TEST_PROP = 1,
} MyTestProperty;
typedef enum
{
SIGNAL_TEST_SIGNAL1,
SIGNAL_TEST_SIGNAL2,
} MyTestSignal;
static guint signals[SIGNAL_TEST_SIGNAL2 + 1] = { 0, };
static void
my_test_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GTest *test = MY_TEST (object);
switch (prop_id)
{
case PROP_TEST_PROP:
test->value = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
my_test_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GTest *test = MY_TEST (object);
switch (prop_id)
{
case PROP_TEST_PROP:
g_value_set_int (value, test->value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
my_test_test_signal2 (GTest *test,
gint an_int)
{
}
static void
my_test_emit_test_signal1 (GTest *test,
gint vint)
{
g_signal_emit (G_OBJECT (test), signals[SIGNAL_TEST_SIGNAL1], 0, vint);
}
static void
my_test_emit_test_signal2 (GTest *test,
gint vint)
{
g_signal_emit (G_OBJECT (test), signals[SIGNAL_TEST_SIGNAL2], 0, vint);
}
static void
my_test_class_init (GTestClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = my_test_set_property;
gobject_class->get_property = my_test_get_property;
signals[SIGNAL_TEST_SIGNAL1] =
g_signal_new ("test-signal1", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GTestClass, test_signal1), NULL, NULL,
g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
signals[SIGNAL_TEST_SIGNAL2] =
g_signal_new ("test-signal2", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GTestClass, test_signal2), NULL, NULL,
g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEST_PROP,
g_param_spec_int ("test-prop", "Test Prop", "Test property",
0, 1, 0, G_PARAM_READWRITE));
klass->test_signal2 = my_test_test_signal2;
}
static void
test_closure (GClosure *closure)
{
/* try to produce high contention in closure->ref_count */
guint i = 0, n = g_random_int () % 199;
for (i = 0; i < n; i++)
g_closure_ref (closure);
g_closure_sink (closure); /* NOP */
for (i = 0; i < n; i++)
g_closure_unref (closure);
}
static gpointer
thread1_main (gpointer user_data)
{
TestClosureRefcountData *data = user_data;
guint i = 0;
for (i = 1; !g_atomic_int_get (&data->stopping); i++)
{
test_closure (data->closure);
if (i % 10000 == 0)
{
g_test_message ("Yielding from thread1");
g_thread_yield (); /* force context switch */
g_atomic_int_set (&data->seen_thread1, TRUE);
}
}
return NULL;
}
static gpointer
thread2_main (gpointer user_data)
{
TestClosureRefcountData *data = user_data;
guint i = 0;
for (i = 1; !g_atomic_int_get (&data->stopping); i++)
{
test_closure (data->closure);
if (i % 10000 == 0)
{
g_test_message ("Yielding from thread2");
g_thread_yield (); /* force context switch */
g_atomic_int_set (&data->seen_thread2, TRUE);
}
}
return NULL;
}
static void
test_signal_handler (GTest *test,
gint vint,
gpointer user_data)
{
TestClosureRefcountData *data = user_data;
g_assert_true (test->test_pointer1 == TEST_POINTER1);
data->seen_signal_handler = TRUE;
data->seen_test_int1 |= vint == TEST_INT1;
data->seen_test_int2 |= vint == TEST_INT2;
}
static void
destroy_data (gpointer user_data,
GClosure *closure)
{
TestClosureRefcountData *data = user_data;
data->seen_cleanup = TRUE;
g_assert_true (data->closure == closure);
g_assert_cmpint (closure->ref_count, ==, 0);
}
static void
test_emissions (GTest *test)
{
my_test_emit_test_signal1 (test, TEST_INT1);
my_test_emit_test_signal2 (test, TEST_INT2);
}
/* Test that closure refcounting works even when high contested between three
* threads (the main thread, thread1 and thread2). Both child threads are
* contesting refs/unrefs, while the main thread periodically emits signals
* which also do refs/unrefs on closures. */
static void
test_closure_refcount (void)
{
GThread *thread1, *thread2;
TestClosureRefcountData test_data = { 0, };
GClosure *closure;
GTest *object;
guint i, n_iterations;
object = g_object_new (G_TYPE_TEST, NULL);
closure = g_cclosure_new (G_CALLBACK (test_signal_handler), &test_data, destroy_data);
g_signal_connect_closure (object, "test-signal1", closure, FALSE);
g_signal_connect_closure (object, "test-signal2", closure, FALSE);
test_data.stopping = FALSE;
test_data.closure = closure;
thread1 = g_thread_new ("thread1", thread1_main, &test_data);
thread2 = g_thread_new ("thread2", thread2_main, &test_data);
/* The 16-bit compare-and-swap operations currently used for closure
* refcounts are really slow on some ARM CPUs, notably Cortex-A57.
* Reduce the number of iterations so that the test completes in a
* finite time, but don't reduce it so much that the main thread
* starves the other threads and causes a test failure.
*
* https://gitlab.gnome.org/GNOME/glib/issues/1316
* aka https://bugs.debian.org/880883 */
#if defined(__aarch64__) || defined(__arm__)
n_iterations = 100000;
#else
n_iterations = 1000000;
#endif
/* Run the test for a reasonably high number of iterations, and ensure we
* dont terminate until at least 10000 iterations have completed in both
* thread1 and thread2. Even though @n_iterations is high, we cant guarantee
* that the scheduler allocates time fairly (or at all!) to thread1 or
* thread2. */
for (i = 1;
i < n_iterations ||
!g_atomic_int_get (&test_data.seen_thread1) ||
!g_atomic_int_get (&test_data.seen_thread2);
i++)
{
test_emissions (object);
if (i % 10000 == 0)
{
g_test_message ("Yielding from main thread");
g_thread_yield (); /* force context switch */
}
}
g_atomic_int_set (&test_data.stopping, TRUE);
g_test_message ("Stopping");
/* wait for thread shutdown */
g_thread_join (thread1);
g_thread_join (thread2);
/* finalize object, destroy signals, run cleanup code */
g_object_unref (object);
g_test_message ("Stopped");
g_assert_true (g_atomic_int_get (&test_data.seen_thread1));
g_assert_true (g_atomic_int_get (&test_data.seen_thread2));
g_assert_true (test_data.seen_test_int1);
g_assert_true (test_data.seen_test_int2);
g_assert_true (test_data.seen_signal_handler);
g_assert_true (test_data.seen_cleanup);
}
int
main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/closure/refcount", test_closure_refcount);
return g_test_run ();
}

223
gobject/tests/closure.c Normal file
View file

@ -0,0 +1,223 @@
#include <glib-object.h>
#ifdef G_OS_UNIX
#include <glib-unix.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#endif
static void
test_source (GSource *one, GCallback quit_callback)
{
GClosure *closure;
GMainLoop *loop;
/* Callback with GMainLoop user_data */
loop = g_main_loop_new (NULL, FALSE);
closure = g_cclosure_new (quit_callback, loop, NULL);
g_source_set_closure (one, closure);
g_source_attach (one, NULL);
g_main_loop_run (loop);
g_source_destroy (one);
g_main_loop_unref (loop);
}
static gboolean
simple_quit_callback (gpointer user_data)
{
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
return TRUE;
}
static void
test_closure_idle (void)
{
GSource *source;
source = g_idle_source_new ();
test_source (source, G_CALLBACK (simple_quit_callback));
g_source_unref (source);
}
static void
test_closure_timeout (void)
{
GSource *source;
source = g_timeout_source_new (10);
test_source (source, G_CALLBACK (simple_quit_callback));
g_source_unref (source);
}
static gboolean
iochannel_quit_callback (GIOChannel *channel,
GIOCondition cond,
gpointer user_data)
{
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
return TRUE;
}
static void
test_closure_iochannel (void)
{
GIOChannel *chan;
GSource *source;
char *path;
GError *error = NULL;
if (g_path_is_absolute (g_get_prgname ()))
path = g_strdup (g_get_prgname ());
else
{
path = g_test_build_filename (G_TEST_BUILT,
g_get_prgname (),
NULL);
}
chan = g_io_channel_new_file (path, "r", &error);
g_assert_no_error (error);
g_free (path);
source = g_io_create_watch (chan, G_IO_IN);
test_source (source, G_CALLBACK (iochannel_quit_callback));
g_source_unref (source);
g_io_channel_unref (chan);
}
static void
test_closure_child (void)
{
GSource *source;
GPid pid;
GError *error = NULL;
gchar *argv[3];
g_assert (g_getenv ("DO_NOT_ACCIDENTALLY_RECURSE") == NULL);
g_setenv ("DO_NOT_ACCIDENTALLY_RECURSE", "1", TRUE);
if (g_path_is_absolute (g_get_prgname ()))
argv[0] = g_strdup (g_get_prgname ());
else
{
argv[0] = g_test_build_filename (G_TEST_BUILT,
g_get_prgname (),
NULL);
}
argv[1] = "-l";
argv[2] = NULL;
g_spawn_async (NULL, argv, NULL,
G_SPAWN_STDOUT_TO_DEV_NULL |
G_SPAWN_STDERR_TO_DEV_NULL |
G_SPAWN_DO_NOT_REAP_CHILD,
NULL, NULL,
&pid, &error);
g_assert_no_error (error);
g_free (argv[0]);
source = g_child_watch_source_new (pid);
test_source (source, G_CALLBACK (iochannel_quit_callback));
g_source_unref (source);
}
#ifdef G_OS_UNIX
static gboolean
fd_quit_callback (gint fd,
GIOCondition condition,
gpointer user_data)
{
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
return TRUE;
}
static void
test_closure_fd (void)
{
gint fd;
GSource *source;
fd = open ("/dev/null", O_RDONLY);
g_assert (fd != -1);
source = g_unix_fd_source_new (fd, G_IO_IN);
test_source (source, G_CALLBACK (fd_quit_callback));
g_source_unref (source);
close (fd);
}
static gboolean
send_usr1 (gpointer user_data)
{
kill (getpid (), SIGUSR1);
return FALSE;
}
static gboolean
closure_quit_callback (gpointer user_data)
{
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
return TRUE;
}
static void
test_closure_signal (void)
{
GSource *source;
g_idle_add_full (G_PRIORITY_LOW, send_usr1, NULL, NULL);
source = g_unix_signal_source_new (SIGUSR1);
test_source (source, G_CALLBACK (closure_quit_callback));
g_source_unref (source);
}
#endif
int
main (int argc,
char *argv[])
{
#ifndef G_OS_WIN32
sigset_t sig_mask, old_mask;
sigemptyset (&sig_mask);
sigaddset (&sig_mask, SIGUSR1);
if (sigprocmask (SIG_UNBLOCK, &sig_mask, &old_mask) == 0)
{
if (sigismember (&old_mask, SIGUSR1))
g_message ("SIGUSR1 was blocked, unblocking it");
}
#endif
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/closure/idle", test_closure_idle);
g_test_add_func ("/closure/timeout", test_closure_timeout);
g_test_add_func ("/closure/iochannel", test_closure_iochannel);
g_test_add_func ("/closure/child", test_closure_child);
#ifdef G_OS_UNIX
g_test_add_func ("/closure/fd", test_closure_fd);
g_test_add_func ("/closure/signal", test_closure_signal);
#endif
return g_test_run ();
}

26
gobject/tests/cxx.cpp Normal file
View file

@ -0,0 +1,26 @@
/* Copyright (C) 2001 Sebastian Wilhelmi <wilhelmi@google.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/* A trivial C++ program to be compiled in C++ mode, which
* smoketests that the GObject headers are valid C++ headers. */
#include <glib-object.h>
int
main ()
{
return 0;
}

View file

@ -0,0 +1,368 @@
/* GLib testing framework examples and tests
* Copyright (C) 2008 Imendio AB
* Authors: Tim Janik
*
* This work is provided "as is"; redistribution and modification
* in whole or in part, in any medium, physical or electronic is
* permitted without restriction.
*
* This work 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.
*
* In no event shall the authors or contributors be liable for any
* direct, indirect, incidental, special, exemplary, or consequential
* damages (including, but not limited to, procurement of substitute
* goods or services; loss of use, data, or profits; or business
* interruption) however caused and on any theory of liability, whether
* in contract, strict liability, or tort (including negligence or
* otherwise) arising in any way out of the use of this software, even
* if advised of the possibility of such damage.
*/
#include <glib.h>
#include <glib-object.h>
/* This test tests the macros for defining dynamic types.
*/
static GMutex sync_mutex;
static gboolean loaded = FALSE;
/* MODULE */
typedef struct _TestModule TestModule;
typedef struct _TestModuleClass TestModuleClass;
#define TEST_TYPE_MODULE (test_module_get_type ())
#define TEST_MODULE(module) (G_TYPE_CHECK_INSTANCE_CAST ((module), TEST_TYPE_MODULE, TestModule))
#define TEST_MODULE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), TEST_TYPE_MODULE, TestModuleClass))
#define TEST_IS_MODULE(module) (G_TYPE_CHECK_INSTANCE_TYPE ((module), TEST_TYPE_MODULE))
#define TEST_IS_MODULE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), TEST_TYPE_MODULE))
#define TEST_MODULE_GET_CLASS(module) (G_TYPE_INSTANCE_GET_CLASS ((module), TEST_TYPE_MODULE, TestModuleClass))
typedef void (*TestModuleRegisterFunc) (GTypeModule *module);
struct _TestModule
{
GTypeModule parent_instance;
TestModuleRegisterFunc register_func;
};
struct _TestModuleClass
{
GTypeModuleClass parent_class;
};
static GType test_module_get_type (void);
static gboolean
test_module_load (GTypeModule *module)
{
TestModule *test_module = TEST_MODULE (module);
test_module->register_func (module);
return TRUE;
}
static void
test_module_unload (GTypeModule *module)
{
}
static void
test_module_class_init (TestModuleClass *class)
{
GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
module_class->load = test_module_load;
module_class->unload = test_module_unload;
}
static GType test_module_get_type (void)
{
static GType object_type = 0;
if (!object_type) {
static const GTypeInfo object_info =
{
sizeof (TestModuleClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) test_module_class_init,
(GClassFinalizeFunc) NULL,
NULL,
sizeof (TestModule),
0,
(GInstanceInitFunc)NULL,
NULL,
};
object_type = g_type_register_static (G_TYPE_TYPE_MODULE, "TestModule", &object_info, 0);
}
return object_type;
}
static GTypeModule *
test_module_new (TestModuleRegisterFunc register_func)
{
TestModule *test_module = g_object_new (TEST_TYPE_MODULE, NULL);
GTypeModule *module = G_TYPE_MODULE (test_module);
test_module->register_func = register_func;
/* Register the types initially */
g_type_module_use (module);
g_type_module_unuse (module);
return G_TYPE_MODULE (module);
}
#define DYNAMIC_OBJECT_TYPE (dynamic_object_get_type ())
typedef GObject DynamicObject;
typedef struct _DynamicObjectClass DynamicObjectClass;
struct _DynamicObjectClass
{
GObjectClass parent_class;
guint val;
};
static GType dynamic_object_get_type (void);
G_DEFINE_DYNAMIC_TYPE(DynamicObject, dynamic_object, G_TYPE_OBJECT)
static void
dynamic_object_class_init (DynamicObjectClass *class)
{
class->val = 42;
g_assert (loaded == FALSE);
loaded = TRUE;
}
static void
dynamic_object_class_finalize (DynamicObjectClass *class)
{
g_assert (loaded == TRUE);
loaded = FALSE;
}
static void
dynamic_object_init (DynamicObject *dynamic_object)
{
}
static void
module_register (GTypeModule *module)
{
dynamic_object_register_type (module);
}
#define N_THREADS 100
#define N_REFS 10000
static gpointer
ref_unref_thread (gpointer data)
{
gint i;
/* first, synchronize with other threads,
*/
if (g_test_verbose())
g_printerr ("WAITING!\n");
g_mutex_lock (&sync_mutex);
g_mutex_unlock (&sync_mutex);
if (g_test_verbose ())
g_printerr ("STARTING\n");
/* ref/unref the klass 10000000 times */
for (i = N_REFS; i; i--) {
if (g_test_verbose ())
if (i % 10)
g_printerr ("%d\n", i);
g_type_class_unref (g_type_class_ref ((GType) data));
}
if (g_test_verbose())
g_printerr ("DONE !\n");
return NULL;
}
static void
test_multithreaded_dynamic_type_init (void)
{
GTypeModule *module;
DynamicObjectClass *class;
/* Create N_THREADS threads that are going to just ref/unref a class */
GThread *threads[N_THREADS];
guint i;
module = test_module_new (module_register);
g_assert (module != NULL);
/* Not loaded until we call ref for the first time */
class = g_type_class_peek (DYNAMIC_OBJECT_TYPE);
g_assert (class == NULL);
g_assert (!loaded);
/* pause newly created threads */
g_mutex_lock (&sync_mutex);
/* create threads */
for (i = 0; i < N_THREADS; i++) {
threads[i] = g_thread_new ("test", ref_unref_thread, (gpointer) DYNAMIC_OBJECT_TYPE);
}
/* execute threads */
g_mutex_unlock (&sync_mutex);
for (i = 0; i < N_THREADS; i++) {
g_thread_join (threads[i]);
}
}
enum
{
PROP_0,
PROP_FOO
};
typedef struct _DynObj DynObj;
typedef struct _DynObjClass DynObjClass;
typedef struct _DynIfaceInterface DynIfaceInterface;
struct _DynObj
{
GObject obj;
gint foo;
};
struct _DynObjClass
{
GObjectClass class;
};
struct _DynIfaceInterface
{
GTypeInterface iface;
};
static void dyn_obj_iface_init (DynIfaceInterface *iface);
static GType dyn_iface_get_type (void);
G_DEFINE_INTERFACE (DynIface, dyn_iface, G_TYPE_OBJECT)
static GType dyn_obj_get_type (void);
G_DEFINE_DYNAMIC_TYPE_EXTENDED(DynObj, dyn_obj, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE_DYNAMIC(dyn_iface_get_type (), dyn_obj_iface_init))
static void
dyn_iface_default_init (DynIfaceInterface *iface)
{
g_object_interface_install_property (iface,
g_param_spec_int ("foo", NULL, NULL, 0, 100, 0, G_PARAM_READWRITE));
}
static void
dyn_obj_iface_init (DynIfaceInterface *iface)
{
}
static void
dyn_obj_init (DynObj *obj)
{
obj->foo = 0;
}
static void
set_prop (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
DynObj *obj = (DynObj *)object;
switch (prop_id)
{
case PROP_FOO:
obj->foo = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
get_prop (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
DynObj *obj = (DynObj *)object;
switch (prop_id)
{
case PROP_FOO:
g_value_set_int (value, obj->foo);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
dyn_obj_class_init (DynObjClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->set_property = set_prop;
object_class->get_property = get_prop;
g_object_class_override_property (object_class, PROP_FOO, "foo");
}
static void
dyn_obj_class_finalize (DynObjClass *class)
{
}
static void
mod_register (GTypeModule *module)
{
dyn_obj_register_type (module);
}
static void
test_dynamic_interface_properties (void)
{
GTypeModule *module;
DynObj *obj;
gint val;
module = test_module_new (mod_register);
g_assert (module != NULL);
obj = g_object_new (dyn_obj_get_type (), "foo", 1, NULL);
g_object_get (obj, "foo", &val, NULL);
g_assert_cmpint (val, ==, 1);
g_object_unref (obj);
}
int
main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/GObject/threaded-dynamic-ref-unref-init", test_multithreaded_dynamic_type_init);
g_test_add_func ("/GObject/dynamic-interface-properties", test_dynamic_interface_properties);
return g_test_run();
}

170
gobject/tests/enums.c Normal file
View file

@ -0,0 +1,170 @@
#include <glib-object.h>
static const GEnumValue my_enum_values[] =
{
{ 1, "the first value", "one" },
{ 2, "the second value", "two" },
{ 3, "the third value", "three" },
{ 0, NULL, NULL }
};
static void
test_enum_basic (void)
{
GType type;
GEnumClass *class;
GEnumValue *val;
GValue value = G_VALUE_INIT;
gchar *to_string;
type = g_enum_register_static ("MyEnum", my_enum_values);
g_value_init (&value, type);
g_assert (G_VALUE_HOLDS_ENUM (&value));
g_value_set_enum (&value, 2);
g_assert_cmpint (g_value_get_enum (&value), ==, 2);
g_value_unset (&value);
class = g_type_class_ref (type);
g_assert_cmpint (class->minimum, ==, 1);
g_assert_cmpint (class->maximum, ==, 3);
g_assert_cmpint (class->n_values, ==, 3);
val = g_enum_get_value (class, 2);
g_assert (val != NULL);
g_assert_cmpstr (val->value_name, ==, "the second value");
val = g_enum_get_value (class, 15);
g_assert (val == NULL);
val = g_enum_get_value_by_name (class, "the third value");
g_assert (val != NULL);
g_assert_cmpint (val->value, ==, 3);
val = g_enum_get_value_by_name (class, "the color purple");
g_assert (val == NULL);
val = g_enum_get_value_by_nick (class, "one");
g_assert (val != NULL);
g_assert_cmpint (val->value, ==, 1);
val = g_enum_get_value_by_nick (class, "purple");
g_assert (val == NULL);
to_string = g_enum_to_string (type, 2);
g_assert_cmpstr (to_string, ==, "the second value");
g_free (to_string);
to_string = g_enum_to_string (type, 15);
g_assert_cmpstr (to_string, ==, "15");
g_free (to_string);
g_type_class_unref (class);
}
static const GFlagsValue my_flag_values[] =
{
{ 0, "no flags", "none" },
{ 1, "the first flag", "one" },
{ 2, "the second flag", "two" },
{ 8, "the third flag", "three" },
{ 0, NULL, NULL }
};
static const GFlagsValue no_default_flag_values[] =
{
{ 1, "the first flag", "one" },
{ 0, NULL, NULL }
};
static void
test_flags_transform_to_string (const GValue *value)
{
GValue tmp = G_VALUE_INIT;
g_value_init (&tmp, G_TYPE_STRING);
g_value_transform (value, &tmp);
g_value_unset (&tmp);
}
static void
test_flags_basic (void)
{
GType type, no_default_type;
GFlagsClass *class;
GFlagsValue *val;
GValue value = G_VALUE_INIT;
gchar *to_string;
type = g_flags_register_static ("MyFlags", my_flag_values);
no_default_type = g_flags_register_static ("NoDefaultFlags",
no_default_flag_values);
g_value_init (&value, type);
g_assert (G_VALUE_HOLDS_FLAGS (&value));
g_value_set_flags (&value, 2|8);
g_assert_cmpint (g_value_get_flags (&value), ==, 2|8);
class = g_type_class_ref (type);
g_assert_cmpint (class->mask, ==, 1|2|8);
g_assert_cmpint (class->n_values, ==, 4);
val = g_flags_get_first_value (class, 2|8);
g_assert (val != NULL);
g_assert_cmpstr (val->value_name, ==, "the second flag");
val = g_flags_get_first_value (class, 16);
g_assert (val == NULL);
val = g_flags_get_value_by_name (class, "the third flag");
g_assert (val != NULL);
g_assert_cmpint (val->value, ==, 8);
val = g_flags_get_value_by_name (class, "the color purple");
g_assert (val == NULL);
val = g_flags_get_value_by_nick (class, "one");
g_assert (val != NULL);
g_assert_cmpint (val->value, ==, 1);
val = g_flags_get_value_by_nick (class, "purple");
g_assert (val == NULL);
test_flags_transform_to_string (&value);
g_value_unset (&value);
to_string = g_flags_to_string (type, 1|8);
g_assert_cmpstr (to_string, ==, "the first flag | the third flag");
g_free (to_string);
to_string = g_flags_to_string (type, 0);
g_assert_cmpstr (to_string, ==, "no flags");
g_free (to_string);
to_string = g_flags_to_string (type, 16);
g_assert_cmpstr (to_string, ==, "0x10");
g_free (to_string);
to_string = g_flags_to_string (type, 1|16);
g_assert_cmpstr (to_string, ==, "the first flag | 0x10");
g_free (to_string);
to_string = g_flags_to_string (no_default_type, 0);
g_assert_cmpstr (to_string, ==, "0x0");
g_free (to_string);
to_string = g_flags_to_string (no_default_type, 16);
g_assert_cmpstr (to_string, ==, "0x10");
g_free (to_string);
g_type_class_unref (class);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/enum/basic", test_enum_basic);
g_test_add_func ("/flags/basic", test_flags_basic);
return g_test_run ();
}

179
gobject/tests/flags.c Normal file
View file

@ -0,0 +1,179 @@
/* flags.c
* Copyright (C) 2018 Arthur Demchenkov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*/
#include <glib-object.h>
/* Check that validation of flags works on architectures where
* #gint and #glong are different sizes, as the flags are cast
* between types a few times.
*
* See: https://gitlab.gnome.org/GNOME/glib/issues/1572
*/
enum {
PROP_FLAGS = 1
};
typedef struct _GTest GTest;
typedef struct _GTestClass GTestClass;
typedef enum {
NO_FLAG = 0,
LOWEST_FLAG = 1,
HIGHEST_FLAG = 1 << 31
} MyFlagsEnum;
struct _GTest {
GObject object;
MyFlagsEnum flags;
};
struct _GTestClass {
GObjectClass parent_class;
};
static GType my_test_get_type (void);
static GType my_test_flags_get_type (void);
#define G_TYPE_TEST (my_test_get_type())
#define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
G_DEFINE_TYPE (GTest, my_test, G_TYPE_OBJECT)
static void my_test_class_init (GTestClass * klass);
static void my_test_init (GTest * test);
static void my_test_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void my_test_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static GType
my_test_flags_get_type (void)
{
static GType flags_type = 0;
if (G_UNLIKELY(flags_type == 0))
{
static const GFlagsValue values[] = {
{ LOWEST_FLAG, "LOWEST_FLAG", "lowest" },
{ HIGHEST_FLAG, "HIGHEST_FLAG", "highest" },
{ 0, NULL, NULL }
};
flags_type = g_flags_register_static (g_intern_static_string ("GTestFlags"), values);
}
return flags_type;
}
static void
my_test_class_init (GTestClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = my_test_get_property;
gobject_class->set_property = my_test_set_property;
g_object_class_install_property (gobject_class, 1,
g_param_spec_flags ("flags",
"Flags",
"Flags test property",
my_test_flags_get_type(), 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
}
static void my_test_init (GTest *test)
{
}
static void
my_test_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GTest *test = MY_TEST (object);
switch (prop_id)
{
case PROP_FLAGS:
g_value_set_flags (value, test->flags);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
my_test_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GTest *test = MY_TEST (object);
switch (prop_id)
{
case PROP_FLAGS:
test->flags = g_value_get_flags (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
check_flags_validation (void)
{
guint test_flags[] = {
NO_FLAG,
LOWEST_FLAG,
HIGHEST_FLAG,
LOWEST_FLAG | HIGHEST_FLAG
};
guint flag_read;
gsize i;
for (i = 0; i < G_N_ELEMENTS (test_flags); i++)
{
guint flag_set = test_flags[i];
GObject *test = g_object_new (G_TYPE_TEST,
"flags", flag_set,
NULL);
g_object_get (test, "flags", &flag_read, NULL);
/* This check will fail in case of gint -> glong conversion
* in value_flags_enum_collect_value() */
g_assert_cmpint (flag_read, ==, flag_set);
g_object_unref (test);
}
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/gobject/flags/validate", check_flags_validation);
return g_test_run ();
}

817
gobject/tests/genmarshal.py Normal file
View file

@ -0,0 +1,817 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# Copyright © 2019 Endless Mobile, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
"""Integration tests for glib-genmarshal utility."""
import collections
import os
import shutil
import subprocess
import sys
import tempfile
from textwrap import dedent
import unittest
import taptestrunner
# Disable line length warnings as wrapping the C code templates would be hard
# flake8: noqa: E501
Result = collections.namedtuple("Result", ("info", "out", "err", "subs"))
class TestGenmarshal(unittest.TestCase):
"""Integration test for running glib-genmarshal.
This can be run when installed or uninstalled. When uninstalled, it
requires G_TEST_BUILDDIR and G_TEST_SRCDIR to be set.
The idea with this test harness is to test the glib-genmarshal utility, its
handling of command line arguments, its exit statuses, and its handling of
various marshaller lists. In future we could split the core glib-genmarshal
parsing and generation code out into a library and unit test that, and
convert this test to just check command line behaviour.
"""
# Track the cwd, we want to back out to that to clean up our tempdir
cwd = ""
def setUp(self):
self.timeout_seconds = 10 # seconds per test
self.tmpdir = tempfile.TemporaryDirectory()
self.cwd = os.getcwd()
os.chdir(self.tmpdir.name)
print("tmpdir:", self.tmpdir.name)
if "G_TEST_BUILDDIR" in os.environ:
self.__genmarshal = os.path.join(
os.environ["G_TEST_BUILDDIR"], "..", "glib-genmarshal"
)
else:
self.__genmarshal = shutil.which("glib-genmarshal")
print("genmarshal:", self.__genmarshal)
def tearDown(self):
os.chdir(self.cwd)
self.tmpdir.cleanup()
def runGenmarshal(self, *args):
argv = [self.__genmarshal]
# shebang lines are not supported on native
# Windows consoles
if os.name == "nt":
argv.insert(0, sys.executable)
argv.extend(args)
print("Running:", argv)
env = os.environ.copy()
env["LC_ALL"] = "C.UTF-8"
print("Environment:", env)
# We want to ensure consistent line endings...
info = subprocess.run(
argv,
timeout=self.timeout_seconds,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
universal_newlines=True,
)
info.check_returncode()
out = info.stdout.strip()
err = info.stderr.strip()
# Known substitutions for standard boilerplate
subs = {
"standard_top_comment": "This file is generated by glib-genmarshal, do not modify "
"it. This code is licensed under the same license as the "
"containing project. Note that it links to GLib, so must "
"comply with the LGPL linking clauses.",
"standard_top_pragma": dedent(
"""
#ifndef __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__
#define __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__
"""
).strip(),
"standard_bottom_pragma": dedent(
"""
#endif /* __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__ */
"""
).strip(),
"standard_includes": dedent(
"""
#include <glib-object.h>
"""
).strip(),
"standard_marshal_peek_defines": dedent(
"""
#ifdef G_ENABLE_DEBUG
#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
#define g_marshal_value_peek_char(v) g_value_get_schar (v)
#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
#define g_marshal_value_peek_int(v) g_value_get_int (v)
#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
#define g_marshal_value_peek_long(v) g_value_get_long (v)
#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
#define g_marshal_value_peek_float(v) g_value_get_float (v)
#define g_marshal_value_peek_double(v) g_value_get_double (v)
#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
#define g_marshal_value_peek_param(v) g_value_get_param (v)
#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
#define g_marshal_value_peek_object(v) g_value_get_object (v)
#define g_marshal_value_peek_variant(v) g_value_get_variant (v)
#else /* !G_ENABLE_DEBUG */
/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
* Do not access GValues directly in your code. Instead, use the
* g_value_get_*() functions
*/
#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
#define g_marshal_value_peek_char(v) (v)->data[0].v_int
#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
#define g_marshal_value_peek_int(v) (v)->data[0].v_int
#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
#define g_marshal_value_peek_long(v) (v)->data[0].v_long
#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
#define g_marshal_value_peek_float(v) (v)->data[0].v_float
#define g_marshal_value_peek_double(v) (v)->data[0].v_double
#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer
#endif /* !G_ENABLE_DEBUG */
"""
).strip(),
}
result = Result(info, out, err, subs)
print("Output:", result.out)
return result
def runGenmarshalWithList(self, list_contents, *args):
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix=".list", delete=False
) as list_file:
# Write out the list.
list_file.write(list_contents.encode("utf-8"))
print(list_file.name + ":", list_contents)
list_file.flush()
header_result = self.runGenmarshal(list_file.name, "--header", *args)
body_result = self.runGenmarshal(list_file.name, "--body", *args)
header_result.subs["list_path"] = list_file.name
body_result.subs["list_path"] = list_file.name
return (header_result, body_result)
def test_help(self):
"""Test the --help argument."""
result = self.runGenmarshal("--help")
self.assertIn("usage: glib-genmarshal", result.out)
def test_no_args(self):
"""Test running with no arguments at all."""
result = self.runGenmarshal()
self.assertEqual("", result.err)
self.assertEqual("", result.out)
def test_empty_list(self):
"""Test running with an empty list."""
(header_result, body_result) = self.runGenmarshalWithList("", "--quiet")
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
{standard_includes}
G_BEGIN_DECLS
G_END_DECLS
{standard_bottom_pragma}
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_includes}
{standard_marshal_peek_defines}
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
def test_void_boolean(self):
"""Test running with a basic VOID:BOOLEAN list."""
(header_result, body_result) = self.runGenmarshalWithList(
"VOID:BOOLEAN", "--quiet"
)
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
{standard_includes}
G_BEGIN_DECLS
/* VOID:BOOLEAN ({list_path}:1) */
#define g_cclosure_user_marshal_VOID__BOOLEAN g_cclosure_marshal_VOID__BOOLEAN
G_END_DECLS
{standard_bottom_pragma}
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_includes}
{standard_marshal_peek_defines}
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
def test_void_boolean_int64(self):
"""Test running with a non-trivial VOID:BOOLEAN,INT64 list."""
(header_result, body_result) = self.runGenmarshalWithList(
"VOID:BOOLEAN,INT64", "--quiet"
)
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
{standard_includes}
G_BEGIN_DECLS
/* VOID:BOOLEAN,INT64 ({list_path}:1) */
extern
void g_cclosure_user_marshal_VOID__BOOLEAN_INT64 (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
G_END_DECLS
{standard_bottom_pragma}
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_includes}
{standard_marshal_peek_defines}
/* VOID:BOOLEAN,INT64 ({list_path}:1) */
void
g_cclosure_user_marshal_VOID__BOOLEAN_INT64 (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint G_GNUC_UNUSED,
gpointer marshal_data)
{{
typedef void (*GMarshalFunc_VOID__BOOLEAN_INT64) (gpointer data1,
gboolean arg1,
gint64 arg2,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__BOOLEAN_INT64 callback;
g_return_if_fail (n_param_values == 3);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
}}
else
{{
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__BOOLEAN_INT64) (marshal_data ? marshal_data : cc->callback);
callback (data1,
g_marshal_value_peek_boolean (param_values + 1),
g_marshal_value_peek_int64 (param_values + 2),
data2);
}}
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
def test_void_variant_nostdinc_valist_marshaller(self):
"""Test running with a basic VOID:VARIANT list, but without the
standard marshallers, and with valist support enabled. This checks that
the valist marshaller for VARIANT correctly sinks floating variants.
See issue #1793.
"""
(header_result, body_result) = self.runGenmarshalWithList(
"VOID:VARIANT", "--quiet", "--nostdinc", "--valist-marshaller"
)
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
G_BEGIN_DECLS
/* VOID:VARIANT ({list_path}:1) */
extern
void g_cclosure_user_marshal_VOID__VARIANT (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
extern
void g_cclosure_user_marshal_VOID__VARIANTv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
G_END_DECLS
{standard_bottom_pragma}
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_marshal_peek_defines}
/* VOID:VARIANT ({list_path}:1) */
void
g_cclosure_user_marshal_VOID__VARIANT (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint G_GNUC_UNUSED,
gpointer marshal_data)
{{
typedef void (*GMarshalFunc_VOID__VARIANT) (gpointer data1,
gpointer arg1,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__VARIANT callback;
g_return_if_fail (n_param_values == 2);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
}}
else
{{
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__VARIANT) (marshal_data ? marshal_data : cc->callback);
callback (data1,
g_marshal_value_peek_variant (param_values + 1),
data2);
}}
void
g_cclosure_user_marshal_VOID__VARIANTv (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types)
{{
typedef void (*GMarshalFunc_VOID__VARIANT) (gpointer data1,
gpointer arg1,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__VARIANT callback;
gpointer arg0;
va_list args_copy;
G_VA_COPY (args_copy, args);
arg0 = (gpointer) va_arg (args_copy, gpointer);
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
arg0 = g_variant_ref_sink (arg0);
va_end (args_copy);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = instance;
}}
else
{{
data1 = instance;
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__VARIANT) (marshal_data ? marshal_data : cc->callback);
callback (data1,
arg0,
data2);
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
g_variant_unref (arg0);
}}
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
def test_void_string_nostdinc(self):
"""Test running with a basic VOID:STRING list, but without the
standard marshallers, and with valist support enabled. This checks that
the valist marshaller for STRING correctly skips a string copy if the
argument is static.
See issue #1792.
"""
(header_result, body_result) = self.runGenmarshalWithList(
"VOID:STRING", "--quiet", "--nostdinc", "--valist-marshaller"
)
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
G_BEGIN_DECLS
/* VOID:STRING ({list_path}:1) */
extern
void g_cclosure_user_marshal_VOID__STRING (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
extern
void g_cclosure_user_marshal_VOID__STRINGv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
G_END_DECLS
{standard_bottom_pragma}
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_marshal_peek_defines}
/* VOID:STRING ({list_path}:1) */
void
g_cclosure_user_marshal_VOID__STRING (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint G_GNUC_UNUSED,
gpointer marshal_data)
{{
typedef void (*GMarshalFunc_VOID__STRING) (gpointer data1,
gpointer arg1,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__STRING callback;
g_return_if_fail (n_param_values == 2);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
}}
else
{{
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__STRING) (marshal_data ? marshal_data : cc->callback);
callback (data1,
g_marshal_value_peek_string (param_values + 1),
data2);
}}
void
g_cclosure_user_marshal_VOID__STRINGv (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types)
{{
typedef void (*GMarshalFunc_VOID__STRING) (gpointer data1,
gpointer arg1,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__STRING callback;
gpointer arg0;
va_list args_copy;
G_VA_COPY (args_copy, args);
arg0 = (gpointer) va_arg (args_copy, gpointer);
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
arg0 = g_strdup (arg0);
va_end (args_copy);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = instance;
}}
else
{{
data1 = instance;
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__STRING) (marshal_data ? marshal_data : cc->callback);
callback (data1,
arg0,
data2);
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
g_free (arg0);
}}
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
def test_void_param_nostdinc(self):
"""Test running with a basic VOID:PARAM list, but without the
standard marshallers, and with valist support enabled. This checks that
the valist marshaller for PARAM correctly skips a param copy if the
argument is static.
See issue #1792.
"""
self.maxDiff = None # TODO
(header_result, body_result) = self.runGenmarshalWithList(
"VOID:PARAM", "--quiet", "--nostdinc", "--valist-marshaller"
)
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
G_BEGIN_DECLS
/* VOID:PARAM ({list_path}:1) */
extern
void g_cclosure_user_marshal_VOID__PARAM (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
extern
void g_cclosure_user_marshal_VOID__PARAMv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
G_END_DECLS
{standard_bottom_pragma}
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_marshal_peek_defines}
/* VOID:PARAM ({list_path}:1) */
void
g_cclosure_user_marshal_VOID__PARAM (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint G_GNUC_UNUSED,
gpointer marshal_data)
{{
typedef void (*GMarshalFunc_VOID__PARAM) (gpointer data1,
gpointer arg1,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__PARAM callback;
g_return_if_fail (n_param_values == 2);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
}}
else
{{
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__PARAM) (marshal_data ? marshal_data : cc->callback);
callback (data1,
g_marshal_value_peek_param (param_values + 1),
data2);
}}
void
g_cclosure_user_marshal_VOID__PARAMv (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types)
{{
typedef void (*GMarshalFunc_VOID__PARAM) (gpointer data1,
gpointer arg1,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__PARAM callback;
gpointer arg0;
va_list args_copy;
G_VA_COPY (args_copy, args);
arg0 = (gpointer) va_arg (args_copy, gpointer);
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
arg0 = g_param_spec_ref (arg0);
va_end (args_copy);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = instance;
}}
else
{{
data1 = instance;
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__PARAM) (marshal_data ? marshal_data : cc->callback);
callback (data1,
arg0,
data2);
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
g_param_spec_unref (arg0);
}}
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
if __name__ == "__main__":
unittest.main(testRunner=taptestrunner.TAPTestRunner())

View file

@ -0,0 +1,645 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2001, 2003 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include <glib-object.h>
#include "testcommon.h"
/* This test tests interface properties, implementing interface
* properties and #GParamSpecOverride.
*
* Four properties are tested:
*
* prop1: Defined in TestIface, Implemented in BaseObject with a GParamSpecOverride
* prop2: Defined in TestIface, Implemented in BaseObject with a new property
* prop3: Defined in TestIface, Implemented in BaseObject, Overridden in DerivedObject
* prop4: Defined in BaseObject, Overridden in DerivedObject
*/
static GType base_object_get_type (void);
static GType derived_object_get_type (void);
enum {
BASE_PROP_0,
BASE_PROP1,
BASE_PROP2,
BASE_PROP3,
BASE_PROP4
};
enum {
DERIVED_PROP_0,
DERIVED_PROP3,
DERIVED_PROP4
};
/*
* BaseObject, a parent class for DerivedObject
*/
#define BASE_TYPE_OBJECT (base_object_get_type ())
#define BASE_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BASE_TYPE_OBJECT, BaseObject))
typedef struct _BaseObject BaseObject;
typedef struct _BaseObjectClass BaseObjectClass;
struct _BaseObject
{
GObject parent_instance;
gint val1;
gint val2;
gint val3;
gint val4;
};
struct _BaseObjectClass
{
GObjectClass parent_class;
};
GObjectClass *base_parent_class;
/*
* DerivedObject, the child class of DerivedObject
*/
#define DERIVED_TYPE_OBJECT (derived_object_get_type ())
typedef struct _DerivedObject DerivedObject;
typedef struct _DerivedObjectClass DerivedObjectClass;
struct _DerivedObject
{
BaseObject parent_instance;
};
struct _DerivedObjectClass
{
BaseObjectClass parent_class;
};
/*
* The interface
*/
typedef struct _TestIfaceClass TestIfaceClass;
struct _TestIfaceClass
{
GTypeInterface base_iface;
};
#define TEST_TYPE_IFACE (test_iface_get_type ())
/* The paramspecs installed on our interface
*/
static GParamSpec *iface_spec1, *iface_spec2, *iface_spec3;
/* The paramspecs inherited by our derived object
*/
static GParamSpec *inherited_spec1, *inherited_spec2, *inherited_spec3, *inherited_spec4;
static void
test_iface_default_init (TestIfaceClass *iface_vtable)
{
inherited_spec1 = iface_spec1 = g_param_spec_int ("prop1",
"Prop1",
"Property 1",
G_MININT, /* min */
0xFFFF, /* max */
42, /* default */
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
g_object_interface_install_property (iface_vtable, iface_spec1);
iface_spec2 = g_param_spec_int ("prop2",
"Prop2",
"Property 2",
G_MININT, /* min */
G_MAXINT, /* max */
0, /* default */
G_PARAM_WRITABLE);
g_object_interface_install_property (iface_vtable, iface_spec2);
inherited_spec3 = iface_spec3 = g_param_spec_int ("prop3",
"Prop3",
"Property 3",
G_MININT, /* min */
G_MAXINT, /* max */
0, /* default */
G_PARAM_READWRITE);
g_object_interface_install_property (iface_vtable, iface_spec3);
}
static DEFINE_IFACE (TestIface, test_iface, NULL, test_iface_default_init)
static GObject*
base_object_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
/* The constructor is the one place where a GParamSpecOverride is visible
* to the outside world, so we do a bunch of checks here
*/
GValue value1 = G_VALUE_INIT;
GValue value2 = G_VALUE_INIT;
GParamSpec *pspec;
g_assert (n_construct_properties == 1);
pspec = construct_properties->pspec;
/* Check we got the param spec we expected
*/
g_assert (G_IS_PARAM_SPEC_OVERRIDE (pspec));
g_assert (pspec->param_id == BASE_PROP1);
g_assert (strcmp (g_param_spec_get_name (pspec), "prop1") == 0);
g_assert (g_param_spec_get_redirect_target (pspec) == iface_spec1);
/* Test redirection of the nick and blurb to the redirect target
*/
g_assert (strcmp (g_param_spec_get_nick (pspec), "Prop1") == 0);
g_assert (strcmp (g_param_spec_get_blurb (pspec), "Property 1") == 0);
/* Test forwarding of the various GParamSpec methods to the redirect target
*/
g_value_init (&value1, G_TYPE_INT);
g_value_init (&value2, G_TYPE_INT);
g_param_value_set_default (pspec, &value1);
g_assert (g_value_get_int (&value1) == 42);
g_value_reset (&value1);
g_value_set_int (&value1, 0x10000);
g_assert (g_param_value_validate (pspec, &value1));
g_assert (g_value_get_int (&value1) == 0xFFFF);
g_assert (!g_param_value_validate (pspec, &value1));
g_value_reset (&value1);
g_value_set_int (&value1, 1);
g_value_set_int (&value2, 2);
g_assert (g_param_values_cmp (pspec, &value1, &value2) < 0);
g_assert (g_param_values_cmp (pspec, &value2, &value1) > 0);
g_value_unset (&value1);
g_value_unset (&value2);
return base_parent_class->constructor (type,
n_construct_properties,
construct_properties);
}
static void
base_object_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BaseObject *base_object = BASE_OBJECT (object);
switch (prop_id)
{
case BASE_PROP1:
g_assert (pspec == inherited_spec1);
base_object->val1 = g_value_get_int (value);
break;
case BASE_PROP2:
g_assert (pspec == inherited_spec2);
base_object->val2 = g_value_get_int (value);
break;
case BASE_PROP3:
g_assert_not_reached ();
break;
case BASE_PROP4:
g_assert_not_reached ();
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
base_object_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BaseObject *base_object = BASE_OBJECT (object);
switch (prop_id)
{
case BASE_PROP1:
g_assert (pspec == inherited_spec1);
g_value_set_int (value, base_object->val1);
break;
case BASE_PROP2:
g_assert (pspec == inherited_spec2);
g_value_set_int (value, base_object->val2);
break;
case BASE_PROP3:
g_assert_not_reached ();
break;
case BASE_PROP4:
g_assert_not_reached ();
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
base_object_notify (GObject *object,
GParamSpec *pspec)
{
/* The property passed to notify is the redirect target, not the
* GParamSpecOverride
*/
g_assert (pspec == inherited_spec1 ||
pspec == inherited_spec2 ||
pspec == inherited_spec3 ||
pspec == inherited_spec4);
}
static void
base_object_class_init (BaseObjectClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
base_parent_class= g_type_class_peek_parent (class);
object_class->constructor = base_object_constructor;
object_class->set_property = base_object_set_property;
object_class->get_property = base_object_get_property;
object_class->notify = base_object_notify;
g_object_class_override_property (object_class, BASE_PROP1, "prop1");
/* We override this one using a real property, not GParamSpecOverride
* We change the flags from READONLY to READWRITE to show that we
* can make the flags less restrictive
*/
inherited_spec2 = g_param_spec_int ("prop2",
"Prop2",
"Property 2",
G_MININT, /* min */
G_MAXINT, /* max */
0, /* default */
G_PARAM_READWRITE);
g_object_class_install_property (object_class, BASE_PROP2, inherited_spec2);
g_object_class_override_property (object_class, BASE_PROP3, "prop3");
inherited_spec4 = g_param_spec_int ("prop4",
"Prop4",
"Property 4",
G_MININT, /* min */
G_MAXINT, /* max */
0, /* default */
G_PARAM_READWRITE);
g_object_class_install_property (object_class, BASE_PROP4, inherited_spec4);
}
static void
base_object_init (BaseObject *base_object)
{
base_object->val1 = 42;
}
static DEFINE_TYPE_FULL (BaseObject, base_object,
base_object_class_init, NULL, base_object_init,
G_TYPE_OBJECT,
INTERFACE (NULL, TEST_TYPE_IFACE))
static void
derived_object_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BaseObject *base_object = BASE_OBJECT (object);
switch (prop_id)
{
case DERIVED_PROP3:
g_assert (pspec == inherited_spec3);
base_object->val3 = g_value_get_int (value);
break;
case DERIVED_PROP4:
g_assert (pspec == inherited_spec4);
base_object->val4 = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
derived_object_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BaseObject *base_object = BASE_OBJECT (object);
switch (prop_id)
{
case DERIVED_PROP3:
g_assert (pspec == inherited_spec3);
g_value_set_int (value, base_object->val3);
break;
case DERIVED_PROP4:
g_assert (pspec == inherited_spec4);
g_value_set_int (value, base_object->val4);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
derived_object_class_init (DerivedObjectClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->set_property = derived_object_set_property;
object_class->get_property = derived_object_get_property;
/* Overriding a property that is itself overriding an interface property */
g_object_class_override_property (object_class, DERIVED_PROP3, "prop3");
/* Overriding a property not from an interface */
g_object_class_override_property (object_class, DERIVED_PROP4, "prop4");
}
static DEFINE_TYPE (DerivedObject, derived_object,
derived_object_class_init, NULL, NULL,
BASE_TYPE_OBJECT)
/* Helper function for testing ...list_properties() */
static void
assert_in_properties (GParamSpec *param_spec,
GParamSpec **properties,
gint n_properties)
{
gint i;
gboolean found = FALSE;
for (i = 0; i < n_properties; i++)
{
if (properties[i] == param_spec)
found = TRUE;
}
g_assert (found);
}
/* Test setting and getting the properties */
static void
test_set (void)
{
BaseObject *object;
gint val1, val2, val3, val4;
object = g_object_new (DERIVED_TYPE_OBJECT, NULL);
g_object_set (object,
"prop1", 0x0101,
"prop2", 0x0202,
"prop3", 0x0303,
"prop4", 0x0404,
NULL);
g_object_get (object,
"prop1", &val1,
"prop2", &val2,
"prop3", &val3,
"prop4", &val4,
NULL);
g_assert (val1 == 0x0101);
g_assert (val2 == 0x0202);
g_assert (val3 == 0x0303);
g_assert (val4 == 0x0404);
g_object_unref (object);
}
/* Test that the right spec is passed on explicit notifications */
static void
test_notify (void)
{
BaseObject *object;
object = g_object_new (DERIVED_TYPE_OBJECT, NULL);
g_object_freeze_notify (G_OBJECT (object));
g_object_notify (G_OBJECT (object), "prop1");
g_object_notify (G_OBJECT (object), "prop2");
g_object_notify (G_OBJECT (object), "prop3");
g_object_notify (G_OBJECT (object), "prop4");
g_object_thaw_notify (G_OBJECT (object));
g_object_unref (object);
}
/* Test g_object_class_find_property() for overridden properties */
static void
test_find_overridden (void)
{
GObjectClass *object_class;
object_class = g_type_class_peek (DERIVED_TYPE_OBJECT);
g_assert (g_object_class_find_property (object_class, "prop1") == inherited_spec1);
g_assert (g_object_class_find_property (object_class, "prop2") == inherited_spec2);
g_assert (g_object_class_find_property (object_class, "prop3") == inherited_spec3);
g_assert (g_object_class_find_property (object_class, "prop4") == inherited_spec4);
}
/* Test g_object_class_list_properties() for overridden properties */
static void
test_list_overridden (void)
{
GObjectClass *object_class;
GParamSpec **properties;
guint n_properties;
object_class = g_type_class_peek (DERIVED_TYPE_OBJECT);
properties = g_object_class_list_properties (object_class, &n_properties);
g_assert (n_properties == 4);
assert_in_properties (inherited_spec1, properties, n_properties);
assert_in_properties (inherited_spec2, properties, n_properties);
assert_in_properties (inherited_spec3, properties, n_properties);
assert_in_properties (inherited_spec4, properties, n_properties);
g_free (properties);
}
/* Test g_object_interface_find_property() */
static void
test_find_interface (void)
{
TestIfaceClass *iface;
iface = g_type_default_interface_peek (TEST_TYPE_IFACE);
g_assert (g_object_interface_find_property (iface, "prop1") == iface_spec1);
g_assert (g_object_interface_find_property (iface, "prop2") == iface_spec2);
g_assert (g_object_interface_find_property (iface, "prop3") == iface_spec3);
}
/* Test g_object_interface_list_properties() */
static void
test_list_interface (void)
{
TestIfaceClass *iface;
GParamSpec **properties;
guint n_properties;
iface = g_type_default_interface_peek (TEST_TYPE_IFACE);
properties = g_object_interface_list_properties (iface, &n_properties);
g_assert (n_properties == 3);
assert_in_properties (iface_spec1, properties, n_properties);
assert_in_properties (iface_spec2, properties, n_properties);
assert_in_properties (iface_spec3, properties, n_properties);
g_free (properties);
}
/* Base2Object, which implements the interface but fails
* to override some of its properties
*/
#define BASE2_TYPE_OBJECT (base2_object_get_type ())
#define BASE2_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BASE2_TYPE_OBJECT, Base2Object))
typedef struct _Base2Object Base2Object;
typedef struct _Base2ObjectClass Base2ObjectClass;
static void
base2_object_test_iface_init (TestIfaceClass *iface)
{
}
enum {
BASE2_PROP_0,
BASE2_PROP1,
BASE2_PROP2
};
struct _Base2Object
{
GObject parent_instance;
};
struct _Base2ObjectClass
{
GObjectClass parent_class;
};
static GType base2_object_get_type (void);
G_DEFINE_TYPE_WITH_CODE (Base2Object, base2_object, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE,
base2_object_test_iface_init))
static void
base2_object_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
case BASE2_PROP1:
g_value_set_int (value, 0);
break;
case BASE2_PROP2:
g_value_set_int (value, 0);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
base2_object_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
case BASE2_PROP1:
break;
case BASE2_PROP2:
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
base2_object_class_init (Base2ObjectClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->set_property = base2_object_set_property;
object_class->get_property = base2_object_get_property;
g_object_class_override_property (object_class, BASE2_PROP1, "prop1");
g_object_class_override_property (object_class, BASE2_PROP2, "prop2");
}
static void
base2_object_init (Base2Object *object)
{
}
static void
test_not_overridden (void)
{
Base2Object *object;
if (!g_test_undefined ())
return;
g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=637738");
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*Base2Object doesn't implement property 'prop3' from interface 'TestIface'*");
object = g_object_new (BASE2_TYPE_OBJECT, NULL);
g_test_assert_expected_messages ();
g_object_unref (object);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/interface/properties/set", test_set);
g_test_add_func ("/interface/properties/notify", test_notify);
g_test_add_func ("/interface/properties/find-overridden", test_find_overridden);
g_test_add_func ("/interface/properties/list-overridden", test_list_overridden);
g_test_add_func ("/interface/properties/find-interface", test_find_interface);
g_test_add_func ("/interface/properties/list-interface", test_list_interface);
g_test_add_func ("/interface/properties/not-overridden", test_not_overridden);
return g_test_run ();
}

View file

@ -0,0 +1,3 @@
VOID:INT,BOOLEAN,CHAR,UCHAR,UINT,LONG,ULONG,ENUM,FLAGS,FLOAT,DOUBLE,STRING,PARAM,BOXED,POINTER,OBJECT,VARIANT,INT64,UINT64
INT:VOID
UINT:VOID

161
gobject/tests/meson.build Normal file
View file

@ -0,0 +1,161 @@
marshalers_h = custom_target('marshalers_h',
output : 'marshalers.h',
input : 'marshalers.list',
command : [
python, glib_genmarshal,
'--prefix=test',
'--valist-marshallers',
'--output=@OUTPUT@',
'--quiet',
'--header',
'@INPUT@',
],
)
marshalers_c = custom_target('marshalers_c',
output : 'marshalers.c',
input : 'marshalers.list',
command : [
python, glib_genmarshal,
'--prefix=test',
'--valist-marshallers',
'--include-header=marshalers.h',
'--output=@OUTPUT@',
'--quiet',
'--body',
'@INPUT@',
],
)
gobject_tests = {
'qdata' : {},
'boxed' : {},
'enums' : {},
'param' : {},
'threadtests' : {},
'dynamictests' : {},
'binding' : {},
'bindinggroup' : {},
'properties' : {},
'reference' : {},
'flags' : {},
'value' : {},
'type' : {},
'gobject-private' : {
'source' : 'private.c',
},
'closure' : {},
'closure-refcount' : { 'suite': ['slow'] },
'object' : {},
'signal-handler' : {},
'ifaceproperties' : {},
'signals' : {
'source' : ['signals.c', marshalers_h, marshalers_c],
},
'signalgroup' : {},
'testing' : {},
'type-flags' : {},
}
if have_cxx
gobject_tests += {
'cxx' : {
'source' : ['cxx.cpp'],
},
}
endif
if cc.get_id() != 'msvc'
gobject_tests += {'autoptr' : {}}
endif
python_tests = [
'genmarshal.py',
'mkenums.py',
]
# FIXME: put common bits of test environment() in one location
# Not entirely random of course, but at least it changes over time
random_number = minor_version + meson.version().split('.').get(1).to_int()
test_env = environment()
test_env.set('G_TEST_SRCDIR', meson.current_source_dir())
test_env.set('G_TEST_BUILDDIR', meson.current_build_dir())
test_env.set('G_DEBUG', 'gc-friendly')
test_env.set('MALLOC_CHECK_', '2')
test_env.set('MALLOC_PERTURB_', '@0@'.format(random_number % 256))
test_deps = [libm, thread_dep, libglib_dep, libgobject_dep]
test_cargs = ['-DG_LOG_DOMAIN="GLib-GObject"', '-UG_DISABLE_ASSERT']
foreach test_name, extra_args : gobject_tests
source = extra_args.get('source', test_name + '.c')
install = installed_tests_enabled and extra_args.get('install', true)
if install
test_conf = configuration_data()
test_conf.set('installed_tests_dir', installed_tests_execdir)
test_conf.set('program', test_name)
test_conf.set('env', '')
configure_file(
input: installed_tests_template_tap,
output: test_name + '.test',
install_dir: installed_tests_metadir,
configuration: test_conf
)
endif
exe = executable(test_name, source,
c_args : test_cargs + extra_args.get('c_args', []),
dependencies : test_deps + extra_args.get('dependencies', []),
install_dir: installed_tests_execdir,
install: install,
)
suite = ['gobject'] + extra_args.get('suite', [])
timeout = suite.contains('slow') ? test_timeout_slow : test_timeout
# FIXME: https://gitlab.gnome.org/GNOME/glib/issues/1316
# aka https://bugs.debian.org/880883
if test_name == 'closure-refcount' and ['arm', 'aarch64'].contains(host_machine.cpu_family())
timeout = timeout * 10
endif
test(test_name, exe, env : test_env, timeout : timeout, suite : suite)
endforeach
foreach test_name : python_tests
test(
test_name,
python,
args: ['-B', files(test_name)],
env: test_env,
suite: ['gobject', 'no-valgrind'],
)
if installed_tests_enabled
install_data(
files(test_name),
install_dir: installed_tests_execdir,
install_mode: 'rwxr-xr-x',
)
test_conf = configuration_data()
test_conf.set('installed_tests_dir', installed_tests_execdir)
test_conf.set('program', test_name)
test_conf.set('env', '')
configure_file(
input: installed_tests_template_tap,
output: test_name + '.test',
install_dir: installed_tests_metadir,
configuration: test_conf,
)
endif
endforeach
# TAP test runner for Python tests
if installed_tests_enabled
install_data(
files('taptestrunner.py'),
install_dir: installed_tests_execdir,
)
endif

739
gobject/tests/mkenums.py Normal file
View file

@ -0,0 +1,739 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# Copyright © 2018 Endless Mobile, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
"""Integration tests for glib-mkenums utility."""
import collections
import os
import shutil
import subprocess
import sys
import tempfile
import textwrap
import unittest
import taptestrunner
Result = collections.namedtuple("Result", ("info", "out", "err", "subs"))
class TestMkenums(unittest.TestCase):
"""Integration test for running glib-mkenums.
This can be run when installed or uninstalled. When uninstalled, it
requires G_TEST_BUILDDIR and G_TEST_SRCDIR to be set.
The idea with this test harness is to test the glib-mkenums utility, its
handling of command line arguments, its exit statuses, and its handling of
various C source codes. In future we could split the core glib-mkenums
parsing and generation code out into a library and unit test that, and
convert this test to just check command line behaviour.
"""
# Track the cwd, we want to back out to that to clean up our tempdir
cwd = ""
rspfile = False
def setUp(self):
self.timeout_seconds = 10 # seconds per test
self.tmpdir = tempfile.TemporaryDirectory()
self.cwd = os.getcwd()
os.chdir(self.tmpdir.name)
print("tmpdir:", self.tmpdir.name)
if "G_TEST_BUILDDIR" in os.environ:
self.__mkenums = os.path.join(
os.environ["G_TEST_BUILDDIR"], "..", "glib-mkenums"
)
else:
self.__mkenums = shutil.which("glib-mkenums")
print("rspfile: {}, mkenums:".format(self.rspfile), self.__mkenums)
def tearDown(self):
os.chdir(self.cwd)
self.tmpdir.cleanup()
def _write_rspfile(self, argv):
import shlex
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, mode="w", delete=False
) as f:
contents = " ".join([shlex.quote(arg) for arg in argv])
print("Response file contains:", contents)
f.write(contents)
f.flush()
return f.name
def runMkenums(self, *args):
if self.rspfile:
rspfile = self._write_rspfile(args)
args = ["@" + rspfile]
argv = [self.__mkenums]
# shebang lines are not supported on native
# Windows consoles
if os.name == "nt":
argv.insert(0, sys.executable)
argv.extend(args)
print("Running:", argv)
env = os.environ.copy()
env["LC_ALL"] = "C.UTF-8"
print("Environment:", env)
# We want to ensure consistent line endings...
info = subprocess.run(
argv,
timeout=self.timeout_seconds,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
universal_newlines=True,
)
info.check_returncode()
out = info.stdout.strip()
err = info.stderr.strip()
# Known substitutions for standard boilerplate
subs = {
"standard_top_comment": "This file is generated by glib-mkenums, do not modify "
"it. This code is licensed under the same license as the "
"containing project. Note that it links to GLib, so must "
"comply with the LGPL linking clauses.",
"standard_bottom_comment": "Generated data ends here",
}
result = Result(info, out, err, subs)
print("Output:", result.out)
return result
def runMkenumsWithTemplate(self, template_contents, *args):
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix=".template", delete=False
) as template_file:
# Write out the template.
template_file.write(template_contents.encode("utf-8"))
print(template_file.name + ":", template_contents)
template_file.flush()
return self.runMkenums("--template", template_file.name, *args)
def runMkenumsWithAllSubstitutions(self, *args):
"""Run glib-mkenums with a template which outputs all substitutions."""
template_contents = """
/*** BEGIN file-header ***/
file-header
/*** END file-header ***/
/*** BEGIN file-production ***/
file-production
filename: @filename@
basename: @basename@
/*** END file-production ***/
/*** BEGIN enumeration-production ***/
enumeration-production
EnumName: @EnumName@
enum_name: @enum_name@
ENUMNAME: @ENUMNAME@
ENUMSHORT: @ENUMSHORT@
ENUMPREFIX: @ENUMPREFIX@
enumsince: @enumsince@
type: @type@
Type: @Type@
TYPE: @TYPE@
/*** END enumeration-production ***/
/*** BEGIN value-header ***/
value-header
EnumName: @EnumName@
enum_name: @enum_name@
ENUMNAME: @ENUMNAME@
ENUMSHORT: @ENUMSHORT@
ENUMPREFIX: @ENUMPREFIX@
enumsince: @enumsince@
type: @type@
Type: @Type@
TYPE: @TYPE@
/*** END value-header ***/
/*** BEGIN value-production ***/
value-production
VALUENAME: @VALUENAME@
valuenick: @valuenick@
valuenum: @valuenum@
type: @type@
Type: @Type@
TYPE: @TYPE@
/*** END value-production ***/
/*** BEGIN value-tail ***/
value-tail
EnumName: @EnumName@
enum_name: @enum_name@
ENUMNAME: @ENUMNAME@
ENUMSHORT: @ENUMSHORT@
ENUMPREFIX: @ENUMPREFIX@
enumsince: @enumsince@
type: @type@
Type: @Type@
TYPE: @TYPE@
/*** END value-tail ***/
/*** BEGIN comment ***/
comment
comment: @comment@
/*** END comment ***/
/*** BEGIN file-tail ***/
file-tail
/*** END file-tail ***/
"""
return self.runMkenumsWithTemplate(template_contents, *args)
def runMkenumsWithHeader(self, h_contents, encoding="utf-8"):
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix=".h", delete=False
) as h_file:
# Write out the header to be scanned.
h_file.write(h_contents.encode(encoding))
print(h_file.name + ":", h_contents)
h_file.flush()
# Run glib-mkenums with a template which outputs all substitutions.
result = self.runMkenumsWithAllSubstitutions(h_file.name)
# Known substitutions for generated filenames.
result.subs.update(
{"filename": h_file.name, "basename": os.path.basename(h_file.name)}
)
return result
def assertSingleEnum(
self,
result,
enum_name_camel,
enum_name_lower,
enum_name_upper,
enum_name_short,
enum_prefix,
enum_since,
type_lower,
type_camel,
type_upper,
value_name,
value_nick,
value_num,
):
"""Assert that out (from runMkenumsWithHeader()) contains a single
enum and value matching the given arguments."""
subs = dict(
{
"enum_name_camel": enum_name_camel,
"enum_name_lower": enum_name_lower,
"enum_name_upper": enum_name_upper,
"enum_name_short": enum_name_short,
"enum_prefix": enum_prefix,
"enum_since": enum_since,
"type_lower": type_lower,
"type_camel": type_camel,
"type_upper": type_upper,
"value_name": value_name,
"value_nick": value_nick,
"value_num": value_num,
},
**result.subs
)
self.assertEqual(
"""
comment
comment: {standard_top_comment}
file-header
file-production
filename: {filename}
basename: {basename}
enumeration-production
EnumName: {enum_name_camel}
enum_name: {enum_name_lower}
ENUMNAME: {enum_name_upper}
ENUMSHORT: {enum_name_short}
ENUMPREFIX: {enum_prefix}
enumsince: {enum_since}
type: {type_lower}
Type: {type_camel}
TYPE: {type_upper}
value-header
EnumName: {enum_name_camel}
enum_name: {enum_name_lower}
ENUMNAME: {enum_name_upper}
ENUMSHORT: {enum_name_short}
ENUMPREFIX: {enum_prefix}
enumsince: {enum_since}
type: {type_lower}
Type: {type_camel}
TYPE: {type_upper}
value-production
VALUENAME: {value_name}
valuenick: {value_nick}
valuenum: {value_num}
type: {type_lower}
Type: {type_camel}
TYPE: {type_upper}
value-tail
EnumName: {enum_name_camel}
enum_name: {enum_name_lower}
ENUMNAME: {enum_name_upper}
ENUMSHORT: {enum_name_short}
ENUMPREFIX: {enum_prefix}
enumsince: {enum_since}
type: {type_lower}
Type: {type_camel}
TYPE: {type_upper}
file-tail
comment
comment: {standard_bottom_comment}
""".format(
**subs
).strip(),
result.out,
)
def test_help(self):
"""Test the --help argument."""
result = self.runMkenums("--help")
self.assertIn("usage: glib-mkenums", result.out)
def test_no_args(self):
"""Test running with no arguments at all."""
result = self.runMkenums()
self.assertEqual("", result.err)
self.assertEqual(
"""/* {standard_top_comment} */
/* {standard_bottom_comment} */""".format(
**result.subs
),
result.out.strip(),
)
def test_empty_template(self):
"""Test running with an empty template and no header files."""
result = self.runMkenumsWithTemplate("")
self.assertEqual("", result.err)
self.assertEqual(
"""/* {standard_top_comment} */
/* {standard_bottom_comment} */""".format(
**result.subs
),
result.out.strip(),
)
def test_no_headers(self):
"""Test running with a complete template, but no header files."""
result = self.runMkenumsWithAllSubstitutions()
self.assertEqual("", result.err)
self.assertEqual(
"""
comment
comment: {standard_top_comment}
file-header
file-tail
comment
comment: {standard_bottom_comment}
""".format(
**result.subs
).strip(),
result.out,
)
def test_empty_header(self):
"""Test an empty header."""
result = self.runMkenumsWithHeader("")
self.assertEqual("", result.err)
self.assertEqual(
"""
comment
comment: {standard_top_comment}
file-header
file-tail
comment
comment: {standard_bottom_comment}
""".format(
**result.subs
).strip(),
result.out,
)
def test_enum_name(self):
"""Test typedefs with an enum and a typedef name. Bug #794506."""
h_contents = """
typedef enum _SomeEnumIdentifier {
ENUM_VALUE
} SomeEnumIdentifier;
"""
result = self.runMkenumsWithHeader(h_contents)
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"SomeEnumIdentifier",
"some_enum_identifier",
"SOME_ENUM_IDENTIFIER",
"ENUM_IDENTIFIER",
"SOME",
"",
"enum",
"Enum",
"ENUM",
"ENUM_VALUE",
"value",
"0",
)
def test_non_utf8_encoding(self):
"""Test source files with non-UTF-8 encoding. Bug #785113."""
h_contents = """
/* Copyright © La Peña */
typedef enum {
ENUM_VALUE
} SomeEnumIdentifier;
"""
result = self.runMkenumsWithHeader(h_contents, encoding="iso-8859-1")
self.assertIn("WARNING: UnicodeWarning: ", result.err)
self.assertSingleEnum(
result,
"SomeEnumIdentifier",
"some_enum_identifier",
"SOME_ENUM_IDENTIFIER",
"ENUM_IDENTIFIER",
"SOME",
"",
"enum",
"Enum",
"ENUM",
"ENUM_VALUE",
"value",
"0",
)
def test_reproducible(self):
"""Test builds are reproducible regardless of file ordering.
Bug #691436."""
template_contents = "template"
h_contents1 = """
typedef enum {
FIRST,
} Header1;
"""
h_contents2 = """
typedef enum {
SECOND,
} Header2;
"""
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix="1.h", delete=False
) as h_file1, tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix="2.h", delete=False
) as h_file2:
# Write out the headers.
h_file1.write(h_contents1.encode("utf-8"))
h_file2.write(h_contents2.encode("utf-8"))
h_file1.flush()
h_file2.flush()
# Run glib-mkenums with the headers in one order, and then again
# in another order.
result1 = self.runMkenumsWithTemplate(
template_contents, h_file1.name, h_file2.name
)
self.assertEqual("", result1.err)
result2 = self.runMkenumsWithTemplate(
template_contents, h_file2.name, h_file1.name
)
self.assertEqual("", result2.err)
# The output should be the same.
self.assertEqual(result1.out, result2.out)
def test_no_nick(self):
"""Test trigraphs with a desc but no nick. Issue #1360."""
h_contents = """
typedef enum {
GEGL_SAMPLER_NEAREST = 0, /*< desc="nearest" >*/
} GeglSamplerType;
"""
result = self.runMkenumsWithHeader(h_contents)
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"GeglSamplerType",
"gegl_sampler_type",
"GEGL_SAMPLER_TYPE",
"SAMPLER_TYPE",
"GEGL",
"",
"enum",
"Enum",
"ENUM",
"GEGL_SAMPLER_NEAREST",
"nearest",
"0",
)
def test_filename_basename_in_fhead_ftail(self):
template_contents = """
/*** BEGIN file-header ***/
file-header
filename: @filename@
basename: @basename@
/*** END file-header ***/
/*** BEGIN comment ***/
comment
comment: @comment@
/*** END comment ***/
/*** BEGIN file-tail ***/
file-tail
filename: @filename@
basename: @basename@
/*** END file-tail ***/"""
result = self.runMkenumsWithTemplate(template_contents)
self.assertEqual(
textwrap.dedent(
"""
WARNING: @filename@ used in file-header section.
WARNING: @basename@ used in file-header section.
WARNING: @filename@ used in file-tail section.
WARNING: @basename@ used in file-tail section.
"""
).strip(),
result.err,
)
self.assertEqual(
"""
comment
comment: {standard_top_comment}
file-header
filename: @filename@
basename: @basename@
file-tail
filename: @filename@
basename: @basename@
comment
comment: {standard_bottom_comment}
""".format(
**result.subs
).strip(),
result.out,
)
def test_since(self):
"""Test user-provided 'since' version handling
https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1492"""
h_contents = """
typedef enum { /*< since=1.0 >*/
QMI_WMS_MESSAGE_PROTOCOL_CDMA = 0,
} QmiWmsMessageProtocol;
"""
result = self.runMkenumsWithHeader(h_contents)
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"QmiWmsMessageProtocol",
"qmi_wms_message_protocol",
"QMI_WMS_MESSAGE_PROTOCOL",
"WMS_MESSAGE_PROTOCOL",
"QMI",
"1.0",
"enum",
"Enum",
"ENUM",
"QMI_WMS_MESSAGE_PROTOCOL_CDMA",
"cdma",
"0",
)
def test_enum_private_public(self):
"""Test private/public enums. Bug #782162."""
h_contents1 = """
typedef enum {
ENUM_VALUE_PUBLIC1,
/*< private >*/
ENUM_VALUE_PRIVATE,
} SomeEnumA
"""
h_contents2 = """
typedef enum {
/*< private >*/
ENUM_VALUE_PRIVATE,
/*< public >*/
ENUM_VALUE_PUBLIC2,
} SomeEnumB;
"""
result = self.runMkenumsWithHeader(h_contents1)
self.maxDiff = None
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"SomeEnumA",
"some_enum_a",
"SOME_ENUM_A",
"ENUM_A",
"SOME",
"",
"enum",
"Enum",
"ENUM",
"ENUM_VALUE_PUBLIC1",
"public1",
"0",
)
result = self.runMkenumsWithHeader(h_contents2)
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"SomeEnumB",
"some_enum_b",
"SOME_ENUM_B",
"ENUM_B",
"SOME",
"",
"enum",
"Enum",
"ENUM",
"ENUM_VALUE_PUBLIC2",
"public2",
"0",
)
def test_available_in(self):
"""Test GLIB_AVAILABLE_ENUMERATOR_IN_2_68 handling
https://gitlab.gnome.org/GNOME/glib/-/issues/2327"""
h_contents = """
typedef enum {
G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER GLIB_AVAILABLE_ENUMERATOR_IN_2_68 = (1<<2)
} GDBusServerFlags;
"""
result = self.runMkenumsWithHeader(h_contents)
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"GDBusServerFlags",
"g_dbus_server_flags",
"G_DBUS_SERVER_FLAGS",
"DBUS_SERVER_FLAGS",
"G",
"",
"flags",
"Flags",
"FLAGS",
"G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER",
"user",
"4",
)
def test_deprecated_in(self):
"""Test GLIB_DEPRECATED_ENUMERATOR_IN_2_68 handling
https://gitlab.gnome.org/GNOME/glib/-/issues/2327"""
h_contents = """
typedef enum {
G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER GLIB_DEPRECATED_ENUMERATOR_IN_2_68 = (1<<2)
} GDBusServerFlags;
"""
result = self.runMkenumsWithHeader(h_contents)
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"GDBusServerFlags",
"g_dbus_server_flags",
"G_DBUS_SERVER_FLAGS",
"DBUS_SERVER_FLAGS",
"G",
"",
"flags",
"Flags",
"FLAGS",
"G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER",
"user",
"4",
)
def test_deprecated_in_for(self):
"""Test GLIB_DEPRECATED_ENUMERATOR_IN_2_68_FOR() handling
https://gitlab.gnome.org/GNOME/glib/-/issues/2327"""
h_contents = """
typedef enum {
G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER GLIB_DEPRECATED_ENUMERATOR_IN_2_68_FOR(G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER2) = (1<<2)
} GDBusServerFlags;
"""
result = self.runMkenumsWithHeader(h_contents)
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"GDBusServerFlags",
"g_dbus_server_flags",
"G_DBUS_SERVER_FLAGS",
"DBUS_SERVER_FLAGS",
"G",
"",
"flags",
"Flags",
"FLAGS",
"G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER",
"user",
"4",
)
class TestRspMkenums(TestMkenums):
"""Run all tests again in @rspfile mode"""
rspfile = True
if __name__ == "__main__":
unittest.main(testRunner=taptestrunner.TAPTestRunner())

150
gobject/tests/object.c Normal file
View file

@ -0,0 +1,150 @@
#include <glib-object.h>
/* --------------------------------- */
/* test_object_constructor_singleton */
typedef GObject MySingletonObject;
typedef GObjectClass MySingletonObjectClass;
GType my_singleton_object_get_type (void);
G_DEFINE_TYPE (MySingletonObject, my_singleton_object, G_TYPE_OBJECT)
static MySingletonObject *singleton;
static void
my_singleton_object_init (MySingletonObject *obj)
{
}
static GObject *
my_singleton_object_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_params)
{
GObject *object;
if (singleton)
return g_object_ref (singleton);
object = G_OBJECT_CLASS (my_singleton_object_parent_class)->
constructor (type, n_construct_properties, construct_params);
singleton = (MySingletonObject *)object;
return object;
}
static void
my_singleton_object_finalize (MySingletonObject *obj)
{
singleton = NULL;
G_OBJECT_CLASS (my_singleton_object_parent_class)->finalize (obj);
}
static void
my_singleton_object_class_init (MySingletonObjectClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructor = my_singleton_object_constructor;
object_class->finalize = my_singleton_object_finalize;
}
static void
test_object_constructor_singleton (void)
{
MySingletonObject *one, *two, *three;
one = g_object_new (my_singleton_object_get_type (), NULL);
g_assert_cmpint (G_OBJECT (one)->ref_count, ==, 1);
two = g_object_new (my_singleton_object_get_type (), NULL);
g_assert (one == two);
g_assert_cmpint (G_OBJECT (two)->ref_count, ==, 2);
three = g_object_new (my_singleton_object_get_type (), NULL);
g_assert (one == three);
g_assert_cmpint (G_OBJECT (three)->ref_count, ==, 3);
g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one);
g_object_unref (one);
g_assert (one != NULL);
g_object_unref (three);
g_object_unref (two);
g_assert (one == NULL);
}
/* ----------------------------------- */
/* test_object_constructor_infanticide */
typedef GObject MyInfanticideObject;
typedef GObjectClass MyInfanticideObjectClass;
GType my_infanticide_object_get_type (void);
G_DEFINE_TYPE (MyInfanticideObject, my_infanticide_object, G_TYPE_OBJECT)
static void
my_infanticide_object_init (MyInfanticideObject *obj)
{
}
static GObject *
my_infanticide_object_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_params)
{
GObject *object;
object = G_OBJECT_CLASS (my_infanticide_object_parent_class)->
constructor (type, n_construct_properties, construct_params);
g_object_unref (object);
return NULL;
}
static void
my_infanticide_object_class_init (MyInfanticideObjectClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructor = my_infanticide_object_constructor;
}
static void
test_object_constructor_infanticide (void)
{
GObject *obj;
int i;
g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=661576");
for (i = 0; i < 1000; i++)
{
g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_CRITICAL,
"*finalized while still in-construction*");
g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_CRITICAL,
"*Custom constructor*returned NULL*");
obj = g_object_new (my_infanticide_object_get_type (), NULL);
g_assert_null (obj);
g_test_assert_expected_messages ();
}
}
/* --------------------------------- */
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/object/constructor/singleton", test_object_constructor_singleton);
g_test_add_func ("/object/constructor/infanticide", test_object_constructor_infanticide);
return g_test_run ();
}

1266
gobject/tests/param.c Normal file

File diff suppressed because it is too large Load diff

261
gobject/tests/private.c Normal file
View file

@ -0,0 +1,261 @@
/* We are testing some deprecated APIs here */
#ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#endif
#include <glib-object.h>
typedef struct {
GObject parent_instance;
} TestObject;
typedef struct {
int dummy_0;
float dummy_1;
} TestObjectPrivate;
typedef struct {
GObjectClass parent_class;
} TestObjectClass;
GType test_object_get_type (void);
G_DEFINE_TYPE_WITH_CODE (TestObject, test_object, G_TYPE_OBJECT,
G_ADD_PRIVATE (TestObject))
static void
test_object_class_init (TestObjectClass *klass)
{
}
static void
test_object_init (TestObject *self)
{
TestObjectPrivate *priv = test_object_get_instance_private (self);
if (g_test_verbose ())
g_printerr ("Offset of %sPrivate for type '%s': %d\n",
G_OBJECT_TYPE_NAME (self),
G_OBJECT_TYPE_NAME (self),
TestObject_private_offset);
priv->dummy_0 = 42;
priv->dummy_1 = 3.14159f;
}
static int
test_object_get_dummy_0 (TestObject *self)
{
TestObjectPrivate *priv = test_object_get_instance_private (self);
return priv->dummy_0;
}
static float
test_object_get_dummy_1 (TestObject *self)
{
TestObjectPrivate *priv = test_object_get_instance_private (self);
return priv->dummy_1;
}
typedef struct {
TestObject parent_instance;
} TestDerived;
typedef struct {
char *dummy_2;
} TestDerivedPrivate;
typedef struct {
TestObjectClass parent_class;
} TestDerivedClass;
GType test_derived_get_type (void);
G_DEFINE_TYPE_WITH_CODE (TestDerived, test_derived, test_object_get_type (),
G_ADD_PRIVATE (TestDerived))
static void
test_derived_finalize (GObject *obj)
{
TestDerivedPrivate *priv = test_derived_get_instance_private ((TestDerived *) obj);
g_free (priv->dummy_2);
G_OBJECT_CLASS (test_derived_parent_class)->finalize (obj);
}
static void
test_derived_class_init (TestDerivedClass *klass)
{
G_OBJECT_CLASS (klass)->finalize = test_derived_finalize;
}
static void
test_derived_init (TestDerived *self)
{
TestDerivedPrivate *priv = test_derived_get_instance_private (self);
if (g_test_verbose ())
g_printerr ("Offset of %sPrivate for type '%s': %d\n",
G_OBJECT_TYPE_NAME (self),
G_OBJECT_TYPE_NAME (self),
TestDerived_private_offset);
priv->dummy_2 = g_strdup ("Hello");
}
static const char *
test_derived_get_dummy_2 (TestDerived *self)
{
TestDerivedPrivate *priv = test_derived_get_instance_private (self);
return priv->dummy_2;
}
typedef struct {
TestObject parent_instance;
} TestMixed;
typedef struct {
gint dummy_3;
} TestMixedPrivate;
typedef struct {
TestObjectClass parent_class;
} TestMixedClass;
GType test_mixed_get_type (void);
G_DEFINE_TYPE (TestMixed, test_mixed, test_object_get_type ())
static void
test_mixed_class_init (TestMixedClass *klass)
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
g_type_class_add_private (klass, sizeof (TestMixedPrivate));
G_GNUC_END_IGNORE_DEPRECATIONS
}
static void
test_mixed_init (TestMixed *self)
{
TestMixedPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, test_mixed_get_type (), TestMixedPrivate);
if (g_test_verbose ())
g_printerr ("Offset of %sPrivate for type '%s': %d\n",
G_OBJECT_TYPE_NAME (self),
G_OBJECT_TYPE_NAME (self),
TestMixed_private_offset);
priv->dummy_3 = 47;
}
static gint
test_mixed_get_dummy_3 (TestMixed *self)
{
TestMixedPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, test_mixed_get_type (), TestMixedPrivate);
return priv->dummy_3;
}
typedef struct {
TestMixed parent_instance;
} TestMixedDerived;
typedef struct {
gint64 dummy_4;
} TestMixedDerivedPrivate;
typedef struct {
TestMixedClass parent_class;
} TestMixedDerivedClass;
GType test_mixed_derived_get_type (void);
G_DEFINE_TYPE_WITH_CODE (TestMixedDerived, test_mixed_derived, test_mixed_get_type (),
G_ADD_PRIVATE (TestMixedDerived))
static void
test_mixed_derived_class_init (TestMixedDerivedClass *klass)
{
}
static void
test_mixed_derived_init (TestMixedDerived *self)
{
TestMixedDerivedPrivate *priv = test_mixed_derived_get_instance_private (self);
if (g_test_verbose ())
g_printerr ("Offset of %sPrivate for type '%s': %d\n",
G_OBJECT_TYPE_NAME (self),
G_OBJECT_TYPE_NAME (self),
TestMixedDerived_private_offset);
priv->dummy_4 = g_get_monotonic_time ();
}
static gint64
test_mixed_derived_get_dummy_4 (TestMixedDerived *self)
{
TestMixedDerivedPrivate *priv = test_mixed_derived_get_instance_private (self);
return priv->dummy_4;
}
static void
private_instance (void)
{
TestObject *obj = g_object_new (test_object_get_type (), NULL);
gpointer class;
gint offset;
g_assert_cmpint (test_object_get_dummy_0 (obj), ==, 42);
g_assert_cmpfloat (test_object_get_dummy_1 (obj), ==, 3.14159f);
class = g_type_class_ref (test_object_get_type ());
offset = g_type_class_get_instance_private_offset (class);
g_type_class_unref (class);
g_assert (offset == TestObject_private_offset);
g_object_unref (obj);
}
static void
private_derived_instance (void)
{
TestDerived *obj = g_object_new (test_derived_get_type (), NULL);
g_assert_cmpstr (test_derived_get_dummy_2 (obj), ==, "Hello");
g_assert_cmpint (test_object_get_dummy_0 ((TestObject *) obj), ==, 42);
g_object_unref (obj);
}
static void
private_mixed_derived_instance (void)
{
TestMixedDerived *derived = g_object_new (test_mixed_derived_get_type (), NULL);
TestMixed *mixed = g_object_new (test_mixed_get_type (), NULL);
g_assert_cmpint (test_mixed_get_dummy_3 (mixed), ==, 47);
g_assert (test_mixed_derived_get_dummy_4 (derived) <= g_get_monotonic_time ());
g_object_unref (derived);
g_object_unref (mixed);
}
int
main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/private/instance", private_instance);
g_test_add_func ("/private/derived-instance", private_derived_instance);
g_test_add_func ("/private/mixed-derived-instance", private_mixed_derived_instance);
return g_test_run ();
}

658
gobject/tests/properties.c Normal file
View file

@ -0,0 +1,658 @@
#include <stdlib.h>
#include <gstdio.h>
#include <glib-object.h>
typedef struct _TestObject {
GObject parent_instance;
gint foo;
gboolean bar;
gchar *baz;
gchar *quux;
} TestObject;
typedef struct _TestObjectClass {
GObjectClass parent_class;
} TestObjectClass;
enum { PROP_0, PROP_FOO, PROP_BAR, PROP_BAZ, PROP_QUUX, N_PROPERTIES };
static GParamSpec *properties[N_PROPERTIES] = { NULL, };
static GType test_object_get_type (void);
G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT)
static void
test_object_set_foo (TestObject *obj,
gint foo)
{
if (obj->foo != foo)
{
obj->foo = foo;
g_assert (properties[PROP_FOO] != NULL);
g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_FOO]);
}
}
static void
test_object_set_bar (TestObject *obj,
gboolean bar)
{
bar = !!bar;
if (obj->bar != bar)
{
obj->bar = bar;
g_assert (properties[PROP_BAR] != NULL);
g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAR]);
}
}
static void
test_object_set_baz (TestObject *obj,
const gchar *baz)
{
if (g_strcmp0 (obj->baz, baz) != 0)
{
g_free (obj->baz);
obj->baz = g_strdup (baz);
g_assert (properties[PROP_BAZ] != NULL);
g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAZ]);
}
}
static void
test_object_set_quux (TestObject *obj,
const gchar *quux)
{
if (g_strcmp0 (obj->quux, quux) != 0)
{
g_free (obj->quux);
obj->quux = g_strdup (quux);
g_assert (properties[PROP_QUUX] != NULL);
g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_QUUX]);
}
}
static void
test_object_finalize (GObject *gobject)
{
TestObject *self = (TestObject *) gobject;
g_free (self->baz);
g_free (self->quux);
/* When the ref_count of an object is zero it is still
* possible to notify the property, but it should do
* nothing and silently quit (bug #705570)
*/
g_object_notify (gobject, "foo");
g_object_notify_by_pspec (gobject, properties[PROP_BAR]);
G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject);
}
static void
test_object_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
TestObject *tobj = (TestObject *) gobject;
g_assert_cmpint (prop_id, !=, 0);
g_assert_true (prop_id < N_PROPERTIES && pspec == properties[prop_id]);
switch (prop_id)
{
case PROP_FOO:
test_object_set_foo (tobj, g_value_get_int (value));
break;
case PROP_BAR:
test_object_set_bar (tobj, g_value_get_boolean (value));
break;
case PROP_BAZ:
test_object_set_baz (tobj, g_value_get_string (value));
break;
case PROP_QUUX:
test_object_set_quux (tobj, g_value_get_string (value));
break;
default:
g_assert_not_reached ();
}
}
static void
test_object_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
TestObject *tobj = (TestObject *) gobject;
g_assert_cmpint (prop_id, !=, 0);
g_assert_true (prop_id < N_PROPERTIES && pspec == properties[prop_id]);
switch (prop_id)
{
case PROP_FOO:
g_value_set_int (value, tobj->foo);
break;
case PROP_BAR:
g_value_set_boolean (value, tobj->bar);
break;
case PROP_BAZ:
g_value_set_string (value, tobj->baz);
break;
case PROP_QUUX:
g_value_set_string (value, tobj->quux);
break;
default:
g_assert_not_reached ();
}
}
static void
test_object_class_init (TestObjectClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
properties[PROP_FOO] = g_param_spec_int ("foo", "Foo", "Foo",
-1, G_MAXINT,
0,
G_PARAM_READWRITE);
properties[PROP_BAR] = g_param_spec_boolean ("bar", "Bar", "Bar",
FALSE,
G_PARAM_READWRITE);
properties[PROP_BAZ] = g_param_spec_string ("baz", "Baz", "Baz",
NULL,
G_PARAM_READWRITE);
properties[PROP_QUUX] = g_param_spec_string ("quux", "quux", "quux",
NULL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
gobject_class->set_property = test_object_set_property;
gobject_class->get_property = test_object_get_property;
gobject_class->finalize = test_object_finalize;
g_object_class_install_properties (gobject_class, N_PROPERTIES, properties);
}
static void
test_object_init (TestObject *self)
{
self->foo = 42;
self->bar = TRUE;
self->baz = g_strdup ("Hello");
self->quux = NULL;
}
static void
properties_install (void)
{
TestObject *obj = g_object_new (test_object_get_type (), NULL);
GParamSpec *pspec;
g_assert (properties[PROP_FOO] != NULL);
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (obj), "foo");
g_assert (properties[PROP_FOO] == pspec);
g_object_unref (obj);
}
typedef struct {
const gchar *name;
GParamSpec *pspec;
gboolean fired;
} TestNotifyClosure;
static void
on_notify (GObject *gobject,
GParamSpec *pspec,
TestNotifyClosure *closure)
{
g_assert (closure->pspec == pspec);
g_assert_cmpstr (closure->name, ==, pspec->name);
closure->fired = TRUE;
}
static void
properties_notify (void)
{
TestObject *obj = g_object_new (test_object_get_type (), NULL);
TestNotifyClosure closure;
g_assert (properties[PROP_FOO] != NULL);
g_assert (properties[PROP_QUUX] != NULL);
g_signal_connect (obj, "notify", G_CALLBACK (on_notify), &closure);
closure.name = "foo";
closure.pspec = properties[PROP_FOO];
closure.fired = FALSE;
g_object_set (obj, "foo", 47, NULL);
g_assert (closure.fired);
closure.name = "baz";
closure.pspec = properties[PROP_BAZ];
closure.fired = FALSE;
g_object_set (obj, "baz", "something new", NULL);
g_assert (closure.fired);
/* baz lacks explicit notify, so we will see this twice */
closure.fired = FALSE;
g_object_set (obj, "baz", "something new", NULL);
g_assert (closure.fired);
/* quux on the other hand, ... */
closure.name = "quux";
closure.pspec = properties[PROP_QUUX];
closure.fired = FALSE;
g_object_set (obj, "quux", "something new", NULL);
g_assert (closure.fired);
/* no change; no notify */
closure.fired = FALSE;
g_object_set (obj, "quux", "something new", NULL);
g_assert (!closure.fired);
g_object_unref (obj);
}
typedef struct {
GParamSpec *pspec[3];
gint pos;
} Notifys;
static void
on_notify2 (GObject *gobject,
GParamSpec *pspec,
Notifys *n)
{
g_assert (n->pspec[n->pos] == pspec);
n->pos++;
}
static void
properties_notify_queue (void)
{
TestObject *obj = g_object_new (test_object_get_type (), NULL);
Notifys n;
g_assert (properties[PROP_FOO] != NULL);
n.pspec[0] = properties[PROP_BAZ];
n.pspec[1] = properties[PROP_BAR];
n.pspec[2] = properties[PROP_FOO];
n.pos = 0;
g_signal_connect (obj, "notify", G_CALLBACK (on_notify2), &n);
g_object_freeze_notify (G_OBJECT (obj));
g_object_set (obj, "foo", 47, NULL);
g_object_set (obj, "bar", TRUE, "foo", 42, "baz", "abc", NULL);
g_object_thaw_notify (G_OBJECT (obj));
g_assert (n.pos == 3);
g_object_unref (obj);
}
static void
properties_construct (void)
{
TestObject *obj;
gint val;
gboolean b;
gchar *s;
g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=630357");
/* more than 16 args triggers a realloc in g_object_new_valist() */
obj = g_object_new (test_object_get_type (),
"foo", 1,
"foo", 2,
"foo", 3,
"foo", 4,
"foo", 5,
"bar", FALSE,
"foo", 6,
"foo", 7,
"foo", 8,
"foo", 9,
"foo", 10,
"baz", "boo",
"foo", 11,
"foo", 12,
"foo", 13,
"foo", 14,
"foo", 15,
"foo", 16,
"foo", 17,
"foo", 18,
NULL);
g_object_get (obj, "foo", &val, NULL);
g_assert (val == 18);
g_object_get (obj, "bar", &b, NULL);
g_assert (!b);
g_object_get (obj, "baz", &s, NULL);
g_assert_cmpstr (s, ==, "boo");
g_free (s);
g_object_unref (obj);
}
static void
properties_testv_with_no_properties (void)
{
TestObject *test_obj;
const char *prop_names[4] = { "foo", "bar", "baz", "quux" };
GValue values_out[4] = { G_VALUE_INIT };
guint i;
/* Test newv_with_properties && getv */
test_obj = (TestObject *) g_object_new_with_properties (
test_object_get_type (), 0, NULL, NULL);
g_object_getv (G_OBJECT (test_obj), 4, prop_names, values_out);
/* It should have init values */
g_assert_cmpint (g_value_get_int (&values_out[0]), ==, 42);
g_assert_true (g_value_get_boolean (&values_out[1]));
g_assert_cmpstr (g_value_get_string (&values_out[2]), ==, "Hello");
g_assert_cmpstr (g_value_get_string (&values_out[3]), ==, NULL);
for (i = 0; i < 4; i++)
g_value_unset (&values_out[i]);
g_object_unref (test_obj);
}
static void
properties_testv_with_valid_properties (void)
{
TestObject *test_obj;
const char *prop_names[4] = { "foo", "bar", "baz", "quux" };
GValue values_in[4] = { G_VALUE_INIT };
GValue values_out[4] = { G_VALUE_INIT };
guint i;
g_value_init (&(values_in[0]), G_TYPE_INT);
g_value_set_int (&(values_in[0]), 100);
g_value_init (&(values_in[1]), G_TYPE_BOOLEAN);
g_value_set_boolean (&(values_in[1]), TRUE);
g_value_init (&(values_in[2]), G_TYPE_STRING);
g_value_set_string (&(values_in[2]), "pigs");
g_value_init (&(values_in[3]), G_TYPE_STRING);
g_value_set_string (&(values_in[3]), "fly");
/* Test newv_with_properties && getv */
test_obj = (TestObject *) g_object_new_with_properties (
test_object_get_type (), 4, prop_names, values_in);
g_object_getv (G_OBJECT (test_obj), 4, prop_names, values_out);
g_assert_cmpint (g_value_get_int (&values_out[0]), ==, 100);
g_assert_true (g_value_get_boolean (&values_out[1]));
g_assert_cmpstr (g_value_get_string (&values_out[2]), ==, "pigs");
g_assert_cmpstr (g_value_get_string (&values_out[3]), ==, "fly");
for (i = 0; i < G_N_ELEMENTS (values_out); i++)
g_value_unset (&values_out[i]);
/* Test newv2 && getv */
g_value_set_string (&(values_in[2]), "Elmo knows");
g_value_set_string (&(values_in[3]), "where you live");
g_object_setv (G_OBJECT (test_obj), 4, prop_names, values_in);
g_object_getv (G_OBJECT (test_obj), 4, prop_names, values_out);
g_assert_cmpint (g_value_get_int (&values_out[0]), ==, 100);
g_assert_true (g_value_get_boolean (&values_out[1]));
g_assert_cmpstr (g_value_get_string (&values_out[2]), ==, "Elmo knows");
g_assert_cmpstr (g_value_get_string (&values_out[3]), ==, "where you live");
for (i = 0; i < G_N_ELEMENTS (values_in); i++)
g_value_unset (&values_in[i]);
for (i = 0; i < G_N_ELEMENTS (values_out); i++)
g_value_unset (&values_out[i]);
g_object_unref (test_obj);
}
static void
properties_testv_with_invalid_property_type (void)
{
if (g_test_subprocess ())
{
TestObject *test_obj;
const char *invalid_prop_names[1] = { "foo" };
GValue values_in[1] = { G_VALUE_INIT };
g_value_init (&(values_in[0]), G_TYPE_STRING);
g_value_set_string (&(values_in[0]), "fly");
test_obj = (TestObject *) g_object_new_with_properties (
test_object_get_type (), 1, invalid_prop_names, values_in);
/* should give a warning */
g_object_unref (test_obj);
}
g_test_trap_subprocess (NULL, 0, 0);
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*WARNING*foo*gint*gchararray*");
}
static void
properties_testv_with_invalid_property_names (void)
{
if (g_test_subprocess ())
{
TestObject *test_obj;
const char *invalid_prop_names[4] = { "foo", "boo", "moo", "poo" };
GValue values_in[4] = { G_VALUE_INIT };
g_value_init (&(values_in[0]), G_TYPE_INT);
g_value_set_int (&(values_in[0]), 100);
g_value_init (&(values_in[1]), G_TYPE_BOOLEAN);
g_value_set_boolean (&(values_in[1]), TRUE);
g_value_init (&(values_in[2]), G_TYPE_STRING);
g_value_set_string (&(values_in[2]), "pigs");
g_value_init (&(values_in[3]), G_TYPE_STRING);
g_value_set_string (&(values_in[3]), "fly");
test_obj = (TestObject *) g_object_new_with_properties (
test_object_get_type (), 4, invalid_prop_names, values_in);
/* This call should give 3 Critical warnings. Actually, a critical warning
* shouldn't make g_object_new_with_properties to fail when a bad named
* property is given, because, it will just ignore that property. However,
* for test purposes, it is considered that the test doesn't pass.
*/
g_object_unref (test_obj);
}
g_test_trap_subprocess (NULL, 0, 0);
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*CRITICAL*g_object_new_is_valid_property*boo*");
}
static void
properties_testv_getv (void)
{
TestObject *test_obj;
const char *prop_names[4] = { "foo", "bar", "baz", "quux" };
GValue values_out_initialized[4] = { G_VALUE_INIT };
GValue values_out_uninitialized[4] = { G_VALUE_INIT };
guint i;
g_value_init (&(values_out_initialized[0]), G_TYPE_INT);
g_value_init (&(values_out_initialized[1]), G_TYPE_BOOLEAN);
g_value_init (&(values_out_initialized[2]), G_TYPE_STRING);
g_value_init (&(values_out_initialized[3]), G_TYPE_STRING);
test_obj = (TestObject *) g_object_new_with_properties (
test_object_get_type (), 0, NULL, NULL);
/* Test g_object_getv for an initialized values array */
g_object_getv (G_OBJECT (test_obj), 4, prop_names, values_out_initialized);
/* It should have init values */
g_assert_cmpint (g_value_get_int (&values_out_initialized[0]), ==, 42);
g_assert_true (g_value_get_boolean (&values_out_initialized[1]));
g_assert_cmpstr (g_value_get_string (&values_out_initialized[2]), ==, "Hello");
g_assert_cmpstr (g_value_get_string (&values_out_initialized[3]), ==, NULL);
/* Test g_object_getv for an uninitialized values array */
g_object_getv (G_OBJECT (test_obj), 4, prop_names, values_out_uninitialized);
/* It should have init values */
g_assert_cmpint (g_value_get_int (&values_out_uninitialized[0]), ==, 42);
g_assert_true (g_value_get_boolean (&values_out_uninitialized[1]));
g_assert_cmpstr (g_value_get_string (&values_out_uninitialized[2]), ==, "Hello");
g_assert_cmpstr (g_value_get_string (&values_out_uninitialized[3]), ==, NULL);
for (i = 0; i < 4; i++)
{
g_value_unset (&values_out_initialized[i]);
g_value_unset (&values_out_uninitialized[i]);
}
g_object_unref (test_obj);
}
static void
properties_get_property (void)
{
TestObject *test_obj;
struct {
const char *name;
GType gtype;
GValue value;
} test_props[] = {
{ "foo", G_TYPE_INT, G_VALUE_INIT },
{ "bar", G_TYPE_INVALID, G_VALUE_INIT },
{ "bar", G_TYPE_STRING, G_VALUE_INIT },
};
gsize i;
g_test_summary ("g_object_get_property() accepts uninitialized, "
"initialized, and transformable values");
for (i = 0; i < G_N_ELEMENTS (test_props); i++)
{
if (test_props[i].gtype != G_TYPE_INVALID)
g_value_init (&(test_props[i].value), test_props[i].gtype);
}
test_obj = (TestObject *) g_object_new_with_properties (test_object_get_type (), 0, NULL, NULL);
g_test_message ("Test g_object_get_property with an initialized value");
g_object_get_property (G_OBJECT (test_obj), test_props[0].name, &(test_props[0].value));
g_assert_cmpint (g_value_get_int (&(test_props[0].value)), ==, 42);
g_test_message ("Test g_object_get_property with an uninitialized value");
g_object_get_property (G_OBJECT (test_obj), test_props[1].name, &(test_props[1].value));
g_assert_true (g_value_get_boolean (&(test_props[1].value)));
g_test_message ("Test g_object_get_property with a transformable value");
g_object_get_property (G_OBJECT (test_obj), test_props[2].name, &(test_props[2].value));
g_assert_true (G_VALUE_HOLDS_STRING (&(test_props[2].value)));
g_assert_cmpstr (g_value_get_string (&(test_props[2].value)), ==, "TRUE");
for (i = 0; i < G_N_ELEMENTS (test_props); i++)
g_value_unset (&(test_props[i].value));
g_object_unref (test_obj);
}
static void
properties_testv_notify_queue (void)
{
TestObject *test_obj;
const char *prop_names[3] = { "foo", "bar", "baz" };
GValue values_in[3] = { G_VALUE_INIT };
Notifys n;
guint i;
g_value_init (&(values_in[0]), G_TYPE_INT);
g_value_set_int (&(values_in[0]), 100);
g_value_init (&(values_in[1]), G_TYPE_BOOLEAN);
g_value_set_boolean (&(values_in[1]), TRUE);
g_value_init (&(values_in[2]), G_TYPE_STRING);
g_value_set_string (&(values_in[2]), "");
/* Test newv_with_properties && getv */
test_obj = (TestObject *) g_object_new_with_properties (
test_object_get_type (), 0, NULL, NULL);
g_assert_nonnull (properties[PROP_FOO]);
n.pspec[0] = properties[PROP_BAZ];
n.pspec[1] = properties[PROP_BAR];
n.pspec[2] = properties[PROP_FOO];
n.pos = 0;
g_signal_connect (test_obj, "notify", G_CALLBACK (on_notify2), &n);
g_object_freeze_notify (G_OBJECT (test_obj));
{
g_object_setv (G_OBJECT (test_obj), 3, prop_names, values_in);
/* Set "foo" to 70 */
g_value_set_int (&(values_in[0]), 100);
g_object_setv (G_OBJECT (test_obj), 1, prop_names, values_in);
}
g_object_thaw_notify (G_OBJECT (test_obj));
g_assert_cmpint (n.pos, ==, 3);
for (i = 0; i < 3; i++)
g_value_unset (&values_in[i]);
g_object_unref (test_obj);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/properties/install", properties_install);
g_test_add_func ("/properties/notify", properties_notify);
g_test_add_func ("/properties/notify-queue", properties_notify_queue);
g_test_add_func ("/properties/construct", properties_construct);
g_test_add_func ("/properties/get-property", properties_get_property);
g_test_add_func ("/properties/testv_with_no_properties",
properties_testv_with_no_properties);
g_test_add_func ("/properties/testv_with_valid_properties",
properties_testv_with_valid_properties);
g_test_add_func ("/properties/testv_with_invalid_property_type",
properties_testv_with_invalid_property_type);
g_test_add_func ("/properties/testv_with_invalid_property_names",
properties_testv_with_invalid_property_names);
g_test_add_func ("/properties/testv_getv", properties_testv_getv);
g_test_add_func ("/properties/testv_notify_queue",
properties_testv_notify_queue);
return g_test_run ();
}

123
gobject/tests/qdata.c Normal file
View file

@ -0,0 +1,123 @@
/*
* Copyright 2012 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* See the included COPYING file for more information.
*/
#include <glib-object.h>
gboolean fail;
#define THREADS 10
#define ROUNDS 10000
GObject *object;
gint bucket[THREADS]; /* accessed from multiple threads, but should never be contested due to the sequence of thread operations */
static gpointer
thread_func (gpointer data)
{
gint idx = GPOINTER_TO_INT (data);
gint i;
gint d;
gint value;
gint new_value;
for (i = 0; i < ROUNDS; i++)
{
d = g_random_int_range (-10, 100);
bucket[idx] += d;
retry:
value = GPOINTER_TO_INT (g_object_get_data (object, "test"));
new_value = value + d;
if (fail)
g_object_set_data (object, "test", GINT_TO_POINTER (new_value));
else
{
if (!g_object_replace_data (object, "test",
GINT_TO_POINTER (value),
GINT_TO_POINTER (new_value),
NULL, NULL))
goto retry;
}
g_thread_yield ();
}
return NULL;
}
static void
test_qdata_threaded (void)
{
gint sum;
gint i;
GThread *threads[THREADS];
gint result;
object = g_object_new (G_TYPE_OBJECT, NULL);
g_object_set_data (object, "test", GINT_TO_POINTER (0));
for (i = 0; i < THREADS; i++)
bucket[i] = 0;
for (i = 0; i < THREADS; i++)
threads[i] = g_thread_new ("qdata", thread_func, GINT_TO_POINTER (i));
for (i = 0; i < THREADS; i++)
g_thread_join (threads[i]);
sum = 0;
for (i = 0; i < THREADS; i++)
sum += bucket[i];
result = GPOINTER_TO_INT (g_object_get_data (object, "test"));
g_assert_cmpint (sum, ==, result);
g_object_unref (object);
}
static void
test_qdata_dup (void)
{
gchar *s, *s2;
GQuark quark;
gboolean b;
quark = g_quark_from_static_string ("test");
object = g_object_new (G_TYPE_OBJECT, NULL);
s = g_strdup ("s");
g_object_set_qdata_full (object, quark, s, g_free);
s2 = g_object_dup_qdata (object, quark, (GDuplicateFunc)g_strdup, NULL);
g_assert_cmpstr (s, ==, s2);
g_assert (s != s2);
g_free (s2);
b = g_object_replace_qdata (object, quark, s, "s2", NULL, NULL);
g_assert (b);
g_free (s);
g_object_unref (object);
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
fail = !!g_getenv ("FAIL");
g_test_add_func ("/qdata/threaded", test_qdata_threaded);
g_test_add_func ("/qdata/dup", test_qdata_dup);
return g_test_run ();
}

973
gobject/tests/reference.c Normal file
View file

@ -0,0 +1,973 @@
#include <glib-object.h>
static void
test_fundamentals (void)
{
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_NONE));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_INTERFACE));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_CHAR));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_UCHAR));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_BOOLEAN));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_INT));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_UINT));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_LONG));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_ULONG));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_INT64));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_UINT64));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_ENUM));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_FLAGS));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_FLOAT));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_DOUBLE));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_STRING));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_POINTER));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_BOXED));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_PARAM));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_OBJECT));
g_assert (G_TYPE_OBJECT == g_object_get_type ());
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_VARIANT));
g_assert (G_TYPE_IS_DERIVED (G_TYPE_INITIALLY_UNOWNED));
g_assert (g_type_fundamental_next () == G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST));
}
static void
test_type_qdata (void)
{
gchar *data;
g_type_set_qdata (G_TYPE_ENUM, g_quark_from_string ("bla"), "bla");
data = g_type_get_qdata (G_TYPE_ENUM, g_quark_from_string ("bla"));
g_assert_cmpstr (data, ==, "bla");
}
static void
test_type_query (void)
{
GTypeQuery query;
g_type_query (G_TYPE_ENUM, &query);
g_assert_cmpint (query.type, ==, G_TYPE_ENUM);
g_assert_cmpstr (query.type_name, ==, "GEnum");
g_assert_cmpint (query.class_size, ==, sizeof (GEnumClass));
g_assert_cmpint (query.instance_size, ==, 0);
}
typedef struct _MyObject MyObject;
typedef struct _MyObjectClass MyObjectClass;
typedef struct _MyObjectClassPrivate MyObjectClassPrivate;
struct _MyObject
{
GObject parent_instance;
gint count;
};
struct _MyObjectClass
{
GObjectClass parent_class;
};
struct _MyObjectClassPrivate
{
gint secret_class_count;
};
static GType my_object_get_type (void);
G_DEFINE_TYPE_WITH_CODE (MyObject, my_object, G_TYPE_OBJECT,
g_type_add_class_private (g_define_type_id, sizeof (MyObjectClassPrivate)) );
static void
my_object_init (MyObject *obj)
{
obj->count = 42;
}
static void
my_object_class_init (MyObjectClass *klass)
{
}
static void
test_class_private (void)
{
GObject *obj;
MyObjectClass *class;
MyObjectClassPrivate *priv;
obj = g_object_new (my_object_get_type (), NULL);
class = g_type_class_ref (my_object_get_type ());
priv = G_TYPE_CLASS_GET_PRIVATE (class, my_object_get_type (), MyObjectClassPrivate);
priv->secret_class_count = 13;
g_type_class_unref (class);
g_object_unref (obj);
g_assert_cmpint (g_type_qname (my_object_get_type ()), ==, g_quark_from_string ("MyObject"));
}
static void
test_clear (void)
{
GObject *o = NULL;
GObject *tmp;
g_clear_object (&o);
g_assert (o == NULL);
tmp = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (tmp->ref_count, ==, 1);
o = g_object_ref (tmp);
g_assert (o != NULL);
g_assert_cmpint (tmp->ref_count, ==, 2);
g_clear_object (&o);
g_assert_cmpint (tmp->ref_count, ==, 1);
g_assert (o == NULL);
g_object_unref (tmp);
}
static void
test_clear_function (void)
{
GObject *o = NULL;
GObject *tmp;
(g_clear_object) (&o);
g_assert (o == NULL);
tmp = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (tmp->ref_count, ==, 1);
o = g_object_ref (tmp);
g_assert (o != NULL);
g_assert_cmpint (tmp->ref_count, ==, 2);
(g_clear_object) (&o);
g_assert_cmpint (tmp->ref_count, ==, 1);
g_assert (o == NULL);
g_object_unref (tmp);
}
static void
test_set (void)
{
GObject *o = NULL;
GObject *tmp;
gpointer tmp_weak = NULL;
g_assert (!g_set_object (&o, NULL));
g_assert (o == NULL);
tmp = g_object_new (G_TYPE_OBJECT, NULL);
tmp_weak = tmp;
g_object_add_weak_pointer (tmp, &tmp_weak);
g_assert_cmpint (tmp->ref_count, ==, 1);
g_assert (g_set_object (&o, tmp));
g_assert (o == tmp);
g_assert_cmpint (tmp->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (tmp->ref_count, ==, 1);
/* Setting it again shouldnt cause finalisation. */
g_assert (!g_set_object (&o, tmp));
g_assert (o == tmp);
g_assert_cmpint (tmp->ref_count, ==, 1);
g_assert_nonnull (tmp_weak);
g_assert (g_set_object (&o, NULL));
g_assert (o == NULL);
g_assert_null (tmp_weak);
}
static void
test_set_function (void)
{
GObject *o = NULL;
GObject *tmp;
gpointer tmp_weak = NULL;
g_assert (!(g_set_object) (&o, NULL));
g_assert (o == NULL);
tmp = g_object_new (G_TYPE_OBJECT, NULL);
tmp_weak = tmp;
g_object_add_weak_pointer (tmp, &tmp_weak);
g_assert_cmpint (tmp->ref_count, ==, 1);
g_assert ((g_set_object) (&o, tmp));
g_assert (o == tmp);
g_assert_cmpint (tmp->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (tmp->ref_count, ==, 1);
/* Setting it again shouldnt cause finalisation. */
g_assert (!(g_set_object) (&o, tmp));
g_assert (o == tmp);
g_assert_cmpint (tmp->ref_count, ==, 1);
g_assert_nonnull (tmp_weak);
g_assert ((g_set_object) (&o, NULL));
g_assert (o == NULL);
g_assert_null (tmp_weak);
}
static void
test_set_derived_type (void)
{
GBinding *obj = NULL;
GObject *o = NULL;
GBinding *b = NULL;
g_test_summary ("Check that g_set_object() doesnt give strict aliasing "
"warnings when used on types derived from GObject");
g_assert_false (g_set_object (&o, NULL));
g_assert_null (o);
g_assert_false (g_set_object (&b, NULL));
g_assert_null (b);
obj = g_object_new (my_object_get_type (), NULL);
g_assert_true (g_set_object (&o, G_OBJECT (obj)));
g_assert_true (o == G_OBJECT (obj));
g_assert_true (g_set_object (&b, obj));
g_assert_true (b == obj);
g_object_unref (obj);
g_clear_object (&b);
g_clear_object (&o);
}
static void
toggle_cb (gpointer data, GObject *obj, gboolean is_last)
{
gboolean *b = data;
*b = TRUE;
}
static void
test_object_value (void)
{
GObject *v;
GObject *v2;
GValue value = G_VALUE_INIT;
gboolean toggled = FALSE;
g_value_init (&value, G_TYPE_OBJECT);
v = g_object_new (G_TYPE_OBJECT, NULL);
g_object_add_toggle_ref (v, toggle_cb, &toggled);
g_value_take_object (&value, v);
v2 = g_value_get_object (&value);
g_assert (v2 == v);
v2 = g_value_dup_object (&value);
g_assert (v2 == v); /* objects use ref/unref for copy/free */
g_object_unref (v2);
g_assert (!toggled);
g_value_unset (&value);
g_assert (toggled);
/* test the deprecated variant too */
g_value_init (&value, G_TYPE_OBJECT);
/* get a new reference */
g_object_ref (v);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
g_value_set_object_take_ownership (&value, v);
G_GNUC_END_IGNORE_DEPRECATIONS
toggled = FALSE;
g_value_unset (&value);
g_assert (toggled);
g_object_remove_toggle_ref (v, toggle_cb, &toggled);
}
static void
test_initially_unowned (void)
{
GObject *obj;
obj = g_object_new (G_TYPE_INITIALLY_UNOWNED, NULL);
g_assert (g_object_is_floating (obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_ref_sink (obj);
g_assert (!g_object_is_floating (obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_ref_sink (obj);
g_assert (!g_object_is_floating (obj));
g_assert_cmpint (obj->ref_count, ==, 2);
g_object_unref (obj);
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_force_floating (obj);
g_assert (g_object_is_floating (obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_ref_sink (obj);
g_object_unref (obj);
obj = g_object_new (G_TYPE_INITIALLY_UNOWNED, NULL);
g_assert_true (g_object_is_floating (obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_take_ref (obj);
g_assert_false (g_object_is_floating (obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_take_ref (obj);
g_assert_false (g_object_is_floating (obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_unref (obj);
}
static void
test_weak_pointer (void)
{
GObject *obj;
gpointer weak;
gpointer weak2;
weak = weak2 = obj = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_add_weak_pointer (obj, &weak);
g_object_add_weak_pointer (obj, &weak2);
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert (weak == obj);
g_assert (weak2 == obj);
g_object_remove_weak_pointer (obj, &weak2);
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert (weak == obj);
g_assert (weak2 == obj);
g_object_unref (obj);
g_assert (weak == NULL);
g_assert (weak2 == obj);
}
static void
test_weak_pointer_clear (void)
{
GObject *obj;
gpointer weak = NULL;
g_clear_weak_pointer (&weak);
g_assert_null (weak);
weak = obj = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_add_weak_pointer (obj, &weak);
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_true (weak == obj);
g_clear_weak_pointer (&weak);
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_null (weak);
g_object_unref (obj);
}
static void
test_weak_pointer_clear_function (void)
{
GObject *obj;
gpointer weak = NULL;
(g_clear_weak_pointer) (&weak);
g_assert_null (weak);
weak = obj = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_add_weak_pointer (obj, &weak);
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_true (weak == obj);
(g_clear_weak_pointer) (&weak);
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_null (weak);
g_object_unref (obj);
}
static void
test_weak_pointer_set (void)
{
GObject *obj;
gpointer weak = NULL;
g_assert_false (g_set_weak_pointer (&weak, NULL));
g_assert_null (weak);
obj = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_true (g_set_weak_pointer (&weak, obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_true (weak == obj);
g_assert_true (g_set_weak_pointer (&weak, NULL));
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_null (weak);
g_assert_true (g_set_weak_pointer (&weak, obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_true (weak == obj);
g_object_unref (obj);
g_assert_null (weak);
}
static void
test_weak_pointer_set_function (void)
{
GObject *obj;
gpointer weak = NULL;
g_assert_false ((g_set_weak_pointer) (&weak, NULL));
g_assert_null (weak);
obj = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_true ((g_set_weak_pointer) (&weak, obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_true (weak == obj);
g_assert_true ((g_set_weak_pointer) (&weak, NULL));
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_null (weak);
g_assert_true ((g_set_weak_pointer) (&weak, obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_true (weak == obj);
g_object_unref (obj);
g_assert_null (weak);
}
/* See gobject/tests/threadtests.c for the threaded version */
static void
test_weak_ref (void)
{
GObject *obj;
GObject *obj2;
GObject *tmp;
GWeakRef weak = { { GUINT_TO_POINTER (0xDEADBEEFU) } };
GWeakRef weak2 = { { GUINT_TO_POINTER (0xDEADBEEFU) } };
GWeakRef weak3 = { { GUINT_TO_POINTER (0xDEADBEEFU) } };
GWeakRef *dynamic_weak = g_new (GWeakRef, 1);
/* you can initialize to empty like this... */
g_weak_ref_init (&weak2, NULL);
g_assert (g_weak_ref_get (&weak2) == NULL);
/* ... or via an initializer */
g_weak_ref_init (&weak3, NULL);
g_assert (g_weak_ref_get (&weak3) == NULL);
obj = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (obj->ref_count, ==, 1);
obj2 = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (obj2->ref_count, ==, 1);
/* you can init with an object (even if uninitialized) */
g_weak_ref_init (&weak, obj);
g_weak_ref_init (dynamic_weak, obj);
/* or set to point at an object, if initialized (maybe to 0) */
g_weak_ref_set (&weak2, obj);
g_weak_ref_set (&weak3, obj);
/* none of this affects its refcount */
g_assert_cmpint (obj->ref_count, ==, 1);
/* getting the value takes a ref */
tmp = g_weak_ref_get (&weak);
g_assert (tmp == obj);
g_assert_cmpint (obj->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (obj->ref_count, ==, 1);
tmp = g_weak_ref_get (&weak2);
g_assert (tmp == obj);
g_assert_cmpint (obj->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (obj->ref_count, ==, 1);
tmp = g_weak_ref_get (&weak3);
g_assert (tmp == obj);
g_assert_cmpint (obj->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (obj->ref_count, ==, 1);
tmp = g_weak_ref_get (dynamic_weak);
g_assert (tmp == obj);
g_assert_cmpint (obj->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (obj->ref_count, ==, 1);
/* clearing a weak ref stops tracking */
g_weak_ref_clear (&weak);
/* setting a weak ref to NULL stops tracking too */
g_weak_ref_set (&weak2, NULL);
g_assert (g_weak_ref_get (&weak2) == NULL);
g_weak_ref_clear (&weak2);
/* setting a weak ref to a new object stops tracking the old one */
g_weak_ref_set (dynamic_weak, obj2);
tmp = g_weak_ref_get (dynamic_weak);
g_assert (tmp == obj2);
g_assert_cmpint (obj2->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (obj2->ref_count, ==, 1);
g_assert_cmpint (obj->ref_count, ==, 1);
/* free the object: weak3 is the only one left pointing there */
g_object_unref (obj);
g_assert (g_weak_ref_get (&weak3) == NULL);
/* setting a weak ref to a new object stops tracking the old one */
g_weak_ref_set (dynamic_weak, obj2);
tmp = g_weak_ref_get (dynamic_weak);
g_assert (tmp == obj2);
g_assert_cmpint (obj2->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (obj2->ref_count, ==, 1);
g_weak_ref_clear (&weak3);
/* unset dynamic_weak... */
g_weak_ref_set (dynamic_weak, NULL);
g_assert_null (g_weak_ref_get (dynamic_weak));
/* initializing a weak reference to an object that had before works */
g_weak_ref_set (dynamic_weak, obj2);
tmp = g_weak_ref_get (dynamic_weak);
g_assert_true (tmp == obj2);
g_assert_cmpint (obj2->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (obj2->ref_count, ==, 1);
/* clear and free dynamic_weak... */
g_weak_ref_clear (dynamic_weak);
/* ... to prove that doing so stops this from being a use-after-free */
g_object_unref (obj2);
g_free (dynamic_weak);
}
G_DECLARE_FINAL_TYPE (WeakReffedObject, weak_reffed_object,
WEAK, REFFED_OBJECT, GObject)
struct _WeakReffedObject
{
GObject parent;
GWeakRef *weak_ref;
};
G_DEFINE_TYPE (WeakReffedObject, weak_reffed_object, G_TYPE_OBJECT)
static void
weak_reffed_object_dispose (GObject *object)
{
WeakReffedObject *weak_reffed = WEAK_REFFED_OBJECT (object);
g_assert_cmpint (object->ref_count, ==, 1);
g_weak_ref_set (weak_reffed->weak_ref, object);
G_OBJECT_CLASS (weak_reffed_object_parent_class)->dispose (object);
g_assert_null (g_weak_ref_get (weak_reffed->weak_ref));
}
static void
weak_reffed_object_init (WeakReffedObject *connector)
{
}
static void
weak_reffed_object_class_init (WeakReffedObjectClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = weak_reffed_object_dispose;
}
static void
test_weak_ref_on_dispose (void)
{
WeakReffedObject *obj;
GWeakRef weak = { { GUINT_TO_POINTER (0xDEADBEEFU) } };
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2390");
g_test_summary ("Test that a weak ref set during dispose vfunc is cleared");
g_weak_ref_init (&weak, NULL);
obj = g_object_new (weak_reffed_object_get_type (), NULL);
obj->weak_ref = &weak;
g_assert_cmpint (G_OBJECT (obj)->ref_count, ==, 1);
g_clear_object (&obj);
g_assert_null (g_weak_ref_get (&weak));
}
static void
test_weak_ref_on_run_dispose (void)
{
GObject *obj;
GWeakRef weak = { { GUINT_TO_POINTER (0xDEADBEEFU) } };
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/865");
g_test_summary ("Test that a weak ref is cleared on g_object_run_dispose()");
obj = g_object_new (G_TYPE_OBJECT, NULL);
g_weak_ref_init (&weak, obj);
g_assert_true (obj == g_weak_ref_get (&weak));
g_object_unref (obj);
g_object_run_dispose (obj);
g_assert_null (g_weak_ref_get (&weak));
g_clear_object (&obj);
g_assert_null (g_weak_ref_get (&weak));
}
static void
on_weak_ref_toggle_notify (gpointer data,
GObject *object,
gboolean is_last_ref)
{
GWeakRef *weak = data;
if (is_last_ref)
g_weak_ref_set (weak, object);
}
static void
on_weak_ref_toggle_notify_disposed (gpointer data,
GObject *object)
{
g_assert_cmpint (object->ref_count, ==, 1);
g_object_ref (object);
g_object_unref (object);
}
static void
test_weak_ref_on_toggle_notify (void)
{
GObject *obj;
GWeakRef weak = { { GUINT_TO_POINTER (0xDEADBEEFU) } };
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2390");
g_test_summary ("Test that a weak ref set on toggle notify is cleared");
g_weak_ref_init (&weak, NULL);
obj = g_object_new (G_TYPE_OBJECT, NULL);
g_object_add_toggle_ref (obj, on_weak_ref_toggle_notify, &weak);
g_object_weak_ref (obj, on_weak_ref_toggle_notify_disposed, NULL);
g_object_unref (obj);
g_assert_cmpint (obj->ref_count, ==, 1);
g_clear_object (&obj);
g_assert_null (g_weak_ref_get (&weak));
}
typedef struct
{
gboolean should_be_last;
gint count;
} Count;
static void
toggle_notify (gpointer data,
GObject *obj,
gboolean is_last)
{
Count *c = data;
g_assert (is_last == c->should_be_last);
c->count++;
}
static void
test_toggle_ref (void)
{
GObject *obj;
Count c, c2;
obj = g_object_new (G_TYPE_OBJECT, NULL);
g_object_add_toggle_ref (obj, toggle_notify, &c);
g_object_add_toggle_ref (obj, toggle_notify, &c2);
c.should_be_last = c2.should_be_last = TRUE;
c.count = c2.count = 0;
g_object_unref (obj);
g_assert_cmpint (c.count, ==, 0);
g_assert_cmpint (c2.count, ==, 0);
g_object_ref (obj);
g_assert_cmpint (c.count, ==, 0);
g_assert_cmpint (c2.count, ==, 0);
g_object_remove_toggle_ref (obj, toggle_notify, &c2);
g_object_unref (obj);
g_assert_cmpint (c.count, ==, 1);
c.should_be_last = FALSE;
g_object_ref (obj);
g_assert_cmpint (c.count, ==, 2);
c.should_be_last = TRUE;
g_object_unref (obj);
g_assert_cmpint (c.count, ==, 3);
g_object_remove_toggle_ref (obj, toggle_notify, &c);
}
static gboolean global_destroyed;
static gint global_value;
static void
data_destroy (gpointer data)
{
g_assert_cmpint (GPOINTER_TO_INT (data), ==, global_value);
global_destroyed = TRUE;
}
static void
test_object_qdata (void)
{
GObject *obj;
gpointer v;
GQuark quark;
obj = g_object_new (G_TYPE_OBJECT, NULL);
global_value = 1;
global_destroyed = FALSE;
g_object_set_data_full (obj, "test", GINT_TO_POINTER (1), data_destroy);
v = g_object_get_data (obj, "test");
g_assert_cmpint (GPOINTER_TO_INT (v), ==, 1);
g_object_set_data_full (obj, "test", GINT_TO_POINTER (2), data_destroy);
g_assert (global_destroyed);
global_value = 2;
global_destroyed = FALSE;
v = g_object_steal_data (obj, "test");
g_assert_cmpint (GPOINTER_TO_INT (v), ==, 2);
g_assert (!global_destroyed);
global_value = 1;
global_destroyed = FALSE;
quark = g_quark_from_string ("test");
g_object_set_qdata_full (obj, quark, GINT_TO_POINTER (1), data_destroy);
v = g_object_get_qdata (obj, quark);
g_assert_cmpint (GPOINTER_TO_INT (v), ==, 1);
g_object_set_qdata_full (obj, quark, GINT_TO_POINTER (2), data_destroy);
g_assert (global_destroyed);
global_value = 2;
global_destroyed = FALSE;
v = g_object_steal_qdata (obj, quark);
g_assert_cmpint (GPOINTER_TO_INT (v), ==, 2);
g_assert (!global_destroyed);
g_object_set_qdata_full (obj, quark, GINT_TO_POINTER (3), data_destroy);
global_value = 3;
global_destroyed = FALSE;
g_object_unref (obj);
g_assert (global_destroyed);
}
typedef struct {
const gchar *value;
gint refcount;
} Value;
static gpointer
ref_value (gpointer value, gpointer user_data)
{
Value *v = value;
Value **old_value_p = user_data;
if (old_value_p)
*old_value_p = v;
if (v)
v->refcount += 1;
return value;
}
static void
unref_value (gpointer value)
{
Value *v = value;
v->refcount -= 1;
if (v->refcount == 0)
g_free (value);
}
static
Value *
new_value (const gchar *s)
{
Value *v;
v = g_new (Value, 1);
v->value = s;
v->refcount = 1;
return v;
}
static void
test_object_qdata2 (void)
{
GObject *obj;
Value *v, *v1, *v2, *v3, *old_val;
GDestroyNotify old_destroy;
gboolean res;
obj = g_object_new (G_TYPE_OBJECT, NULL);
v1 = new_value ("bla");
g_object_set_data_full (obj, "test", v1, unref_value);
v = g_object_get_data (obj, "test");
g_assert_cmpstr (v->value, ==, "bla");
g_assert_cmpint (v->refcount, ==, 1);
v = g_object_dup_data (obj, "test", ref_value, &old_val);
g_assert (old_val == v1);
g_assert_cmpstr (v->value, ==, "bla");
g_assert_cmpint (v->refcount, ==, 2);
unref_value (v);
v = g_object_dup_data (obj, "nono", ref_value, &old_val);
g_assert (old_val == NULL);
g_assert (v == NULL);
v2 = new_value ("not");
res = g_object_replace_data (obj, "test", v1, v2, unref_value, &old_destroy);
g_assert (res == TRUE);
g_assert (old_destroy == unref_value);
g_assert_cmpstr (v1->value, ==, "bla");
g_assert_cmpint (v1->refcount, ==, 1);
v = g_object_get_data (obj, "test");
g_assert_cmpstr (v->value, ==, "not");
g_assert_cmpint (v->refcount, ==, 1);
v3 = new_value ("xyz");
res = g_object_replace_data (obj, "test", v1, v3, unref_value, &old_destroy);
g_assert (res == FALSE);
g_assert_cmpstr (v2->value, ==, "not");
g_assert_cmpint (v2->refcount, ==, 1);
unref_value (v1);
res = g_object_replace_data (obj, "test", NULL, v3, unref_value, &old_destroy);
g_assert (res == FALSE);
g_assert_cmpstr (v2->value, ==, "not");
g_assert_cmpint (v2->refcount, ==, 1);
res = g_object_replace_data (obj, "test", v2, NULL, unref_value, &old_destroy);
g_assert (res == TRUE);
g_assert (old_destroy == unref_value);
g_assert_cmpstr (v2->value, ==, "not");
g_assert_cmpint (v2->refcount, ==, 1);
unref_value (v2);
v = g_object_get_data (obj, "test");
g_assert (v == NULL);
res = g_object_replace_data (obj, "test", NULL, v3, unref_value, &old_destroy);
g_assert (res == TRUE);
v = g_object_get_data (obj, "test");
g_assert (v == v3);
ref_value (v3, NULL);
g_assert_cmpint (v3->refcount, ==, 2);
g_object_unref (obj);
g_assert_cmpint (v3->refcount, ==, 1);
unref_value (v3);
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/type/fundamentals", test_fundamentals);
g_test_add_func ("/type/qdata", test_type_qdata);
g_test_add_func ("/type/query", test_type_query);
g_test_add_func ("/type/class-private", test_class_private);
g_test_add_func ("/object/clear", test_clear);
g_test_add_func ("/object/clear-function", test_clear_function);
g_test_add_func ("/object/set", test_set);
g_test_add_func ("/object/set-function", test_set_function);
g_test_add_func ("/object/set/derived-type", test_set_derived_type);
g_test_add_func ("/object/value", test_object_value);
g_test_add_func ("/object/initially-unowned", test_initially_unowned);
g_test_add_func ("/object/weak-pointer", test_weak_pointer);
g_test_add_func ("/object/weak-pointer/clear", test_weak_pointer_clear);
g_test_add_func ("/object/weak-pointer/clear-function", test_weak_pointer_clear_function);
g_test_add_func ("/object/weak-pointer/set", test_weak_pointer_set);
g_test_add_func ("/object/weak-pointer/set-function", test_weak_pointer_set_function);
g_test_add_func ("/object/weak-ref", test_weak_ref);
g_test_add_func ("/object/weak-ref/on-dispose", test_weak_ref_on_dispose);
g_test_add_func ("/object/weak-ref/on-run-dispose", test_weak_ref_on_run_dispose);
g_test_add_func ("/object/weak-ref/on-toggle-notify", test_weak_ref_on_toggle_notify);
g_test_add_func ("/object/toggle-ref", test_toggle_ref);
g_test_add_func ("/object/qdata", test_object_qdata);
g_test_add_func ("/object/qdata2", test_object_qdata2);
return g_test_run ();
}

View file

@ -0,0 +1,299 @@
#include <glib-object.h>
typedef struct {
GObject instance;
} MyObj;
typedef struct {
GObjectClass parent_class;
} MyObjClass;
enum {
SIGNAL1,
SIGNAL2,
LAST_SIGNAL
};
guint signals[LAST_SIGNAL];
GType my_obj_get_type (void);
G_DEFINE_TYPE (MyObj, my_obj, G_TYPE_OBJECT)
static void
my_obj_init (MyObj *o)
{
}
static void
my_obj_class_init (MyObjClass *class)
{
signals[SIGNAL1] =
g_signal_new ("signal1",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL, G_TYPE_NONE, 0);
signals[SIGNAL2] =
g_signal_new ("signal2",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL, G_TYPE_NONE, 0);
}
static void
nop (void)
{
}
#define HANDLERS 500000
static void
test_connect_many (void)
{
MyObj *o;
gdouble time_elapsed;
gint i;
o = g_object_new (my_obj_get_type (), NULL);
g_test_timer_start ();
for (i = 0; i < HANDLERS; i++)
g_signal_connect (o, "signal1", G_CALLBACK (nop), NULL);
time_elapsed = g_test_timer_elapsed ();
g_object_unref (o);
g_test_minimized_result (time_elapsed, "connected %u handlers in %6.3f seconds", HANDLERS, time_elapsed);
}
static void
test_disconnect_many_ordered (void)
{
MyObj *o;
gulong handlers[HANDLERS];
gdouble time_elapsed;
gint i;
o = g_object_new (my_obj_get_type (), NULL);
for (i = 0; i < HANDLERS; i++)
handlers[i] = g_signal_connect (o, "signal1", G_CALLBACK (nop), NULL);
g_test_timer_start ();
for (i = 0; i < HANDLERS; i++)
g_signal_handler_disconnect (o, handlers[i]);
time_elapsed = g_test_timer_elapsed ();
g_object_unref (o);
g_test_minimized_result (time_elapsed, "disconnected %u handlers in %6.3f seconds", HANDLERS, time_elapsed);
}
static void
test_disconnect_many_inverse (void)
{
MyObj *o;
gulong handlers[HANDLERS];
gdouble time_elapsed;
gint i;
o = g_object_new (my_obj_get_type (), NULL);
for (i = 0; i < HANDLERS; i++)
handlers[i] = g_signal_connect (o, "signal1", G_CALLBACK (nop), NULL);
g_test_timer_start ();
for (i = HANDLERS - 1; i >= 0; i--)
g_signal_handler_disconnect (o, handlers[i]);
time_elapsed = g_test_timer_elapsed ();
g_object_unref (o);
g_test_minimized_result (time_elapsed, "disconnected %u handlers in %6.3f seconds", HANDLERS, time_elapsed);
}
static void
test_disconnect_many_random (void)
{
MyObj *o;
gulong handlers[HANDLERS];
gulong id;
gdouble time_elapsed;
gint i, j;
o = g_object_new (my_obj_get_type (), NULL);
for (i = 0; i < HANDLERS; i++)
handlers[i] = g_signal_connect (o, "signal1", G_CALLBACK (nop), NULL);
for (i = 0; i < HANDLERS; i++)
{
j = g_test_rand_int_range (0, HANDLERS);
id = handlers[i];
handlers[i] = handlers[j];
handlers[j] = id;
}
g_test_timer_start ();
for (i = 0; i < HANDLERS; i++)
g_signal_handler_disconnect (o, handlers[i]);
time_elapsed = g_test_timer_elapsed ();
g_object_unref (o);
g_test_minimized_result (time_elapsed, "disconnected %u handlers in %6.3f seconds", HANDLERS, time_elapsed);
}
static void
test_disconnect_2_signals (void)
{
MyObj *o;
gulong handlers[HANDLERS];
gulong id;
gdouble time_elapsed;
gint i, j;
o = g_object_new (my_obj_get_type (), NULL);
for (i = 0; i < HANDLERS; i++)
{
if (i % 2 == 0)
handlers[i] = g_signal_connect (o, "signal1", G_CALLBACK (nop), NULL);
else
handlers[i] = g_signal_connect (o, "signal2", G_CALLBACK (nop), NULL);
}
for (i = 0; i < HANDLERS; i++)
{
j = g_test_rand_int_range (0, HANDLERS);
id = handlers[i];
handlers[i] = handlers[j];
handlers[j] = id;
}
g_test_timer_start ();
for (i = 0; i < HANDLERS; i++)
g_signal_handler_disconnect (o, handlers[i]);
time_elapsed = g_test_timer_elapsed ();
g_object_unref (o);
g_test_minimized_result (time_elapsed, "disconnected %u handlers in %6.3f seconds", HANDLERS, time_elapsed);
}
static void
test_disconnect_2_objects (void)
{
MyObj *o1, *o2, *o;
gulong handlers[HANDLERS];
MyObj *objects[HANDLERS];
gulong id;
gdouble time_elapsed;
gint i, j;
o1 = g_object_new (my_obj_get_type (), NULL);
o2 = g_object_new (my_obj_get_type (), NULL);
for (i = 0; i < HANDLERS; i++)
{
if (i % 2 == 0)
{
handlers[i] = g_signal_connect (o1, "signal1", G_CALLBACK (nop), NULL);
objects[i] = o1;
}
else
{
handlers[i] = g_signal_connect (o2, "signal1", G_CALLBACK (nop), NULL);
objects[i] = o2;
}
}
for (i = 0; i < HANDLERS; i++)
{
j = g_test_rand_int_range (0, HANDLERS);
id = handlers[i];
handlers[i] = handlers[j];
handlers[j] = id;
o = objects[i];
objects[i] = objects[j];
objects[j] = o;
}
g_test_timer_start ();
for (i = 0; i < HANDLERS; i++)
g_signal_handler_disconnect (objects[i], handlers[i]);
time_elapsed = g_test_timer_elapsed ();
g_object_unref (o1);
g_object_unref (o2);
g_test_minimized_result (time_elapsed, "disconnected %u handlers in %6.3f seconds", HANDLERS, time_elapsed);
}
static void
test_block_many (void)
{
MyObj *o;
gulong handlers[HANDLERS];
gulong id;
gdouble time_elapsed;
gint i, j;
o = g_object_new (my_obj_get_type (), NULL);
for (i = 0; i < HANDLERS; i++)
handlers[i] = g_signal_connect (o, "signal1", G_CALLBACK (nop), NULL);
for (i = 0; i < HANDLERS; i++)
{
j = g_test_rand_int_range (0, HANDLERS);
id = handlers[i];
handlers[i] = handlers[j];
handlers[j] = id;
}
g_test_timer_start ();
for (i = 0; i < HANDLERS; i++)
g_signal_handler_block (o, handlers[i]);
for (i = HANDLERS - 1; i >= 0; i--)
g_signal_handler_unblock (o, handlers[i]);
time_elapsed = g_test_timer_elapsed ();
g_object_unref (o);
g_test_minimized_result (time_elapsed, "blocked and unblocked %u handlers in %6.3f seconds", HANDLERS, time_elapsed);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
if (g_test_perf ())
{
g_test_add_func ("/signal/handler/connect-many", test_connect_many);
g_test_add_func ("/signal/handler/disconnect-many-ordered", test_disconnect_many_ordered);
g_test_add_func ("/signal/handler/disconnect-many-inverse", test_disconnect_many_inverse);
g_test_add_func ("/signal/handler/disconnect-many-random", test_disconnect_many_random);
g_test_add_func ("/signal/handler/disconnect-2-signals", test_disconnect_2_signals);
g_test_add_func ("/signal/handler/disconnect-2-objects", test_disconnect_2_objects);
g_test_add_func ("/signal/handler/block-many", test_block_many);
}
return g_test_run ();
}

650
gobject/tests/signalgroup.c Normal file
View file

@ -0,0 +1,650 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
*
* Copyright (C) 2015-2022 Christian Hergert <christian@hergert.me>
* Copyright (C) 2015 Garrett Regier <garrettregier@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include <glib-object.h>
G_DECLARE_FINAL_TYPE (SignalTarget, signal_target, TEST, SIGNAL_TARGET, GObject)
struct _SignalTarget
{
GObject parent_instance;
};
G_DEFINE_TYPE (SignalTarget, signal_target, G_TYPE_OBJECT)
static G_DEFINE_QUARK (detail, signal_detail);
enum {
THE_SIGNAL,
NEVER_EMITTED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
static void
signal_target_class_init (SignalTargetClass *klass)
{
signals[THE_SIGNAL] =
g_signal_new ("the-signal",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL, NULL, NULL,
G_TYPE_NONE,
1,
G_TYPE_OBJECT);
signals[NEVER_EMITTED] =
g_signal_new ("never-emitted",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE,
1,
G_TYPE_OBJECT);
}
static void
signal_target_init (SignalTarget *self)
{
}
static gint global_signal_calls;
static gint global_weak_notify_called;
static void
connect_before_cb (SignalTarget *target,
GSignalGroup *group,
gint *signal_calls)
{
SignalTarget *readback;
g_assert_true (TEST_IS_SIGNAL_TARGET (target));
g_assert_true (G_IS_SIGNAL_GROUP (group));
g_assert_nonnull (signal_calls);
g_assert_true (signal_calls == &global_signal_calls);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target);
g_object_unref (readback);
*signal_calls += 1;
}
static void
connect_after_cb (SignalTarget *target,
GSignalGroup *group,
gint *signal_calls)
{
SignalTarget *readback;
g_assert_true (TEST_IS_SIGNAL_TARGET (target));
g_assert_true (G_IS_SIGNAL_GROUP (group));
g_assert_nonnull (signal_calls);
g_assert_true (signal_calls == &global_signal_calls);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target);
g_object_unref (readback);
g_assert_cmpint (*signal_calls, ==, 4);
*signal_calls += 1;
}
static void
connect_swapped_cb (gint *signal_calls,
GSignalGroup *group,
SignalTarget *target)
{
SignalTarget *readback;
g_assert_true (signal_calls != NULL);
g_assert_true (signal_calls == &global_signal_calls);
g_assert_true (G_IS_SIGNAL_GROUP (group));
g_assert_true (TEST_IS_SIGNAL_TARGET (target));
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target);
g_object_unref (readback);
*signal_calls += 1;
}
static void
connect_object_cb (SignalTarget *target,
GSignalGroup *group,
GObject *object)
{
SignalTarget *readback;
gint *signal_calls;
g_assert_true (TEST_IS_SIGNAL_TARGET (target));
g_assert_true (G_IS_SIGNAL_GROUP (group));
g_assert_true (G_IS_OBJECT (object));
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target);
g_object_unref (readback);
signal_calls = g_object_get_data (object, "signal-calls");
g_assert_nonnull (signal_calls);
g_assert_true (signal_calls == &global_signal_calls);
*signal_calls += 1;
}
static void
connect_bad_detail_cb (SignalTarget *target,
GSignalGroup *group,
GObject *object)
{
g_error ("This detailed signal is never emitted!");
}
static void
connect_never_emitted_cb (SignalTarget *target,
gboolean *weak_notify_called)
{
g_error ("This signal is never emitted!");
}
static void
connect_data_notify_cb (gboolean *weak_notify_called,
GClosure *closure)
{
g_assert_nonnull (weak_notify_called);
g_assert_true (weak_notify_called == &global_weak_notify_called);
g_assert_nonnull (closure);
g_assert_false (*weak_notify_called);
*weak_notify_called = TRUE;
}
static void
connect_data_weak_notify_cb (gboolean *weak_notify_called,
GSignalGroup *group)
{
g_assert_nonnull (weak_notify_called);
g_assert_true (weak_notify_called == &global_weak_notify_called);
g_assert_true (G_IS_SIGNAL_GROUP (group));
g_assert_true (*weak_notify_called);
}
static void
connect_all_signals (GSignalGroup *group)
{
GObject *object;
/* Check that these are called in the right order */
g_signal_group_connect (group,
"the-signal",
G_CALLBACK (connect_before_cb),
&global_signal_calls);
g_signal_group_connect_after (group,
"the-signal",
G_CALLBACK (connect_after_cb),
&global_signal_calls);
/* Check that this is called with the arguments swapped */
g_signal_group_connect_swapped (group,
"the-signal",
G_CALLBACK (connect_swapped_cb),
&global_signal_calls);
/* Check that this is called with the arguments swapped */
object = g_object_new (G_TYPE_OBJECT, NULL);
g_object_set_data (object, "signal-calls", &global_signal_calls);
g_signal_group_connect_object (group,
"the-signal",
G_CALLBACK (connect_object_cb),
object,
0);
g_object_weak_ref (G_OBJECT (group),
(GWeakNotify)g_object_unref,
object);
/* Check that a detailed signal is handled correctly */
g_signal_group_connect (group,
"the-signal::detail",
G_CALLBACK (connect_before_cb),
&global_signal_calls);
g_signal_group_connect (group,
"the-signal::bad-detail",
G_CALLBACK (connect_bad_detail_cb),
NULL);
/* Check that the notify is called correctly */
global_weak_notify_called = FALSE;
g_signal_group_connect_data (group,
"never-emitted",
G_CALLBACK (connect_never_emitted_cb),
&global_weak_notify_called,
(GClosureNotify)connect_data_notify_cb,
0);
g_object_weak_ref (G_OBJECT (group),
(GWeakNotify)connect_data_weak_notify_cb,
&global_weak_notify_called);
}
static void
assert_signals (SignalTarget *target,
GSignalGroup *group,
gboolean success)
{
g_assert (TEST_IS_SIGNAL_TARGET (target));
g_assert (group == NULL || G_IS_SIGNAL_GROUP (group));
global_signal_calls = 0;
g_signal_emit (target, signals[THE_SIGNAL],
signal_detail_quark (), group);
g_assert_cmpint (global_signal_calls, ==, success ? 5 : 0);
}
static void
dummy_handler (void)
{
}
static void
test_signal_group_invalid (void)
{
GObject *invalid_target = g_object_new (G_TYPE_OBJECT, NULL);
SignalTarget *target = g_object_new (signal_target_get_type (), NULL);
GSignalGroup *group = g_signal_group_new (signal_target_get_type ());
/* Invalid Target Type */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*g_type_is_a*G_TYPE_OBJECT*");
g_signal_group_new (G_TYPE_DATE_TIME);
g_test_assert_expected_messages ();
/* Invalid Target */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*Failed to set GSignalGroup of target type SignalTarget using target * of type GObject*");
g_signal_group_set_target (group, invalid_target);
g_assert_finalize_object (group);
g_test_assert_expected_messages ();
/* Invalid Signal Name */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*g_signal_parse_name*");
group = g_signal_group_new (signal_target_get_type ());
g_signal_group_connect (group,
"does-not-exist",
G_CALLBACK (connect_before_cb),
NULL);
g_test_assert_expected_messages ();
g_assert_finalize_object (group);
/* Invalid Callback */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*c_handler != NULL*");
group = g_signal_group_new (signal_target_get_type ());
g_signal_group_connect (group,
"the-signal",
G_CALLBACK (NULL),
NULL);
g_test_assert_expected_messages ();
g_assert_finalize_object (group);
/* Connecting after setting target */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*Cannot add signals after setting target*");
group = g_signal_group_new (signal_target_get_type ());
g_signal_group_set_target (group, target);
g_signal_group_connect (group,
"the-signal",
G_CALLBACK (dummy_handler),
NULL);
g_test_assert_expected_messages ();
g_assert_finalize_object (group);
g_assert_finalize_object (target);
g_assert_finalize_object (invalid_target);
}
static void
test_signal_group_simple (void)
{
SignalTarget *target;
GSignalGroup *group;
SignalTarget *readback;
/* Set the target before connecting the signals */
group = g_signal_group_new (signal_target_get_type ());
target = g_object_new (signal_target_get_type (), NULL);
g_assert_null (g_signal_group_dup_target (group));
g_signal_group_set_target (group, target);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target);
g_object_unref (readback);
g_assert_finalize_object (group);
assert_signals (target, NULL, FALSE);
g_assert_finalize_object (target);
group = g_signal_group_new (signal_target_get_type ());
target = g_object_new (signal_target_get_type (), NULL);
connect_all_signals (group);
g_signal_group_set_target (group, target);
assert_signals (target, group, TRUE);
g_assert_finalize_object (target);
g_assert_finalize_object (group);
}
static void
test_signal_group_changing_target (void)
{
SignalTarget *target1, *target2;
GSignalGroup *group = g_signal_group_new (signal_target_get_type ());
SignalTarget *readback;
connect_all_signals (group);
g_assert_null (g_signal_group_dup_target (group));
/* Set the target after connecting the signals */
target1 = g_object_new (signal_target_get_type (), NULL);
g_signal_group_set_target (group, target1);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target1);
g_object_unref (readback);
assert_signals (target1, group, TRUE);
/* Set the same target */
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target1);
g_object_unref (readback);
g_signal_group_set_target (group, target1);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target1);
g_object_unref (readback);
assert_signals (target1, group, TRUE);
/* Set a new target when the current target is non-NULL */
target2 = g_object_new (signal_target_get_type (), NULL);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target1);
g_object_unref (readback);
g_signal_group_set_target (group, target2);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target2);
g_object_unref (readback);
assert_signals (target2, group, TRUE);
g_assert_finalize_object (target2);
g_assert_finalize_object (target1);
g_assert_finalize_object (group);
}
static void
assert_blocking (SignalTarget *target,
GSignalGroup *group,
gint count)
{
gint i;
assert_signals (target, group, TRUE);
/* Assert that multiple blocks are effective */
for (i = 0; i < count; ++i)
{
g_signal_group_block (group);
assert_signals (target, group, FALSE);
}
/* Assert that the signal is not emitted after the first unblock */
for (i = 0; i < count; ++i)
{
assert_signals (target, group, FALSE);
g_signal_group_unblock (group);
}
assert_signals (target, group, TRUE);
}
static void
test_signal_group_blocking (void)
{
SignalTarget *target1, *target2, *readback;
GSignalGroup *group = g_signal_group_new (signal_target_get_type ());
/* Test blocking and unblocking null target */
g_signal_group_block (group);
g_signal_group_unblock (group);
connect_all_signals (group);
g_assert_null (g_signal_group_dup_target (group));
target1 = g_object_new (signal_target_get_type (), NULL);
g_signal_group_set_target (group, target1);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target1);
g_object_unref (readback);
assert_blocking (target1, group, 1);
assert_blocking (target1, group, 3);
assert_blocking (target1, group, 15);
/* Assert that blocking transfers across changing the target */
g_signal_group_block (group);
g_signal_group_block (group);
/* Set a new target when the current target is non-NULL */
target2 = g_object_new (signal_target_get_type (), NULL);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target1);
g_object_unref (readback);
g_signal_group_set_target (group, target2);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target2);
g_object_unref (readback);
assert_signals (target2, group, FALSE);
g_signal_group_unblock (group);
assert_signals (target2, group, FALSE);
g_signal_group_unblock (group);
assert_signals (target2, group, TRUE);
g_assert_finalize_object (target2);
g_assert_finalize_object (target1);
g_assert_finalize_object (group);
}
static void
test_signal_group_weak_ref_target (void)
{
SignalTarget *target = g_object_new (signal_target_get_type (), NULL);
GSignalGroup *group = g_signal_group_new (signal_target_get_type ());
SignalTarget *readback;
g_assert_null (g_signal_group_dup_target (group));
g_signal_group_set_target (group, target);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target);
g_object_unref (readback);
g_assert_finalize_object (target);
g_assert_null (g_signal_group_dup_target (group));
g_assert_finalize_object (group);
}
static void
test_signal_group_connect_object (void)
{
GObject *object = g_object_new (G_TYPE_OBJECT, NULL);
SignalTarget *target = g_object_new (signal_target_get_type (), NULL);
GSignalGroup *group = g_signal_group_new (signal_target_get_type ());
SignalTarget *readback;
/* We already do basic connect_object() tests in connect_signals(),
* this is only needed to test the specifics of connect_object()
*/
g_signal_group_connect_object (group,
"the-signal",
G_CALLBACK (connect_object_cb),
object,
0);
g_assert_null (g_signal_group_dup_target (group));
g_signal_group_set_target (group, target);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target);
g_object_unref (readback);
g_assert_finalize_object (object);
/* This would cause a warning if the SignalGroup did not
* have a weakref on the object as it would try to connect again
*/
g_signal_group_set_target (group, NULL);
g_assert_null (g_signal_group_dup_target (group));
g_signal_group_set_target (group, target);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target);
g_object_unref (readback);
g_assert_finalize_object (group);
g_assert_finalize_object (target);
}
static void
test_signal_group_signal_parsing (void)
{
g_test_trap_subprocess ("/GObject/SignalGroup/signal-parsing/subprocess", 0,
G_TEST_SUBPROCESS_INHERIT_STDERR);
g_test_trap_assert_passed ();
g_test_trap_assert_stderr ("");
}
static void
test_signal_group_signal_parsing_subprocess (void)
{
GSignalGroup *group;
/* Check that the class has not been created and with it the
* signals registered. This will cause g_signal_parse_name()
* to fail unless GSignalGroup calls g_type_class_ref().
*/
g_assert_null (g_type_class_peek (signal_target_get_type ()));
group = g_signal_group_new (signal_target_get_type ());
g_signal_group_connect (group,
"the-signal",
G_CALLBACK (connect_before_cb),
NULL);
g_assert_finalize_object (group);
}
static void
test_signal_group_properties (void)
{
GSignalGroup *group;
SignalTarget *target, *other;
GType gtype;
group = g_signal_group_new (signal_target_get_type ());
g_object_get (group,
"target", &target,
"target-type", &gtype,
NULL);
g_assert_cmpint (gtype, ==, signal_target_get_type ());
g_assert_null (target);
target = g_object_new (signal_target_get_type (), NULL);
g_object_set (group, "target", target, NULL);
g_object_get (group, "target", &other, NULL);
g_assert_true (target == other);
g_object_unref (other);
g_assert_finalize_object (target);
g_assert_null (g_signal_group_dup_target (group));
g_assert_finalize_object (group);
}
G_DECLARE_INTERFACE (SignalThing, signal_thing, SIGNAL, THING, GObject)
struct _SignalThingInterface
{
GTypeInterface iface;
void (*changed) (SignalThing *thing);
};
G_DEFINE_INTERFACE (SignalThing, signal_thing, G_TYPE_OBJECT)
static guint signal_thing_changed;
static void
signal_thing_default_init (SignalThingInterface *iface)
{
signal_thing_changed =
g_signal_new ("changed",
G_TYPE_FROM_INTERFACE (iface),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (SignalThingInterface, changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
}
G_GNUC_NORETURN static void
thing_changed_cb (SignalThing *thing,
gpointer user_data G_GNUC_UNUSED)
{
g_assert_not_reached ();
}
static void
test_signal_group_interface (void)
{
GSignalGroup *group;
group = g_signal_group_new (signal_thing_get_type ());
g_signal_group_connect (group,
"changed",
G_CALLBACK (thing_changed_cb),
NULL);
g_assert_finalize_object (group);
}
gint
main (gint argc,
gchar *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/GObject/SignalGroup/invalid", test_signal_group_invalid);
g_test_add_func ("/GObject/SignalGroup/simple", test_signal_group_simple);
g_test_add_func ("/GObject/SignalGroup/changing-target", test_signal_group_changing_target);
g_test_add_func ("/GObject/SignalGroup/blocking", test_signal_group_blocking);
g_test_add_func ("/GObject/SignalGroup/weak-ref-target", test_signal_group_weak_ref_target);
g_test_add_func ("/GObject/SignalGroup/connect-object", test_signal_group_connect_object);
g_test_add_func ("/GObject/SignalGroup/signal-parsing", test_signal_group_signal_parsing);
g_test_add_func ("/GObject/SignalGroup/signal-parsing/subprocess", test_signal_group_signal_parsing_subprocess);
g_test_add_func ("/GObject/SignalGroup/properties", test_signal_group_properties);
g_test_add_func ("/GObject/SignalGroup/interface", test_signal_group_interface);
return g_test_run ();
}

1828
gobject/tests/signals.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,186 @@
#!/usr/bin/env python
# coding=utf-8
# Copyright (c) 2015 Remko Tronçon (https://el-tramo.be)
# Copied from https://github.com/remko/pycotap/
#
# Released under the MIT license
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import unittest
import sys
import base64
from io import StringIO
# Log modes
class LogMode(object):
LogToError, LogToDiagnostics, LogToYAML, LogToAttachment = range(4)
class TAPTestResult(unittest.TestResult):
def __init__(self, output_stream, error_stream, message_log, test_output_log):
super(TAPTestResult, self).__init__(self, output_stream)
self.output_stream = output_stream
self.error_stream = error_stream
self.orig_stdout = None
self.orig_stderr = None
self.message = None
self.test_output = None
self.message_log = message_log
self.test_output_log = test_output_log
self.output_stream.write("TAP version 13\n")
self._set_streams()
def printErrors(self):
self.print_raw("1..%d\n" % self.testsRun)
self._reset_streams()
def _set_streams(self):
self.orig_stdout = sys.stdout
self.orig_stderr = sys.stderr
if self.message_log == LogMode.LogToError:
self.message = self.error_stream
else:
self.message = StringIO()
if self.test_output_log == LogMode.LogToError:
self.test_output = self.error_stream
else:
self.test_output = StringIO()
if self.message_log == self.test_output_log:
self.test_output = self.message
sys.stdout = sys.stderr = self.test_output
def _reset_streams(self):
sys.stdout = self.orig_stdout
sys.stderr = self.orig_stderr
def print_raw(self, text):
self.output_stream.write(text)
self.output_stream.flush()
def print_result(self, result, test, directive=None):
self.output_stream.write("%s %d %s" % (result, self.testsRun, test.id()))
if directive:
self.output_stream.write(" # " + directive)
self.output_stream.write("\n")
self.output_stream.flush()
def ok(self, test, directive=None):
self.print_result("ok", test, directive)
def not_ok(self, test):
self.print_result("not ok", test)
def startTest(self, test):
super(TAPTestResult, self).startTest(test)
def stopTest(self, test):
super(TAPTestResult, self).stopTest(test)
if self.message_log == self.test_output_log:
logs = [(self.message_log, self.message, "output")]
else:
logs = [
(self.test_output_log, self.test_output, "test_output"),
(self.message_log, self.message, "message"),
]
for log_mode, log, log_name in logs:
if log_mode != LogMode.LogToError:
output = log.getvalue()
if len(output):
if log_mode == LogMode.LogToYAML:
self.print_raw(" ---\n")
self.print_raw(" " + log_name + ": |\n")
self.print_raw(
" " + output.rstrip().replace("\n", "\n ") + "\n"
)
self.print_raw(" ...\n")
elif log_mode == LogMode.LogToAttachment:
self.print_raw(" ---\n")
self.print_raw(" " + log_name + ":\n")
self.print_raw(" File-Name: " + log_name + ".txt\n")
self.print_raw(" File-Type: text/plain\n")
self.print_raw(
" File-Content: " + base64.b64encode(output) + "\n"
)
self.print_raw(" ...\n")
else:
self.print_raw(
"# " + output.rstrip().replace("\n", "\n# ") + "\n"
)
# Truncate doesn't change the current stream position.
# Seek to the beginning to avoid extensions on subsequent writes.
log.seek(0)
log.truncate(0)
def addSuccess(self, test):
super(TAPTestResult, self).addSuccess(test)
self.ok(test)
def addError(self, test, err):
super(TAPTestResult, self).addError(test, err)
self.message.write(self.errors[-1][1] + "\n")
self.not_ok(test)
def addFailure(self, test, err):
super(TAPTestResult, self).addFailure(test, err)
self.message.write(self.failures[-1][1] + "\n")
self.not_ok(test)
def addSkip(self, test, reason):
super(TAPTestResult, self).addSkip(test, reason)
self.ok(test, "SKIP " + reason)
def addExpectedFailure(self, test, err):
super(TAPTestResult, self).addExpectedFailure(test, err)
self.ok(test)
def addUnexpectedSuccess(self, test):
super(TAPTestResult, self).addUnexpectedSuccess(test)
self.message.write("Unexpected success" + "\n")
self.not_ok(test)
class TAPTestRunner(object):
def __init__(
self,
message_log=LogMode.LogToYAML,
test_output_log=LogMode.LogToDiagnostics,
output_stream=sys.stdout,
error_stream=sys.stderr,
):
self.output_stream = output_stream
self.error_stream = error_stream
self.message_log = message_log
self.test_output_log = test_output_log
def run(self, test):
result = TAPTestResult(
self.output_stream,
self.error_stream,
self.message_log,
self.test_output_log,
)
test(result)
result.printErrors()
return result

105
gobject/tests/testcommon.h Normal file
View file

@ -0,0 +1,105 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2003 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __TEST_COMMON_H__
#define __TEST_COMMON_H__
G_BEGIN_DECLS
#define DEFINE_TYPE_FULL(name, prefix, \
class_init, base_init, instance_init, \
parent_type, interface_decl) \
GType \
prefix ## _get_type (void) \
{ \
static GType object_type = 0; \
\
if (!object_type) \
{ \
static const GTypeInfo object_info = \
{ \
sizeof (name ## Class), \
(GBaseInitFunc) base_init, \
(GBaseFinalizeFunc) NULL, \
(GClassInitFunc) class_init, \
(GClassFinalizeFunc) NULL, \
NULL, /* class_data */ \
sizeof (name), \
0, /* n_prelocs */ \
(GInstanceInitFunc) instance_init, \
(const GTypeValueTable *) NULL, \
}; \
\
object_type = g_type_register_static (parent_type, \
# name, \
&object_info, 0); \
interface_decl \
} \
\
return object_type; \
}
#define DEFINE_TYPE(name, prefix, \
class_init, base_init, instance_init, \
parent_type) \
DEFINE_TYPE_FULL(name, prefix, class_init, base_init, \
instance_init, parent_type, {})
#define DEFINE_IFACE(name, prefix, base_init, dflt_init) \
GType \
prefix ## _get_type (void) \
{ \
static GType iface_type = 0; \
\
if (!iface_type) \
{ \
static const GTypeInfo iface_info = \
{ \
sizeof (name ## Class), \
(GBaseInitFunc) base_init, \
(GBaseFinalizeFunc) NULL, \
(GClassInitFunc) dflt_init, \
(GClassFinalizeFunc) NULL, \
(gconstpointer) NULL, \
(guint16) 0, \
(guint16) 0, \
(GInstanceInitFunc) NULL, \
(const GTypeValueTable*) NULL, \
}; \
\
iface_type = g_type_register_static (G_TYPE_INTERFACE, \
# name, \
&iface_info, 0); \
} \
return iface_type; \
}
#define INTERFACE_FULL(type, init_func, iface_type) \
{ \
static GInterfaceInfo const iface = \
{ \
(GInterfaceInitFunc) init_func, NULL, NULL \
}; \
\
g_type_add_interface_static (type, iface_type, &iface); \
}
#define INTERFACE(init_func, iface_type) \
INTERFACE_FULL(object_type, init_func, iface_type)
G_END_DECLS
#endif /* __TEST_COMMON_H__ */

71
gobject/tests/testing.c Normal file
View file

@ -0,0 +1,71 @@
/* GLib testing framework examples and tests
*
* Copyright © 2019 Endless Mobile, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Philip Withnall <withnall@endlessm.com>
*/
#include "config.h"
/* We want to distinguish between messages originating from libglib
* and messages originating from this program.
*/
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "testing"
#include <glib.h>
#include <glib-object.h>
#include <locale.h>
#include <stdlib.h>
static void
test_assert_finalize_object_subprocess_bad (void)
{
GObject *obj = g_object_new (G_TYPE_OBJECT, NULL);
g_object_ref (obj);
/* This should emit an assertion failure. */
g_assert_finalize_object (obj);
g_object_unref (obj);
exit (0);
}
static void
test_assert_finalize_object (void)
{
GObject *obj = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_finalize_object (obj);
g_test_trap_subprocess ("/assert/finalize_object/subprocess/bad", 0, 0);
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*g_assert_finalize_object:*'weak_pointer' should be NULL*");
}
int
main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/assert/finalize_object", test_assert_finalize_object);
g_test_add_func ("/assert/finalize_object/subprocess/bad",
test_assert_finalize_object_subprocess_bad);
return g_test_run ();
}

525
gobject/tests/threadtests.c Normal file
View file

@ -0,0 +1,525 @@
/* GLib testing framework examples and tests
* Copyright (C) 2008 Imendio AB
* Authors: Tim Janik
*
* This work is provided "as is"; redistribution and modification
* in whole or in part, in any medium, physical or electronic is
* permitted without restriction.
*
* This work 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.
*
* In no event shall the authors or contributors be liable for any
* direct, indirect, incidental, special, exemplary, or consequential
* damages (including, but not limited to, procurement of substitute
* goods or services; loss of use, data, or profits; or business
* interruption) however caused and on any theory of liability, whether
* in contract, strict liability, or tort (including negligence or
* otherwise) arising in any way out of the use of this software, even
* if advised of the possibility of such damage.
*/
#ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#endif
#include <glib.h>
#include <glib-object.h>
static int mtsafe_call_counter = 0; /* multi thread safe call counter, must be accessed atomically */
static int unsafe_call_counter = 0; /* single-threaded call counter */
static GCond sync_cond;
static GMutex sync_mutex;
#define NUM_COUNTER_INCREMENTS 100000
static void
call_counter_init (gpointer tclass)
{
int i;
for (i = 0; i < NUM_COUNTER_INCREMENTS; i++)
{
int saved_unsafe_call_counter = unsafe_call_counter;
g_atomic_int_add (&mtsafe_call_counter, 1); /* real call count update */
g_thread_yield(); /* let concurrent threads corrupt the unsafe_call_counter state */
unsafe_call_counter = 1 + saved_unsafe_call_counter; /* non-atomic counter update */
}
}
static void interface_per_class_init (void) { call_counter_init (NULL); }
/* define 3 test interfaces */
typedef GTypeInterface MyFace0Interface;
static GType my_face0_get_type (void);
G_DEFINE_INTERFACE (MyFace0, my_face0, G_TYPE_OBJECT)
static void my_face0_default_init (MyFace0Interface *iface) { call_counter_init (iface); }
typedef GTypeInterface MyFace1Interface;
static GType my_face1_get_type (void);
G_DEFINE_INTERFACE (MyFace1, my_face1, G_TYPE_OBJECT)
static void my_face1_default_init (MyFace1Interface *iface) { call_counter_init (iface); }
/* define 3 test objects, adding interfaces 0 & 1, and adding interface 2 after class initialization */
typedef GObject MyTester0;
typedef GObjectClass MyTester0Class;
static GType my_tester0_get_type (void);
G_DEFINE_TYPE_WITH_CODE (MyTester0, my_tester0, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (my_face0_get_type(), interface_per_class_init)
G_IMPLEMENT_INTERFACE (my_face1_get_type(), interface_per_class_init))
static void my_tester0_init (MyTester0*t) {}
static void my_tester0_class_init (MyTester0Class*c) { call_counter_init (c); }
typedef GObject MyTester1;
typedef GObjectClass MyTester1Class;
/* Disabled for now (see https://bugzilla.gnome.org/show_bug.cgi?id=687659) */
#if 0
typedef GTypeInterface MyFace2Interface;
static GType my_face2_get_type (void);
G_DEFINE_INTERFACE (MyFace2, my_face2, G_TYPE_OBJECT)
static void my_face2_default_init (MyFace2Interface *iface) { call_counter_init (iface); }
static GType my_tester1_get_type (void);
G_DEFINE_TYPE_WITH_CODE (MyTester1, my_tester1, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (my_face0_get_type(), interface_per_class_init)
G_IMPLEMENT_INTERFACE (my_face1_get_type(), interface_per_class_init))
static void my_tester1_init (MyTester1*t) {}
static void my_tester1_class_init (MyTester1Class*c) { call_counter_init (c); }
typedef GObject MyTester2;
typedef GObjectClass MyTester2Class;
static GType my_tester2_get_type (void);
G_DEFINE_TYPE_WITH_CODE (MyTester2, my_tester2, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (my_face0_get_type(), interface_per_class_init)
G_IMPLEMENT_INTERFACE (my_face1_get_type(), interface_per_class_init))
static void my_tester2_init (MyTester2*t) {}
static void my_tester2_class_init (MyTester2Class*c) { call_counter_init (c); }
static gpointer
tester_init_thread (gpointer data)
{
const GInterfaceInfo face2_interface_info = { (GInterfaceInitFunc) interface_per_class_init, NULL, NULL };
gpointer klass;
/* first, synchronize with other threads,
* then run interface and class initializers,
* using unsafe_call_counter concurrently
*/
g_mutex_lock (&sync_mutex);
g_mutex_unlock (&sync_mutex);
/* test default interface initialization for face0 */
g_type_default_interface_unref (g_type_default_interface_ref (my_face0_get_type()));
/* test class initialization, face0 per-class initializer, face1 default and per-class initializer */
klass = g_type_class_ref ((GType) data);
/* test face2 default and per-class initializer, after class_init */
g_type_add_interface_static (G_TYPE_FROM_CLASS (klass), my_face2_get_type(), &face2_interface_info);
/* cleanups */
g_type_class_unref (klass);
return NULL;
}
static void
test_threaded_class_init (void)
{
GThread *t1, *t2, *t3;
/* pause newly created threads */
g_mutex_lock (&sync_mutex);
/* create threads */
t1 = g_thread_create (tester_init_thread, (gpointer) my_tester0_get_type(), TRUE, NULL);
t2 = g_thread_create (tester_init_thread, (gpointer) my_tester1_get_type(), TRUE, NULL);
t3 = g_thread_create (tester_init_thread, (gpointer) my_tester2_get_type(), TRUE, NULL);
/* execute threads */
g_mutex_unlock (&sync_mutex);
while (g_atomic_int_get (&mtsafe_call_counter) < (3 + 3 + 3 * 3) * NUM_COUNTER_INCREMENTS)
{
if (g_test_verbose())
g_printerr ("Initializers counted: %u\n", g_atomic_int_get (&mtsafe_call_counter));
g_usleep (50 * 1000); /* wait for threads to complete */
}
if (g_test_verbose())
g_printerr ("Total initializers: %u\n", g_atomic_int_get (&mtsafe_call_counter));
/* ensure non-corrupted counter updates */
g_assert_cmpint (g_atomic_int_get (&mtsafe_call_counter), ==, unsafe_call_counter);
g_thread_join (t1);
g_thread_join (t2);
g_thread_join (t3);
}
#endif
typedef struct {
GObject parent;
char *name;
} PropTester;
typedef GObjectClass PropTesterClass;
static GType prop_tester_get_type (void);
G_DEFINE_TYPE (PropTester, prop_tester, G_TYPE_OBJECT)
#define PROP_NAME 1
static void
prop_tester_init (PropTester* t)
{
if (t->name == NULL)
{ } /* needs unit test framework initialization: g_test_bug ("race initializing properties"); */
}
static void
prop_tester_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{}
static void
prop_tester_class_init (PropTesterClass *c)
{
int i;
GParamSpec *param;
GObjectClass *gobject_class = G_OBJECT_CLASS (c);
gobject_class->set_property = prop_tester_set_property; /* silence GObject checks */
g_mutex_lock (&sync_mutex);
g_cond_signal (&sync_cond);
g_mutex_unlock (&sync_mutex);
for (i = 0; i < 100; i++) /* wait a bit. */
g_thread_yield();
call_counter_init (c);
param = g_param_spec_string ("name", "name_i18n",
"yet-more-wasteful-i18n",
NULL,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK);
g_object_class_install_property (gobject_class, PROP_NAME, param);
}
static gpointer
object_create (gpointer data)
{
GObject *obj = g_object_new (prop_tester_get_type(), "name", "fish", NULL);
g_object_unref (obj);
return NULL;
}
static void
test_threaded_object_init (void)
{
GThread *creator;
g_mutex_lock (&sync_mutex);
creator = g_thread_create (object_create, NULL, TRUE, NULL);
/* really provoke the race */
g_cond_wait (&sync_cond, &sync_mutex);
object_create (NULL);
g_mutex_unlock (&sync_mutex);
g_thread_join (creator);
}
typedef struct {
MyTester0 *strong;
guint unref_delay;
} UnrefInThreadData;
static gpointer
unref_in_thread (gpointer p)
{
UnrefInThreadData *data = p;
g_usleep (data->unref_delay);
g_object_unref (data->strong);
return NULL;
}
/* undefine to see this test fail without GWeakRef */
#define HAVE_G_WEAK_REF
#define SLEEP_MIN_USEC 1
#define SLEEP_MAX_USEC 10
static void
test_threaded_weak_ref (void)
{
guint i;
guint get_wins = 0, unref_wins = 0;
guint n;
if (g_test_thorough ())
n = NUM_COUNTER_INCREMENTS;
else
n = NUM_COUNTER_INCREMENTS / 20;
#ifdef G_OS_WIN32
/* On Windows usleep has millisecond resolution and gets rounded up
* leading to the test running for a long time. */
n /= 10;
#endif
for (i = 0; i < n; i++)
{
UnrefInThreadData data;
#ifdef HAVE_G_WEAK_REF
/* GWeakRef<MyTester0> in C++ terms */
GWeakRef weak;
#else
gpointer weak;
#endif
MyTester0 *strengthened;
guint get_delay;
GThread *thread;
GError *error = NULL;
if (g_test_verbose () && (i % (n/20)) == 0)
g_printerr ("%u%%\n", ((i * 100) / n));
/* Have an object and a weak ref to it */
data.strong = g_object_new (my_tester0_get_type (), NULL);
#ifdef HAVE_G_WEAK_REF
g_weak_ref_init (&weak, data.strong);
#else
weak = data.strong;
g_object_add_weak_pointer ((GObject *) weak, &weak);
#endif
/* Delay for a random time on each side of the race, to perturb the
* timing. Ideally, we want each side to win half the races; on
* smcv's laptop, these timings are about right.
*/
data.unref_delay = g_random_int_range (SLEEP_MIN_USEC / 2, SLEEP_MAX_USEC / 2);
get_delay = g_random_int_range (SLEEP_MIN_USEC, SLEEP_MAX_USEC);
/* One half of the race is to unref the shared object */
thread = g_thread_create (unref_in_thread, &data, TRUE, &error);
g_assert_no_error (error);
/* The other half of the race is to get the object from the "global
* singleton"
*/
g_usleep (get_delay);
#ifdef HAVE_G_WEAK_REF
strengthened = g_weak_ref_get (&weak);
#else
/* Spot the unsafe pointer access! In GDBusConnection this is rather
* better-hidden, but ends up with essentially the same thing, albeit
* cleared in dispose() rather than by a traditional weak pointer
*/
strengthened = weak;
if (strengthened != NULL)
g_object_ref (strengthened);
#endif
if (strengthened != NULL)
g_assert (G_IS_OBJECT (strengthened));
/* Wait for the thread to run */
g_thread_join (thread);
if (strengthened != NULL)
{
get_wins++;
g_assert (G_IS_OBJECT (strengthened));
g_object_unref (strengthened);
}
else
{
unref_wins++;
}
#ifdef HAVE_G_WEAK_REF
g_weak_ref_clear (&weak);
#else
if (weak != NULL)
g_object_remove_weak_pointer (weak, &weak);
#endif
}
if (g_test_verbose ())
g_printerr ("Race won by get %u times, unref %u times\n",
get_wins, unref_wins);
}
typedef struct
{
GObject *object;
GWeakRef *weak;
gint started; /* (atomic) */
gint finished; /* (atomic) */
gint disposing; /* (atomic) */
} ThreadedWeakRefData;
static void
on_weak_ref_disposed (gpointer data,
GObject *gobj)
{
ThreadedWeakRefData *thread_data = data;
/* Wait until the thread has started */
while (!g_atomic_int_get (&thread_data->started))
continue;
g_atomic_int_set (&thread_data->disposing, 1);
/* Wait for the thread to act, so that the object is still valid */
while (!g_atomic_int_get (&thread_data->finished))
continue;
g_atomic_int_set (&thread_data->disposing, 0);
}
static gpointer
on_other_thread_weak_ref (gpointer user_data)
{
ThreadedWeakRefData *thread_data = user_data;
GObject *object = thread_data->object;
g_atomic_int_set (&thread_data->started, 1);
/* Ensure we've started disposal */
while (!g_atomic_int_get (&thread_data->disposing))
continue;
g_object_ref (object);
g_weak_ref_set (thread_data->weak, object);
g_object_unref (object);
g_assert_cmpint (thread_data->disposing, ==, 1);
g_atomic_int_set (&thread_data->finished, 1);
return NULL;
}
static void
test_threaded_weak_ref_finalization (void)
{
GObject *obj = g_object_new (G_TYPE_OBJECT, NULL);
GWeakRef weak = { { GUINT_TO_POINTER (0xDEADBEEFU) } };
ThreadedWeakRefData thread_data = {
.object = obj, .weak = &weak, .started = 0, .finished = 0
};
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2390");
g_test_summary ("Test that a weak ref added by another thread during dispose "
"of a GObject is cleared during finalisation. "
"Use on_weak_ref_disposed() to synchronize the other thread "
"with the dispose vfunc.");
g_weak_ref_init (&weak, NULL);
g_object_weak_ref (obj, on_weak_ref_disposed, &thread_data);
g_assert_cmpint (obj->ref_count, ==, 1);
g_thread_unref (g_thread_new ("on_other_thread",
on_other_thread_weak_ref,
&thread_data));
g_object_unref (obj);
/* This is what this test is about: at this point the weak reference
* should have been unset (and not point to a dead object either). */
g_assert_null (g_weak_ref_get (&weak));
}
typedef struct
{
GObject *object;
int done; /* (atomic) */
int toggles; /* (atomic) */
} ToggleNotifyThreadData;
static gpointer
on_reffer_thread (gpointer user_data)
{
ToggleNotifyThreadData *thread_data = user_data;
while (!g_atomic_int_get (&thread_data->done))
{
g_object_ref (thread_data->object);
g_object_unref (thread_data->object);
}
return NULL;
}
static void
on_toggle_notify (gpointer data,
GObject *object,
gboolean is_last_ref)
{
/* Anything could be put here, but we don't care for this test.
* Actually having this empty made the bug to happen more frequently (being
* timing related).
*/
}
static gpointer
on_toggler_thread (gpointer user_data)
{
ToggleNotifyThreadData *thread_data = user_data;
while (!g_atomic_int_get (&thread_data->done))
{
g_object_ref (thread_data->object);
g_object_remove_toggle_ref (thread_data->object, on_toggle_notify, thread_data);
g_object_add_toggle_ref (thread_data->object, on_toggle_notify, thread_data);
g_object_unref (thread_data->object);
g_atomic_int_add (&thread_data->toggles, 1);
}
return NULL;
}
static void
test_threaded_toggle_notify (void)
{
GObject *object = g_object_new (G_TYPE_OBJECT, NULL);
ToggleNotifyThreadData data = { object, FALSE, 0 };
GThread *threads[3];
gsize i;
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/2394");
g_test_summary ("Test that toggle reference notifications can be changed "
"safely from another (the main) thread without causing the "
"notifying thread to abort");
g_object_add_toggle_ref (object, on_toggle_notify, &data);
g_object_unref (object);
g_assert_cmpint (object->ref_count, ==, 1);
threads[0] = g_thread_new ("on_reffer_thread", on_reffer_thread, &data);
threads[1] = g_thread_new ("on_another_reffer_thread", on_reffer_thread, &data);
threads[2] = g_thread_new ("on_main_toggler_thread", on_toggler_thread, &data);
/* We need to wait here for the threads to run for a bit in order to make the
* race to happen, so we wait for an high number of toggle changes to be met
* so that we can be consistent on each platform.
*/
while (g_atomic_int_get (&data.toggles) < 1000000)
;
g_atomic_int_set (&data.done, TRUE);
for (i = 0; i < G_N_ELEMENTS (threads); i++)
g_thread_join (threads[i]);
g_assert_cmpint (object->ref_count, ==, 1);
g_clear_object (&object);
}
int
main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
/* g_test_add_func ("/GObject/threaded-class-init", test_threaded_class_init); */
g_test_add_func ("/GObject/threaded-object-init", test_threaded_object_init);
g_test_add_func ("/GObject/threaded-weak-ref", test_threaded_weak_ref);
g_test_add_func ("/GObject/threaded-weak-ref/on-finalization",
test_threaded_weak_ref_finalization);
g_test_add_func ("/GObject/threaded-toggle-notify",
test_threaded_toggle_notify);
return g_test_run();
}

View file

@ -0,0 +1,88 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
// SPDX-FileCopyrightText: 2021 Emmanuele Bassi
#include <glib-object.h>
#define TEST_TYPE_FINAL (test_final_get_type())
G_DECLARE_FINAL_TYPE (TestFinal, test_final, TEST, FINAL, GObject)
struct _TestFinal
{
GObject parent_instance;
};
struct _TestFinalClass
{
GObjectClass parent_class;
};
G_DEFINE_FINAL_TYPE (TestFinal, test_final, G_TYPE_OBJECT)
static void
test_final_class_init (TestFinalClass *klass)
{
}
static void
test_final_init (TestFinal *self)
{
}
#define TEST_TYPE_FINAL2 (test_final2_get_type())
G_DECLARE_FINAL_TYPE (TestFinal2, test_final2, TEST, FINAL2, TestFinal)
struct _TestFinal2
{
TestFinal parent_instance;
};
struct _TestFinal2Class
{
TestFinalClass parent_class;
};
G_DEFINE_TYPE (TestFinal2, test_final2, TEST_TYPE_FINAL)
static void
test_final2_class_init (TestFinal2Class *klass)
{
}
static void
test_final2_init (TestFinal2 *self)
{
}
/* test_type_flags_final: Check that trying to derive from a final class
* will result in a warning from the type system
*/
static void
test_type_flags_final (void)
{
GType final2_type;
/* This is the message we print out when registering the type */
g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING,
"*cannot derive*");
/* This is the message when we fail to return from the GOnce init
* block within the test_final2_get_type() function
*/
g_test_expect_message ("GLib", G_LOG_LEVEL_CRITICAL,
"*g_once_init_leave: assertion*");
final2_type = TEST_TYPE_FINAL2;
g_assert_true (final2_type == G_TYPE_INVALID);
g_test_assert_expected_messages ();
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/type/flags/final", test_type_flags_final);
return g_test_run ();
}

215
gobject/tests/type.c Normal file
View file

@ -0,0 +1,215 @@
#include <glib-object.h>
static void
test_registration_serial (void)
{
gint serial1, serial2, serial3;
serial1 = g_type_get_type_registration_serial ();
g_pointer_type_register_static ("my+pointer");
serial2 = g_type_get_type_registration_serial ();
g_assert (serial1 != serial2);
serial3 = g_type_get_type_registration_serial ();
g_assert (serial2 == serial3);
}
typedef struct {
GTypeInterface g_iface;
} BarInterface;
GType bar_get_type (void);
G_DEFINE_INTERFACE (Bar, bar, G_TYPE_OBJECT)
static void
bar_default_init (BarInterface *iface)
{
}
typedef struct {
GTypeInterface g_iface;
} FooInterface;
GType foo_get_type (void);
G_DEFINE_INTERFACE_WITH_CODE (Foo, foo, G_TYPE_OBJECT,
g_type_interface_add_prerequisite (g_define_type_id, bar_get_type ()))
static void
foo_default_init (FooInterface *iface)
{
}
typedef struct {
GTypeInterface g_iface;
} BaaInterface;
GType baa_get_type (void);
G_DEFINE_INTERFACE (Baa, baa, G_TYPE_INVALID)
static void
baa_default_init (BaaInterface *iface)
{
}
typedef struct {
GTypeInterface g_iface;
} BooInterface;
GType boo_get_type (void);
G_DEFINE_INTERFACE_WITH_CODE (Boo, boo, G_TYPE_INVALID,
g_type_interface_add_prerequisite (g_define_type_id, baa_get_type ()))
static void
boo_default_init (BooInterface *iface)
{
}
typedef struct {
GTypeInterface g_iface;
} BibiInterface;
GType bibi_get_type (void);
G_DEFINE_INTERFACE (Bibi, bibi, G_TYPE_INITIALLY_UNOWNED)
static void
bibi_default_init (BibiInterface *iface)
{
}
typedef struct {
GTypeInterface g_iface;
} BozoInterface;
GType bozo_get_type (void);
G_DEFINE_INTERFACE_WITH_CODE (Bozo, bozo, G_TYPE_INVALID,
g_type_interface_add_prerequisite (g_define_type_id, foo_get_type ());
g_type_interface_add_prerequisite (g_define_type_id, bibi_get_type ()))
static void
bozo_default_init (BozoInterface *iface)
{
}
static void
test_interface_prerequisite (void)
{
GType *prereqs;
guint n_prereqs;
gpointer iface;
gpointer parent;
prereqs = g_type_interface_prerequisites (foo_get_type (), &n_prereqs);
g_assert_cmpint (n_prereqs, ==, 2);
g_assert (prereqs[0] == bar_get_type ());
g_assert (prereqs[1] == G_TYPE_OBJECT);
g_assert (g_type_interface_instantiatable_prerequisite (foo_get_type ()) == G_TYPE_OBJECT);
iface = g_type_default_interface_ref (foo_get_type ());
parent = g_type_interface_peek_parent (iface);
g_assert (parent == NULL);
g_type_default_interface_unref (iface);
g_free (prereqs);
g_assert_cmpint (g_type_interface_instantiatable_prerequisite (baa_get_type ()), ==, G_TYPE_INVALID);
g_assert_cmpint (g_type_interface_instantiatable_prerequisite (boo_get_type ()), ==, G_TYPE_INVALID);
g_assert_cmpint (g_type_interface_instantiatable_prerequisite (bozo_get_type ()), ==, G_TYPE_INITIALLY_UNOWNED);
}
typedef struct {
GTypeInterface g_iface;
} BazInterface;
GType baz_get_type (void);
G_DEFINE_INTERFACE (Baz, baz, G_TYPE_OBJECT)
static void
baz_default_init (BazInterface *iface)
{
}
typedef struct {
GObject parent;
} Bazo;
typedef struct {
GObjectClass parent_class;
} BazoClass;
GType bazo_get_type (void);
static void bazo_iface_init (BazInterface *i);
G_DEFINE_TYPE_WITH_CODE (Bazo, bazo, G_TYPE_INITIALLY_UNOWNED,
G_IMPLEMENT_INTERFACE (baz_get_type (),
bazo_iface_init);)
static void
bazo_init (Bazo *b)
{
}
static void
bazo_class_init (BazoClass *c)
{
}
static void
bazo_iface_init (BazInterface *i)
{
}
static gint check_called;
static void
check_func (gpointer check_data,
gpointer g_iface)
{
g_assert (check_data == &check_called);
check_called++;
}
static void
test_interface_check (void)
{
GObject *o;
check_called = 0;
g_type_add_interface_check (&check_called, check_func);
o = g_object_new (bazo_get_type (), NULL);
g_object_unref (o);
g_assert_cmpint (check_called, ==, 1);
g_type_remove_interface_check (&check_called, check_func);
}
static void
test_next_base (void)
{
GType type;
type = g_type_next_base (bazo_get_type (), G_TYPE_OBJECT);
g_assert (type == G_TYPE_INITIALLY_UNOWNED);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/type/registration-serial", test_registration_serial);
g_test_add_func ("/type/interface-prerequisite", test_interface_prerequisite);
g_test_add_func ("/type/interface-check", test_interface_check);
g_test_add_func ("/type/next-base", test_next_base);
return g_test_run ();
}

752
gobject/tests/value.c Normal file
View file

@ -0,0 +1,752 @@
/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_30
#include <glib.h>
#include <glib-object.h>
#include "gobject/gvaluecollector.h"
static void
test_enum_transformation (void)
{
GType type;
GValue orig = G_VALUE_INIT;
GValue xform = G_VALUE_INIT;
GEnumValue values[] = { {0,"0","0"}, {1,"1","1"}};
type = g_enum_register_static ("TestEnum", values);
g_value_init (&orig, type);
g_value_set_enum (&orig, 1);
memset (&xform, 0, sizeof (GValue));
g_value_init (&xform, G_TYPE_CHAR);
g_value_transform (&orig, &xform);
g_assert_cmpint (g_value_get_char (&xform), ==, 1);
g_assert_cmpint (g_value_get_schar (&xform), ==, 1);
memset (&xform, 0, sizeof (GValue));
g_value_init (&xform, G_TYPE_UCHAR);
g_value_transform (&orig, &xform);
g_assert_cmpint (g_value_get_uchar (&xform), ==, 1);
memset (&xform, 0, sizeof (GValue));
g_value_init (&xform, G_TYPE_INT);
g_value_transform (&orig, &xform);
g_assert_cmpint (g_value_get_int (&xform), ==, 1);
memset (&xform, 0, sizeof (GValue));
g_value_init (&xform, G_TYPE_UINT);
g_value_transform (&orig, &xform);
g_assert_cmpint (g_value_get_uint (&xform), ==, 1);
memset (&xform, 0, sizeof (GValue));
g_value_init (&xform, G_TYPE_LONG);
g_value_transform (&orig, &xform);
g_assert_cmpint (g_value_get_long (&xform), ==, 1);
memset (&xform, 0, sizeof (GValue));
g_value_init (&xform, G_TYPE_ULONG);
g_value_transform (&orig, &xform);
g_assert_cmpint (g_value_get_ulong (&xform), ==, 1);
memset (&xform, 0, sizeof (GValue));
g_value_init (&xform, G_TYPE_INT64);
g_value_transform (&orig, &xform);
g_assert_cmpint (g_value_get_int64 (&xform), ==, 1);
memset (&xform, 0, sizeof (GValue));
g_value_init (&xform, G_TYPE_UINT64);
g_value_transform (&orig, &xform);
g_assert_cmpint (g_value_get_uint64 (&xform), ==, 1);
}
static void
test_gtype_value (void)
{
GType type;
GValue value = G_VALUE_INIT;
GValue copy = G_VALUE_INIT;
g_value_init (&value, G_TYPE_GTYPE);
g_value_set_gtype (&value, G_TYPE_BOXED);
type = g_value_get_gtype (&value);
g_assert_true (type == G_TYPE_BOXED);
g_value_init (&copy, G_TYPE_GTYPE);
g_value_copy (&value, &copy);
type = g_value_get_gtype (&copy);
g_assert_true (type == G_TYPE_BOXED);
}
static gchar *
collect (GValue *value, ...)
{
gchar *error;
va_list var_args;
error = NULL;
va_start (var_args, value);
G_VALUE_COLLECT (value, var_args, 0, &error);
va_end (var_args);
return error;
}
static gchar *
lcopy (GValue *value, ...)
{
gchar *error;
va_list var_args;
error = NULL;
va_start (var_args, value);
G_VALUE_LCOPY (value, var_args, 0, &error);
va_end (var_args);
return error;
}
static void
test_collection (void)
{
GValue value = G_VALUE_INIT;
gchar *error;
g_value_init (&value, G_TYPE_CHAR);
error = collect (&value, 'c');
g_assert_null (error);
g_assert_cmpint (g_value_get_char (&value), ==, 'c');
g_value_unset (&value);
g_value_init (&value, G_TYPE_UCHAR);
error = collect (&value, 129);
g_assert_null (error);
g_assert_cmpint (g_value_get_uchar (&value), ==, 129);
g_value_unset (&value);
g_value_init (&value, G_TYPE_BOOLEAN);
error = collect (&value, TRUE);
g_assert_null (error);
g_assert_cmpint (g_value_get_boolean (&value), ==, TRUE);
g_value_unset (&value);
g_value_init (&value, G_TYPE_INT);
error = collect (&value, G_MAXINT);
g_assert_null (error);
g_assert_cmpint (g_value_get_int (&value), ==, G_MAXINT);
g_value_unset (&value);
g_value_init (&value, G_TYPE_UINT);
error = collect (&value, G_MAXUINT);
g_assert_null (error);
g_assert_cmpuint (g_value_get_uint (&value), ==, G_MAXUINT);
g_value_unset (&value);
g_value_init (&value, G_TYPE_LONG);
error = collect (&value, G_MAXLONG);
g_assert_null (error);
g_assert_cmpint (g_value_get_long (&value), ==, G_MAXLONG);
g_value_unset (&value);
g_value_init (&value, G_TYPE_ULONG);
error = collect (&value, G_MAXULONG);
g_assert_null (error);
g_assert_cmpuint (g_value_get_ulong (&value), ==, G_MAXULONG);
g_value_unset (&value);
g_value_init (&value, G_TYPE_INT64);
error = collect (&value, G_MAXINT64);
g_assert_null (error);
g_assert_cmpint (g_value_get_int64 (&value), ==, G_MAXINT64);
g_value_unset (&value);
g_value_init (&value, G_TYPE_UINT64);
error = collect (&value, G_MAXUINT64);
g_assert_null (error);
g_assert_cmpuint (g_value_get_uint64 (&value), ==, G_MAXUINT64);
g_value_unset (&value);
g_value_init (&value, G_TYPE_FLOAT);
error = collect (&value, G_MAXFLOAT);
g_assert_null (error);
g_assert_cmpfloat (g_value_get_float (&value), ==, G_MAXFLOAT);
g_value_unset (&value);
g_value_init (&value, G_TYPE_DOUBLE);
error = collect (&value, G_MAXDOUBLE);
g_assert_null (error);
g_assert_cmpfloat (g_value_get_double (&value), ==, G_MAXDOUBLE);
g_value_unset (&value);
g_value_init (&value, G_TYPE_STRING);
error = collect (&value, "string ?");
g_assert_null (error);
g_assert_cmpstr (g_value_get_string (&value), ==, "string ?");
g_value_unset (&value);
g_value_init (&value, G_TYPE_GTYPE);
error = collect (&value, G_TYPE_BOXED);
g_assert_null (error);
g_assert_true (g_value_get_gtype (&value) == G_TYPE_BOXED);
g_value_unset (&value);
g_value_init (&value, G_TYPE_VARIANT);
error = collect (&value, g_variant_new_uint32 (42));
g_assert_null (error);
g_assert_true (g_variant_is_of_type (g_value_get_variant (&value),
G_VARIANT_TYPE ("u")));
g_assert_cmpuint (g_variant_get_uint32 (g_value_get_variant (&value)), ==, 42);
g_value_unset (&value);
}
static void
test_copying (void)
{
GValue value = G_VALUE_INIT;
gchar *error;
{
gchar c = 0;
g_value_init (&value, G_TYPE_CHAR);
g_value_set_char (&value, 'c');
error = lcopy (&value, &c);
g_assert_null (error);
g_assert_cmpint (c, ==, 'c');
}
{
guchar c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_UCHAR);
g_value_set_uchar (&value, 129);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert_cmpint (c, ==, 129);
}
{
gint c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, G_MAXINT);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert_cmpint (c, ==, G_MAXINT);
}
{
guint c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_UINT);
g_value_set_uint (&value, G_MAXUINT);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert_cmpuint (c, ==, G_MAXUINT);
}
{
glong c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_LONG);
g_value_set_long (&value, G_MAXLONG);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert (c == G_MAXLONG);
}
{
gulong c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_ULONG);
g_value_set_ulong (&value, G_MAXULONG);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert (c == G_MAXULONG);
}
{
gint64 c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_INT64);
g_value_set_int64 (&value, G_MAXINT64);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert (c == G_MAXINT64);
}
{
guint64 c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_UINT64);
g_value_set_uint64 (&value, G_MAXUINT64);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert (c == G_MAXUINT64);
}
{
gfloat c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_FLOAT);
g_value_set_float (&value, G_MAXFLOAT);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert (c == G_MAXFLOAT);
}
{
gdouble c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_DOUBLE);
g_value_set_double (&value, G_MAXDOUBLE);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert (c == G_MAXDOUBLE);
}
{
gchar *c = NULL;
g_value_unset (&value);
g_value_init (&value, G_TYPE_STRING);
g_value_set_string (&value, "string ?");
error = lcopy (&value, &c);
g_assert_null (error);
g_assert_cmpstr (c, ==, "string ?");
g_free (c);
}
{
GType c = G_TYPE_NONE;
g_value_unset (&value);
g_value_init (&value, G_TYPE_GTYPE);
g_value_set_gtype (&value, G_TYPE_BOXED);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert_true (c == G_TYPE_BOXED);
}
{
GVariant *c = NULL;
g_value_unset (&value);
g_value_init (&value, G_TYPE_VARIANT);
g_value_set_variant (&value, g_variant_new_uint32 (42));
error = lcopy (&value, &c);
g_assert_null (error);
g_assert_nonnull (c);
g_assert (g_variant_is_of_type (c, G_VARIANT_TYPE ("u")));
g_assert_cmpuint (g_variant_get_uint32 (c), ==, 42);
g_variant_unref (c);
g_value_unset (&value);
}
}
static void
test_value_basic (void)
{
GValue value = G_VALUE_INIT;
g_assert_false (G_IS_VALUE (&value));
g_assert_false (G_VALUE_HOLDS_INT (&value));
g_value_unset (&value);
g_assert_false (G_IS_VALUE (&value));
g_assert_false (G_VALUE_HOLDS_INT (&value));
g_value_init (&value, G_TYPE_INT);
g_assert_true (G_IS_VALUE (&value));
g_assert_true (G_VALUE_HOLDS_INT (&value));
g_assert_false (G_VALUE_HOLDS_UINT (&value));
g_assert_cmpint (g_value_get_int (&value), ==, 0);
g_value_set_int (&value, 10);
g_assert_cmpint (g_value_get_int (&value), ==, 10);
g_value_reset (&value);
g_assert_true (G_IS_VALUE (&value));
g_assert_true (G_VALUE_HOLDS_INT (&value));
g_assert_cmpint (g_value_get_int (&value), ==, 0);
g_value_unset (&value);
g_assert_false (G_IS_VALUE (&value));
g_assert_false (G_VALUE_HOLDS_INT (&value));
}
static void
test_value_string (void)
{
const gchar *static1 = "static1";
const gchar *static2 = "static2";
const gchar *storedstr;
const gchar *copystr;
gchar *str1, *str2;
GValue value = G_VALUE_INIT;
GValue copy = G_VALUE_INIT;
g_test_summary ("Test that G_TYPE_STRING GValue copy properly");
/*
* Regular strings (ownership not passed)
*/
/* Create a regular string gvalue and make sure it copies the provided string */
g_value_init (&value, G_TYPE_STRING);
g_assert_true (G_VALUE_HOLDS_STRING (&value));
/* The string contents should be empty at this point */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr == NULL);
g_value_set_string (&value, static1);
/* The contents should be a copy of the same string */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr != static1);
g_assert_cmpstr (storedstr, ==, static1);
/* Check g_value_dup_string() provides a copy */
str1 = g_value_dup_string (&value);
g_assert_true (storedstr != str1);
g_assert_cmpstr (str1, ==, static1);
g_free (str1);
/* Copying a regular string gvalue should copy the contents */
g_value_init (&copy, G_TYPE_STRING);
g_value_copy (&value, &copy);
copystr = g_value_get_string (&copy);
g_assert_true (copystr != storedstr);
g_assert_cmpstr (copystr, ==, static1);
g_value_unset (&copy);
/* Setting a new string should change the contents */
g_value_set_string (&value, static2);
/* The contents should be a copy of that *new* string */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr != static2);
g_assert_cmpstr (storedstr, ==, static2);
/* Setting a static string over that should also change it (test for
* coverage and valgrind) */
g_value_set_static_string (&value, static1);
storedstr = g_value_get_string (&value);
g_assert_true (storedstr != static2);
g_assert_cmpstr (storedstr, ==, static1);
/* Giving a string directly (ownership passed) should replace the content */
str2 = g_strdup (static2);
g_value_take_string (&value, str2);
storedstr = g_value_get_string (&value);
g_assert_true (storedstr != static2);
g_assert_cmpstr (storedstr, ==, str2);
g_value_unset (&value);
/*
* Regular strings (ownership passed)
*/
g_value_init (&value, G_TYPE_STRING);
g_assert_true (G_VALUE_HOLDS_STRING (&value));
str1 = g_strdup (static1);
g_value_take_string (&value, str1);
/* The contents should be the string we provided */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr == str1);
/* But g_value_dup_string() should provide a copy */
str2 = g_value_dup_string (&value);
g_assert_true (storedstr != str2);
g_assert_cmpstr (str2, ==, static1);
g_free (str2);
/* Copying a regular string gvalue (even with ownership passed) should copy
* the contents */
g_value_init (&copy, G_TYPE_STRING);
g_value_copy (&value, &copy);
copystr = g_value_get_string (&copy);
g_assert_true (copystr != storedstr);
g_assert_cmpstr (copystr, ==, static1);
g_value_unset (&copy);
/* Setting a new regular string should change the contents */
g_value_set_string (&value, static2);
/* The contents should be a copy of that *new* string */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr != static2);
g_assert_cmpstr (storedstr, ==, static2);
g_value_unset (&value);
/*
* Static strings
*/
g_value_init (&value, G_TYPE_STRING);
g_assert_true (G_VALUE_HOLDS_STRING (&value));
g_value_set_static_string (&value, static1);
/* The contents should be the string we provided */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr == static1);
/* But g_value_dup_string() should provide a copy */
str2 = g_value_dup_string (&value);
g_assert_true (storedstr != str2);
g_assert_cmpstr (str2, ==, static1);
g_free (str2);
/* Copying a static string gvalue should *actually* copy the contents */
g_value_init (&copy, G_TYPE_STRING);
g_value_copy (&value, &copy);
copystr = g_value_get_string (&copy);
g_assert_true (copystr != static1);
g_value_unset (&copy);
/* Setting a new string should change the contents */
g_value_set_static_string (&value, static2);
/* The contents should be a copy of that *new* string */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr != static1);
g_assert_cmpstr (storedstr, ==, static2);
g_value_unset (&value);
/*
* Interned/Canonical strings
*/
static1 = g_intern_static_string (static1);
g_value_init (&value, G_TYPE_STRING);
g_assert_true (G_VALUE_HOLDS_STRING (&value));
g_value_set_interned_string (&value, static1);
g_assert_true (G_VALUE_IS_INTERNED_STRING (&value));
/* The contents should be the string we provided */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr == static1);
/* But g_value_dup_string() should provide a copy */
str2 = g_value_dup_string (&value);
g_assert_true (storedstr != str2);
g_assert_cmpstr (str2, ==, static1);
g_free (str2);
/* Copying an interned string gvalue should *not* copy the contents
* and should still be an interned string */
g_value_init (&copy, G_TYPE_STRING);
g_value_copy (&value, &copy);
g_assert_true (G_VALUE_IS_INTERNED_STRING (&copy));
copystr = g_value_get_string (&copy);
g_assert_true (copystr == static1);
g_value_unset (&copy);
/* Setting a new interned string should change the contents */
static2 = g_intern_static_string (static2);
g_value_set_interned_string (&value, static2);
g_assert_true (G_VALUE_IS_INTERNED_STRING (&value));
/* The contents should be the interned string */
storedstr = g_value_get_string (&value);
g_assert_cmpstr (storedstr, ==, static2);
/* Setting a new regular string should change the contents */
g_value_set_string (&value, static2);
g_assert_false (G_VALUE_IS_INTERNED_STRING (&value));
/* The contents should be a copy of that *new* string */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr != static2);
g_assert_cmpstr (storedstr, ==, static2);
g_value_unset (&value);
}
static gint
cmpint (gconstpointer a, gconstpointer b)
{
const GValue *aa = a;
const GValue *bb = b;
return g_value_get_int (aa) - g_value_get_int (bb);
}
static void
test_valuearray_basic (void)
{
GValueArray *a;
GValueArray *a2;
GValue v = G_VALUE_INIT;
GValue *p;
guint i;
a = g_value_array_new (20);
g_value_init (&v, G_TYPE_INT);
for (i = 0; i < 100; i++)
{
g_value_set_int (&v, i);
g_value_array_append (a, &v);
}
g_assert_cmpint (a->n_values, ==, 100);
p = g_value_array_get_nth (a, 5);
g_assert_cmpint (g_value_get_int (p), ==, 5);
for (i = 20; i < 100; i+= 5)
g_value_array_remove (a, 100 - i);
for (i = 100; i < 150; i++)
{
g_value_set_int (&v, i);
g_value_array_prepend (a, &v);
}
g_value_array_sort (a, cmpint);
for (i = 0; i < a->n_values - 1; i++)
g_assert_cmpint (g_value_get_int (&a->values[i]), <=, g_value_get_int (&a->values[i+1]));
a2 = g_value_array_copy (a);
for (i = 0; i < a->n_values; i++)
g_assert_cmpint (g_value_get_int (&a->values[i]), ==, g_value_get_int (&a2->values[i]));
g_value_array_free (a);
g_value_array_free (a2);
}
/* We create some dummy objects with this relationship:
*
* GObject TestInterface
* / \ / /
* TestObjectA TestObjectB /
* / \ /
* TestObjectA1 TestObjectA2-------
*
* ie: TestObjectA1 and TestObjectA2 are subclasses of TestObjectA
* and TestObjectB is related to neither. TestObjectA2 and TestObjectB
* implement TestInterface
*/
typedef GTypeInterface TestInterfaceInterface;
static GType test_interface_get_type (void);
G_DEFINE_INTERFACE (TestInterface, test_interface, G_TYPE_OBJECT)
static void test_interface_default_init (TestInterfaceInterface *iface) { }
static GType test_object_a_get_type (void);
typedef GObject TestObjectA; typedef GObjectClass TestObjectAClass;
G_DEFINE_TYPE (TestObjectA, test_object_a, G_TYPE_OBJECT)
static void test_object_a_class_init (TestObjectAClass *class) { }
static void test_object_a_init (TestObjectA *a) { }
static GType test_object_b_get_type (void);
typedef GObject TestObjectB; typedef GObjectClass TestObjectBClass;
static void test_object_b_iface_init (TestInterfaceInterface *iface) { }
G_DEFINE_TYPE_WITH_CODE (TestObjectB, test_object_b, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (test_interface_get_type (), test_object_b_iface_init))
static void test_object_b_class_init (TestObjectBClass *class) { }
static void test_object_b_init (TestObjectB *b) { }
static GType test_object_a1_get_type (void);
typedef GObject TestObjectA1; typedef GObjectClass TestObjectA1Class;
G_DEFINE_TYPE (TestObjectA1, test_object_a1, test_object_a_get_type ())
static void test_object_a1_class_init (TestObjectA1Class *class) { }
static void test_object_a1_init (TestObjectA1 *c) { }
static GType test_object_a2_get_type (void);
typedef GObject TestObjectA2; typedef GObjectClass TestObjectA2Class;
static void test_object_a2_iface_init (TestInterfaceInterface *iface) { }
G_DEFINE_TYPE_WITH_CODE (TestObjectA2, test_object_a2, test_object_a_get_type (),
G_IMPLEMENT_INTERFACE (test_interface_get_type (), test_object_a2_iface_init))
static void test_object_a2_class_init (TestObjectA2Class *class) { }
static void test_object_a2_init (TestObjectA2 *b) { }
static void
test_value_transform_object (void)
{
GValue src = G_VALUE_INIT;
GValue dest = G_VALUE_INIT;
GObject *object;
guint i, s, d;
GType types[] = {
G_TYPE_OBJECT,
test_interface_get_type (),
test_object_a_get_type (),
test_object_b_get_type (),
test_object_a1_get_type (),
test_object_a2_get_type ()
};
for (i = 0; i < G_N_ELEMENTS (types); i++)
{
if (!G_TYPE_IS_CLASSED (types[i]))
continue;
object = g_object_new (types[i], NULL);
for (s = 0; s < G_N_ELEMENTS (types); s++)
{
if (!G_TYPE_CHECK_INSTANCE_TYPE (object, types[s]))
continue;
g_value_init (&src, types[s]);
g_value_set_object (&src, object);
for (d = 0; d < G_N_ELEMENTS (types); d++)
{
g_test_message ("Next: %s object in GValue of %s to GValue of %s", g_type_name (types[i]), g_type_name (types[s]), g_type_name (types[d]));
g_assert_true (g_value_type_transformable (types[s], types[d]));
g_value_init (&dest, types[d]);
g_assert_true (g_value_transform (&src, &dest));
g_assert_cmpint (g_value_get_object (&dest) != NULL, ==, G_TYPE_CHECK_INSTANCE_TYPE (object, types[d]));
g_value_unset (&dest);
}
g_value_unset (&src);
}
g_object_unref (object);
}
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/value/basic", test_value_basic);
g_test_add_func ("/value/array/basic", test_valuearray_basic);
g_test_add_func ("/value/collection", test_collection);
g_test_add_func ("/value/copying", test_copying);
g_test_add_func ("/value/enum-transformation", test_enum_transformation);
g_test_add_func ("/value/gtype", test_gtype_value);
g_test_add_func ("/value/string", test_value_string);
g_test_add_func ("/value/transform-object", test_value_transform_object);
return g_test_run ();
}