Import Upstream version 2.72.4
This commit is contained in:
commit
4ef3ff9793
2003 changed files with 1332420 additions and 0 deletions
172
gobject/gatomicarray.c
Normal file
172
gobject/gatomicarray.c
Normal 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
58
gobject/gatomicarray.h
Normal 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
1637
gobject/gbinding.c
Normal file
File diff suppressed because it is too large
Load diff
154
gobject/gbinding.h
Normal file
154
gobject/gbinding.h
Normal 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
679
gobject/gbindinggroup.c
Normal 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
85
gobject/gbindinggroup.h
Normal 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
565
gobject/gboxed.c
Normal 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
|
||||
* type’s ‘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 caller’s reference to @v_boxed;
|
||||
* the caller doesn’t 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
122
gobject/gboxed.h
Normal 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
1990
gobject/gclosure.c
Normal file
File diff suppressed because it is too large
Load diff
321
gobject/gclosure.h
Normal file
321
gobject/gclosure.h
Normal 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
760
gobject/genums.c
Normal 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 enum’s 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
279
gobject/genums.h
Normal 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__ */
|
||||
47
gobject/glib-enumtypes.c.template
Normal file
47
gobject/glib-enumtypes.c.template
Normal 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 ***/
|
||||
24
gobject/glib-enumtypes.h.template
Normal file
24
gobject/glib-enumtypes.h.template
Normal 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
1080
gobject/glib-genmarshal.in
Executable file
File diff suppressed because it is too large
Load diff
806
gobject/glib-mkenums.in
Executable file
806
gobject/glib-mkenums.in
Executable 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
395
gobject/glib-types.h
Normal 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);
|
||||
* /* do something with writers */
|
||||
* 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
2521
gobject/gmarshal.c
Normal file
File diff suppressed because it is too large
Load diff
434
gobject/gmarshal.h
Normal file
434
gobject/gmarshal.h
Normal 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__ */
|
||||
31
gobject/gobject-autocleanups.h
Normal file
31
gobject/gobject-autocleanups.h
Normal 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
223
gobject/gobject-query.c
Normal 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
4871
gobject/gobject.c
Normal file
File diff suppressed because it is too large
Load diff
946
gobject/gobject.h
Normal file
946
gobject/gobject.h
Normal 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 it’s 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
30
gobject/gobject.rc.in
Normal 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
199
gobject/gobject.stp.in
Normal 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
352
gobject/gobject_gdb.py
Normal 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
13
gobject/gobject_probes.d
Normal 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
43
gobject/gobject_trace.h
Normal 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__ */
|
||||
197
gobject/gobjectnotifyqueue.c
Normal file
197
gobject/gobjectnotifyqueue.c
Normal 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
1619
gobject/gparam.c
Normal file
File diff suppressed because it is too large
Load diff
458
gobject/gparam.h
Normal file
458
gobject/gparam.h
Normal 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
2622
gobject/gparamspecs.c
Normal file
File diff suppressed because it is too large
Load diff
1173
gobject/gparamspecs.h
Normal file
1173
gobject/gparamspecs.h
Normal file
File diff suppressed because it is too large
Load diff
4075
gobject/gsignal.c
Normal file
4075
gobject/gsignal.c
Normal file
File diff suppressed because it is too large
Load diff
640
gobject/gsignal.h
Normal file
640
gobject/gsignal.h
Normal 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
912
gobject/gsignalgroup.c
Normal 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
93
gobject/gsignalgroup.h
Normal 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
322
gobject/gsourceclosure.c
Normal 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 (¶ms[0], G_TYPE_IO_CHANNEL);
|
||||
g_value_set_boxed (¶ms[0], channel);
|
||||
|
||||
g_value_init (¶ms[1], G_TYPE_IO_CONDITION);
|
||||
g_value_set_flags (¶ms[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 (¶ms[0]);
|
||||
g_value_unset (¶ms[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 (¶ms[0], G_TYPE_ULONG);
|
||||
g_value_set_ulong (¶ms[0], pid);
|
||||
#endif
|
||||
#ifdef G_OS_WIN32
|
||||
g_value_init (¶ms[0], G_TYPE_POINTER);
|
||||
g_value_set_pointer (¶ms[0], pid);
|
||||
#endif
|
||||
|
||||
g_value_init (¶ms[1], G_TYPE_INT);
|
||||
g_value_set_int (¶ms[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 (¶ms[0]);
|
||||
g_value_unset (¶ms[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 (¶ms[0], G_TYPE_INT);
|
||||
g_value_set_int (¶ms[0], fd);
|
||||
|
||||
g_value_init (¶ms[1], G_TYPE_IO_CONDITION);
|
||||
g_value_set_flags (¶ms[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 (¶ms[0]);
|
||||
g_value_unset (¶ms[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
38
gobject/gsourceclosure.h
Normal 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
113
gobject/gtype-private.h
Normal 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
5024
gobject/gtype.c
Normal file
File diff suppressed because it is too large
Load diff
2515
gobject/gtype.h
Normal file
2515
gobject/gtype.h
Normal file
File diff suppressed because it is too large
Load diff
602
gobject/gtypemodule.c
Normal file
602
gobject/gtypemodule.c
Normal 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
300
gobject/gtypemodule.h
Normal 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
203
gobject/gtypeplugin.c
Normal 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
134
gobject/gtypeplugin.h
Normal 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
675
gobject/gvalue.c
Normal 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
210
gobject/gvalue.h
Normal 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
370
gobject/gvaluearray.c
Normal 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
104
gobject/gvaluearray.h
Normal 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
265
gobject/gvaluecollector.h
Normal 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 value’s 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
428
gobject/gvaluetransform.c
Normal 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
1471
gobject/gvaluetypes.c
Normal file
File diff suppressed because it is too large
Load diff
316
gobject/gvaluetypes.h
Normal file
316
gobject/gvaluetypes.h
Normal 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__ */
|
||||
10
gobject/libgobject-gdb.py.in
Normal file
10
gobject/libgobject-gdb.py.in
Normal 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
190
gobject/meson.build
Normal 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
18
gobject/tests/.gitignore
vendored
Normal 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
237
gobject/tests/autoptr.c
Normal 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
1102
gobject/tests/binding.c
Normal file
File diff suppressed because it is too large
Load diff
694
gobject/tests/bindinggroup.c
Normal file
694
gobject/tests/bindinggroup.c
Normal 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
704
gobject/tests/boxed.c
Normal 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 ();
|
||||
}
|
||||
337
gobject/tests/closure-refcount.c
Normal file
337
gobject/tests/closure-refcount.c
Normal 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
|
||||
* don’t terminate until at least 10000 iterations have completed in both
|
||||
* thread1 and thread2. Even though @n_iterations is high, we can’t 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
223
gobject/tests/closure.c
Normal 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
26
gobject/tests/cxx.cpp
Normal 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;
|
||||
}
|
||||
368
gobject/tests/dynamictests.c
Normal file
368
gobject/tests/dynamictests.c
Normal 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
170
gobject/tests/enums.c
Normal 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
179
gobject/tests/flags.c
Normal 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
817
gobject/tests/genmarshal.py
Normal 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())
|
||||
645
gobject/tests/ifaceproperties.c
Normal file
645
gobject/tests/ifaceproperties.c
Normal 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 ();
|
||||
}
|
||||
3
gobject/tests/marshalers.list
Normal file
3
gobject/tests/marshalers.list
Normal 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
161
gobject/tests/meson.build
Normal 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
739
gobject/tests/mkenums.py
Normal 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
150
gobject/tests/object.c
Normal 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
1266
gobject/tests/param.c
Normal file
File diff suppressed because it is too large
Load diff
261
gobject/tests/private.c
Normal file
261
gobject/tests/private.c
Normal 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
658
gobject/tests/properties.c
Normal 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
123
gobject/tests/qdata.c
Normal 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
973
gobject/tests/reference.c
Normal 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 shouldn’t 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 shouldn’t 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() doesn’t 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 ();
|
||||
}
|
||||
299
gobject/tests/signal-handler.c
Normal file
299
gobject/tests/signal-handler.c
Normal 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
650
gobject/tests/signalgroup.c
Normal 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", >ype,
|
||||
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
1828
gobject/tests/signals.c
Normal file
File diff suppressed because it is too large
Load diff
186
gobject/tests/taptestrunner.py
Normal file
186
gobject/tests/taptestrunner.py
Normal 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
105
gobject/tests/testcommon.h
Normal 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
71
gobject/tests/testing.c
Normal 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
525
gobject/tests/threadtests.c
Normal 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();
|
||||
}
|
||||
88
gobject/tests/type-flags.c
Normal file
88
gobject/tests/type-flags.c
Normal 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
215
gobject/tests/type.c
Normal 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
752
gobject/tests/value.c
Normal 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 (©, G_TYPE_GTYPE);
|
||||
g_value_copy (&value, ©);
|
||||
type = g_value_get_gtype (©);
|
||||
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 (©, G_TYPE_STRING);
|
||||
g_value_copy (&value, ©);
|
||||
copystr = g_value_get_string (©);
|
||||
g_assert_true (copystr != storedstr);
|
||||
g_assert_cmpstr (copystr, ==, static1);
|
||||
g_value_unset (©);
|
||||
|
||||
/* 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 (©, G_TYPE_STRING);
|
||||
g_value_copy (&value, ©);
|
||||
copystr = g_value_get_string (©);
|
||||
g_assert_true (copystr != storedstr);
|
||||
g_assert_cmpstr (copystr, ==, static1);
|
||||
g_value_unset (©);
|
||||
|
||||
/* 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 (©, G_TYPE_STRING);
|
||||
g_value_copy (&value, ©);
|
||||
copystr = g_value_get_string (©);
|
||||
g_assert_true (copystr != static1);
|
||||
g_value_unset (©);
|
||||
|
||||
/* 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 (©, G_TYPE_STRING);
|
||||
g_value_copy (&value, ©);
|
||||
g_assert_true (G_VALUE_IS_INTERNED_STRING (©));
|
||||
copystr = g_value_get_string (©);
|
||||
g_assert_true (copystr == static1);
|
||||
g_value_unset (©);
|
||||
|
||||
/* 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 ();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue