diff options
author | Tim Janik <timj@gtk.org> | 2000-10-25 20:36:35 +0000 |
---|---|---|
committer | Tim Janik <timj@src.gnome.org> | 2000-10-25 20:36:35 +0000 |
commit | ee23c09e83d06a0d131ebd234c132f1c3602c019 (patch) | |
tree | b24f3cdd0758fed1a483abc645936ebae963b898 /gobject | |
parent | b13320f78ba3b484a26b6430152b35aaac24ff10 (diff) |
added newly added gobject/ headers.
Tue Oct 24 22:09:14 2000 Tim Janik <timj@gtk.org>
* glib-object.h: added newly added gobject/ headers.
* gmesage.c: print g_message() output to stderr instead of stdout.
Wed Oct 25 20:27:02 2000 Tim Janik <timj@gtk.org>
* gtype.c (g_type_free_instance): for the moment, freeing object
structures will fill their memory portion with 0xAA. there's a
FIXME there, remove this line at a later point.
Tue Oct 24 23:10:26 2000 Tim Janik <timj@gtk.org>
* glib-genmarshal.1:
* glib-genmarshal.c: added publically installed marshaller generator.
* gtype.h: added G_TYPE_INSTANCE_GET_INTERFACE() to retrive a certain
interface VTable from instances.
Mon Oct 23 08:28:15 2000 Tim Janik <timj@gtk.org>
* gobject.[hc]: new functions for closure maintenance:
(g_object_watch_closure): maintain validity of the object and
the closure for objects that are used as data part of a closure.
(g_cclosure_new_object): convenience function to create C closures
that have an object as data argument.
(g_closure_new_object): convenience function to create closures
that have an object as data argument.
* gclosure.[hc]: implementation of GClosure mechanism.
a closure is basically an encapsulation of a callback function
and its environment. ideally, most places supporting callback
functions will simply take a GClosure* pointer and thus unify
callback environments wrg destroy notification etc.
GClosure provides destroy notifiers for arbitrary data pointers,
reference counting, invalidation notification (it can be invalidated
which is merely a deactivate state) and a marshallinbg abstraction.
GCClosure is also provided in these files, they present a specialized
GClosure implementation for C language callbacks.
* genum.c: macro cleanups.
* gboxed.[hc]: new files, for boxed type abstraction.
(g_boxed_copy): copy a boxed structure
(g_boxed_free): free a boxed structure
(g_value_set_boxed):
(g_value_get_boxed): standard GValue functions for boxed types
(g_boxed_type_register_static): convenience function for easy
introduction of new G_TYPE_BOXED derivatives.
* gparam.[hc]: introduced g_param_type_register_static(), a short hand
for creation of new GParamSpec derived types.
* gtype.[hc]: many fixes, introduced ability to flag individual
type nodes as ABSTRACT upon registration, added value_peek_pointer()
to the value table to peek at GValue contents as a pointer for types
that support this. fixed up GValue checks.
* gvalue.[hc]: added g_value_fits_pointer() and g_value_get_as_pointer()
to peek at the value contents as pointer.
* *.[hc]: adaptions to type macro fixes and changes in the type
registration API.
* many const corrections over the place.
Sat Oct 21 02:49:56 2000 Tim Janik <timj@gtk.org>
* gtype.c (g_type_conforms_to): this function basically behaves like
and is_a check, except that it _additionally_ features interfaces
for instantiatable types. enforce this in the second branch as well
(`type' conforms_to `type') even if `type' is not an interface type.
Fri Oct 20 15:31:04 2000 Tim Janik <timj@gtk.org>
* gvaluetypes.[hc]: added G_TYPE_POINTER implementation from jrb.
* gtype.[hc]:
* gobject.c:
* gvaluetypes.c: added GTypeValueTable.value_peek_pointer and
suitable implementations of this for G_TYPE_STRING, G_TYPE_OBJECT
and G_TYPE_POINTER.
Mon Aug 21 04:13:37 2000 Tim Janik <timj@gtk.org>
* gbsearcharray.[hc]: long standing needed generic implementation
of a binary searchable, sorted and dynamically sized array.
Diffstat (limited to 'gobject')
-rw-r--r-- | gobject/ChangeLog | 87 | ||||
-rw-r--r-- | gobject/Makefile.am | 46 | ||||
-rw-r--r-- | gobject/gboxed.c | 316 | ||||
-rw-r--r-- | gobject/gboxed.h | 64 | ||||
-rw-r--r-- | gobject/gbsearcharray.c | 164 | ||||
-rw-r--r-- | gobject/gbsearcharray.h | 134 | ||||
-rw-r--r-- | gobject/gclosure.c | 534 | ||||
-rw-r--r-- | gobject/gclosure.h | 185 | ||||
-rw-r--r-- | gobject/genums.c | 187 | ||||
-rw-r--r-- | gobject/genums.h | 8 | ||||
-rw-r--r-- | gobject/glib-genmarshal.1 | 196 | ||||
-rw-r--r-- | gobject/glib-genmarshal.c | 693 | ||||
-rw-r--r-- | gobject/gobject-query.c | 2 | ||||
-rw-r--r-- | gobject/gobject.c | 144 | ||||
-rw-r--r-- | gobject/gobject.h | 73 | ||||
-rw-r--r-- | gobject/gparam.c | 90 | ||||
-rw-r--r-- | gobject/gparam.h | 66 | ||||
-rw-r--r-- | gobject/gparamspecs.c | 205 | ||||
-rw-r--r-- | gobject/gsignal.c | 1335 | ||||
-rw-r--r-- | gobject/gsignal.h | 142 | ||||
-rw-r--r-- | gobject/gtype.c | 221 | ||||
-rw-r--r-- | gobject/gtype.h | 106 | ||||
-rw-r--r-- | gobject/gvalue.c | 40 | ||||
-rw-r--r-- | gobject/gvalue.h | 7 | ||||
-rw-r--r-- | gobject/gvaluetypes.c | 132 | ||||
-rw-r--r-- | gobject/gvaluetypes.h | 90 |
26 files changed, 4813 insertions, 454 deletions
diff --git a/gobject/ChangeLog b/gobject/ChangeLog index eddfe3f6d..5b0bb74b6 100644 --- a/gobject/ChangeLog +++ b/gobject/ChangeLog @@ -1,3 +1,86 @@ +Wed Oct 25 20:27:02 2000 Tim Janik <timj@gtk.org> + + * gtype.c (g_type_free_instance): for the moment, freeing object + structures will fill their memory portion with 0xAA. there's a + FIXME there, remove this line at a later point. + +Tue Oct 24 23:10:26 2000 Tim Janik <timj@gtk.org> + + * glib-genmarshal.1: + * glib-genmarshal.c: added publically installed marshaller generator. + + * gtype.h: added G_TYPE_INSTANCE_GET_INTERFACE() to retrive a certain + interface VTable from instances. + +Mon Oct 23 08:28:15 2000 Tim Janik <timj@gtk.org> + + * gobject.[hc]: new functions for closure maintenance: + (g_object_watch_closure): maintain validity of the object and + the closure for objects that are used as data part of a closure. + (g_cclosure_new_object): convenience function to create C closures + that have an object as data argument. + (g_closure_new_object): convenience function to create closures + that have an object as data argument. + + * gclosure.[hc]: implementation of GClosure mechanism. + a closure is basically an encapsulation of a callback function + and its environment. ideally, most places supporting callback + functions will simply take a GClosure* pointer and thus unify + callback environments wrg destroy notification etc. + GClosure provides destroy notifiers for arbitrary data pointers, + reference counting, invalidation notification (it can be invalidated + which is merely a deactivate state) and a marshallinbg abstraction. + GCClosure is also provided in these files, they present a specialized + GClosure implementation for C language callbacks. + + * genum.c: macro cleanups. + + * gboxed.[hc]: new files, for boxed type abstraction. + (g_boxed_copy): copy a boxed structure + (g_boxed_free): free a boxed structure + (g_value_set_boxed): + (g_value_get_boxed): standard GValue functions for boxed types + (g_boxed_type_register_static): convenience function for easy + introduction of new G_TYPE_BOXED derivatives. + + * gparam.[hc]: introduced g_param_type_register_static(), a short hand + for creation of new GParamSpec derived types. + + * gtype.[hc]: many fixes, introduced ability to flag individual + type nodes as ABSTRACT upon registration, added value_peek_pointer() + to the value table to peek at GValue contents as a pointer for types + that support this. fixed up GValue checks. + + * gvalue.[hc]: added g_value_fits_pointer() and g_value_get_as_pointer() + to peek at the value contents as pointer. + + * *.[hc]: adaptions to type macro fixes and changes in the type + registration API. + + * many const corrections over the place. + +Sat Oct 21 02:49:56 2000 Tim Janik <timj@gtk.org> + + * gtype.c (g_type_conforms_to): this function basically behaves like + and is_a check, except that it _additionally_ features interfaces + for instantiatable types. enforce this in the second branch as well + (`type' conforms_to `type') even if `type' is not an interface type. + +Fri Oct 20 15:31:04 2000 Tim Janik <timj@gtk.org> + + * gvaluetypes.[hc]: added G_TYPE_POINTER implementation from jrb. + + * gtype.[hc]: + * gobject.c: + * gvaluetypes.c: added GTypeValueTable.value_peek_pointer and + suitable implementations of this for G_TYPE_STRING, G_TYPE_OBJECT + and G_TYPE_POINTER. + +Mon Aug 21 04:13:37 2000 Tim Janik <timj@gtk.org> + + * gbsearcharray.[hc]: long standing needed generic implementation + of a binary searchable, sorted and dynamically sized array. + 2000-10-15 Raja R Harinath <harinath@cs.umn.edu> * Makefile.am (BUILT_EXTRA_DIST): New variable. @@ -255,9 +338,5 @@ Sun Apr 2 04:54:36 2000 Tim Janik <timj@gtk.org> * glib-genums.[hc]: enum/flags type implementation, based on bseenum.[hc]. - * glib-extra.[hc]: GLib additions, including 1.3 compatibility - routines and various other functions, from string manipulation - over list manipulation up to a unix signal GSource. - * glib-gtype.[hc]: GLib Type System implementation, heavily based on BSE's dynamic type system. diff --git a/gobject/Makefile.am b/gobject/Makefile.am index 88e563418..7e2b34a94 100644 --- a/gobject/Makefile.am +++ b/gobject/Makefile.am @@ -3,7 +3,8 @@ # ## Process this file with automake to produce Makefile.in -INCLUDES = -I$(top_srcdir) -I$(top_builddir) -I. @GLIB_DEBUG_FLAGS@ +SUBDIRS = +INCLUDES = -I$(top_srcdir) -I$(top_builddir) @GLIB_DEBUG_FLAGS@ # libraries to compile and install lib_LTLIBRARIES = libgobject-1.3.la @@ -22,28 +23,38 @@ libgobject_1_3_la_LIBADD = # $(libglib) # setup source file variables # # GObject header files for public installation (non-generated) -gobject_public_h_sources = \ - gvalue.h \ - gvaluetypes.h \ - gparam.h \ - gparamspecs.h \ +gobject_public_h_sources = @STRIP_BEGIN@ \ + gboxed.h \ + gbsearcharray.h \ + gclosure.h \ genums.h \ gobject.h \ + gparam.h \ + gparamspecs.h \ + gsignal.h \ gtype.h \ - gvaluecollector.h + gvalue.h \ + gvaluecollector.h \ + gvaluetypes.h \ +@STRIP_END@ # private GObject header files gobject_private_h_sources = # GObject C sources to build the library from -gobject_c_sources = \ - gvalue.c \ - gvaluetypes.c \ - gparam.c \ - gparamspecs.c \ +gobject_c_sources = @STRIP_BEGIN@ \ + gboxed.c \ + gbsearcharray.c \ + gclosure.c \ genums.c \ gobject.c \ - gtype.c + gparam.c \ + gparamspecs.c \ + gsignal.c \ + gtype.c \ + gvalue.c \ + gvaluetypes.c \ +@STRIP_END@ # non-header sources (headers should be specified in the above variables) # that don't serve as direct make target sources, i.e. they don't have @@ -66,12 +77,19 @@ EXTRA_DIST += $(gobject_extra_sources) # # programs to compile and install # -bin_PROGRAMS = gobject-query +bin_PROGRAMS = gobject-query glib-genmarshal # source files gobject_query_SOURCES = gobject-query.c +glib_genmarshal_SOURCES = glib-genmarshal.c # link programs against libgobject progs_LDADD = ../libglib-1.3.la libgobject-1.3.la gobject_query_LDADD = $(progs_LDADD) +glib_genmarshal_LDADD = $(progs_LDADD) + +# +# manual pages to install +# +man_MANS = glib-genmarshal.1 # # auxillary files diff --git a/gobject/gboxed.c b/gobject/gboxed.c new file mode 100644 index 000000000..c78612cdd --- /dev/null +++ b/gobject/gboxed.c @@ -0,0 +1,316 @@ +/* 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 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include "gboxed.h" + +#include "gbsearcharray.h" +#include "gvalue.h" +#include "gvaluecollector.h" + + + +/* --- typedefs & structures --- */ +typedef struct +{ + GType type; + GBoxedCopyFunc copy; + GBoxedFreeFunc free; +} BoxedNode; + + +/* --- prototypes --- */ +static gint boxed_nodes_cmp (gconstpointer p1, + gconstpointer p2); + + +/* --- variables --- */ +static GBSearchArray boxed_bsa = { boxed_nodes_cmp, sizeof (BoxedNode), 0, 0, NULL }; + + +/* --- functions --- */ +static gint +boxed_nodes_cmp (gconstpointer p1, + gconstpointer p2) +{ + const BoxedNode *node1 = p1, *node2 = p2; + + return G_BSEARCH_ARRAY_CMP (node1->type, node2->type); +} + +void +g_boxed_type_init (void) /* sync with gtype.c */ +{ + static 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_TYPE_BOXED + */ + type = g_type_register_fundamental (G_TYPE_BOXED, "GBoxed", &info, &finfo, G_TYPE_FLAG_ABSTRACT); + g_assert (type == G_TYPE_BOXED); +} + +static void +boxed_proxy_value_init (GValue *value) +{ + value->data[0].v_pointer = 0; +} + +static void +boxed_proxy_value_free (GValue *value) +{ + if (value->data[0].v_pointer) + { + BoxedNode key, *node; + + key.type = value->g_type; + node = g_bsearch_array_lookup (&boxed_bsa, &key); + node->free (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) + { + BoxedNode key, *node; + + key.type = src_value->g_type; + node = g_bsearch_array_lookup (&boxed_bsa, &key); + dest_value->data[0].v_pointer = node->copy (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 nth_value, + GType *collect_type, + GTypeCValue *collect_value) +{ + BoxedNode key, *node; + + key.type = value->g_type; + node = g_bsearch_array_lookup (&boxed_bsa, &key); + value->data[0].v_pointer = node->copy (collect_value->v_pointer); + + *collect_type = 0; + return NULL; +} + +static gchar* +boxed_proxy_lcopy_value (const GValue *value, + guint nth_value, + GType *collect_type, + GTypeCValue *collect_value) +{ + BoxedNode key, *node; + gpointer *boxed_p = collect_value->v_pointer; + + if (!boxed_p) + return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value)); + + key.type = value->g_type; + node = g_bsearch_array_lookup (&boxed_bsa, &key); + *boxed_p = node->copy (value->data[0].v_pointer); + + *collect_type = 0; + return NULL; +} + +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, + G_VALUE_COLLECT_POINTER, + boxed_proxy_collect_value, + G_VALUE_COLLECT_POINTER, + boxed_proxy_lcopy_value, + }; + static const 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 successfull registration */ + if (type) + { + BoxedNode key; + + key.type = type; + key.copy = boxed_copy; + key.free = boxed_free; + g_bsearch_array_insert (&boxed_bsa, &key, TRUE); + } + + return type; +} + +GBoxed* +g_boxed_copy (GType boxed_type, + gpointer src_boxed) +{ + GTypeValueTable *value_table; + + 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); + if (!value_table) + g_return_val_if_fail (G_TYPE_IS_VALUE_TYPE (boxed_type), NULL); + + /* check if our proxying implementation is used, we can short-cut here */ + if (value_table->value_copy == boxed_proxy_value_copy) + { + BoxedNode key, *node; + + key.type = boxed_type; + node = g_bsearch_array_lookup (&boxed_bsa, &key); + src_boxed = node->copy (src_boxed); + } + else + { + GValue src_value, dest_value; + + /* we heavil rely on the gvalue.c implementation here */ + + memset (&src_value.data, 0, sizeof (src_value.data)); + memset (&dest_value.data, 0, sizeof (dest_value.data)); + dest_value.g_type = boxed_type; + src_value.g_type = boxed_type; + src_value.data[0].v_pointer = src_boxed; + value_table->value_copy (&src_value, &dest_value); + if (dest_value.data[1].v_ulong || + dest_value.data[2].v_ulong || + dest_value.data[3].v_ulong) + g_warning ("the copy_value() implementation of type `%s' seems to make use of reserved GValue fields", + g_type_name (boxed_type)); + + src_boxed = dest_value.data[0].v_pointer; + } + + return src_boxed; +} + +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); + if (!value_table) + g_return_if_fail (G_TYPE_IS_VALUE_TYPE (boxed_type)); + + /* check if our proxying implementation is used, we can short-cut here */ + if (value_table->value_free == boxed_proxy_value_free) + { + BoxedNode key, *node; + + key.type = boxed_type; + node = g_bsearch_array_lookup (&boxed_bsa, &key); + node->free (boxed); + } + else + { + GValue value; + + /* we heavil rely on the gvalue.c implementation here */ + memset (&value.data, 0, sizeof (value.data)); + value.g_type = boxed_type; + value.data[0].v_pointer = boxed; + value_table->value_free (&value); + } +} + +void +g_value_set_boxed (GValue *value, + gpointer boxed) +{ + g_return_if_fail (G_IS_VALUE_BOXED (value)); + g_return_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value))); + + if (value->data[0].v_pointer) + g_boxed_free (G_VALUE_TYPE (value), value->data[0].v_pointer); + value->data[0].v_pointer = boxed ? g_boxed_copy (G_VALUE_TYPE (value), boxed) : NULL; +} + +gpointer +g_value_get_boxed (GValue *value) +{ + g_return_val_if_fail (G_IS_VALUE_BOXED (value), NULL); + g_return_val_if_fail (G_TYPE_IS_VALUE (G_VALUE_TYPE (value)), NULL); + + return value->data[0].v_pointer; +} + +gpointer +g_value_dup_boxed (GValue *value) +{ + g_return_val_if_fail (G_IS_VALUE_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; +} diff --git a/gobject/gboxed.h b/gobject/gboxed.h new file mode 100644 index 000000000..edd51b06e --- /dev/null +++ b/gobject/gboxed.h @@ -0,0 +1,64 @@ +/* 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 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __G_BOXED_H__ +#define __G_BOXED_H__ + +#include <gobject/gtype.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* --- type macros --- */ +#define G_TYPE_IS_BOXED(type) (G_TYPE_FUNDAMENTAL (type) == G_TYPE_BOXED) +#define G_IS_VALUE_BOXED(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_BOXED)) + + +/* --- typedefs --- */ +typedef struct _GBoxed GBoxed; +typedef gpointer (*GBoxedCopyFunc) (gpointer boxed); +typedef void (*GBoxedFreeFunc) (gpointer boxed); + + +/* --- prototypes --- */ +GBoxed* g_boxed_copy (GType boxed_type, + gpointer src_boxed); +void g_boxed_free (GType boxed_type, + gpointer boxed); +void g_value_set_boxed (GValue *value, + gpointer boxed); +gpointer g_value_get_boxed (GValue *value); +gpointer g_value_dup_boxed (GValue *value); + + +/* --- convenience --- */ +GType g_boxed_type_register_static (const gchar *name, + GBoxedCopyFunc boxed_copy, + GBoxedFreeFunc boxed_free); + + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __G_BOXED_H__ */ diff --git a/gobject/gbsearcharray.c b/gobject/gbsearcharray.c new file mode 100644 index 000000000..9aa1f450f --- /dev/null +++ b/gobject/gbsearcharray.c @@ -0,0 +1,164 @@ +/* GObject - GLib Type, Object, Parameter and Signal Library + * Copyright (C) 2000 Tim Janik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ +#define G_IMPLEMENT_INLINES 1 +#define __G_BSEARCHARRAY_C__ +#include "gbsearcharray.h" + +#include <string.h> + + +/* --- structures --- */ +static inline guint +upper_power2 (guint number) +{ + return number ? 1 << g_bit_storage (number - 1) : 0; +} + +static inline gpointer +bsearch_array_insert (GBSearchArray *barray, + gconstpointer key_node, + gboolean replace) +{ + gint sizeof_node; + guint8 *check; + + sizeof_node = barray->sizeof_node; + if (barray->n_nodes == 0) + { + guint new_size = barray->sizeof_node; + + if (barray->flags & G_BSEARCH_ALIGN_POWER2) + new_size = upper_power2 (new_size); + barray->nodes = g_realloc (barray->nodes, new_size); + barray->n_nodes = 1; + check = barray->nodes; + replace = TRUE; + } + else + { + GBSearchCompareFunc cmp_func = barray->cmp_func; + guint n_nodes = barray->n_nodes; + guint8 *nodes = barray->nodes; + gint cmp; + guint i; + + nodes -= sizeof_node; + do + { + i = (n_nodes + 1) >> 1; + check = nodes + i * sizeof_node; + cmp = cmp_func (key_node, check); + if (cmp > 0) + { + n_nodes -= i; + nodes = check; + } + else if (cmp < 0) + n_nodes = i - 1; + else /* if (cmp == 0) */ + goto SKIP_GROW; + } + while (n_nodes); + /* grow */ + if (cmp > 0) + check += sizeof_node; + i = (check - ((guint8*) barray->nodes)) / sizeof_node; + n_nodes = barray->n_nodes++; + if (barray->flags & G_BSEARCH_ALIGN_POWER2) + { + guint new_size = upper_power2 (barray->n_nodes * sizeof_node); + guint old_size = upper_power2 (n_nodes * sizeof_node); + + if (new_size != old_size) + barray->nodes = g_realloc (barray->nodes, new_size); + } + else + barray->nodes = g_realloc (barray->nodes, barray->n_nodes * sizeof_node); + check = barray->nodes + i * sizeof_node; + g_memmove (check + sizeof_node, check, (n_nodes - i) * sizeof_node); + replace = TRUE; + SKIP_GROW: + } + if (replace) + memcpy (check, key_node, sizeof_node); + + return check; +} + +gpointer +g_bsearch_array_insert (GBSearchArray *barray, + gconstpointer key_node, + gboolean replace_existing) +{ + g_return_val_if_fail (barray != NULL, NULL); + g_return_val_if_fail (key_node != NULL, NULL); + + return bsearch_array_insert (barray, key_node, replace_existing); +} + +void +g_bsearch_array_remove_node (GBSearchArray *barray, + gpointer _node_in_array) +{ + guint8 *nodes, *bound, *node_in_array = _node_in_array; + guint old_size; + + g_return_if_fail (barray != NULL); + + nodes = barray->nodes; + old_size = barray->sizeof_node; + old_size *= barray->n_nodes; /* beware of int widths */ + bound = nodes + old_size; + + g_return_if_fail (node_in_array >= nodes && node_in_array < bound); + + bound -= barray->sizeof_node; + barray->n_nodes -= 1; + g_memmove (node_in_array, node_in_array + barray->sizeof_node, (bound - node_in_array) / barray->sizeof_node); + + if ((barray->flags & G_BSEARCH_DEFER_SHRINK) == 0) + { + guint new_size = bound - nodes; /* old_size - barray->sizeof_node */ + + if (barray->flags & G_BSEARCH_ALIGN_POWER2) + { + new_size = upper_power2 (new_size); + old_size = upper_power2 (old_size); + if (old_size != new_size) + barray->nodes = g_realloc (barray->nodes, new_size); + } + else + barray->nodes = g_realloc (barray->nodes, new_size); + } +} + +void +g_bsearch_array_remove (GBSearchArray *barray, + gconstpointer key_node) +{ + gpointer node_in_array; + + g_return_if_fail (barray != NULL); + + node_in_array = g_bsearch_array_lookup (barray, key_node); + if (!node_in_array) + g_warning (G_STRLOC ": unable to remove unexistant node"); + else + g_bsearch_array_remove_node (barray, node_in_array); +} diff --git a/gobject/gbsearcharray.h b/gobject/gbsearcharray.h new file mode 100644 index 000000000..aadaac828 --- /dev/null +++ b/gobject/gbsearcharray.h @@ -0,0 +1,134 @@ +/* GObject - GLib Type, Object, Parameter and Signal Library + * Copyright (C) 2000 Tim Janik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * gbsearcharray.h: binary searchable sorted array maintenance + */ +#ifndef __G_BSEARCH_ARRAY_H__ +#define __G_BSEARCH_ARRAY_H__ + +#include <gobject/gtype.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* helper macro to avoid signed overflow for value comparisions */ +#define G_BSEARCH_ARRAY_CMP(v1,v2) ((v1) < (v2) ? -1 : (v1) > (v2) ? 1 : 0) + + +/* --- typedefs --- */ +typedef struct _GBSearchArray GBSearchArray; +typedef gint (*GBSearchCompareFunc) (gconstpointer bsearch_node1, + gconstpointer bsearch_node2); +typedef enum +{ + G_BSEARCH_ALIGN_POWER2 = 1 << 0, + G_BSEARCH_DEFER_SHRINK = 1 << 1 +} GBSearchFlags; + + +/* --- structures --- */ +struct _GBSearchArray +{ + GBSearchCompareFunc cmp_func; + guint16 sizeof_node; + guint16 flags; + guint n_nodes; + gpointer nodes; +}; + + +/* --- prototypes --- */ +gpointer g_bsearch_array_insert (GBSearchArray *barray, + gconstpointer key_node, + gboolean replace_existing); +void g_bsearch_array_remove (GBSearchArray *barray, + gconstpointer key_node); +void g_bsearch_array_remove_node (GBSearchArray *barray, + gpointer node_in_array); +G_INLINE_FUNC +gpointer g_bsearch_array_lookup (GBSearchArray *barray, + gconstpointer key_node); +G_INLINE_FUNC +gpointer g_bsearch_array_get_nth (GBSearchArray *barray, + guint n); + + +/* --- implementation details --- */ +#if defined (G_CAN_INLINE) || defined (__G_BSEARCHARRAY_C__) +G_INLINE_FUNC gpointer +g_bsearch_array_lookup (GBSearchArray *barray, + gconstpointer key_node) +{ + if (barray->n_nodes > 0) + { + GBSearchCompareFunc cmp_func = barray->cmp_func; + gint sizeof_node = barray->sizeof_node; + guint n_nodes = barray->n_nodes; + guint8 *nodes = barray->nodes; + + nodes -= sizeof_node; + do + { + guint8 *check; + guint i; + register gint cmp; + + i = (n_nodes + 1) >> 1; + check = nodes + i * sizeof_node; + cmp = cmp_func (key_node, check); + if (cmp == 0) + return check; + else if (cmp > 0) + { + n_nodes -= i; + nodes = check; + } + else /* if (cmp < 0) */ + n_nodes = i - 1; + } + while (n_nodes); + } + + return NULL; +} +G_INLINE_FUNC gpointer +g_bsearch_array_get_nth (GBSearchArray *barray, + guint n) +{ + if (n < barray->n_nodes) + { + guint8 *nodes = barray->nodes; + + return nodes + n * barray->sizeof_node; + } + else + return NULL; +} +#endif /* G_CAN_INLINE && __G_BSEARCHARRAY_C__ */ + + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __G_BSEARCH_ARRAY_H__ */ diff --git a/gobject/gclosure.c b/gobject/gclosure.c new file mode 100644 index 000000000..31cdca669 --- /dev/null +++ b/gobject/gclosure.c @@ -0,0 +1,534 @@ +/* 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 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include "gclosure.h" + +#include "gvalue.h" + + +/* FIXME: need caching allocators + */ + +#define CLOSURE_MAX_REF_COUNT ((1 << 15) - 1) +#define CLOSURE_MAX_N_GUARDS ((1 << 1) - 1) +#define CLOSURE_MAX_N_FNOTIFIERS ((1 << 2) - 1) +#define CLOSURE_MAX_N_INOTIFIERS ((1 << 8) - 1) +#define CLOSURE_N_MFUNCS(cl) ((cl)->meta_marshal + \ + ((cl)->n_guards << 1L)) +#define CLOSURE_N_NOTIFIERS(cl) (CLOSURE_N_MFUNCS (cl) + \ + (cl)->n_fnotifiers + \ + (cl)->n_inotifiers) +enum { + FNOTIFY, + INOTIFY, + PRE_NOTIFY, + POST_NOTIFY +}; + + +/* --- functions --- */ +GClosure* +g_closure_new_simple (guint sizeof_closure, + gpointer data) +{ + GClosure *closure; + + g_return_val_if_fail (sizeof_closure >= sizeof (GClosure), NULL); + + closure = g_malloc (sizeof_closure); + closure->ref_count = 1; + closure->meta_marshal = 0; + closure->n_guards = 0; + closure->n_fnotifiers = 0; + closure->n_inotifiers = 0; + closure->in_inotify = FALSE; + closure->floating = TRUE; + closure->derivative_flag = 0; + closure->in_marshal = FALSE; + closure->is_invalid = FALSE; + closure->marshal = NULL; + closure->data = data; + closure->notifiers = NULL; + memset (G_STRUCT_MEMBER_P (closure, sizeof (*closure)), 0, sizeof_closure - sizeof (*closure)); + + return closure; +} + +static inline void +closure_invoke_notifiers (GClosure *closure, + guint notify_type) +{ + /* notifier layout: + * meta_marshal n_guards n_guards n_fnotif. n_inotifiers + * ->[[meta_marshal][pre_guards][post_guards][fnotifers][inotifiers]] + * + * CLOSURE_N_MFUNCS(cl) = meta_marshal + n_guards + n_guards; + * CLOSURE_N_NOTIFIERS(cl) = CLOSURE_N_MFUNCS(cl) + n_fnotifiers + n_inotifiers + * + * constrains/catches: + * - closure->notifiers may be reloacted during callback + * - closure->n_fnotifiers and closure->n_inotifiers may change during callback + * - i.e. callbacks can be removed/added during invocation + * - have to prepare for callback removal during invocation (->marshal & ->data) + * - have to distinguish (->marshal & ->data) for INOTIFY/FNOTIFY (->in_inotify) + * + closure->n_guards is const during PRE_NOTIFY & POST_NOTIFY + * + closure->meta_marshal is const for all cases + * + none of the callbacks can cause recursion + * + closure->n_inotifiers is const 0 during FNOTIFY + */ + switch (notify_type) + { + GClosureNotifyData *ndata; + guint i, offs; + case FNOTIFY: + while (closure->n_fnotifiers) + { + register guint n = --closure->n_fnotifiers; + + ndata = closure->notifiers + CLOSURE_N_MFUNCS (closure) + n; + closure->marshal = (gpointer) ndata->notify; + closure->data = ndata->data; + ndata->notify (ndata->data, closure); + } + closure->marshal = NULL; + closure->data = NULL; + break; + case INOTIFY: + closure->in_inotify = TRUE; + while (closure->n_inotifiers) + { + register guint n = --closure->n_inotifiers; + + ndata = closure->notifiers + CLOSURE_N_MFUNCS (closure) + closure->n_fnotifiers + n; + closure->marshal = (gpointer) ndata->notify; + closure->data = ndata->data; + ndata->notify (ndata->data, closure); + } + closure->marshal = NULL; + closure->data = NULL; + closure->in_inotify = FALSE; + break; + case PRE_NOTIFY: + i = closure->n_guards; + offs = closure->meta_marshal; + while (i--) + { + ndata = closure->notifiers + offs + i; + ndata->notify (ndata->data, closure); + } + break; + case POST_NOTIFY: + i = closure->n_guards; + offs = closure->meta_marshal + i; + while (i--) + { + ndata = closure->notifiers + offs + i; + ndata->notify (ndata->data, closure); + } + break; + } +} + +void +g_closure_set_meta_marshal (GClosure *closure, + gpointer marshal_data, + GClosureMarshal meta_marshal) +{ + GClosureNotifyData *notifiers; + guint n; + + g_return_if_fail (closure != NULL); + g_return_if_fail (meta_marshal != NULL); + g_return_if_fail (closure->is_invalid == FALSE); + g_return_if_fail (closure->in_marshal == FALSE); + g_return_if_fail (closure->meta_marshal == FALSE); + + n = CLOSURE_N_NOTIFIERS (closure); + notifiers = closure->notifiers; + closure->notifiers = g_renew (GClosureNotifyData, NULL, CLOSURE_N_NOTIFIERS (closure) + 1); + closure->notifiers[0].data = marshal_data; + closure->notifiers[0].notify = (GClosureNotify) meta_marshal; + if (notifiers) + { + /* usually the meta marshal will be setup right after creation, so the + * memcpy() should be rare-case scenario + */ + memcpy (closure->notifiers + 1, notifiers, CLOSURE_N_NOTIFIERS (closure) * sizeof (notifiers[0])); + g_free (notifiers); + } + closure->meta_marshal = 1; +} + +void +g_closure_add_marshal_guards (GClosure *closure, + gpointer pre_marshal_data, + GClosureNotify pre_marshal_notify, + gpointer post_marshal_data, + GClosureNotify post_marshal_notify) +{ + guint i; + + g_return_if_fail (closure != NULL); + g_return_if_fail (pre_marshal_notify != NULL); + g_return_if_fail (post_marshal_notify != NULL); + g_return_if_fail (closure->is_invalid == FALSE); + g_return_if_fail (closure->in_marshal == FALSE); + g_return_if_fail (closure->n_guards < CLOSURE_MAX_N_GUARDS); + + closure->notifiers = g_renew (GClosureNotifyData, closure->notifiers, CLOSURE_N_NOTIFIERS (closure) + 2); + if (closure->n_inotifiers) + closure->notifiers[(CLOSURE_N_MFUNCS (closure) + + closure->n_fnotifiers + + closure->n_inotifiers + 1)] = closure->notifiers[(CLOSURE_N_MFUNCS (closure) + + closure->n_fnotifiers + 0)]; + if (closure->n_inotifiers > 1) + closure->notifiers[(CLOSURE_N_MFUNCS (closure) + + closure->n_fnotifiers + + closure->n_inotifiers)] = closure->notifiers[(CLOSURE_N_MFUNCS (closure) + + closure->n_fnotifiers + 1)]; + if (closure->n_fnotifiers) + closure->notifiers[(CLOSURE_N_MFUNCS (closure) + + closure->n_fnotifiers + 1)] = closure->notifiers[CLOSURE_N_MFUNCS (closure) + 0]; + if (closure->n_fnotifiers > 1) + closure->notifiers[(CLOSURE_N_MFUNCS (closure) + + closure->n_fnotifiers)] = closure->notifiers[CLOSURE_N_MFUNCS (closure) + 1]; + if (closure->n_guards) + closure->notifiers[(closure->meta_marshal + + closure->n_guards + + closure->n_guards + 1)] = closure->notifiers[closure->meta_marshal + closure->n_guards]; + i = closure->n_guards++; + closure->notifiers[closure->meta_marshal + i].data = pre_marshal_data; + closure->notifiers[closure->meta_marshal + i].notify = pre_marshal_notify; + closure->notifiers[closure->meta_marshal + i + i].data = post_marshal_data; + closure->notifiers[closure->meta_marshal + i + i].notify = post_marshal_notify; +} + +void +g_closure_add_fnotify (GClosure *closure, + gpointer notify_data, + GClosureNotify notify_func) +{ + guint i; + + g_return_if_fail (closure != NULL); + g_return_if_fail (notify_func != NULL); + g_return_if_fail (closure->n_fnotifiers < CLOSURE_MAX_N_FNOTIFIERS); + + closure->notifiers = g_renew (GClosureNotifyData, closure->notifiers, CLOSURE_N_NOTIFIERS (closure) + 1); + if (closure->n_inotifiers) + closure->notifiers[(CLOSURE_N_MFUNCS (closure) + + closure->n_fnotifiers + + closure->n_inotifiers)] = closure->notifiers[(CLOSURE_N_MFUNCS (closure) + + closure->n_fnotifiers + 0)]; + i = CLOSURE_N_MFUNCS (closure) + closure->n_fnotifiers++; + closure->notifiers[i].data = notify_data; + closure->notifiers[i].notify = notify_func; +} + +void +g_closure_add_inotify (GClosure *closure, + gpointer notify_data, + GClosureNotify notify_func) +{ + guint i; + + g_return_if_fail (closure != NULL); + g_return_if_fail (notify_func != NULL); + g_return_if_fail (closure->is_invalid == FALSE); + g_return_if_fail (closure->n_inotifiers < CLOSURE_MAX_N_INOTIFIERS); + + closure->notifiers = g_renew (GClosureNotifyData, closure->notifiers, CLOSURE_N_NOTIFIERS (closure) + 1); + i = CLOSURE_N_MFUNCS (closure) + closure->n_fnotifiers + closure->n_inotifiers++; + closure->notifiers[i].data = notify_data; + closure->notifiers[i].notify = notify_func; +} + +static inline gboolean +closure_try_remove_inotify (GClosure *closure, + gpointer notify_data, + GClosureNotify notify_func) +{ + GClosureNotifyData *ndata, *nlast; + + nlast = closure->notifiers + CLOSURE_N_NOTIFIERS (closure) - 1; + for (ndata = nlast + 1 - closure->n_inotifiers; ndata <= nlast; ndata++) + if (ndata->notify == notify_func && ndata->data == notify_data) + { + closure->n_inotifiers -= 1; + if (ndata < nlast) + *ndata = *nlast; + + return TRUE; + } + return FALSE; +} + +static inline gboolean +closure_try_remove_fnotify (GClosure *closure, + gpointer notify_data, + GClosureNotify notify_func) +{ + GClosureNotifyData *ndata, *nlast; + + nlast = closure->notifiers + CLOSURE_N_NOTIFIERS (closure) - closure->n_inotifiers - 1; + for (ndata = nlast + 1 - closure->n_fnotifiers; ndata <= nlast; ndata++) + if (ndata->notify == notify_func && ndata->data == notify_data) + { + closure->n_fnotifiers -= 1; + if (ndata < nlast) + *ndata = *nlast; + if (closure->n_inotifiers) + closure->notifiers[(CLOSURE_N_MFUNCS (closure) + + closure->n_fnotifiers)] = closure->notifiers[(CLOSURE_N_MFUNCS (closure) + + closure->n_fnotifiers + + closure->n_inotifiers)]; + return TRUE; + } + return FALSE; +} + +GClosure* +g_closure_ref (GClosure *closure) +{ + g_return_val_if_fail (closure != NULL, NULL); + g_return_val_if_fail (closure->ref_count > 0, NULL); + g_return_val_if_fail (closure->ref_count < CLOSURE_MAX_REF_COUNT, NULL); + + /* floating is basically a kludge to avoid creating closures + * with a ref_count of 0. so the first one doing _ref() will + * own the closure's initial ref_count + */ + if (closure->floating) + closure->floating = FALSE; + else + closure->ref_count += 1; + + return closure; +} + +void +g_closure_invalidate (GClosure *closure) +{ + g_return_if_fail (closure != NULL); + + if (!closure->is_invalid) + { + closure->ref_count += 1; /* preserve floating flag */ + closure->is_invalid = TRUE; + closure_invoke_notifiers (closure, INOTIFY); + g_closure_unref (closure); + } +} + +void +g_closure_unref (GClosure *closure) +{ + g_return_if_fail (closure != NULL); + g_return_if_fail (closure->ref_count > 0); + + if (closure->ref_count == 1) /* last unref, invalidate first */ + g_closure_invalidate (closure); + + closure->ref_count -= 1; + + if (closure->ref_count == 0) + { + closure_invoke_notifiers (closure, FNOTIFY); + g_free (closure); + } +} + +void +g_closure_remove_inotify (GClosure *closure, + gpointer notify_data, + GClosureNotify notify_func) +{ + g_return_if_fail (closure != NULL); + g_return_if_fail (notify_func != NULL); + + if (closure->is_invalid && closure->in_inotify && /* account removal of notify_func() while its called */ + ((gpointer) closure->marshal) == ((gpointer) notify_func) && closure->data == notify_data) + closure->marshal = NULL; + else if (!closure_try_remove_inotify (closure, notify_data, notify_func)) + g_warning (G_STRLOC ": unable to remove uninstalled invalidation notifier: %p (%p)", + notify_func, notify_data); +} + +void +g_closure_remove_fnotify (GClosure *closure, + gpointer notify_data, + GClosureNotify notify_func) +{ + g_return_if_fail (closure != NULL); + g_return_if_fail (notify_func != NULL); + + if (closure->is_invalid && !closure->in_inotify && /* account removal of notify_func() while its called */ + ((gpointer) closure->marshal) == ((gpointer) notify_func) && closure->data == notify_data) + closure->marshal = NULL; + else if (!closure_try_remove_fnotify (closure, notify_data, notify_func)) + g_warning (G_STRLOC ": unable to remove uninstalled finalization notifier: %p (%p)", + notify_func, notify_data); +} + +void +g_closure_invoke (GClosure *closure, + guint invocation_hint, + GValue /*out*/ *return_value, + guint n_param_values, + const GValue *param_values) +{ + g_return_if_fail (closure != NULL); + g_return_if_fail (closure->marshal || closure->meta_marshal); + + if (!closure->is_invalid) + { + GClosureMarshal marshal; + gpointer marshal_data; + gboolean in_marshal = closure->in_marshal; + + closure->ref_count += 1; /* preserve floating flag */ + closure->in_marshal = TRUE; + if (closure->meta_marshal) + { + marshal_data = closure->notifiers[0].data; + marshal = (GClosureMarshal) closure->notifiers[0].notify; + } + else + { + marshal_data = NULL; + marshal = closure->marshal; + } + if (!in_marshal) + closure_invoke_notifiers (closure, PRE_NOTIFY); + marshal (closure, invocation_hint, + return_value, + n_param_values, param_values, + marshal_data); + if (!in_marshal) + closure_invoke_notifiers (closure, POST_NOTIFY); + closure->in_marshal = in_marshal; + g_closure_unref (closure); + } +} + +void +g_closure_set_marshal (GClosure *closure, + GClosureMarshal marshal) +{ + g_return_if_fail (closure != NULL); + g_return_if_fail (marshal != NULL); + + if (closure->marshal && closure->marshal != marshal) + g_warning ("attempt to override closure->marshal (%p) with new marshal (%p)", + closure->marshal, marshal); + else + closure->marshal = marshal; +} + +GClosure* +g_cclosure_new (GCallback callback_func, + gpointer user_data, + GClosureNotify destroy_data) +{ + GClosure *closure; + + g_return_val_if_fail (callback_func != NULL, NULL); + + closure = g_closure_new_simple (sizeof (GCClosure), user_data); + if (destroy_data) + g_closure_add_fnotify (closure, user_data, destroy_data); + ((GCClosure*) closure)->callback = callback_func; + + return closure; +} + +GClosure* +g_cclosure_new_swap (GCallback callback_func, + gpointer user_data, + GClosureNotify destroy_data) +{ + GClosure *closure; + + g_return_val_if_fail (callback_func != NULL, NULL); + + closure = g_closure_new_simple (sizeof (GCClosure), user_data); + if (destroy_data) + g_closure_add_fnotify (closure, user_data, destroy_data); + ((GCClosure*) closure)->callback = callback_func; + closure->derivative_flag = TRUE; + + return closure; +} + +static void +g_type_class_meta_marshal (GClosure *closure, + guint invocation_hint, + GValue /*out*/ *return_value, + guint n_param_values, + const GValue *param_values, + gpointer marshal_data) +{ + GTypeClass *class; + gpointer callback; + /* GType itype = GPOINTER_TO_UINT (closure->data); */ + guint offset = GPOINTER_TO_UINT (marshal_data); + + class = G_TYPE_INSTANCE_GET_CLASS (g_value_get_as_pointer (param_values + 0), itype, GTypeClass); + callback = G_STRUCT_MEMBER (gpointer, class, offset); + if (callback) + closure->marshal (closure, invocation_hint, return_value, + n_param_values, param_values, callback); +} + +static void +g_type_iface_meta_marshal (GClosure *closure, + guint invocation_hint, + GValue /*out*/ *return_value, + guint n_param_values, + const GValue *param_values, + gpointer marshal_data) +{ + GTypeClass *class; + gpointer callback; + GType itype = GPOINTER_TO_UINT (closure->data); + guint offset = GPOINTER_TO_UINT (marshal_data); + + class = G_TYPE_INSTANCE_GET_INTERFACE (g_value_get_as_pointer (param_values + 0), itype, GTypeClass); + callback = G_STRUCT_MEMBER (gpointer, class, offset); + if (callback) + closure->marshal (closure, invocation_hint, return_value, + n_param_values, param_values, callback); +} + +GClosure* +g_signal_type_closure_new (GType itype, + guint struct_offset) +{ + GClosure *closure; + + g_return_val_if_fail (G_TYPE_IS_CLASSED (itype) || G_TYPE_IS_INTERFACE (itype), NULL); + g_return_val_if_fail (struct_offset >= sizeof (GTypeClass), NULL); + + closure = g_closure_new_simple (sizeof (GClosure), GUINT_TO_POINTER (itype)); + if (G_TYPE_IS_INTERFACE (itype)) + g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_iface_meta_marshal); + else + g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_class_meta_marshal); + + return closure; +} diff --git a/gobject/gclosure.h b/gobject/gclosure.h new file mode 100644 index 000000000..423ccebb4 --- /dev/null +++ b/gobject/gclosure.h @@ -0,0 +1,185 @@ +/* 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 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __G_CLOSURE_H__ +#define __G_CLOSURE_H__ + + +#include <gobject/gtype.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + +/* --- defines --- */ +#define G_CLOSURE_NEEDS_MARSHAL(closure) (((GClosure*) (closure))->marshal == NULL) +#define G_CCLOSURE_SWAP_DATA(cclosure) (((GClosure*) (closure))->derivative_flag) + + +/* -- typedefs --- */ +typedef struct _GClosure GClosure; +typedef struct _GClosureNotifyData GClosureNotifyData; +typedef gpointer GCallback; +typedef void (*GClosureNotify) (gpointer data, + GClosure *closure); +typedef void (*GClosureMarshal) (GClosure *closure, + guint invocation_hint, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer marshal_data); +typedef struct _GCClosure GCClosure; + + +/* --- structures --- */ +struct _GClosureNotifyData +{ + gpointer data; + GClosureNotify notify; +}; +struct _GClosure +{ + /*< private >*/ guint ref_count : 15; + /*< private >*/ guint meta_marshal : 1; + /*< private >*/ guint n_guards : 1; + /*< private >*/ guint n_fnotifiers : 2; /* finalization notifiers */ + /*< private >*/ guint n_inotifiers : 8; /* invalidation notifiers */ + /*< private >*/ guint in_inotify : 1; + /*< private >*/ guint floating : 1; + /*< protected >*/ guint derivative_flag : 1; + /*< puplic >*/ guint in_marshal : 1; + /*< public >*/ guint is_invalid : 1; + + /*< private >*/ void (*marshal) (GClosure *closure, + guint invocation_hint, + GValue /*out*/ *return_value, + guint n_param_values, + const GValue *param_values, + gpointer marshal_data); + /*< protected >*/ gpointer data; + + /*< private >*/ GClosureNotifyData *notifiers; + + /* invariants/constrains: + * - ->marshal and ->data are _invalid_ as soon as ->is_invalid==TRUE + * - invocation of all inotifiers occours prior to fnotifiers + * - order of inotifiers is random + * inotifiers may _not_ free/invalidate parameter values (e.g. ->data) + * - order of fnotifiers is random + * - notifiers may only be removed before or during their 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 --- */ +GClosure* g_cclosure_new (GCallback callback_func, + gpointer user_data, + GClosureNotify destroy_data); +GClosure* g_cclosure_new_swap (GCallback callback_func, + gpointer user_data, + GClosureNotify destroy_data); +GClosure* g_signal_type_closure_new (GType itype, + guint struct_offset); + + +/* --- prototypes --- */ +GClosure* g_closure_ref (GClosure *closure); +void g_closure_unref (GClosure *closure); +/* intimidating */ +GClosure* g_closure_new_simple (guint sizeof_closure, + gpointer data); +void g_closure_add_fnotify (GClosure *closure, + gpointer notify_data, + GClosureNotify notify_func); +void g_closure_remove_fnotify (GClosure *closure, + gpointer notify_data, + GClosureNotify notify_func); +void g_closure_add_inotify (GClosure *closure, + gpointer notify_data, + GClosureNotify notify_func); +void g_closure_remove_inotify (GClosure *closure, + gpointer notify_data, + GClosureNotify notify_func); +void g_closure_add_marshal_guards (GClosure *closure, + gpointer pre_marshal_data, + GClosureNotify pre_marshal_notify, + gpointer post_marshal_data, + GClosureNotify post_marshal_notify); +void g_closure_set_marshal (GClosure *closure, + GClosureMarshal marshal); +void g_closure_set_meta_marshal (GClosure *closure, + gpointer marshal_data, + GClosureMarshal meta_marshal); +void g_closure_invalidate (GClosure *closure); +void g_closure_invoke (GClosure *closure, + guint invocation_hint, + GValue /*out*/ *return_value, + guint n_param_values, + const GValue *param_values); + + +/* + data_object::destroy -> closure_invalidate(); + closure_invalidate() -> disconnect(closure); + disconnect(closure) -> (unlink) closure_unref(); + closure_finalize() -> g_free (data_string); + + 1) need GObject and GType in glib + 2) need GParam + 3) need to resolve dtor cycles + 4) need GSignal move + 5) destroy on last caller ref or last data ref? + + + random remarks: + - don't mandate signals for GObject + - OTOH, don't mandate GObject for GSignal + - need marshaller repo with decent aliasing to base types + - provide marshaller collection, virtually covering anything out there + - at that point, still need GSignalCMarhsaller to g_signal_new() ? + - can we combine varargs collect mechanisms with marshaller stubs? + for out values (i.e. returntypes), that might get rid of the following + point... + - char* return signals with connections ala: + connect({ return "static data that can't work"; }), + connect({ return g_strdup ("properly duplicated string"); }) + won't work anymore. CRASH + + problems: + - accumulator needs gboolean to indicate EMISSION_STOP + - accumulator needs data +*/ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __G_CLOSURE_H__ */ diff --git a/gobject/genums.c b/gobject/genums.c index f18db25cf..e60ec2048 100644 --- a/gobject/genums.c +++ b/gobject/genums.c @@ -30,14 +30,14 @@ static void g_enum_class_init (GEnumClass *class, gpointer class_data); static void g_flags_class_init (GFlagsClass *class, gpointer class_data); -static void g_value_enum_init (GValue *value); -static void g_value_enum_copy_value (const GValue *src_value, +static void value_flags_enum_init (GValue *value); +static void value_flags_enum_copy_value (const GValue *src_value, GValue *dest_value); -static gchar* g_value_enum_collect_value (GValue *value, - guint nth_value, - GType *collect_type, - GTypeCValue *collect_value); -static gchar* g_value_enum_lcopy_value (const GValue *value, +static gchar* value_flags_enum_collect_value (GValue *value, + guint nth_value, + GType *collect_type, + GTypeCValue *collect_value); +static gchar* value_flags_enum_lcopy_value (const GValue *value, guint nth_value, GType *collect_type, GTypeCValue *collect_value); @@ -48,57 +48,106 @@ void g_enum_types_init (void) /* sync with gtype.c */ { static gboolean initialized = FALSE; - static const GTypeFundamentalInfo finfo = { - G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_DERIVABLE, + 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 */ + G_VALUE_COLLECT_INT, /* collect_type */ + value_flags_enum_collect_value, /* collect_value */ + G_VALUE_COLLECT_POINTER, /* lcopy_type */ + value_flags_enum_lcopy_value, /* lcopy_value */ }; static GTypeInfo info = { - 0 /* class_size */, - NULL /* base_init */, - NULL /* base_finalize */, - NULL /* class_init */, - NULL /* class_finalize */, - NULL /* class_data */, + 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 GTypeValueTable value_table = { - g_value_enum_init, /* value_init */ - NULL, /* value_free */ - g_value_enum_copy_value, /* value_copy */ - G_VALUE_COLLECT_INT, /* collect_type */ - g_value_enum_collect_value, /* collect_value */ - G_VALUE_COLLECT_POINTER, /* lcopy_type */ - g_value_enum_lcopy_value, /* lcopy_value */ + static const GTypeFundamentalInfo finfo = { + G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_DERIVABLE, }; GType type; g_return_if_fail (initialized == FALSE); initialized = TRUE; - - info.value_table = &value_table; - + /* G_TYPE_ENUM */ info.class_size = sizeof (GEnumClass); - type = g_type_register_fundamental (G_TYPE_ENUM, "GEnum", &info, &finfo); + type = g_type_register_fundamental (G_TYPE_ENUM, "GEnum", &info, &finfo, G_TYPE_FLAG_ABSTRACT); g_assert (type == G_TYPE_ENUM); /* G_TYPE_FLAGS */ info.class_size = sizeof (GFlagsClass); - type = g_type_register_fundamental (G_TYPE_FLAGS, "GFlags", &info, &finfo); + type = g_type_register_fundamental (G_TYPE_FLAGS, "GFlags", &info, &finfo, G_TYPE_FLAG_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 nth_value, + GType *collect_type, + GTypeCValue *collect_value) +{ + value->data[0].v_long = collect_value->v_int; + + *collect_type = 0; + return NULL; +} + +static gchar* +value_flags_enum_lcopy_value (const GValue *value, + guint nth_value, + GType *collect_type, + GTypeCValue *collect_value) +{ + gint *int_p = collect_value->v_pointer; + + if (!int_p) + return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value)); + + *int_p = value->data[0].v_long; + + *collect_type = 0; + return NULL; +} + GType g_enum_register_static (const gchar *name, const GEnumValue *const_static_values) { GTypeInfo enum_type_info = { - sizeof (GEnumClass), - NULL /* base_init */, - NULL /* base_finalize */, + sizeof (GEnumClass), /* class_size */ + NULL, /* base_init */ + NULL, /* base_finalize */ (GClassInitFunc) g_enum_class_init, - NULL /* class_finalize */, - NULL /* class_data */, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, /* instance_size */ + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value_table */ }; GType type; @@ -107,7 +156,7 @@ g_enum_register_static (const gchar *name, enum_type_info.class_data = const_static_values; - type = g_type_register_static (G_TYPE_ENUM, name, &enum_type_info); + type = g_type_register_static (G_TYPE_ENUM, name, &enum_type_info, 0); return type; } @@ -117,12 +166,16 @@ g_flags_register_static (const gchar *name, const GFlagsValue *const_static_values) { GTypeInfo flags_type_info = { - sizeof (GFlagsClass), - NULL /* base_init */, - NULL /* base_finalize */, + sizeof (GFlagsClass), /* class_size */ + NULL, /* base_init */ + NULL, /* base_finalize */ (GClassInitFunc) g_flags_class_init, - NULL /* class_finalize */, - NULL /* class_data */, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, /* instance_size */ + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value_table */ }; GType type; @@ -131,7 +184,7 @@ g_flags_register_static (const gchar *name, flags_type_info.class_data = const_static_values; - type = g_type_register_static (G_TYPE_FLAGS, name, &flags_type_info); + type = g_type_register_static (G_TYPE_FLAGS, name, &flags_type_info, 0); return type; } @@ -335,15 +388,15 @@ g_value_set_enum (GValue *value, gint v_enum) { g_return_if_fail (G_IS_VALUE_ENUM (value)); - + value->data[0].v_long = v_enum; } gint -g_value_get_enum (GValue *value) +g_value_get_enum (const GValue *value) { g_return_val_if_fail (G_IS_VALUE_ENUM (value), 0); - + return value->data[0].v_long; } @@ -352,56 +405,14 @@ g_value_set_flags (GValue *value, guint v_flags) { g_return_if_fail (G_IS_VALUE_FLAGS (value)); - + value->data[0].v_ulong = v_flags; } guint -g_value_get_flags (GValue *value) +g_value_get_flags (const GValue *value) { g_return_val_if_fail (G_IS_VALUE_FLAGS (value), 0); - - return value->data[0].v_ulong; -} - -static void -g_value_enum_init (GValue *value) -{ - value->data[0].v_long = 0; -} - -static void -g_value_enum_copy_value (const GValue *src_value, - GValue *dest_value) -{ - dest_value->data[0].v_long = src_value->data[0].v_long; -} - -static gchar* -g_value_enum_collect_value (GValue *value, - guint nth_value, - GType *collect_type, - GTypeCValue *collect_value) -{ - value->data[0].v_long = collect_value->v_int; - - *collect_type = 0; - return NULL; -} - -static gchar* -g_value_enum_lcopy_value (const GValue *value, - guint nth_value, - GType *collect_type, - GTypeCValue *collect_value) -{ - gint *int_p = collect_value->v_pointer; - - if (!int_p) - return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value)); - *int_p = value->data[0].v_long; - - *collect_type = 0; - return NULL; + return value->data[0].v_ulong; } diff --git a/gobject/genums.h b/gobject/genums.h index f04b7d60f..7fc48ba95 100644 --- a/gobject/genums.h +++ b/gobject/genums.h @@ -38,8 +38,8 @@ extern "C" { #define G_IS_FLAGS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_FLAGS)) #define G_FLAGS_CLASS_TYPE(class) (G_TYPE_FROM_CLASS (class)) #define G_FLAGS_CLASS_TYPE_NAME(class) (g_type_name (G_FLAGS_TYPE (class))) -#define G_IS_VALUE_ENUM(value) (G_TYPE_CHECK_CLASS_TYPE ((value), G_TYPE_ENUM)) -#define G_IS_VALUE_FLAGS(value) (G_TYPE_CHECK_CLASS_TYPE ((value), G_TYPE_FLAGS)) +#define G_IS_VALUE_ENUM(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_ENUM)) +#define G_IS_VALUE_FLAGS(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_FLAGS)) /* --- enum/flag values & classes --- */ @@ -93,10 +93,10 @@ GFlagsValue* g_flags_get_value_by_nick (GFlagsClass *flags_class, const gchar *nick); void g_value_set_enum (GValue *value, gint v_enum); -gint g_value_get_enum (GValue *value); +gint g_value_get_enum (const GValue *value); void g_value_set_flags (GValue *value, guint v_flags); -guint g_value_get_flags (GValue *value); +guint g_value_get_flags (const GValue *value); diff --git a/gobject/glib-genmarshal.1 b/gobject/glib-genmarshal.1 new file mode 100644 index 000000000..d68ff6b1a --- /dev/null +++ b/gobject/glib-genmarshal.1 @@ -0,0 +1,196 @@ +.TH GLIB-GENMARSHAL 1 "18 Oct 2000" +.SH NAME +glib-genmarshal \- C code marshaller generation utility for GLib closures +.SH SYNOPSIS + +\fBglib-genmarshal\fP [\fIoptions\fP] [\fIfiles...\fP] + +.SH DESCRIPTION +\fBglib-genmarshal\fP is a small utility that generates C code marshallers +for callback functions of the GClosure mechanism in the GObject sublibrary +of GLib. The marshaller functions have a standard signature, they get passed +in the invoking closure, an array of value structures holding the callback +function parameters and a value structure for the return value of the +callback. The marshaller is then responsible to call the respective C code +function of the closure with all the parameters on the stack and to collect +its return value. + +.SH INVOCATION + +\fBglib-genmarshal\fP takes a list of marshallers to generate as input. +The marshaller list is either read from standard input or from files +passed as additional arguments on the command line. + +.SS Options +.TP +\fI--header +Generate header file contents of the marshallers. +.TP +\fI--body +Generate C code file contents of the marshallers. +.TP +\fI--prefix=string, --prefix string +Specify marshaller prefix. The default prefix is `\fIg_cclosure_marshal\fP'. +.TP +\fI--skip-source +Skip source location remarks in generated comments. +.TP +\fI--g-fatal-warnings +Make warnings fatal, that is, exit immediately once a warning occours. +.TP +\fI-h, --help\fP +Print brief help and exit. +.TP +\fI-v, --version\fP +Print version and exit. +.PP + +.SS Marshaller list format +.PP +The marshaller lists are processed line by line, a line can contain a +comment in the form of +.RS +.PP +# this is a comment +.PP +.RE +or a marshaller specification of the form +.RS +.PP +\fIRTYPE\fP:\fBPTYPE\fP +.PP +\fIRTYPE\fP:\fBPTYPE\fP,\fBPTYPE\fP +.PP +\fIRTYPE\fP:\fBPTYPE\fP,\fBPTYPE\fP,\fBPTYPE\fP +.PP +# up to 16 \fBPTYPE\fPs may be present +.PP +.RE +The \fIRTYPE\fP part specifies the callback's return type and +the \fBPTYPE\fPs right to the colon specify the callback's +parameter list, except for the first and the last arguments which +are always pointers. +.PP + +.SS Parameter types +Currently, the following types are supported: +.TP 12 +\fIVOID +indicates no return type, or no extra parameters. if \fIVOID\fP is used as +the parameter list, no additional parameters may be present. +.TP 12 +\fIBOOLEAN +for boolean types (gboolean) +.TP 12 +\fICHAR +for signed char types (gchar) +.TP 12 +\fIUCHAR +for unsigned char types (guchar) +.TP 12 +\fIINT +for signed integer types (gint) +.TP 12 +\fIUINT +for unsigned integer types (guint) +.TP 12 +\fILONG +for signed long integer types (glong) +.TP 12 +\fIULONG +for unsigned long integer types (gulong) +.TP 12 +\fIENUM +for enumeration types (gint) +.TP 12 +\fIFLAGS +for flag enumeration types (guint) +.TP 12 +\fIFLOAT +for single-precision float types (gfloat) +.TP 12 +\fIDOUBLE +for double-precision float types (gdouble) +.TP 12 +\fISTRING +for string types (gchar*) +.TP 12 +\fIBOXED +for boxed (anonymous but reference counted) types (GBoxed*) +.TP 12 +\fIPOINTER +for anonymous pointer types (gpointer) +.TP 12 +\fIOBJECT +for GObject or derived types (GObject*) +.TP 12 +\fINONE +deprecated alias for \fIVOID\fP +.TP 12 +\fIBOOL +deprecated alias for \fIBOOLEAN\fP + +.SH EXAMPLE +To generate marshallers for the following callback functions: +.PP +.RS +.nf +void foo (gpointer data1, + gpointer data2); +void bar (gpointer data1, + gint param1, + gpointer data2); +gfloat baz (gpointer data1, + gboolean param1, + guchar param2, + gpointer data2); +.fi +.RE +.PP +The marshaller list has to look like this: +.PP +.RS +.nf +VOID:VOID +VOID:INT +FLOAT:BOOLEAN,UCHAR +.fi +.RE +.PP +The generated marshallers have the arguments encoded +in their function name. For this particular list, they +are +g_cclosure_marshal_VOID__VOID(), +g_cclosure_marshal_VOID__INT(), +g_cclosure_marshal_FLOAT__BOOLEAN_UCHAR(). +.PP +They can be used directly for GClosures or be passed in as +the GSignalCMarshaller c_marshaller; argument upon creation +of signals: +.PP +.nf +GClosure *cc_foo, *cc_bar, *cc_baz; + +cc_foo = g_cclosure_new (NULL, foo, NULL); +g_closure_set_marshal (cc_foo, g_cclosure_marshal_VOID__VOID); +cc_bar = g_cclosure_new (NULL, bar, NULL); +g_closure_set_marshal (cc_bar, g_cclosure_marshal_VOID__INT); +cc_baz = g_cclosure_new (NULL, baz, NULL); +g_closure_set_marshal (cc_baz, g_cclosure_marshal_FLOAT__BOOLEAN_UCHAR); +.fi +.PP + + +.SH SEE ALSO +\fB +glib-config(1) +\fP + +.SH BUGS +None known yet. + +.SH AUTHOR +.B glib-genmarshal +has been written by Tim Janik <timj@gtk.org>. +.PP +This manual page was provided by Tim Janik <timj@gtk.org>. diff --git a/gobject/glib-genmarshal.c b/gobject/glib-genmarshal.c new file mode 100644 index 000000000..34081f70f --- /dev/null +++ b/gobject/glib-genmarshal.c @@ -0,0 +1,693 @@ +/* GLIB-GenMarshal - Marshaller generator for GObject 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include <glib-object.h> + +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + + +/* --- defines --- */ +#define PRG_NAME "glib-genmarshal" +#define PKG_NAME "GLib" +#define PKG_HTTP_HOME "http://www.gtk.org" + + +/* --- typedefs & structures --- */ +typedef struct _Argument Argument; +typedef struct _Signature Signature; +struct _Argument +{ + gchar *pname; /* parsed name */ + const gchar *sname; /* signature name */ + const gchar *func; /* functional extension */ + const gchar *cname; /* C name */ +}; +struct _Signature +{ + gchar *ploc; + Argument *rarg; + GList *args; /* of type Argument* */ +}; + + +/* --- prototypes --- */ +static void parse_args (gint *argc_p, + gchar ***argv_p); +static void print_blurb (FILE *bout, + gboolean print_help); + + +/* --- variables --- */ +static FILE *fout = NULL; +static GScannerConfig scanner_config_template = +{ + ( + " \t\r" /* "\n" is statement delimiter */ + ) /* cset_skip_characters */, + ( + G_CSET_a_2_z + "_" + G_CSET_A_2_Z + ) /* cset_identifier_first */, + ( + G_CSET_a_2_z + "_0123456789" + G_CSET_A_2_Z + ) /* cset_identifier_nth */, + ( "#\n" ) /* cpair_comment_single */, + + FALSE /* case_sensitive */, + + TRUE /* skip_comment_multi */, + TRUE /* skip_comment_single */, + TRUE /* scan_comment_multi */, + TRUE /* scan_identifier */, + FALSE /* scan_identifier_1char */, + FALSE /* scan_identifier_NULL */, + TRUE /* scan_symbols */, + FALSE /* scan_binary */, + TRUE /* scan_octal */, + TRUE /* scan_float */, + TRUE /* scan_hex */, + FALSE /* scan_hex_dollar */, + TRUE /* scan_string_sq */, + TRUE /* scan_string_dq */, + TRUE /* numbers_2_int */, + FALSE /* int_2_float */, + FALSE /* identifier_2_string */, + TRUE /* char_2_token */, + FALSE /* symbol_2_token */, + FALSE /* scope_0_fallback */, +}; +static gchar *marshaller_prefix = "g_cclosure_marshal"; +static GHashTable *marshallers = NULL; +static gboolean gen_cheader = FALSE; +static gboolean gen_cbody = FALSE; +static gboolean skip_ploc = FALSE; + + +/* --- functions --- */ +static gboolean +complete_arg (Argument *arg, + gboolean is_return) +{ + static const Argument inout_arguments[] = { + /* pname, sname, func, cname */ + { "VOID", "VOID", NULL, "void", }, + { "BOOLEAN", "BOOLEAN", "boolean", "gboolean", }, + { "CHAR", "CHAR", "char", "gchar", }, + { "UCHAR", "UCHAR", "uchar", "guchar", }, + { "INT", "INT", "int", "gint", }, + { "UINT", "UINT", "uint", "guint", }, + { "LONG", "LONG", "long", "glong", }, + { "ULONG", "ULONG", "ulong", "gulong", }, + { "ENUM", "ENUM", "enum", "gint", }, + { "FLAGS", "FLAGS", "flags", "guint", }, + { "FLOAT", "FLOAT", "float", "gfloat", }, + { "DOUBLE", "DOUBLE", "double", "gdouble", }, + /* deprecated: */ + { "NONE", "VOID", NULL, "void", }, + { "BOOL", "BOOLEAN", "boolean", "gboolean", }, + }; + static const Argument in_arguments[] = { + { "STRING", "POINTER", "as_pointer", "gpointer", }, + { "BOXED", "POINTER", "as_pointer", "gpointer", }, + { "POINTER", "POINTER", "as_pointer", "gpointer", }, + { "OBJECT", "POINTER", "as_pointer", "gpointer", }, + }; + static const Argument out_arguments[] = { + { "STRING", "STRING", "string", "gchar*", }, + { "BOXED", "BOXED", "boxed", "gpointer", }, + { "POINTER", "POINTER", "pointer", "gpointer", }, + { "OBJECT", "OBJECT", "object", "GObject*", }, + }; + const guint n_inout_arguments = sizeof (inout_arguments) / sizeof (inout_arguments[0]); + const guint n_out_arguments = sizeof (out_arguments) / sizeof (out_arguments[0]); + const guint n_in_arguments = sizeof (in_arguments) / sizeof (in_arguments[0]); + const Argument *arguments; + guint i, n_arguments; + + g_return_val_if_fail (arg != NULL, FALSE); + + arguments = inout_arguments; + n_arguments = n_inout_arguments; + for (i = 0; i < n_arguments; i++) + if (strcmp (arguments[i].pname, arg->pname) == 0) + { + arg->sname = arguments[i].sname; + arg->func = arguments[i].func; + arg->cname = arguments[i].cname; + + return TRUE; + } + arguments = is_return ? out_arguments : in_arguments; + n_arguments = is_return ? n_out_arguments : n_in_arguments; + for (i = 0; i < n_arguments; i++) + if (strcmp (arguments[i].pname, arg->pname) == 0) + { + arg->sname = arguments[i].sname; + arg->func = arguments[i].func; + arg->cname = arguments[i].cname; + + return TRUE; + } + + return FALSE; +} + +static const gchar* +pad (const gchar *string) +{ +#define PAD_LENGTH 12 + static gchar *buffer = NULL; + gint i; + + g_return_val_if_fail (string != NULL, NULL); + + if (!buffer) + buffer = g_new (gchar, PAD_LENGTH + 1); + + /* paranoid check */ + if (strlen (string) >= PAD_LENGTH) + { + g_free (buffer); + buffer = g_strdup_printf ("%s ", string); + g_warning ("overfull string (%u bytes) for padspace", strlen (string)); + + return buffer; + } + + for (i = 0; i < PAD_LENGTH; i++) + { + gboolean done = *string == 0; + + buffer[i] = done ? ' ' : *string++; + } + buffer[i] = 0; + + return buffer; +} + +static const gchar* +indent (guint n_spaces) +{ + static gchar *buffer; + static guint blength = 0; + + if (blength <= n_spaces) + { + blength = n_spaces + 1; + g_free (buffer); + buffer = g_new (gchar, blength); + } + memset (buffer, ' ', n_spaces); + buffer[n_spaces] = 0; + + return buffer; +} + +static void +generate_marshal (const gchar *signame, + Signature *sig) +{ + guint ind, a; + GList *node; + + if (g_hash_table_lookup (marshallers, signame)) + return; + else + { + gchar *tmp = g_strdup (signame); + + g_hash_table_insert (marshallers, tmp, tmp); + } + + if (gen_cheader) + { + ind = fprintf (fout, "extern void "); + ind += fprintf (fout, "%s_%s (", marshaller_prefix, signame); + fprintf (fout, "GClosure *closure,\n"); + fprintf (fout, "%sguint invocation_hint,\n", indent (ind)); + fprintf (fout, "%sGValue *return_value,\n", indent (ind)); + fprintf (fout, "%sguint n_param_values,\n", indent (ind)); + fprintf (fout, "%sconst GValue *param_values,\n", indent (ind)); + fprintf (fout, "%sgpointer marshal_data);\n", indent (ind)); + } + if (gen_cbody) + { + /* cfile marhsal header */ + fprintf (fout, "void\n"); + ind = fprintf (fout, "%s_%s (", marshaller_prefix, signame); + fprintf (fout, "GClosure *closure,\n"); + fprintf (fout, "%sguint invocation_hint,\n", indent (ind)); + fprintf (fout, "%sGValue *return_value,\n", indent (ind)); + fprintf (fout, "%sguint n_param_values,\n", indent (ind)); + fprintf (fout, "%sconst GValue *param_values,\n", indent (ind)); + fprintf (fout, "%sgpointer marshal_data)\n", indent (ind)); + fprintf (fout, "{\n"); + + /* cfile GSignalFunc typedef */ + ind = fprintf (fout, " typedef %s (*GSignalFunc_%s) (", sig->rarg->cname, signame); + fprintf (fout, "%s data1,\n", pad ("gpointer")); + for (a = 1, node = sig->args; node; node = node->next) + { + Argument *arg = node->data; + + if (arg->func) + fprintf (fout, "%s%s arg_%d,\n", indent (ind), pad (arg->cname), a++); + } + fprintf (fout, "%s%s data2);\n", indent (ind), pad ("gpointer")); + + /* cfile marshal variables */ + fprintf (fout, " register GSignalFunc_%s callback;\n", signame); + fprintf (fout, " register GCClosure *cc = (GCClosure*) closure;\n"); + fprintf (fout, " register gpointer data1, data2;\n"); + if (sig->rarg->func) + fprintf (fout, " %s v_return;\n", sig->rarg->cname); + + if (sig->args || sig->rarg->func) + { + fprintf (fout, "\n"); + + if (sig->rarg->func) + fprintf (fout, " g_return_if_fail (return_value != NULL);\n"); + if (sig->args) + { + for (a = 0, node = sig->args; node; node = node->next) + { + Argument *arg = node->data; + + if (arg->func) + a++; + } + fprintf (fout, " g_return_if_fail (n_param_values >= %u);\n", 1 + a); + } + } + + /* cfile marshal data1, data2 and callback setup */ + fprintf (fout, "\n"); + fprintf (fout, " if (G_CCLOSURE_SWAP_DATA (closure))\n {\n"); + fprintf (fout, " data1 = closure->data;\n"); + fprintf (fout, " data2 = g_value_get_as_pointer (param_values + 0);\n"); + fprintf (fout, " }\n else\n {\n"); + fprintf (fout, " data1 = g_value_get_as_pointer (param_values + 0);\n"); + fprintf (fout, " data2 = closure->data;\n"); + fprintf (fout, " }\n"); + fprintf (fout, " callback = (GSignalFunc_%s) (marshal_data ? marshal_data : cc->callback);\n", signame); + + /* cfile marshal callback action */ + fprintf (fout, "\n"); + ind = fprintf (fout, " %s callback (", sig->rarg->func ? " v_return =" : ""); + fprintf (fout, "data1,\n"); + for (a = 1, node = sig->args; node; node = node->next) + { + Argument *arg = node->data; + + if (arg->func) + fprintf (fout, "%sg_value_get_%s (param_values + %d),\n", indent (ind), arg->func, a++); + } + fprintf (fout, "%sdata2);\n", indent (ind)); + + /* cfile marshal return value storage */ + if (sig->rarg->func) + { + fprintf (fout, "\n"); + fprintf (fout, " g_value_set_%s (return_value, v_return);\n", sig->rarg->func); + } + + /* cfile marshal footer */ + fprintf (fout, "}\n"); + } +} + +static void +process_signature (Signature *sig) +{ + gchar *pname, *sname; + GList *node; + + /* lookup and complete info on arguments */ + if (!complete_arg (sig->rarg, TRUE)) + { + g_warning ("unknown type: %s", sig->rarg->pname); + return; + } + for (node = sig->args; node; node = node->next) + { + Argument *arg = node->data; + + if (!complete_arg (arg, FALSE)) + { + g_warning ("unknown type: %s", arg->pname); + return; + } + } + + /* construct requested marshaller name and technical marshaller name */ + pname = g_strconcat (sig->rarg->pname, "_", NULL); + sname = g_strconcat (sig->rarg->sname, "_", NULL); + for (node = sig->args; node; node = node->next) + { + Argument *arg = node->data; + gchar *tmp; + + tmp = sname; + sname = g_strconcat (tmp, "_", arg->sname, NULL); + g_free (tmp); + tmp = pname; + pname = g_strconcat (tmp, "_", arg->pname, NULL); + g_free (tmp); + } + + /* introductionary comment */ + fprintf (fout, "\n/* %s", sig->rarg->pname); + for (node = sig->args; node; node = node->next) + { + Argument *arg = node->data; + + fprintf (fout, "%c%s", node->prev ? ',' : ':', arg->pname); + } + if (!skip_ploc) + fprintf (fout, " (%s)", sig->ploc); + fprintf (fout, " */\n"); + + /* generate signature marshaller */ + generate_marshal (sname, sig); + + /* put out marshaler alias if required */ + if (gen_cheader && !g_hash_table_lookup (marshallers, pname)) + { + gchar *tmp = g_strdup (pname); + + fprintf (fout, "#define %s_%s\t%s_%s\n", marshaller_prefix, pname, marshaller_prefix, sname); + + g_hash_table_insert (marshallers, tmp, tmp); + } + + g_free (pname); + g_free (sname); +} + +static Argument* +new_arg (const gchar *pname) +{ + Argument *arg = g_new0 (Argument, 1); + + arg->pname = g_strdup (pname); + + return arg; +} + +static guint +parse_line (GScanner *scanner, + Signature *sig) +{ + /* parse identifier for return value */ + if (g_scanner_get_next_token (scanner) != G_TOKEN_IDENTIFIER) + return G_TOKEN_IDENTIFIER; + sig->rarg = new_arg (scanner->value.v_identifier); + + /* keep a note on the location */ + sig->ploc = g_strdup_printf ("%s:%u", scanner->input_name, scanner->line); + + /* expect ':' */ + if (g_scanner_get_next_token (scanner) != ':') + return ':'; + + /* parse first argument */ + if (g_scanner_get_next_token (scanner) != G_TOKEN_IDENTIFIER) + return G_TOKEN_IDENTIFIER; + sig->args = g_list_append (sig->args, new_arg (scanner->value.v_identifier)); + + /* parse rest of argument list */ + while (g_scanner_peek_next_token (scanner) == ',') + { + /* eat comma */ + g_scanner_get_next_token (scanner); + + /* parse arg identifier */ + if (g_scanner_get_next_token (scanner) != G_TOKEN_IDENTIFIER) + return G_TOKEN_IDENTIFIER; + sig->args = g_list_append (sig->args, new_arg (scanner->value.v_identifier)); + } + + /* expect end of line, done */ + if (g_scanner_get_next_token (scanner) != '\n') + return '\n'; + + /* success */ + return G_TOKEN_NONE; +} + +static gboolean +string_key_destroy (gpointer key, + gpointer value, + gpointer user_data) +{ + g_free (key); + + return TRUE; +} + +int +main (int argc, + char *argv[]) +{ + GScanner *scanner; + GSList *slist, *files = NULL; + gint i; + + /* parse args and do fast exits */ + parse_args (&argc, &argv); + + /* list input files */ + for (i = 1; i < argc; i++) + files = g_slist_prepend (files, argv[i]); + if (files) + files = g_slist_reverse (files); + else + files = g_slist_prepend (files, "/dev/stdin"); + + /* setup auxillary structs */ + scanner = g_scanner_new (&scanner_config_template); + fout = stdout; + marshallers = g_hash_table_new (g_str_hash, g_str_equal); + + /* process input files */ + for (slist = files; slist; slist = slist->next) + { + gchar *file = slist->data; + gint fd = open (file, O_RDONLY); + + if (fd < 0) + { + g_warning ("failed to open \"%s\": %s", file, g_strerror (errno)); + continue; + } + + /* set file name for error reports */ + scanner->input_name = file; + + /* parse & process file */ + g_scanner_input_file (scanner, fd); + + /* scanning loop, we parse the input untill it's end is reached, + * or our sub routine came across invalid syntax + */ + do + { + guint expected_token = G_TOKEN_NONE; + + switch (g_scanner_peek_next_token (scanner)) + { + case '\n': + /* eat newline and restart */ + g_scanner_get_next_token (scanner); + continue; + case G_TOKEN_EOF: + /* done */ + break; + default: + /* parse and process signatures */ + { + Signature signature = { NULL, NULL, NULL }; + GList *node; + + expected_token = parse_line (scanner, &signature); + + /* once we got a valid signature, process it */ + if (expected_token == G_TOKEN_NONE) + process_signature (&signature); + + /* clean up signature contents */ + g_free (signature.ploc); + if (signature.rarg) + g_free (signature.rarg->pname); + g_free (signature.rarg); + for (node = signature.args; node; node = node->next) + { + Argument *arg = node->data; + + g_free (arg->pname); + g_free (arg); + } + g_list_free (signature.args); + } + break; + } + + /* bail out on errors */ + if (expected_token != G_TOKEN_NONE) + { + g_scanner_unexp_token (scanner, expected_token, "type name", NULL, NULL, NULL, TRUE); + break; + } + + g_scanner_peek_next_token (scanner); + } + while (scanner->next_token != G_TOKEN_EOF); + + close (fd); + } + + /* clean up */ + g_slist_free (files); + g_scanner_destroy (scanner); + g_hash_table_foreach_remove (marshallers, string_key_destroy, NULL); + g_hash_table_destroy (marshallers); + + return 0; +} + +static void +parse_args (gint *argc_p, + gchar ***argv_p) +{ + guint argc = *argc_p; + gchar **argv = *argv_p; + guint i, e; + + for (i = 1; i < argc; i++) + { + if (strcmp ("--header", argv[i]) == 0) + { + gen_cheader = TRUE; + argv[i] = NULL; + } + else if (strcmp ("--body", argv[i]) == 0) + { + gen_cbody = TRUE; + argv[i] = NULL; + } + else if (strcmp ("--skip-source", argv[i]) == 0) + { + skip_ploc = TRUE; + argv[i] = NULL; + } + else if ((strcmp ("--prefix", argv[i]) == 0) || + (strncmp ("--prefix=", argv[i], 9) == 0)) + { + gchar *equal = argv[i] + 8; + + if (*equal == '=') + marshaller_prefix = g_strdup (equal + 1); + else if (i + 1 < argc) + { + marshaller_prefix = g_strdup (argv[i + 1]); + argv[i] = NULL; + i += 1; + } + argv[i] = NULL; + } + else if (strcmp ("-h", argv[i]) == 0 || + strcmp ("--help", argv[i]) == 0) + { + print_blurb (stderr, TRUE); + argv[i] = NULL; + exit (0); + } + else if (strcmp ("-v", argv[i]) == 0 || + strcmp ("--version", argv[i]) == 0) + { + print_blurb (stderr, FALSE); + argv[i] = NULL; + exit (0); + } + else if (strcmp (argv[i], "--g-fatal-warnings") == 0) + { + GLogLevelFlags fatal_mask; + + 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); + + argv[i] = NULL; + } + } + + e = 0; + for (i = 1; i < argc; i++) + { + if (e) + { + if (argv[i]) + { + argv[e++] = argv[i]; + argv[i] = NULL; + } + } + else if (!argv[i]) + e = i; + } + if (e) + *argc_p = e; +} + +static void +print_blurb (FILE *bout, + gboolean print_help) +{ + if (!print_help) + { + fprintf (bout, "%s version ", PRG_NAME); + fprintf (bout, "%u.%u.%u", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION); + fprintf (bout, "\n"); + fprintf (bout, "%s comes with ABSOLUTELY NO WARRANTY.\n", PRG_NAME); + fprintf (bout, "You may redistribute copies of %s under the terms of\n", PRG_NAME); + fprintf (bout, "the GNU General Public License which can be found in the\n"); + fprintf (bout, "%s source package. Sources, examples and contact\n", PKG_NAME); + fprintf (bout, "information are available at %s\n", PKG_HTTP_HOME); + } + else + { + fprintf (bout, "Usage: %s [options] [files...]\n", PRG_NAME); + fprintf (bout, " --header generate C headers\n"); + fprintf (bout, " --body generate C code\n"); + fprintf (bout, " --prefix=string specify marshaller prefix\n"); + fprintf (bout, " --skip-source skip source location comments\n"); + fprintf (bout, " -h, --help show this help message\n"); + fprintf (bout, " -v, --version print version informations\n"); + fprintf (bout, " --g-fatal-warnings make warnings fatal (abort)\n"); + } +} diff --git a/gobject/gobject-query.c b/gobject/gobject-query.c index 3e0b37d53..b80709adc 100644 --- a/gobject/gobject-query.c +++ b/gobject/gobject-query.c @@ -16,7 +16,7 @@ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ -#include "config.h" +#include "../config.h" #include <glib-object.h> diff --git a/gobject/gobject.c b/gobject/gobject.c index 5a703cebb..e5519107c 100644 --- a/gobject/gobject.c +++ b/gobject/gobject.c @@ -46,6 +46,7 @@ static void g_value_object_init (GValue *value); static void g_value_object_free_value (GValue *value); static void g_value_object_copy_value (const GValue *src_value, GValue *dest_value); +static gpointer g_value_object_peek_pointer (const GValue *value); static gchar* g_value_object_collect_value (GValue *value, guint nth_value, GType *collect_type, @@ -59,6 +60,7 @@ static gchar* g_value_object_lcopy_value (const GValue *value, /* --- variables --- */ static GQuark quark_param_id = 0; static GQuark quark_param_changed_queue = 0; +static GQuark quark_closure_array = 0; static GHashTable *param_spec_hash_table = NULL; @@ -122,6 +124,7 @@ g_object_type_init (void) /* sync with gtype.c */ g_value_object_init, /* value_init */ g_value_object_free_value, /* value_free */ g_value_object_copy_value, /* value_copy */ + g_value_object_peek_pointer, /* value_peek_pointer */ G_VALUE_COLLECT_POINTER, /* collect_type */ g_value_object_collect_value, /* collect_value */ G_VALUE_COLLECT_POINTER, /* lcopy_type */ @@ -135,7 +138,7 @@ g_object_type_init (void) /* sync with gtype.c */ /* G_TYPE_OBJECT */ info.value_table = &value_table; - type = g_type_register_fundamental (G_TYPE_OBJECT, "GObject", &info, &finfo); + type = g_type_register_fundamental (G_TYPE_OBJECT, "GObject", &info, &finfo, 0); g_assert (type == G_TYPE_OBJECT); #ifdef DEBUG_OBJECTS @@ -178,6 +181,7 @@ g_object_do_class_init (GObjectClass *class) { quark_param_id = g_quark_from_static_string ("glib-object-param-id"); quark_param_changed_queue = g_quark_from_static_string ("glib-object-param-changed-queue"); + quark_closure_array = g_quark_from_static_string ("GObject-closure-array"); param_spec_hash_table = g_param_spec_hash_table_new (); class->queue_param_changed = g_object_do_queue_param_changed; @@ -810,6 +814,12 @@ g_value_object_copy_value (const GValue *src_value, dest_value->data[0].v_pointer = NULL; } +static gpointer +g_value_object_peek_pointer (const GValue *value) +{ + return value->data[0].v_pointer; +} + static gchar* g_value_object_collect_value (GValue *value, guint nth_value, @@ -874,7 +884,7 @@ g_value_set_object (GValue *value, } GObject* -g_value_get_object (GValue *value) +g_value_get_object (const GValue *value) { g_return_val_if_fail (G_IS_VALUE_OBJECT (value), NULL); @@ -882,9 +892,137 @@ g_value_get_object (GValue *value) } GObject* -g_value_dup_object (GValue *value) +g_value_dup_object (const GValue *value) { g_return_val_if_fail (G_IS_VALUE_OBJECT (value), NULL); return value->data[0].v_pointer ? g_object_ref (value->data[0].v_pointer) : NULL; } + +typedef struct { + GObject *object; + guint n_closures; + GClosure *closures[1]; /* flexible array */ +} CArray; + +static void +object_remove_closure (gpointer data, + GClosure *closure) +{ + GObject *object = data; + CArray *carray = g_object_get_qdata (object, quark_closure_array); + guint i; + + for (i = 0; i < carray->n_closures; i++) + if (carray->closures[i] == closure) + { + carray->n_closures--; + if (i < carray->n_closures) + carray->closures[i] = carray->closures[carray->n_closures]; + return; + } + g_assert_not_reached (); +} + +static void +destroy_closure_array (gpointer data) +{ + CArray *carray = data; + GObject *object = carray->object; + guint i, n = carray->n_closures; + + for (i = 0; i < n; i++) + { + GClosure *closure = carray->closures[i]; + + /* removing object_remove_closure() upfront is probably faster than + * letting it fiddle with quark_closure_array which is empty anyways + */ + g_closure_remove_inotify (closure, object, object_remove_closure); + g_closure_invalidate (closure); + } + g_free (carray); +} + +void +g_object_watch_closure (GObject *object, + GClosure *closure) +{ + CArray *carray; + + g_return_if_fail (G_IS_OBJECT (object)); + g_return_if_fail (closure != NULL); + g_return_if_fail (closure->is_invalid == FALSE); + g_return_if_fail (closure->in_marshal == FALSE); + g_return_if_fail (object->ref_count > 0); /* this doesn't work on finalizing objects */ + + g_closure_add_inotify (closure, object, object_remove_closure); + g_closure_add_marshal_guards (closure, + object, (GClosureNotify) g_object_ref, + object, (GClosureNotify) g_object_unref); + carray = g_object_get_qdata (object, quark_closure_array); + if (!carray) + { + carray = g_renew (CArray, NULL, 1); + carray->object = object; + carray->n_closures = 1; + carray->closures[0] = closure; + g_object_set_qdata_full (object, quark_closure_array, carray, destroy_closure_array); + } + else + { + guint i = carray->n_closures++; + + carray = g_realloc (carray, sizeof (*carray) + sizeof (carray->closures[0]) * i); + carray->closures[i] = closure; + } +} + +GClosure* +g_closure_new_object (guint sizeof_closure, + GObject *object) +{ + GClosure *closure; + + g_return_val_if_fail (G_IS_OBJECT (object), NULL); + g_return_val_if_fail (object->ref_count > 0, NULL); /* this doesn't work on finalizing objects */ + + closure = g_closure_new_simple (sizeof_closure, object); + g_object_watch_closure (object, closure); + + return closure; +} + +GClosure* +g_cclosure_new_object (gpointer _object, + GCallback callback_func) +{ + GObject *object = _object; + GClosure *closure; + + g_return_val_if_fail (G_IS_OBJECT (object), NULL); + g_return_val_if_fail (object->ref_count > 0, NULL); /* this doesn't work on finalizing objects */ + g_return_val_if_fail (callback_func != NULL, NULL); + + closure = g_cclosure_new (callback_func, object, NULL); + g_object_watch_closure (object, closure); + + return closure; +} + +GClosure* +g_cclosure_new_object_swap (gpointer _object, + GCallback callback_func) +{ + GObject *object = _object; + GClosure *closure; + + g_return_val_if_fail (G_IS_OBJECT (object), NULL); + g_return_val_if_fail (object->ref_count > 0, NULL); /* this doesn't work on finalizing objects */ + g_return_val_if_fail (callback_func != NULL, NULL); + + closure = g_cclosure_new_swap (callback_func, object, NULL); + g_object_watch_closure (object, closure); + + return closure; +} diff --git a/gobject/gobject.h b/gobject/gobject.h index 68657abb0..492646888 100644 --- a/gobject/gobject.h +++ b/gobject/gobject.h @@ -16,12 +16,13 @@ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ -#ifndef __G_GOBJECT_H__ -#define __G_GOBJECT_H__ +#ifndef __G_OBJECT_H__ +#define __G_OBJECT_H__ #include <gobject/gtype.h> #include <gobject/gvalue.h> #include <gobject/gparam.h> +#include <gobject/gclosure.h> #ifdef __cplusplus @@ -31,38 +32,35 @@ extern "C" { /* --- type macros --- */ #define G_TYPE_IS_OBJECT(type) (G_TYPE_FUNDAMENTAL (type) == G_TYPE_OBJECT) -#define G_OBJECT(object) (G_IS_OBJECT (object) ? ((GObject*) (object)) : \ - G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_OBJECT, GObject)) -#define G_OBJECT_CLASS(class) (G_IS_OBJECT_CLASS (class) ? ((GObjectClass*) (class)) : \ - G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_OBJECT, GObjectClass)) -#define G_IS_OBJECT(object) (((GObject*) (object)) != NULL && \ - G_IS_OBJECT_CLASS (((GTypeInstance*) (object))->g_class)) -#define G_IS_OBJECT_CLASS(class) (((GTypeClass*) (class)) != NULL && \ - G_TYPE_IS_OBJECT (((GTypeClass*) (class))->g_type)) +#define G_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_OBJECT, GObject)) +#define G_OBJECT_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_OBJECT, GObjectClass)) +#define G_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_OBJECT)) +#define G_IS_OBJECT_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_OBJECT)) #define G_OBJECT_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), G_TYPE_OBJECT, GObjectClass)) #define G_OBJECT_TYPE(object) (G_TYPE_FROM_INSTANCE (object)) #define G_OBJECT_TYPE_NAME(object) (g_type_name (G_OBJECT_TYPE (object))) #define G_OBJECT_CLASS_TYPE(class) (G_TYPE_FROM_CLASS (class)) #define G_OBJECT_CLASS_NAME(class) (g_type_name (G_OBJECT_CLASS_TYPE (class))) -#define G_IS_VALUE_OBJECT(value) (G_TYPE_CHECK_CLASS_TYPE ((value), G_TYPE_OBJECT)) +#define G_IS_VALUE_OBJECT(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_OBJECT)) #define G_NOTIFY_PRIORITY (G_PRIORITY_HIGH_IDLE + 20) /* --- typedefs & structures --- */ -typedef struct _GObject GObject; -typedef struct _GObjectClass GObjectClass; -typedef void (*GObjectGetParamFunc) (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec, - const gchar *trailer); -typedef void (*GObjectSetParamFunc) (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec, - const gchar *trailer); -typedef void (*GObjectFinalizeFunc) (GObject *object); +typedef struct _GObject GObject; +typedef struct _GObjectClass GObjectClass; +typedef struct _GObjectConstructParam GObjectConstructParam; +typedef void (*GObjectGetParamFunc) (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec, + const gchar *trailer); +typedef void (*GObjectSetParamFunc) (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec, + const gchar *trailer); +typedef void (*GObjectFinalizeFunc) (GObject *object); struct _GObject { GTypeInstance g_type_instance; @@ -77,7 +75,10 @@ struct _GObjectClass guint n_param_specs; GParamSpec **param_specs; - + + GObject* (*constructor) (GType type, // FIXME!!! + guint n_construct_params, + GObjectConstructParam *construct_params); void (*get_param) (GObject *object, guint param_id, GValue *value, @@ -85,7 +86,7 @@ struct _GObjectClass const gchar *trailer); void (*set_param) (GObject *object, guint param_id, - GValue *value, + const GValue *value, GParamSpec *pspec, const gchar *trailer); void (*queue_param_changed) (GObject *object, @@ -95,6 +96,12 @@ struct _GObjectClass void (*shutdown) (GObject *object); void (*finalize) (GObject *object); }; +struct _GObjectConstructParam +{ + GParamSpec *pspec; + GValue *value; + gchar *trailer; +}; /* --- prototypes --- */ @@ -142,10 +149,18 @@ void g_object_set_qdata_full (GObject *object, GDestroyNotify destroy); gpointer g_object_steal_qdata (GObject *object, GQuark quark); +void g_object_watch_closure (GObject *object, + GClosure *closure); +GClosure* g_cclosure_new_object (gpointer object, + GCallback callback_func); +GClosure* g_cclosure_new_object_swap (gpointer object, + GCallback callback_func); +GClosure* g_closure_new_object (guint sizeof_closure, + GObject *object); void g_value_set_object (GValue *value, GObject *v_object); -GObject* g_value_get_object (GValue *value); -GObject* g_value_dup_object (GValue *value); +GObject* g_value_get_object (const GValue *value); +GObject* g_value_dup_object (const GValue *value); /* --- implementation macros --- */ @@ -168,4 +183,4 @@ G_STMT_START { \ } #endif /* __cplusplus */ -#endif /* __G_GOBJECT_H__ */ +#endif /* __G_OBJECT_H__ */ diff --git a/gobject/gparam.c b/gobject/gparam.c index e74787851..9ce72c379 100644 --- a/gobject/gparam.c +++ b/gobject/gparam.c @@ -63,7 +63,7 @@ g_param_type_init (void) /* sync with gtype.c */ }; GType type; - type = g_type_register_fundamental (G_TYPE_PARAM, "GParam", ¶m_spec_info, &finfo); + type = g_type_register_fundamental (G_TYPE_PARAM, "GParam", ¶m_spec_info, &finfo, G_TYPE_FLAG_ABSTRACT); g_assert (type == G_TYPE_PARAM); } @@ -380,3 +380,91 @@ g_param_spec_hash_table_lookup (GHashTable *hash_table, return pspec; } + + +/* --- auxillary functions --- */ +typedef struct +{ + /* class portion */ + GType value_type; + void (*finalize) (GParamSpec *pspec); + 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); +} ParamSpecClassInfo; + +static void +param_spec_generic_class_init (gpointer g_class, + gpointer class_data) +{ + GParamSpecClass *class = g_class; + ParamSpecClassInfo *info = class_data; + + class->value_type = info->value_type; + if (info->finalize) + class->finalize = info->finalize; /* optional */ + class->value_set_default = info->value_set_default; + if (info->value_validate) + class->value_validate = info->value_validate; /* optional */ + class->values_cmp = info->values_cmp; + g_free (class_data); +} + +static void +default_value_set_default (GParamSpec *pspec, + GValue *value) +{ + /* value is already zero initialized */ +} + +static gint +default_values_cmp (GParamSpec *pspec, + const GValue *value1, + const GValue *value2) +{ + return memcmp (&value1->data, &value2->data, sizeof (value1->data)); +} + +GType +g_param_type_register_static (const gchar *name, + const GParamSpecTypeInfo *pspec_info) +{ + GTypeInfo info = { + sizeof (GParamSpecClass), /* class_size */ + NULL, /* base_init */ + NULL, /* base_destroy */ + param_spec_generic_class_init, /* class_init */ + NULL, /* class_destroy */ + NULL, /* class_data */ + 0, /* instance_size */ + 16, /* n_preallocs */ + NULL, /* instance_init */ + }; + ParamSpecClassInfo *cinfo; + + g_return_val_if_fail (name != NULL, 0); + g_return_val_if_fail (pspec_info != NULL, 0); + g_return_val_if_fail (g_type_from_name (name) == 0, 0); + g_return_val_if_fail (pspec_info->instance_size >= sizeof (GParamSpec), 0); + g_return_val_if_fail (g_type_name (pspec_info->value_type) != NULL, 0); + /* default: g_return_val_if_fail (pspec_info->value_set_default != NULL, 0); */ + /* optional: g_return_val_if_fail (pspec_info->value_validate != NULL, 0); */ + /* default: g_return_val_if_fail (pspec_info->values_cmp != NULL, 0); */ + + info.instance_size = pspec_info->instance_size; + info.n_preallocs = pspec_info->n_preallocs; + info.instance_init = (GInstanceInitFunc) pspec_info->instance_init; + cinfo = g_new (ParamSpecClassInfo, 1); + cinfo->value_type = pspec_info->value_type; + cinfo->finalize = pspec_info->finalize; + cinfo->value_set_default = pspec_info->value_set_default ? pspec_info->value_set_default : default_value_set_default; + cinfo->value_validate = pspec_info->value_validate; + cinfo->values_cmp = pspec_info->values_cmp ? pspec_info->values_cmp : default_values_cmp; + info.class_data = cinfo; + + return g_type_register_static (G_TYPE_PARAM, name, &info, 0); +} diff --git a/gobject/gparam.h b/gobject/gparam.h index 370fb4ff9..8b1f3f1cf 100644 --- a/gobject/gparam.h +++ b/gobject/gparam.h @@ -113,6 +113,30 @@ gint g_param_values_cmp (GParamSpec *pspec, const GValue *value2); +/* --- convenience functions --- */ +typedef struct _GParamSpecTypeInfo GParamSpecTypeInfo; +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); +}; +GType g_param_type_register_static (const gchar *name, + const GParamSpecTypeInfo *pspec_info); + + /* --- private --- */ gpointer g_param_spec_internal (GType param_type, const gchar *name, @@ -134,44 +158,18 @@ GParamSpec* g_param_spec_hash_table_lookup (GHashTable *hash_table, /* contracts: * - * +++ OUTDATED +++ - * - * class functions may not evaluate param->pspec directly, - * instead, pspec will be passed as argument if required. - * - * void param_init (GParam *param, GParamSpec *pspec): - * initialize param's value to default if pspec is given, - * and to zero-equivalent (a value that doesn't need to be - * free()ed later on) otherwise. - * - * void param_free_value (GParam *param): - * free param's value if required, zero-reinitialization - * of the value is not required. (this class function - * may be NULL for param types that don't need to free - * values, such as ints or floats). - * - * gboolean param_validate (GParam *param, GParamSpec *pspec): - * modify param's value in the least destructive way, so + * 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 param_values_cmp (GParam *param1, GParam *param2, GParamSpec*): - * return param1 - param2, i.e. <0 if param1 < param2, - * >0 if param1 > param2, and 0 if they are equal - * (passing pspec is optional, but recommended) - * - * void param_copy_value (GParam *param_src, GParam *param_dest): - * copy value from param_src to param_dest, param_dest is - * already free()d and zero-initialized, so its value can - * simply be overwritten. (may be NULL for memcpy) - * - * gchar* param_collect_value (): - * class function may be NULL. - * - * gchar* param_lcopy_value (): - * class function may be NULL. - * + * gint values_cmp (GParamSpec *pspec, + * const GValue *value1, + * const GValue *value2): + * return value1 - value2, i.e. <0 if value1 < value2, + * >0 if value1 > value2, and 0 otherwise (they are equal) */ #ifdef __cplusplus diff --git a/gobject/gparamspecs.c b/gobject/gparamspecs.c index d78689f0a..10b8ab3c6 100644 --- a/gobject/gparamspecs.c +++ b/gobject/gparamspecs.c @@ -20,7 +20,7 @@ #include "gvaluecollector.h" #include <string.h> -#include "config.h" /* for SIZEOF_LONG */ +#include "../config.h" /* for SIZEOF_LONG */ #define G_FLOAT_EPSILON (1e-30) #define G_DOUBLE_EPSILON (1e-90) @@ -710,272 +710,229 @@ value_exch_double_float (GValue *value1, /* --- type initialization --- */ -typedef struct { - GType value_type; - void (*finalize) (GParamSpec *pspec); - 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); -} ParamSpecClassInfo; - -static void -param_spec_class_init (gpointer g_class, - gpointer class_data) -{ - GParamSpecClass *class = g_class; - ParamSpecClassInfo *info = class_data; - - g_assert (info->value_type && !G_TYPE_IS_PARAM (info->value_type)); - - class->value_type = info->value_type; - if (info->finalize) - class->finalize = info->finalize; - if (info->value_set_default) - class->value_set_default = info->value_set_default; - if (info->value_validate) - class->value_validate = info->value_validate; - if (info->values_cmp) - class->values_cmp = info->values_cmp; -} - void g_param_spec_types_init (void) /* sync with gtype.c */ { - GTypeInfo info = { - sizeof (GParamSpecClass), /* class_size */ - NULL, /* base_init */ - NULL, /* base_destroy */ - param_spec_class_init, /* class_init */ - NULL, /* class_destroy */ - NULL, /* class_data */ - 0, /* instance_size */ - 16, /* n_preallocs */ - NULL, /* instance_init */ - }; GType type; /* G_TYPE_PARAM_CHAR */ { - static const ParamSpecClassInfo class_info = { + static const GParamSpecTypeInfo pspec_info = { + sizeof (GParamSpecChar), /* instance_size */ + 16, /* n_preallocs */ + param_spec_char_init, /* instance_init */ G_TYPE_CHAR, /* value_type */ NULL, /* finalize */ param_char_set_default, /* value_set_default */ param_char_validate, /* value_validate */ param_int_values_cmp, /* values_cmp */ }; - info.class_data = &class_info; - info.instance_size = sizeof (GParamSpecChar); - info.instance_init = (GInstanceInitFunc) param_spec_char_init; - type = g_type_register_static (G_TYPE_PARAM, "GParamChar", &info); + type = g_param_type_register_static ("GParamChar", &pspec_info); g_assert (type == G_TYPE_PARAM_CHAR); } /* G_TYPE_PARAM_UCHAR */ { - static const ParamSpecClassInfo class_info = { + static const GParamSpecTypeInfo pspec_info = { + sizeof (GParamSpecUChar), /* instance_size */ + 16, /* n_preallocs */ + param_spec_uchar_init, /* instance_init */ G_TYPE_UCHAR, /* value_type */ NULL, /* finalize */ param_uchar_set_default, /* value_set_default */ param_uchar_validate, /* value_validate */ param_uint_values_cmp, /* values_cmp */ }; - info.class_data = &class_info; - info.instance_size = sizeof (GParamSpecUChar); - info.instance_init = (GInstanceInitFunc) param_spec_uchar_init; - type = g_type_register_static (G_TYPE_PARAM, "GParamUChar", &info); + type = g_param_type_register_static ("GParamUChar", &pspec_info); g_assert (type == G_TYPE_PARAM_UCHAR); } /* G_TYPE_PARAM_BOOLEAN */ { - static const ParamSpecClassInfo class_info = { - G_TYPE_BOOLEAN, /* value_type */ - NULL, /* finalize */ - param_boolean_set_default, /* value_set_default */ - param_boolean_validate, /* value_validate */ - param_int_values_cmp, /* values_cmp */ + static const GParamSpecTypeInfo pspec_info = { + sizeof (GParamSpecBoolean), /* instance_size */ + 16, /* n_preallocs */ + NULL, /* instance_init */ + G_TYPE_BOOLEAN, /* value_type */ + NULL, /* finalize */ + param_boolean_set_default, /* value_set_default */ + param_boolean_validate, /* value_validate */ + param_int_values_cmp, /* values_cmp */ }; - info.class_data = &class_info; - info.instance_size = sizeof (GParamSpecBoolean); - info.instance_init = (GInstanceInitFunc) NULL; - type = g_type_register_static (G_TYPE_PARAM, "GParamBoolean", &info); + type = g_param_type_register_static ("GParamBoolean", &pspec_info); g_assert (type == G_TYPE_PARAM_BOOLEAN); } /* G_TYPE_PARAM_INT */ { - static const ParamSpecClassInfo class_info = { + static const GParamSpecTypeInfo pspec_info = { + sizeof (GParamSpecInt), /* instance_size */ + 16, /* n_preallocs */ + param_spec_int_init, /* instance_init */ G_TYPE_INT, /* value_type */ NULL, /* finalize */ param_int_set_default, /* value_set_default */ param_int_validate, /* value_validate */ param_int_values_cmp, /* values_cmp */ }; - info.class_data = &class_info; - info.instance_size = sizeof (GParamSpecInt); - info.instance_init = (GInstanceInitFunc) param_spec_int_init; - type = g_type_register_static (G_TYPE_PARAM, "GParamInt", &info); + type = g_param_type_register_static ("GParamInt", &pspec_info); g_assert (type == G_TYPE_PARAM_INT); } /* G_TYPE_PARAM_UINT */ { - static const ParamSpecClassInfo class_info = { + static const GParamSpecTypeInfo pspec_info = { + sizeof (GParamSpecUInt), /* instance_size */ + 16, /* n_preallocs */ + param_spec_uint_init, /* instance_init */ G_TYPE_UINT, /* value_type */ NULL, /* finalize */ param_uint_set_default, /* value_set_default */ param_uint_validate, /* value_validate */ param_uint_values_cmp, /* values_cmp */ }; - info.class_data = &class_info; - info.instance_size = sizeof (GParamSpecUInt); - info.instance_init = (GInstanceInitFunc) param_spec_uint_init; - type = g_type_register_static (G_TYPE_PARAM, "GParamUInt", &info); + type = g_param_type_register_static ("GParamUInt", &pspec_info); g_assert (type == G_TYPE_PARAM_UINT); } /* G_TYPE_PARAM_LONG */ { - static const ParamSpecClassInfo class_info = { + static const GParamSpecTypeInfo pspec_info = { + sizeof (GParamSpecLong), /* instance_size */ + 16, /* n_preallocs */ + param_spec_long_init, /* instance_init */ G_TYPE_LONG, /* value_type */ NULL, /* finalize */ param_long_set_default, /* value_set_default */ param_long_validate, /* value_validate */ param_long_values_cmp, /* values_cmp */ }; - info.class_data = &class_info; - info.instance_size = sizeof (GParamSpecLong); - info.instance_init = (GInstanceInitFunc) param_spec_long_init; - type = g_type_register_static (G_TYPE_PARAM, "GParamLong", &info); + type = g_param_type_register_static ("GParamLong", &pspec_info); g_assert (type == G_TYPE_PARAM_LONG); } /* G_TYPE_PARAM_ULONG */ { - static const ParamSpecClassInfo class_info = { + static const GParamSpecTypeInfo pspec_info = { + sizeof (GParamSpecULong), /* instance_size */ + 16, /* n_preallocs */ + param_spec_ulong_init, /* instance_init */ G_TYPE_ULONG, /* value_type */ NULL, /* finalize */ param_ulong_set_default, /* value_set_default */ param_ulong_validate, /* value_validate */ param_ulong_values_cmp, /* values_cmp */ }; - info.class_data = &class_info; - info.instance_size = sizeof (GParamSpecULong); - info.instance_init = (GInstanceInitFunc) param_spec_ulong_init; - type = g_type_register_static (G_TYPE_PARAM, "GParamULong", &info); + type = g_param_type_register_static ("GParamULong", &pspec_info); g_assert (type == G_TYPE_PARAM_ULONG); } /* G_TYPE_PARAM_ENUM */ { - static const ParamSpecClassInfo class_info = { + static const GParamSpecTypeInfo pspec_info = { + sizeof (GParamSpecEnum), /* instance_size */ + 16, /* n_preallocs */ + param_spec_enum_init, /* instance_init */ G_TYPE_ENUM, /* value_type */ param_spec_enum_finalize, /* finalize */ param_enum_set_default, /* value_set_default */ param_enum_validate, /* value_validate */ param_long_values_cmp, /* values_cmp */ }; - info.class_data = &class_info; - info.instance_size = sizeof (GParamSpecEnum); - info.instance_init = (GInstanceInitFunc) param_spec_enum_init; - type = g_type_register_static (G_TYPE_PARAM, "GParamEnum", &info); + type = g_param_type_register_static ("GParamEnum", &pspec_info); g_assert (type == G_TYPE_PARAM_ENUM); } /* G_TYPE_PARAM_FLAGS */ { - static const ParamSpecClassInfo class_info = { + static const GParamSpecTypeInfo pspec_info = { + sizeof (GParamSpecFlags), /* instance_size */ + 16, /* n_preallocs */ + param_spec_flags_init, /* instance_init */ G_TYPE_FLAGS, /* value_type */ param_spec_flags_finalize,/* finalize */ param_flags_set_default, /* value_set_default */ param_flags_validate, /* value_validate */ param_ulong_values_cmp, /* values_cmp */ }; - info.class_data = &class_info; - info.instance_size = sizeof (GParamSpecFlags); - info.instance_init = (GInstanceInitFunc) param_spec_flags_init; - type = g_type_register_static (G_TYPE_PARAM, "GParamFlags", &info); + type = g_param_type_register_static ("GParamFlags", &pspec_info); g_assert (type == G_TYPE_PARAM_FLAGS); } /* G_TYPE_PARAM_FLOAT */ { - static const ParamSpecClassInfo class_info = { + static const GParamSpecTypeInfo pspec_info = { + sizeof (GParamSpecFloat), /* instance_size */ + 16, /* n_preallocs */ + param_spec_float_init, /* instance_init */ G_TYPE_FLOAT, /* value_type */ NULL, /* finalize */ param_float_set_default, /* value_set_default */ param_float_validate, /* value_validate */ param_float_values_cmp, /* values_cmp */ }; - info.class_data = &class_info; - info.instance_size = sizeof (GParamSpecFloat); - info.instance_init = (GInstanceInitFunc) param_spec_float_init; - type = g_type_register_static (G_TYPE_PARAM, "GParamFloat", &info); + type = g_param_type_register_static ("GParamFloat", &pspec_info); g_assert (type == G_TYPE_PARAM_FLOAT); } /* G_TYPE_PARAM_DOUBLE */ { - static const ParamSpecClassInfo class_info = { - G_TYPE_DOUBLE, /* value_type */ - NULL, /* finalize */ - param_double_set_default, /* value_set_default */ - param_double_validate, /* value_validate */ - param_double_values_cmp, /* values_cmp */ + static const GParamSpecTypeInfo pspec_info = { + sizeof (GParamSpecDouble), /* instance_size */ + 16, /* n_preallocs */ + param_spec_double_init, /* instance_init */ + G_TYPE_DOUBLE, /* value_type */ + NULL, /* finalize */ + param_double_set_default, /* value_set_default */ + param_double_validate, /* value_validate */ + param_double_values_cmp, /* values_cmp */ }; - info.class_data = &class_info; - info.instance_size = sizeof (GParamSpecDouble); - info.instance_init = (GInstanceInitFunc) param_spec_double_init; - type = g_type_register_static (G_TYPE_PARAM, "GParamDouble", &info); + type = g_param_type_register_static ("GParamDouble", &pspec_info); g_assert (type == G_TYPE_PARAM_DOUBLE); } /* G_TYPE_PARAM_STRING */ { - static const ParamSpecClassInfo class_info = { + static const GParamSpecTypeInfo pspec_info = { + sizeof (GParamSpecString), /* instance_size */ + 16, /* n_preallocs */ + param_spec_string_init, /* instance_init */ G_TYPE_STRING, /* value_type */ param_spec_string_finalize, /* finalize */ param_string_set_default, /* value_set_default */ param_string_validate, /* value_validate */ param_string_values_cmp, /* values_cmp */ }; - info.class_data = &class_info; - info.instance_size = sizeof (GParamSpecString); - info.instance_init = (GInstanceInitFunc) param_spec_string_init; - type = g_type_register_static (G_TYPE_PARAM, "GParamString", &info); + type = g_param_type_register_static ("GParamString", &pspec_info); g_assert (type == G_TYPE_PARAM_STRING); } /* G_TYPE_PARAM_OBJECT */ { - static const ParamSpecClassInfo class_info = { - G_TYPE_OBJECT, /* value_type */ - NULL, /* finalize */ - param_object_set_default, /* value_set_default */ - param_object_validate, /* value_validate */ - param_object_values_cmp, /* values_cmp */ + static const GParamSpecTypeInfo pspec_info = { + sizeof (GParamSpecObject), /* instance_size */ + 16, /* n_preallocs */ + param_spec_object_init, /* instance_init */ + G_TYPE_OBJECT, /* value_type */ + NULL, /* finalize */ + param_object_set_default, /* value_set_default */ + param_object_validate, /* value_validate */ + param_object_values_cmp, /* values_cmp */ }; - info.class_data = &class_info; - info.instance_size = sizeof (GParamSpecObject); - info.instance_init = (GInstanceInitFunc) param_spec_object_init; - type = g_type_register_static (G_TYPE_PARAM, "GParamObject", &info); + type = g_param_type_register_static ("GParamObject", &pspec_info); g_assert (type == G_TYPE_PARAM_OBJECT); } diff --git a/gobject/gsignal.c b/gobject/gsignal.c new file mode 100644 index 000000000..5006ebfed --- /dev/null +++ b/gobject/gsignal.c @@ -0,0 +1,1335 @@ +/* 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 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * this code is based on the original GtkSignal implementation + * for the Gtk+ library by Peter Mattis <petm@xcf.berkeley.edu> + */ +#include "gsignal.h" + +#include "gbsearcharray.h" + + +/* pre allocation configurations + */ +#define BSA_PRE_ALLOC (20) +#define HANDLER_PRE_ALLOC (48) +#define EMISSION_PRE_ALLOC (16) + +#define TIGHT_MEMORY (1) + +#define REPORT_BUG "please report occourance circumstances to gtk-devel-list@gnome.org" + + +/* --- generic allocation --- */ +/* we can special case allocations generically by replacing + * these functions with more speed/memory aware variants + */ +static inline gpointer +g_generic_node_alloc (GTrashStack **trash_stack_p, + guint sizeof_node, + guint nodes_pre_alloc) +{ + gpointer node = g_trash_stack_pop (trash_stack_p); + + if (!node) + { + guint8 *block; + + nodes_pre_alloc = MAX (nodes_pre_alloc, 1); + block = g_malloc (sizeof_node * nodes_pre_alloc); + while (--nodes_pre_alloc) + { + g_trash_stack_push (trash_stack_p, block); + block += sizeof_node; + } + node = block; + } + + return node; +} +static inline void +g_generic_node_free (GTrashStack **trash_stack_p, + gpointer node) +{ + g_trash_stack_push (trash_stack_p, node); +} + + +/* --- typedefs --- */ +typedef struct _SignalNode SignalNode; +typedef struct _SignalKey SignalKey; +typedef struct _Emission Emission; +typedef struct _Handler Handler; +typedef struct _HandlerList HandlerList; +typedef enum +{ + EMISSION_STOP, + EMISSION_RUN, + EMISSION_HOOK, + EMISSION_RESTART +} EmissionState; + + +/* --- prototypes --- */ +static inline guint signal_id_lookup (GQuark quark, + GType itype); +static void signal_destroy_R (SignalNode *signal_node); +static inline HandlerList* handler_list_ensure (guint signal_id, + gpointer instance); +static inline HandlerList* handler_list_lookup (guint signal_id, + gpointer instance); +static inline Handler* handler_new (gboolean after); +static void handler_insert (guint signal_id, + gpointer instance, + Handler *handler); +static Handler* handler_lookup (gpointer instance, + guint handler_id, + guint *signal_id_p); +static Handler* handler_find (gpointer instance, + GSignalMatchType mask, + guint signal_id, + GClosure *closure, + gpointer func, + gpointer data); +static inline void handler_ref (Handler *handler); +static inline void handler_unref_R (guint signal_id, + gpointer instance, + Handler *handler); +static inline void emission_push (Emission **emission_list_p, + guint signal_id, + gpointer instance, + EmissionState *state_p); +static inline void emission_pop (Emission **emission_list_p); +static inline Emission* emission_find (Emission *emission_list, + guint signal_id, + gpointer instance); +static void signal_emit_R (SignalNode *node, + gpointer instance, + GValue *return_value, + const GValue *instance_and_params); + + +/* --- structures --- */ +struct _SignalNode +{ + /* permanent portion */ + guint signal_id; + GType itype; + gchar *name; + guint destroyed : 1; + + /* reinitializable portion */ + guint flags : 8; + guint n_params : 8; + GType *param_types; + GType return_type; + GClosure *class_closure; + GSignalAccumulator accumulator; + GSignalCMarshaller c_marshaller; + GHookList *emission_hooks; +}; + +struct _SignalKey +{ + GType itype; + GQuark quark; + guint signal_id; +}; + +struct _Emission +{ + Emission *next; + guint signal_id; + gpointer instance; + EmissionState *state_p; +}; + +struct _HandlerList +{ + guint signal_id; + Handler *handlers; +}; + +struct _Handler +{ + guint id; + Handler *next; + Handler *prev; + guint ref_count : 16; +#define HANDLER_MAX_REF_COUNT (1 << 16) + guint block_count : 12; +#define HANDLER_MAX_BLOCK_COUNT (1 << 12) + guint after : 1; + GClosure *closure; +}; + + +/* --- variables --- */ +static GBSearchArray g_signal_key_bsa = { NULL, 0, 0, 0, NULL }; +static GHashTable *g_handler_list_bsa_ht = NULL; +static Emission *g_recursive_emissions = NULL; +static Emission *g_restart_emissions = NULL; +static GTrashStack *g_bsa_ts = NULL; +static GTrashStack *g_handler_ts = NULL; +static GTrashStack *g_emission_ts = NULL; +G_LOCK_DEFINE_STATIC (g_signal_mutex); + + +/* --- signal nodes --- */ +static guint g_n_signal_nodes = 0; +static SignalNode **g_signal_nodes = NULL; + +static inline SignalNode* +LOOKUP_SIGNAL_NODE (register guint signal_id) +{ + if (signal_id < g_n_signal_nodes) + return g_signal_nodes[signal_id]; + else + return NULL; +} + + +/* --- functions --- */ +static inline guint +signal_id_lookup (GQuark quark, + GType itype) +{ + SignalKey key, *signal_key; + + key.itype = itype; + key.quark = quark; + + signal_key = g_bsearch_array_lookup (&g_signal_key_bsa, &key); + + return signal_key ? signal_key->signal_id : 0; +} + +static gint +handler_lists_cmp (gconstpointer node1, + gconstpointer node2) +{ + const HandlerList *hlist1 = node1, *hlist2 = node2; + + return G_BSEARCH_ARRAY_CMP (hlist1->signal_id, hlist2->signal_id); +} + +static inline HandlerList* +handler_list_ensure (guint signal_id, + gpointer instance) +{ + GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance); + HandlerList key; + + if (!hlbsa) + { + hlbsa = g_generic_node_alloc (&g_bsa_ts, + sizeof (GBSearchArray), + BSA_PRE_ALLOC); + hlbsa->cmp_func = handler_lists_cmp; + hlbsa->sizeof_node = sizeof (HandlerList); + hlbsa->flags = G_BSEARCH_DEFER_SHRINK; + hlbsa->n_nodes = 0; + hlbsa->nodes = NULL; + g_hash_table_insert (g_handler_list_bsa_ht, instance, hlbsa); + } + key.signal_id = signal_id; + key.handlers = NULL; + + return g_bsearch_array_insert (hlbsa, &key, FALSE); +} + +static inline HandlerList* +handler_list_lookup (guint signal_id, + gpointer instance) +{ + GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance); + HandlerList key; + + key.signal_id = signal_id; + + return hlbsa ? g_bsearch_array_lookup (hlbsa, &key) : NULL; +} + +static Handler* +handler_lookup (gpointer instance, + guint handler_id, + guint *signal_id_p) +{ + GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance); + + if (hlbsa) + { + guint i; + + for (i = 0; i < hlbsa->n_nodes; i++) + { + HandlerList *hlist = g_bsearch_array_get_nth (hlbsa, i); + Handler *handler; + + for (handler = hlist->handlers; handler; handler = handler->next) + if (handler->id == handler_id) + { + if (signal_id_p) + *signal_id_p = hlist->signal_id; + + return handler; + } + } + } + + return NULL; +} + +static Handler* +handler_find (gpointer instance, + GSignalMatchType mask, + guint signal_id, + GClosure *closure, + gpointer func, + gpointer data) +{ + if (mask & G_SIGNAL_MATCH_ID) + { + HandlerList *hlist = handler_list_lookup (signal_id, instance); + Handler *handler; + SignalNode *node; + + if (mask & G_SIGNAL_MATCH_FUNC) + { + node = LOOKUP_SIGNAL_NODE (signal_id); + if (!node || !node->c_marshaller) + return NULL; + } + + mask = ~mask; + for (handler = hlist ? hlist->handlers : NULL; handler; handler = handler->next) + if (((mask & G_SIGNAL_MATCH_CLOSURE) || handler->closure == closure) && + ((mask & G_SIGNAL_MATCH_UNBLOCKED) || handler->block_count == 0) && + ((mask & G_SIGNAL_MATCH_DATA) || handler->closure->data == data) && + ((mask & G_SIGNAL_MATCH_FUNC) || (handler->closure->marshal == node->c_marshaller && + handler->closure->meta_marshal == 0 && + ((GCClosure*) handler->closure)->callback == func))) + return handler; + } + else + { + GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance); + + mask = ~mask; + if (hlbsa) + { + guint i; + + for (i = 0; i < hlbsa->n_nodes; i++) + { + HandlerList *hlist = g_bsearch_array_get_nth (hlbsa, i); + SignalNode *node; + Handler *handler; + + if (!(mask & G_SIGNAL_MATCH_FUNC)) + { + node = LOOKUP_SIGNAL_NODE (hlist->signal_id); + if (!node->c_marshaller) + continue; + } + + for (handler = hlist->handlers; handler; handler = handler->next) + if (((mask & G_SIGNAL_MATCH_CLOSURE) || handler->closure == closure) && + ((mask & G_SIGNAL_MATCH_UNBLOCKED) || handler->block_count == 0) && + ((mask & G_SIGNAL_MATCH_DATA) || handler->closure->data == data) && + ((mask & G_SIGNAL_MATCH_FUNC) || (handler->closure->marshal == node->c_marshaller && + handler->closure->meta_marshal == 0 && + ((GCClosure*) handler->closure)->callback == func))) + return handler; + } + } + } + + return NULL; +} + +static inline Handler* +handler_new (gboolean after) +{ + static guint handler_id = 1; + Handler *handler = g_generic_node_alloc (&g_handler_ts, + sizeof (Handler), + HANDLER_PRE_ALLOC); +#ifndef G_DISABLE_CHECKS + if (handler_id == 0) + g_error (G_STRLOC ": handler id overflow, %s", REPORT_BUG); +#endif + + handler->id = handler_id++; + handler->prev = NULL; + handler->next = NULL; + handler->ref_count = 1; + handler->block_count = 0; + handler->after = after != FALSE; + handler->closure = NULL; + + return handler; +} + +static inline void +handler_ref (Handler *handler) +{ + g_return_if_fail (handler->ref_count > 0); + +#ifndef G_DISABLE_CHECKS + if (handler->ref_count >= HANDLER_MAX_REF_COUNT - 1) + g_error (G_STRLOC ": handler ref_count overflow, %s", REPORT_BUG); +#endif + + handler->ref_count += 1; +} + +static inline void +handler_unref_R (guint signal_id, + gpointer instance, + Handler *handler) +{ + g_return_if_fail (handler->ref_count > 0); + + handler->ref_count -= 1; + if (!handler->ref_count) + { + if (handler->next) + handler->next->prev = handler->prev; + if (handler->prev) /* watch out for g_signal_handlers_destroy()! */ + handler->prev->next = handler->next; + else + { + HandlerList *hlist = handler_list_lookup (signal_id, instance); + + hlist->handlers = handler->next; + } + G_UNLOCK (g_signal_mutex); + g_closure_unref (handler->closure); + G_LOCK (g_signal_mutex); + g_generic_node_free (&g_handler_ts, handler); + } +} + +static void +handler_insert (guint signal_id, + gpointer instance, + Handler *handler) +{ + HandlerList *hlist; + + g_assert (handler->prev == NULL && handler->next == NULL); // FIXME: paranoid + + hlist = handler_list_ensure (signal_id, instance); + if (!hlist->handlers) + hlist->handlers = handler; + else if (hlist->handlers->after && !handler->after) + { + handler->next = hlist->handlers; + hlist->handlers->prev = handler; + hlist->handlers = handler; + } + else + { + Handler *tmp = hlist->handlers; + + if (handler->after) + while (tmp->next) + tmp = tmp->next; + else + while (tmp->next && !tmp->next->after) + tmp = tmp->next; + if (tmp->next) + tmp->next->prev = handler; + handler->next = tmp->next; + handler->prev = tmp; + tmp->next = handler; + } +} + +static inline void +emission_push (Emission **emission_list_p, + guint signal_id, + gpointer instance, + EmissionState *state_p) +{ + Emission *emission = g_generic_node_alloc (&g_emission_ts, + sizeof (Emission), + EMISSION_PRE_ALLOC); + emission->next = *emission_list_p; + emission->signal_id = signal_id; + emission->instance = instance; + emission->state_p = state_p; + *emission_list_p = emission; +} + +static inline void +emission_pop (Emission **emission_list_p) +{ + Emission *emission = *emission_list_p; + + *emission_list_p = emission->next; + g_generic_node_free (&g_emission_ts, emission); +} + +static inline Emission* +emission_find (Emission *emission_list, + guint signal_id, + gpointer instance) +{ + Emission *emission; + + for (emission = emission_list; emission; emission = emission->next) + if (emission->instance == instance && emission->signal_id == signal_id) + return emission; + return NULL; +} + +static gint +signal_key_cmp (gconstpointer node1, + gconstpointer node2) +{ + const SignalKey *key1 = node1, *key2 = node2; + + if (key1->itype == key2->itype) + return G_BSEARCH_ARRAY_CMP (key1->quark, key2->quark); + else + return G_BSEARCH_ARRAY_CMP (key1->itype, key2->itype); +} + +void +g_signal_init (void) /* sync with gtype.c */ +{ + G_LOCK (g_signal_mutex); + if (!g_n_signal_nodes) + { + /* setup signal key array */ + g_signal_key_bsa.cmp_func = signal_key_cmp; + g_signal_key_bsa.sizeof_node = sizeof (SignalKey); + g_signal_key_bsa.flags = 0; /* alloc-only */ + + /* setup handler list binary searchable array hash table (in german, that'd be one word ;) */ + g_handler_list_bsa_ht = g_hash_table_new (g_direct_hash, NULL); + + /* invalid (0) signal_id */ + g_n_signal_nodes = 1; + g_signal_nodes = g_renew (SignalNode*, g_signal_nodes, g_n_signal_nodes); + g_signal_nodes[0] = NULL; + } + G_UNLOCK (g_signal_mutex); +} + +void +g_signals_destroy (GType itype) +{ + guint i; + gboolean found_one = FALSE; + + G_LOCK (g_signal_mutex); + for (i = 0; i < g_n_signal_nodes; i++) + { + SignalNode *node = g_signal_nodes[i]; + + if (node->itype == itype) + { + if (node->destroyed) + g_warning (G_STRLOC ": signal \"%s\" of type `%s' already destroyed", + node->name, + g_type_name (node->itype)); + else + { + found_one = TRUE; + signal_destroy_R (node); + } + } + } + if (!found_one) + g_warning (G_STRLOC ": type `%s' has no signals that could be destroyed", + g_type_name (itype)); + G_UNLOCK (g_signal_mutex); +} + +void +g_signal_stop_emission (gpointer instance, + guint signal_id) +{ + SignalNode *node; + + g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance)); + g_return_if_fail (signal_id > 0); + + G_LOCK (g_signal_mutex); + node = LOOKUP_SIGNAL_NODE (signal_id); + if (node && g_type_conforms_to (G_TYPE_FROM_INSTANCE (instance), node->itype)) + { + Emission *emission_list = node->flags & G_SIGNAL_NO_RECURSE ? g_restart_emissions : g_recursive_emissions; + Emission *emission = emission_find (emission_list, signal_id, instance); + + if (emission) + { + if (*emission->state_p == EMISSION_HOOK) + g_warning (G_STRLOC ": emission of signal \"%s\" for instance `%p' cannot be stopped from emission hook", + node->name, instance); + else if (*emission->state_p == EMISSION_RUN) + *emission->state_p = EMISSION_STOP; + } + else + g_warning (G_STRLOC ": no emission of signal \"%s\" to stop for instance `%p'", + node->name, instance); + } + else + g_warning ("%s: signal id `%u' is invalid for instance `%p'", G_STRLOC, signal_id, instance); + G_UNLOCK (g_signal_mutex); +} + +guint +g_signal_lookup (const gchar *name, + GType itype) +{ + GQuark quark; + + g_return_val_if_fail (name != NULL, 0); + g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (itype) || G_TYPE_IS_INTERFACE (itype), 0); + + G_LOCK (g_signal_mutex); + quark = g_quark_try_string (name); + if (quark) + do + { + guint signal_id = signal_id_lookup (quark, itype); + + if (signal_id) + return signal_id; + + itype = g_type_parent (itype); + } + while (itype); + G_UNLOCK (g_signal_mutex); + + return 0; +} + +gchar* +g_signal_name (guint signal_id) +{ + SignalNode *node; + gchar *name; + + G_LOCK (g_signal_mutex); + node = LOOKUP_SIGNAL_NODE (signal_id); + name = node ? node->name : NULL; + G_UNLOCK (g_signal_mutex); + + return name; +} + +void +g_signal_query (guint signal_id, + GSignalQuery *query) +{ + SignalNode *node; + + g_return_if_fail (query != NULL); + + G_LOCK (g_signal_mutex); + node = LOOKUP_SIGNAL_NODE (signal_id); + if (!node || node->destroyed) + query->signal_id = 0; + else + { + query->signal_id = node->signal_id; + query->signal_name = node->name; + query->itype = node->itype; + query->signal_flags = node->flags; + query->return_type = node->return_type; + query->n_params = node->n_params; + query->param_types = node->param_types; + } + G_UNLOCK (g_signal_mutex); +} + +guint +g_signal_newv (const gchar *signal_name, + GType itype, + GSignalType signal_flags, + GClosure *class_closure, + GSignalAccumulator accumulator, + GSignalCMarshaller c_marshaller, + GType return_type, + guint n_params, + GType *param_types) +{ + gchar *name; + guint signal_id, i; + SignalNode *node; + + g_return_val_if_fail (signal_name != NULL, 0); + g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (itype) || G_TYPE_IS_INTERFACE (itype), 0); + if (n_params) + g_return_val_if_fail (param_types != NULL, 0); + if (return_type != G_TYPE_NONE) + g_return_val_if_fail (accumulator == NULL, 0); + + name = g_strdup (signal_name); + g_strdelimit (name, G_STR_DELIMITERS ":^", '_'); // FIXME do character checks like for types + + G_LOCK (g_signal_mutex); + + signal_id = g_signal_lookup (name, itype); + node = LOOKUP_SIGNAL_NODE (signal_id); + if (node && !node->destroyed) + { + g_warning (G_STRLOC ": signal \"%s\" already exists in the `%s' %s", + name, + g_type_name (node->itype), + G_TYPE_IS_INTERFACE (node->itype) ? "interface" : "class ancestry"); + g_free (name); + G_UNLOCK (g_signal_mutex); + return 0; + } + if (node && node->itype != itype) + { + g_warning (G_STRLOC ": signal \"%s\" for type `%s' was previously created for type `%s'", + name, + g_type_name (itype), + g_type_name (node->itype)); + g_free (name); + G_UNLOCK (g_signal_mutex); + return 0; + } + for (i = 0; i < n_params; i++) + if (!G_TYPE_IS_VALUE (param_types[i]) || + param_types[i] == G_TYPE_ENUM || param_types[i] == G_TYPE_FLAGS) /* FIXME: kludge */ + { + g_warning (G_STRLOC ": parameter %d of type `%s' for signal \"%s::%s\" is not a value type", + i + 1, g_type_name (param_types[i]), g_type_name (itype), name); + g_free (name); + G_UNLOCK (g_signal_mutex); + return 0; + } + if (return_type != G_TYPE_NONE && !G_TYPE_IS_VALUE (return_type)) + { + g_warning (G_STRLOC ": return value of type `%s' for signal \"%s::%s\" is not a value type", + g_type_name (param_types[i]), g_type_name (itype), name); + g_free (name); + G_UNLOCK (g_signal_mutex); + return 0; + } + + /* setup permanent portion of signal node */ + if (!node) + { + SignalKey key; + + signal_id = g_n_signal_nodes++; + node = g_new (SignalNode, 1); + node->signal_id = signal_id; + g_signal_nodes = g_renew (SignalNode*, g_signal_nodes, g_n_signal_nodes); + g_signal_nodes[signal_id] = node; + node->itype = itype; + node->name = name; + key.itype = itype; + key.quark = g_quark_from_string (node->name); + key.signal_id = signal_id; + g_bsearch_array_insert (&g_signal_key_bsa, &key, FALSE); + g_strdelimit (node->name, "_", '-'); + key.quark = g_quark_from_static_string (node->name); + g_bsearch_array_insert (&g_signal_key_bsa, &key, FALSE); + } + node->destroyed = FALSE; + + /* setup reinitializable portion */ + node->flags = signal_flags & (G_SIGNAL_RUN_FIRST | + G_SIGNAL_RUN_LAST | + G_SIGNAL_RUN_CLEANUP | + G_SIGNAL_NO_RECURSE | + G_SIGNAL_ACTION | + G_SIGNAL_NO_HOOKS); + node->n_params = n_params; + node->param_types = g_memdup (param_types, sizeof (GType) * n_params); + node->return_type = return_type; + node->class_closure = class_closure ? g_closure_ref (class_closure) : NULL; + node->accumulator = accumulator; + node->c_marshaller = c_marshaller; + node->emission_hooks = NULL; + if (node->c_marshaller && class_closure && G_CLOSURE_NEEDS_MARSHAL (class_closure)) + g_closure_set_marshal (class_closure, node->c_marshaller); + + G_UNLOCK (g_signal_mutex); + return signal_id; +} + +static void +signal_destroy_R (SignalNode *signal_node) +{ + SignalNode node = *signal_node; + + signal_node->destroyed = TRUE; + + /* reentrancy caution, zero out real contents first */ + signal_node->n_params = 0; + signal_node->param_types = NULL; + signal_node->return_type = 0; + signal_node->class_closure = NULL; + signal_node->accumulator = NULL; + signal_node->c_marshaller = NULL; + signal_node->emission_hooks = NULL; + +#ifndef G_DISABLE_CHECKS + /* check current emissions */ + { + Emission *emission; + + for (emission = (node.flags & G_SIGNAL_NO_RECURSE) ? g_restart_emissions : g_recursive_emissions; + emission; emission = emission->next) + if (emission->signal_id == node.signal_id) + g_critical (G_STRLOC ": signal \"%s\" being destroyed is currently in emission (instance `%p')", + node.name, emission->instance); + } +#endif + + /* free contents that need to + */ + G_UNLOCK (g_signal_mutex); + g_free (node.param_types); + g_closure_unref (node.class_closure); + if (node.emission_hooks) + { + g_hook_list_clear (node.emission_hooks); + g_free (node.emission_hooks); + } + G_LOCK (g_signal_mutex); +} + +guint +g_signal_connect_closure (gpointer instance, + guint signal_id, + GClosure *closure, + gboolean after) +{ + SignalNode *node; + guint handler_id = 0; + + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0); + g_return_val_if_fail (signal_id > 0, 0); + g_return_val_if_fail (closure != NULL, 0); + + G_LOCK (g_signal_mutex); + node = LOOKUP_SIGNAL_NODE (signal_id); + if (node && g_type_conforms_to (G_TYPE_FROM_INSTANCE (instance), node->itype)) + { + Handler *handler = handler_new (after); + + handler_id = handler->id; + handler->closure = g_closure_ref (closure); + handler_insert (signal_id, instance, handler); + if (node->c_marshaller && G_CLOSURE_NEEDS_MARSHAL (closure)) + g_closure_set_marshal (closure, node->c_marshaller); + } + else + g_warning ("%s: signal id `%u' is invalid for instance `%p'", G_STRLOC, signal_id, instance); + G_UNLOCK (g_signal_mutex); + + return handler_id; +} + +void +g_signal_handler_disconnect (gpointer instance, + guint handler_id) +{ + Handler *handler; + guint signal_id; + + g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance)); + g_return_if_fail (handler_id > 0); + + G_LOCK (g_signal_mutex); + handler = handler_lookup (instance, handler_id, &signal_id); + if (handler) + { + handler->id = 0; + handler->block_count = 1; + handler_unref_R (signal_id, instance, handler); + } + else + g_warning ("%s: instance `%p' has no handler with id `%u'", G_STRLOC, instance, handler_id); + G_UNLOCK (g_signal_mutex); +} + +void +g_signal_handlers_destroy (gpointer instance) +{ + GBSearchArray *hlbsa; + + g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance)); + + G_LOCK (g_signal_mutex); + hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance); + if (hlbsa) + { + guint i; + + /* reentrancy caution, delete instance trace first */ + g_hash_table_remove (g_handler_list_bsa_ht, instance); + + for (i = 0; i < hlbsa->n_nodes; i++) + { + HandlerList *hlist = g_bsearch_array_get_nth (hlbsa, i); + Handler *handler = hlist->handlers; + + while (handler) + { + Handler *tmp = handler; + + handler = tmp->next; + tmp->block_count = 1; + /* cruel unlink, this works because _all_ handlers vanish */ + tmp->next = NULL; + tmp->prev = tmp; + if (tmp->id) + { + tmp->id = 0; + handler_unref_R (0, NULL, tmp); + } + } + } + g_free (hlbsa->nodes); + g_generic_node_free (&g_bsa_ts, hlbsa); + } + G_UNLOCK (g_signal_mutex); +} + +void +g_signal_handler_block (gpointer instance, + guint handler_id) +{ + Handler *handler; + + g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance)); + g_return_if_fail (handler_id > 0); + + G_LOCK (g_signal_mutex); + handler = handler_lookup (instance, handler_id, NULL); + if (handler) + { +#ifndef G_DISABLE_CHECKS + if (handler->block_count >= HANDLER_MAX_BLOCK_COUNT - 1) + g_error (G_STRLOC ": handler block_count overflow, %s", REPORT_BUG); +#endif + + handler->block_count += 1; + } + else + g_warning ("%s: instance `%p' has no handler with id `%u'", G_STRLOC, instance, handler_id); + G_UNLOCK (g_signal_mutex); +} + +void +g_signal_handler_unblock (gpointer instance, + guint handler_id) +{ + Handler *handler; + + g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance)); + g_return_if_fail (handler_id > 0); + + G_LOCK (g_signal_mutex); + handler = handler_lookup (instance, handler_id, NULL); + if (handler) + { + if (handler->block_count) + handler->block_count -= 1; + else + g_warning (G_STRLOC ": handler `%u' of instance `%p' is not blocked", handler_id, instance); + } + else + g_warning ("%s: instance `%p' has no handler with id `%u'", G_STRLOC, instance, handler_id); + G_UNLOCK (g_signal_mutex); +} + +guint +g_signal_handler_find (gpointer instance, + GSignalMatchType mask, + guint signal_id, + GClosure *closure, + gpointer func, + gpointer data) +{ + Handler *handler = NULL; + guint handler_id; + + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0); + + G_LOCK (g_signal_mutex); + handler = handler_find (instance, mask, signal_id, closure, func, data); + handler_id = handler ? handler->id : 0; + G_UNLOCK (g_signal_mutex); + + return handler_id; +} + +gboolean +g_signal_handler_pending (gpointer instance, + guint signal_id, + gboolean may_be_blocked) +{ + Handler *handler = NULL; + + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), FALSE); + g_return_val_if_fail (signal_id > 0, FALSE); + + G_LOCK (g_signal_mutex); + handler = handler_find (instance, G_SIGNAL_MATCH_ID, signal_id, NULL, NULL, NULL); + if (!may_be_blocked) + for (; handler; handler = handler->next) + if (!handler->block_count) + break; + G_UNLOCK (g_signal_mutex); + + return handler != NULL; +} + +void +g_signal_emitv (const GValue *instance_and_params, + guint signal_id, + GValue *return_value) +{ + SignalNode *node; + gpointer instance; + const GValue *param_values; + guint i; + + g_return_if_fail (instance_and_params != NULL); + instance = g_value_get_as_pointer (instance_and_params); + g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance)); + g_return_if_fail (signal_id > 0); + + param_values = instance_and_params + 1; + + G_LOCK (g_signal_mutex); + node = LOOKUP_SIGNAL_NODE (signal_id); +#ifndef G_DISABLE_CHECKS + if (!node || !g_type_conforms_to (G_TYPE_FROM_INSTANCE (instance), node->itype)) + g_warning ("%s: signal id `%u' is invalid for instance `%p'", G_STRLOC, signal_id, instance); + for (i = 0; i < node->n_params; i++) + if (!G_VALUE_HOLDS (param_values + i, node->param_types[i])) + { + g_critical (G_STRLOC ": value for `%s' parameter %u for signal \"%s\" is of type `%s'", + g_type_name (node->param_types[i]), + i, + node->name, + G_VALUE_TYPE_NAME (param_values + i)); + G_UNLOCK (g_signal_mutex); + return; + } + if (node->return_type != G_TYPE_NONE) + { + if (!return_value) + { + g_critical (G_STRLOC ": return value `%s' for signal \"%s\" is (NULL)", + g_type_name (node->return_type), + node->name); + G_UNLOCK (g_signal_mutex); + return; + } + else if (!node->accumulator && !G_VALUE_HOLDS (return_value, node->return_type)) + { + g_critical (G_STRLOC ": return value `%s' for signal \"%s\" is of type `%s'", + g_type_name (node->return_type), + node->name, + G_VALUE_TYPE_NAME (return_value)); + G_UNLOCK (g_signal_mutex); + return; + } + } + else + return_value = NULL; +#endif /* !G_DISABLE_CHECKS */ + + signal_emit_R (node, instance, return_value, instance_and_params); + + G_UNLOCK (g_signal_mutex); +} + +static void +signal_emit_R (SignalNode *node, + gpointer instance, + GValue *return_value, + const GValue *instance_and_params) +{ + EmissionState emission_state = 0; + GSignalAccumulator accumulator; + GClosure *class_closure; + HandlerList *hlist; + Handler *handlers; + GValue accu; + gboolean accu_used = FALSE; + guint signal_id = node->signal_id; + + if (node->flags & G_SIGNAL_NO_RECURSE) + { + Emission *emission = emission_find (g_restart_emissions, signal_id, instance); + + if (emission) + { + *emission->state_p = EMISSION_RESTART; + return; + } + } + accumulator = node->accumulator; + if (accumulator) + { + G_UNLOCK (g_signal_mutex); + g_value_init (&accu, node->return_type); + G_LOCK (g_signal_mutex); + } + emission_push ((node->flags & G_SIGNAL_NO_RECURSE) ? &g_restart_emissions : &g_recursive_emissions, + signal_id, instance, &emission_state); + class_closure = node->class_closure; + hlist = handler_list_lookup (signal_id, instance); + handlers = hlist ? hlist->handlers : NULL; + if (handlers) + handler_ref (handlers); + + EMIT_RESTART: + + if ((node->flags & G_SIGNAL_RUN_FIRST) && class_closure) + { + emission_state = EMISSION_RUN; + + G_UNLOCK (g_signal_mutex); + if (accumulator) + { + if (accu_used) + g_value_reset (&accu); + g_closure_invoke (class_closure, + (signal_id << 8) | G_SIGNAL_RUN_FIRST, + &accu, + node->n_params + 1, + instance_and_params); + if (!accumulator (signal_id, return_value, &accu) && + emission_state == EMISSION_RUN) + emission_state = EMISSION_STOP; + accu_used = TRUE; + } + else + g_closure_invoke (class_closure, + (signal_id << 8) | G_SIGNAL_RUN_FIRST, + return_value, + node->n_params + 1, + instance_and_params); + G_LOCK (g_signal_mutex); + + if (emission_state == EMISSION_STOP) + goto EMIT_CLEANUP; + else if (emission_state == EMISSION_RESTART) + goto EMIT_RESTART; + } + + if (node->emission_hooks) + { + emission_state = EMISSION_HOOK; + + G_UNLOCK (g_signal_mutex); + g_print ("emission_hooks()\n"); + G_LOCK (g_signal_mutex); + + if (emission_state == EMISSION_RESTART) + goto EMIT_RESTART; + } + + if (handlers) + { + Handler *handler = handlers; + + emission_state = EMISSION_RUN; + + handler_ref (handler); + do + { + Handler *tmp; + + if (!handler->after && !handler->block_count) + { + G_UNLOCK (g_signal_mutex); + if (accumulator) + { + if (accu_used) + g_value_reset (&accu); + g_closure_invoke (handler->closure, + (signal_id << 8) | G_SIGNAL_RUN_FIRST, + &accu, + node->n_params + 1, + instance_and_params); + if (!accumulator (signal_id, return_value, &accu) && + emission_state == EMISSION_RUN) + emission_state = EMISSION_STOP; + accu_used = TRUE; + } + else + g_closure_invoke (handler->closure, + (signal_id << 8) | G_SIGNAL_RUN_FIRST, + return_value, + node->n_params + 1, + instance_and_params); + G_LOCK (g_signal_mutex); + + tmp = emission_state == EMISSION_RUN ? handler->next : NULL; + } + else + tmp = handler->next; + + if (tmp) + handler_ref (tmp); + handler_unref_R (signal_id, instance, handler); + handler = tmp; + } + while (handler); + + if (emission_state == EMISSION_STOP) + goto EMIT_CLEANUP; + else if (emission_state == EMISSION_RESTART) + goto EMIT_RESTART; + } + + if ((node->flags & G_SIGNAL_RUN_LAST) && class_closure) + { + emission_state = EMISSION_RUN; + + G_UNLOCK (g_signal_mutex); + if (accumulator) + { + if (accu_used) + g_value_reset (&accu); + g_closure_invoke (class_closure, + (signal_id << 8) | G_SIGNAL_RUN_LAST, + &accu, + node->n_params + 1, + instance_and_params); + if (!accumulator (signal_id, return_value, &accu) && + emission_state == EMISSION_RUN) + emission_state = EMISSION_STOP; + accu_used = TRUE; + } + else + g_closure_invoke (class_closure, + (signal_id << 8) | G_SIGNAL_RUN_LAST, + return_value, + node->n_params + 1, + instance_and_params); + G_LOCK (g_signal_mutex); + + if (emission_state == EMISSION_STOP) + goto EMIT_CLEANUP; + else if (emission_state == EMISSION_RESTART) + goto EMIT_RESTART; + } + + if (handlers) + { + Handler *handler = handlers; + + emission_state = EMISSION_RUN; + + handler_ref (handler); + do + { + Handler *tmp; + + if (handler->after && !handler->block_count) + { + G_UNLOCK (g_signal_mutex); + if (accumulator) + { + if (accu_used) + g_value_reset (&accu); + g_closure_invoke (handler->closure, + (signal_id << 8) | G_SIGNAL_RUN_LAST, + &accu, + node->n_params + 1, + instance_and_params); + if (!accumulator (signal_id, return_value, &accu) && + emission_state == EMISSION_RUN) + emission_state = EMISSION_STOP; + accu_used = TRUE; + } + else + g_closure_invoke (handler->closure, + (signal_id << 8) | G_SIGNAL_RUN_LAST, + return_value, + node->n_params + 1, + instance_and_params); + G_LOCK (g_signal_mutex); + + tmp = emission_state == EMISSION_RUN ? handler->next : NULL; + } + else + tmp = handler->next; + + if (tmp) + handler_ref (tmp); + handler_unref_R (signal_id, instance, handler); + handler = tmp; + } + while (handler); + + if (emission_state == EMISSION_STOP) + goto EMIT_CLEANUP; + else if (emission_state == EMISSION_RESTART) + goto EMIT_RESTART; + } + + EMIT_CLEANUP: + + if ((node->flags & G_SIGNAL_RUN_CLEANUP) && class_closure) + { + emission_state = EMISSION_STOP; + + G_UNLOCK (g_signal_mutex); + if (node->return_type != G_TYPE_NONE) + { + if (!accumulator) + g_value_init (&accu, node->return_type); + else if (accu_used) + g_value_reset (&accu); + accu_used = TRUE; + } + g_closure_invoke (class_closure, + (signal_id << 8) | G_SIGNAL_RUN_CLEANUP, + node->return_type != G_TYPE_NONE ? &accu : NULL, + node->n_params + 1, + instance_and_params); + if (node->return_type != G_TYPE_NONE && !accumulator) + g_value_unset (&accu); + G_LOCK (g_signal_mutex); + + if (emission_state == EMISSION_RESTART) + goto EMIT_RESTART; + } + + if (handlers) + handler_unref_R (signal_id, instance, handlers); + + emission_pop ((node->flags & G_SIGNAL_NO_RECURSE) ? &g_restart_emissions : &g_recursive_emissions); + if (accumulator) + { + G_UNLOCK (g_signal_mutex); + g_value_unset (&accu); + G_LOCK (g_signal_mutex); + } +} diff --git a/gobject/gsignal.h b/gobject/gsignal.h new file mode 100644 index 000000000..f2be46f63 --- /dev/null +++ b/gobject/gsignal.h @@ -0,0 +1,142 @@ +/* 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 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __G_SIGNAL_H__ +#define __G_SIGNAL_H__ + + +#include <gobject/gclosure.h> +#include <gobject/gvalue.h> +#include <gobject/gparam.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* --- macros --- */ +#define G_SIGNAL_HINT_ID(hint) ((hint) >> 8) +#define G_SIGNAL_HINT_RUN_TYPE(hint) ((hint) & 0xff) + + +/* --- run & match types --- */ +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_ACTION = 1 << 4, + G_SIGNAL_NO_HOOKS = 1 << 5 +} GSignalType; +typedef enum +{ + G_SIGNAL_MATCH_ID = 1 << 0, + G_SIGNAL_MATCH_CLOSURE = 1 << 1, + G_SIGNAL_MATCH_FUNC = 1 << 2, + G_SIGNAL_MATCH_DATA = 1 << 3, + G_SIGNAL_MATCH_UNBLOCKED = 1 << 4, + G_SIGNAL_MATCH_MASK = 0x1f +} GSignalMatchType; + + +/* --- signal queries --- */ +typedef struct _GSignalQuery GSignalQuery; +struct _GSignalQuery +{ + guint signal_id; + const gchar *signal_name; + GType itype; + GSignalType signal_flags; + GType return_type; + guint n_params; + const GType *param_types; +}; + + +/* --- function types --- */ +typedef gboolean (*GSignalEmissionHook) (guint signal_id, + guint n_values, + const GValue *values); +typedef gboolean (*GSignalAccumulator) (guint signal_id, + GValue *return_accu, + const GValue *return_value); +typedef GClosureMarshal GSignalCMarshaller; + + +/* --- signals --- */ +guint g_signal_newv (const gchar *signal_name, + GType itype, + GSignalType signal_flags, + GClosure *class_closure, + GSignalAccumulator accumulator, + GSignalCMarshaller c_marshaller, + GType return_type, + guint n_params, + GType *param_types); +void g_signal_emitv (const GValue *instance_and_params, + guint signal_id, + GValue *return_value); +guint g_signal_lookup (const gchar *name, + GType itype); +gchar* g_signal_name (guint signal_id); +void g_signal_query (guint signal_id, + GSignalQuery *query); + + +/* --- signal handlers --- */ +guint g_signal_connect_closure (gpointer instance, + guint signal_id, + GClosure *closure, + gboolean after); +void g_signal_handler_disconnect (gpointer instance, + guint handler_id); +void g_signal_handler_block (gpointer instance, + guint handler_id); +void g_signal_handler_unblock (gpointer instance, + guint handler_id); +guint g_signal_handler_find (gpointer instance, + GSignalMatchType mask, + guint signal_id, + GClosure *closure, + gpointer func, + gpointer data); +gboolean g_signal_has_handler_pending (gpointer instance, + guint signal_id, + gboolean may_be_blocked); + + +/* --- signal emissions --- */ +void g_signal_stop_emission (gpointer instance, + guint signal_id); +guint g_signal_add_emission_hook_full (guint signal_id, + GClosure *closure); +void g_signal_remove_emission_hook (guint signal_id, + guint hook_id); + +/*< private >*/ +void g_signal_handlers_destroy (gpointer instance); +void g_signals_destroy (GType itype); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __G_SIGNAL_H__ */ diff --git a/gobject/gtype.c b/gobject/gtype.c index a4818cc6d..f202886a2 100644 --- a/gobject/gtype.c +++ b/gobject/gtype.c @@ -32,20 +32,26 @@ * TODO: * - g_type_from_name() should do an ordered array lookup after fetching the * the quark, instead of a second hashtable lookup. + * - speedup checks for virtual types, steal a bit somewhere * * FIXME: * - force interface initialization for already existing classes + * - make things threadsafe */ -#define G_TYPE_FLAG_MASK (G_TYPE_FLAG_CLASSED | \ - G_TYPE_FLAG_INSTANTIATABLE | \ - G_TYPE_FLAG_DERIVABLE | \ - G_TYPE_FLAG_DEEP_DERIVABLE) +#define TYPE_FUNDAMENTAL_FLAG_MASK (G_TYPE_FLAG_CLASSED | \ + G_TYPE_FLAG_INSTANTIATABLE | \ + G_TYPE_FLAG_DERIVABLE | \ + G_TYPE_FLAG_DEEP_DERIVABLE) +#define TYPE_FLAG_MASK (G_TYPE_FLAG_ABSTRACT) + #define g_type_plugin_ref(p) ((p)->vtable->plugin_ref (p)) #define g_type_plugin_unref(p) ((p)->vtable->plugin_unref (p)) #define g_type_plugin_complete_type_info(p,t,i,v) ((p)->vtable->complete_type_info ((p), (t), (i), (v))) #define g_type_plugin_complete_interface_info(p,f,t,i) ((p)->vtable->complete_interface_info ((p), (f), (t), (i))) + +/* --- typedefs --- */ typedef struct _TypeNode TypeNode; typedef struct _CommonData CommonData; typedef struct _IFaceData IFaceData; @@ -58,6 +64,8 @@ typedef struct _IFaceHolder IFaceHolder; /* --- prototypes --- */ static inline GTypeFundamentalInfo* type_node_fundamental_info (TypeNode *node); +static void type_add_flags (TypeNode *node, + GTypeFlags flags); static void type_data_make (TypeNode *node, const GTypeInfo *info, const GTypeValueTable *value_table); @@ -69,6 +77,10 @@ static void type_data_last_unref (GType type, /* --- structures --- */ +struct _GValue /* kludge, keep in sync with gvalue.h */ +{ + GType g_type; +}; struct _TypeNode { GTypePlugin *plugin; @@ -159,11 +171,12 @@ typedef struct { /* --- variables --- */ static guint n_class_cache_funcs = 0; static ClassCacheFunc *class_cache_funcs = NULL; +static GType last_fundamental_id = 0; +static GQuark quark_type_flags = 0; /* --- externs --- */ const char *g_log_domain_gobject = "GLib-Object"; -static GType last_fundamental_id = 0; /* --- type nodes --- */ @@ -187,11 +200,11 @@ LOOKUP_TYPE_NODE (register GType utype) #define NODE_NAME(node) (g_quark_to_string (node->qname)) static TypeNode* -type_node_any_new (TypeNode *pnode, - GType ftype, - const gchar *name, - GTypePlugin *plugin, - GTypeFlags type_flags) +type_node_any_new (TypeNode *pnode, + GType ftype, + const gchar *name, + GTypePlugin *plugin, + GTypeFundamentalFlags type_flags) { guint branch_last, n_supers = pnode ? pnode->n_supers + 1 : 0; GType type; @@ -204,11 +217,11 @@ type_node_any_new (TypeNode *pnode, g_type_nodes[ftype] = g_renew (TypeNode*, g_type_nodes[ftype], 1 << g_bit_storage (g_branch_seqnos[ftype] - 1)); if (!pnode) - node_size += sizeof (GTypeFundamentalInfo); /* fundamental type info */ - node_size += SIZEOF_BASE_TYPE_NODE (); /* TypeNode structure */ + node_size += sizeof (GTypeFundamentalInfo); /* fundamental type info */ + node_size += SIZEOF_BASE_TYPE_NODE (); /* TypeNode structure */ node_size += (sizeof (GType) * (1 + n_supers + 1)); /* self + ancestors + 0 for ->supers[] */ node = g_malloc0 (node_size); - if (!pnode) /* fundamental type */ + if (!pnode) /* offset fundamental types */ node = G_STRUCT_MEMBER_P (node, sizeof (GTypeFundamentalInfo)); g_type_nodes[ftype][branch_last] = node; @@ -280,9 +293,9 @@ type_node_fundamental_info (TypeNode *node) } static TypeNode* -type_node_fundamental_new (GType ftype, - const gchar *name, - GTypeFlags type_flags) +type_node_fundamental_new (GType ftype, + const gchar *name, + GTypeFundamentalFlags type_flags) { GTypeFundamentalInfo *finfo; TypeNode *node; @@ -290,7 +303,7 @@ type_node_fundamental_new (GType ftype, g_assert (ftype == G_TYPE_FUNDAMENTAL (ftype)); - type_flags &= G_TYPE_FLAG_MASK; + type_flags &= TYPE_FUNDAMENTAL_FLAG_MASK; last_fundamental_id = MAX (last_fundamental_id, ftype + 1); if (last_fundamental_id > flast) @@ -495,6 +508,7 @@ check_value_table (const gchar *type_name, else if (value_table->value_init == NULL) { if (value_table->value_free || value_table->value_copy || + value_table->value_peek_pointer || value_table->collect_type || value_table->collect_value || value_table->lcopy_type || value_table->lcopy_value) g_warning ("cannot handle uninitializable values of type `%s'", @@ -696,6 +710,25 @@ check_interface_info (TypeNode *iface, /* --- type info (type node data) --- */ static void +type_add_flags (TypeNode *node, + GTypeFlags flags) +{ + guint dflags; + + g_return_if_fail ((flags & ~TYPE_FLAG_MASK) == 0); + g_return_if_fail (node != NULL); + + if (!quark_type_flags) + quark_type_flags = g_quark_from_static_string ("GTypeFlags"); + if ((flags & G_TYPE_FLAG_ABSTRACT) && node->is_classed && + node->data && node->data->class.class) + g_warning ("tagging type `%s' as abstract after class initialization", NODE_NAME (node)); + dflags = GPOINTER_TO_UINT (g_type_get_qdata (NODE_TYPE (node), quark_type_flags)); + dflags |= flags; + g_type_set_qdata (NODE_TYPE (node), quark_type_flags, GUINT_TO_POINTER (dflags)); +} + +static void type_data_make (TypeNode *node, const GTypeInfo *info, const GTypeValueTable *value_table) @@ -856,10 +889,10 @@ type_node_add_iface_entry (TypeNode *node, } static void -type_add_interface (TypeNode *node, - TypeNode *iface, - GInterfaceInfo *info, - GTypePlugin *plugin) +type_add_interface (TypeNode *node, + TypeNode *iface, + const GInterfaceInfo *info, + GTypePlugin *plugin) { IFaceHolder *iholder = g_new0 (IFaceHolder, 1); @@ -943,6 +976,12 @@ g_type_create_instance (GType type) type_descriptive_name (type)); return NULL; } + if (G_TYPE_IS_ABSTRACT (type)) + { + g_warning ("cannot create instance of abstract (non-instantiatable) type `%s'", + type_descriptive_name (type)); + return NULL; + } class = g_type_class_ref (type); @@ -992,8 +1031,15 @@ g_type_free_instance (GTypeInstance *instance) type_descriptive_name (class->g_type)); return; } + if (G_TYPE_IS_ABSTRACT (NODE_TYPE (node))) + { + g_warning ("cannot free instance of abstract (non-instantiatable) type `%s'", + NODE_NAME (node)); + return; + } instance->g_class = NULL; + memset (instance, 0xaa, node->data->instance.instance_size); // FIXME if (node->data->instance.n_preallocs) g_chunk_free (instance, node->data->instance.mem_chunk); else @@ -1282,7 +1328,8 @@ GType g_type_register_fundamental (GType type_id, const gchar *type_name, const GTypeInfo *info, - const GTypeFundamentalInfo *finfo) + const GTypeFundamentalInfo *finfo, + GTypeFlags flags) { GTypeFundamentalInfo *node_finfo; TypeNode *node; @@ -1318,6 +1365,7 @@ g_type_register_fundamental (GType type_id, node = type_node_fundamental_new (type_id, type_name, finfo->type_flags); node_finfo = type_node_fundamental_info (node); + type_add_flags (node, flags); if (!check_type_info (NULL, G_TYPE_FUNDAMENTAL (NODE_TYPE (node)), type_name, info)) return NODE_TYPE (node); @@ -1330,7 +1378,8 @@ g_type_register_fundamental (GType type_id, GType g_type_register_static (GType parent_type, const gchar *type_name, - const GTypeInfo *info) + const GTypeInfo *info, + GTypeFlags flags) { TypeNode *pnode, *node; GType type; @@ -1351,12 +1400,13 @@ g_type_register_static (GType parent_type, return 0; if (info->class_finalize) { - g_warning ("class destructor specified for static type `%s'", + g_warning ("class finalizer specified for static type `%s'", type_name); return 0; } node = type_node_new (pnode, type_name, NULL); + type_add_flags (node, flags); type = NODE_TYPE (node); type_data_make (node, info, check_value_table (type_name, info->value_table) ? info->value_table : NULL); @@ -1367,7 +1417,8 @@ g_type_register_static (GType parent_type, GType g_type_register_dynamic (GType parent_type, const gchar *type_name, - GTypePlugin *plugin) + GTypePlugin *plugin, + GTypeFlags flags) { TypeNode *pnode, *node; GType type; @@ -1385,15 +1436,16 @@ g_type_register_dynamic (GType parent_type, pnode = LOOKUP_TYPE_NODE (parent_type); node = type_node_new (pnode, type_name, plugin); + type_add_flags (node, flags); type = NODE_TYPE (node); return type; } void -g_type_add_interface_static (GType instance_type, - GType interface_type, - GInterfaceInfo *info) +g_type_add_interface_static (GType instance_type, + GType interface_type, + const GInterfaceInfo *info) { TypeNode *node; TypeNode *iface; @@ -1674,11 +1726,7 @@ g_type_conforms_to (GType type, } } else - { - TypeNode *node = LOOKUP_TYPE_NODE (type); - - return node && (node->is_iface || node->is_instantiatable); - } + return LOOKUP_TYPE_NODE (type) != NULL; return FALSE; } @@ -1839,20 +1887,34 @@ g_type_set_qdata (GType type, /* --- implementation details --- */ gboolean -g_type_check_flags (GType type, - GTypeFlags flags) +g_type_check_flags (GType type, + guint flags) { TypeNode *node = LOOKUP_TYPE_NODE (type); - - flags &= G_TYPE_FLAG_MASK; + gboolean result = FALSE; + if (node) { - GTypeFundamentalInfo *finfo = type_node_fundamental_info (node); + guint fflags = flags & TYPE_FUNDAMENTAL_FLAG_MASK; + guint tflags = flags & TYPE_FLAG_MASK; + + if (fflags) + { + GTypeFundamentalInfo *finfo = type_node_fundamental_info (node); + + fflags = (finfo->type_flags & fflags) == fflags; + } + else + fflags = TRUE; + + if (tflags) + tflags = (tflags & GPOINTER_TO_UINT (g_type_get_qdata (type, quark_type_flags))) == tflags; + else + tflags = TRUE; - return (finfo->type_flags & flags) != 0; + result = tflags && fflags; } - - return FALSE; + return result; } GTypePlugin* @@ -1874,6 +1936,7 @@ g_type_instance_conforms_to (GTypeInstance *type_instance, GType iface_type) { return (type_instance && type_instance->g_class && + G_TYPE_IS_INSTANTIATABLE (type_instance->g_class->g_type) && g_type_conforms_to (type_instance->g_class->g_type, iface_type)); } @@ -1881,7 +1944,35 @@ gboolean g_type_class_is_a (GTypeClass *type_class, GType is_a_type) { - return (type_class && g_type_is_a (type_class->g_type, is_a_type)); + return (type_class && G_TYPE_IS_CLASSED (type_class->g_type) && + g_type_is_a (type_class->g_type, is_a_type)); +} + +gboolean +g_type_value_conforms_to (GValue *value, + GType type) +{ + TypeNode *node; + + if (!value) + return FALSE; + node = LOOKUP_TYPE_NODE (value->g_type); +#if 0 + if (!G_TYPE_IS_FUNDAMENTAL (value->g_type) && !node || !node->data) + node = LOOKUP_TYPE_NODE (G_TYPE_FUNDAMENTAL (value->g_type)); +#endif + if (!node || !node->data || node->data->common.ref_count < 1 || + !node->data->common.value_table->value_init || + !g_type_conforms_to (value->g_type, type)) + return FALSE; + + return TRUE; +} + +gboolean +g_type_check_value (GValue *value) +{ + return value && g_type_value_conforms_to (value, value->g_type); } GTypeInstance* @@ -1900,9 +1991,9 @@ g_type_check_instance_cast (GTypeInstance *type_instance, type_descriptive_name (iface_type)); return type_instance; } - if (!G_TYPE_IS_CLASSED (type_instance->g_class->g_type)) + if (!G_TYPE_IS_INSTANTIATABLE (type_instance->g_class->g_type)) { - g_warning ("invalid unclassed type `%s' in cast to `%s'", + g_warning ("invalid uninstantiatable type `%s' in cast to `%s'", type_descriptive_name (type_instance->g_class->g_type), type_descriptive_name (iface_type)); return type_instance; @@ -1946,13 +2037,47 @@ g_type_check_class_cast (GTypeClass *type_class, return type_class; } +gboolean +g_type_check_instance (GTypeInstance *type_instance) +{ + /* this function is just here to make the signal system + * conveniently elaborated on instance checks + */ + if (!type_instance) + { + g_warning ("instance is invalid (NULL) pointer"); + return FALSE; + } + if (!type_instance->g_class) + { + g_warning ("instance with invalid (NULL) class pointer"); + return FALSE; + } + if (!G_TYPE_IS_CLASSED (type_instance->g_class->g_type)) + { + g_warning ("instance of invalid unclassed type `%s'", + type_descriptive_name (type_instance->g_class->g_type)); + return FALSE; + } + if (!G_TYPE_IS_INSTANTIATABLE (type_instance->g_class->g_type)) + { + g_warning ("instance of invalid non-instantiatable type `%s'", + type_descriptive_name (type_instance->g_class->g_type)); + return FALSE; + } + + return TRUE; +} + /* --- foreign prototypes --- */ extern void g_value_types_init (void); /* sync with gvaluetypes.c */ extern void g_enum_types_init (void); /* sync with genums.c */ extern void g_param_type_init (void); /* sync with gparam.c */ +extern void g_boxed_type_init (void); /* sync with gboxed.c */ extern void g_object_type_init (void); /* sync with gobject.c */ extern void g_param_spec_types_init (void); /* sync with gparamspecs.c */ +extern void g_signal_init (void); /* sync with gsignal.c */ /* --- initialization --- */ @@ -2004,6 +2129,10 @@ g_type_init (void) */ g_param_type_init (); + /* G_TYPE_PARAM + */ + g_boxed_type_init (); + /* G_TYPE_OBJECT */ g_object_type_init (); @@ -2011,4 +2140,8 @@ g_type_init (void) /* G_TYPE_PARAM_* pspec types */ g_param_spec_types_init (); + + /* Signal system + */ + g_signal_init (); } diff --git a/gobject/gtype.h b/gobject/gtype.h index 60086e8b1..1c8e0b40b 100644 --- a/gobject/gtype.h +++ b/gobject/gtype.h @@ -60,11 +60,11 @@ typedef enum /*< skip >*/ G_TYPE_DOUBLE, G_TYPE_STRING, G_TYPE_PARAM, + G_TYPE_BOXED, + G_TYPE_POINTER, G_TYPE_OBJECT, /* the following reserved ids should vanish soon */ - G_TYPE_GTK_BOXED, - G_TYPE_GTK_POINTER, G_TYPE_GTK_SIGNAL, /* reserved fundamental type ids, @@ -99,12 +99,15 @@ typedef enum /*< skip >*/ /* Type Checking Macros */ +#define G_TYPE_IS_FUNDAMENTAL(type) (G_TYPE_BRANCH_SEQNO (type) == 0) #define G_TYPE_IS_INTERFACE(type) (G_TYPE_FUNDAMENTAL (type) == G_TYPE_INTERFACE) #define G_TYPE_IS_CLASSED(type) (g_type_check_flags ((type), G_TYPE_FLAG_CLASSED)) #define G_TYPE_IS_INSTANTIATABLE(type) (g_type_check_flags ((type), G_TYPE_FLAG_INSTANTIATABLE)) #define G_TYPE_IS_DERIVABLE(type) (g_type_check_flags ((type), G_TYPE_FLAG_DERIVABLE)) #define G_TYPE_IS_DEEP_DERIVABLE(type) (g_type_check_flags ((type), G_TYPE_FLAG_DEEP_DERIVABLE)) +#define G_TYPE_IS_ABSTRACT(type) (g_type_check_flags ((type), G_TYPE_FLAG_ABSTRACT)) #define G_TYPE_IS_PARAM(type) (G_TYPE_FUNDAMENTAL (type) == G_TYPE_PARAM) +#define G_TYPE_IS_VALUE_TYPE(type) (g_type_value_table_peek (type) != NULL) /* Typedefs @@ -143,13 +146,20 @@ struct _GTypeInterface }; -/* Casts, Checks And Convenience Macros For Structured Types +/* Casts, checks and accessors for structured types + * usage of these macros is reserved to type implementations only + * */ +/*< protected >*/ +#define G_TYPE_CHECK_INSTANCE(instance) (_G_TYPE_CHI ((GTypeInstance*) (instance))) #define G_TYPE_CHECK_INSTANCE_CAST(instance, g_type, c_type) (_G_TYPE_CIC ((instance), (g_type), c_type)) -#define G_TYPE_CHECK_CLASS_CAST(g_class, g_type, c_type) (_G_TYPE_CCC ((g_class), (g_type), c_type)) #define G_TYPE_CHECK_INSTANCE_TYPE(instance, g_type) (_G_TYPE_CIT ((instance), (g_type))) -#define G_TYPE_CHECK_CLASS_TYPE(g_class, g_type) (_G_TYPE_CCT ((g_class), (g_type))) #define G_TYPE_INSTANCE_GET_CLASS(instance, g_type, c_type) (_G_TYPE_IGC ((instance), c_type)) +#define G_TYPE_INSTANCE_GET_INTERFACE(instance, g_type, c_type) (_G_TYPE_IGI ((instance), (g_type), c_type)) +#define G_TYPE_CHECK_CLASS_CAST(g_class, g_type, c_type) (_G_TYPE_CCC ((g_class), (g_type), c_type)) +#define G_TYPE_CHECK_CLASS_TYPE(g_class, g_type) (_G_TYPE_CCT ((g_class), (g_type))) +#define G_TYPE_CHECK_VALUE(value) (_G_TYPE_CHV ((value))) +#define G_TYPE_CHECK_VALUE_TYPE(value, g_type) (_G_TYPE_CVT ((value), (g_type))) #define G_TYPE_FROM_INSTANCE(instance) (G_TYPE_FROM_CLASS (((GTypeInstance*) (instance))->g_class)) #define G_TYPE_FROM_CLASS(g_class) (((GTypeClass*) (g_class))->g_type) #define G_TYPE_FROM_INTERFACE(g_iface) (((GTypeInterface*) (g_iface))->g_type) @@ -229,6 +239,10 @@ typedef enum /*< skip >*/ G_TYPE_FLAG_INSTANTIATABLE = (1 << 1), G_TYPE_FLAG_DERIVABLE = (1 << 2), G_TYPE_FLAG_DEEP_DERIVABLE = (1 << 3) +} GTypeFundamentalFlags; +typedef enum /*< skip >*/ +{ + G_TYPE_FLAG_ABSTRACT = (1 << 4) } GTypeFlags; struct _GTypeInfo { @@ -253,7 +267,7 @@ struct _GTypeInfo }; struct _GTypeFundamentalInfo { - GTypeFlags type_flags; + GTypeFundamentalFlags type_flags; }; struct _GInterfaceInfo { @@ -263,61 +277,72 @@ struct _GInterfaceInfo }; struct _GTypeValueTable { - void (*value_init) (GValue *value); - void (*value_free) (GValue *value); - void (*value_copy) (const GValue *src_value, - GValue *dest_value); + void (*value_init) (GValue *value); + void (*value_free) (GValue *value); + void (*value_copy) (const GValue *src_value, + GValue *dest_value); /* varargs functionality (optional) */ - guint collect_type; - gchar* (*collect_value) (GValue *value, - guint nth_value, - GType *collect_type, - GTypeCValue *collect_value); - guint lcopy_type; - gchar* (*lcopy_value) (const GValue *value, - guint nth_value, - GType *collect_type, - GTypeCValue *collect_value); + gpointer (*value_peek_pointer) (const GValue *value); + guint collect_type; + gchar* (*collect_value) (GValue *value, + guint nth_value, + GType *collect_type, + GTypeCValue *collect_value); + guint lcopy_type; + gchar* (*lcopy_value) (const GValue *value, + guint nth_value, + GType *collect_type, + GTypeCValue *collect_value); }; GType g_type_register_static (GType parent_type, const gchar *type_name, - const GTypeInfo *info); + const GTypeInfo *info, + GTypeFlags flags); GType g_type_register_dynamic (GType parent_type, const gchar *type_name, - GTypePlugin *plugin); + GTypePlugin *plugin, + GTypeFlags flags); GType g_type_register_fundamental (GType type_id, const gchar *type_name, const GTypeInfo *info, - const GTypeFundamentalInfo *finfo); + const GTypeFundamentalInfo *finfo, + GTypeFlags flags); void g_type_add_interface_static (GType instance_type, GType interface_type, - GInterfaceInfo *info); + const GInterfaceInfo *info); void g_type_add_interface_dynamic (GType instance_type, GType interface_type, GTypePlugin *plugin); -/* --- implementation details --- */ -gboolean g_type_class_is_a (GTypeClass *g_class, - GType is_a_type); -GTypeClass* g_type_check_class_cast (GTypeClass *g_class, - GType is_a_type); -GTypeInstance* g_type_check_instance_cast (GTypeInstance *instance, - GType iface_type); -gboolean g_type_instance_conforms_to (GTypeInstance *instance, - GType iface_type); +/* --- protected (for fundamental type implementations) --- */ +GTypePlugin* g_type_get_plugin (GType type); +GType g_type_fundamental_last (void); gboolean g_type_check_flags (GType type, - GTypeFlags flags); + guint flags); GTypeInstance* g_type_create_instance (GType type); void g_type_free_instance (GTypeInstance *instance); -GTypeValueTable* g_type_value_table_peek (GType type); void g_type_add_class_cache_func (gpointer cache_data, GTypeClassCacheFunc cache_func); void g_type_remove_class_cache_func (gpointer cache_data, GTypeClassCacheFunc cache_func); void g_type_class_unref_uncached (gpointer g_class); -GTypePlugin* g_type_get_plugin (GType type); -GType g_type_fundamental_last (void); + + +/*< private >*/ +GTypeClass* g_type_check_class_cast (GTypeClass *g_class, + GType is_a_type); +gboolean g_type_class_is_a (GTypeClass *g_class, + GType is_a_type); +GTypeInstance* g_type_check_instance_cast (GTypeInstance *instance, + GType iface_type); +gboolean g_type_instance_conforms_to (GTypeInstance *instance, + GType iface_type); +gboolean g_type_check_value (GValue *value); +gboolean g_type_value_conforms_to (GValue *value, + GType type); +gboolean g_type_check_instance (GTypeInstance *instance); +GTypeValueTable* g_type_value_table_peek (GType type); #ifndef G_DISABLE_CAST_CHECKS @@ -329,9 +354,14 @@ GType g_type_fundamental_last (void); # define _G_TYPE_CIC(ip, gt, ct) ((ct*) ip) # define _G_TYPE_CCC(cp, gt, ct) ((ct*) cp) #endif /* G_DISABLE_CAST_CHECKS */ -#define _G_TYPE_IGC(ip, ct) ((ct*) (((GTypeInstance*) ip)->g_class)) +#define _G_TYPE_CHI(ip) (g_type_check_instance ((GTypeInstance*) ip)) #define _G_TYPE_CIT(ip, gt) (g_type_instance_conforms_to ((GTypeInstance*) ip, gt)) #define _G_TYPE_CCT(cp, gt) (g_type_class_is_a ((GTypeClass*) cp, gt)) +#define _G_TYPE_CVT(vl, gt) (g_type_value_conforms_to ((GValue*) vl, gt)) +#define _G_TYPE_CHV(vl) (g_type_check_value ((GValue*) vl)) +#define _G_TYPE_IGC(ip, ct) ((ct*) (((GTypeInstance*) ip)->g_class)) +#define _G_TYPE_IGI(ip, gt, ct) ((ct*) g_type_interface_peek (((GTypeInstance*) ip)->g_class, gt)) + #ifdef __cplusplus } diff --git a/gobject/gvalue.c b/gobject/gvalue.c index bdc0a9c9f..40938009e 100644 --- a/gobject/gvalue.c +++ b/gobject/gvalue.c @@ -61,11 +61,13 @@ void g_value_copy (const GValue *src_value, GValue *dest_value) { - GTypeValueTable *value_table = g_type_value_table_peek (G_VALUE_TYPE (dest_value)); + GTypeValueTable *value_table; g_return_if_fail (G_IS_VALUE (src_value)); g_return_if_fail (G_IS_VALUE (dest_value)); g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), G_VALUE_TYPE (dest_value))); + + value_table = g_type_value_table_peek (G_VALUE_TYPE (dest_value)); if (!value_table) g_return_if_fail (g_type_value_table_peek (G_VALUE_TYPE (dest_value)) != NULL); @@ -78,6 +80,36 @@ g_value_copy (const GValue *src_value, } } +gboolean +g_value_fits_pointer (const GValue *value) +{ + GTypeValueTable *value_table; + + g_return_val_if_fail (G_IS_VALUE (value), FALSE); + + value_table = g_type_value_table_peek (G_VALUE_TYPE (value)); + if (!value_table) + g_return_val_if_fail (g_type_value_table_peek (G_VALUE_TYPE (value)) != NULL, FALSE); + + return value_table->value_peek_pointer != NULL; +} + +gpointer +g_value_get_as_pointer (const GValue *value) +{ + GTypeValueTable *value_table; + + g_return_val_if_fail (G_IS_VALUE (value), NULL); + + value_table = g_type_value_table_peek (G_VALUE_TYPE (value)); + if (!value_table) + g_return_val_if_fail (g_type_value_table_peek (G_VALUE_TYPE (value)) != NULL, NULL); + if (!value_table->value_peek_pointer) + g_return_val_if_fail (g_value_fits_pointer (value) == TRUE, NULL); + + return value_table->value_peek_pointer (value); +} + void g_value_unset (GValue *value) { @@ -189,8 +221,8 @@ g_value_register_exchange_func (GType value_type1, { ExchangeEntry entry; - g_return_if_fail (G_TYPE_IS_VALUE (value_type1)); - g_return_if_fail (G_TYPE_IS_VALUE (value_type2)); + g_return_if_fail (g_type_name (value_type1) != NULL); + g_return_if_fail (g_type_name (value_type2) != NULL); g_return_if_fail (func != NULL); entry.value_type1 = MIN (value_type1, value_type2); @@ -218,7 +250,7 @@ gboolean g_value_types_exchangable (GType value_type1, GType value_type2) { - g_return_val_if_fail (G_TYPE_IS_VALUE (value_type1), FALSE); + g_return_val_if_fail (G_TYPE_IS_VALUE (value_type1), FALSE); /* these might bite us, think G_TYPE_ENUM */ g_return_val_if_fail (G_TYPE_IS_VALUE (value_type2), FALSE); return exchange_func_lookup (value_type1, value_type2, NULL) != NULL; diff --git a/gobject/gvalue.h b/gobject/gvalue.h index fb41ffa42..d095657ea 100644 --- a/gobject/gvalue.h +++ b/gobject/gvalue.h @@ -31,8 +31,9 @@ extern "C" { /* --- type macros --- */ #define G_TYPE_IS_VALUE(type) (g_type_value_table_peek (type) != NULL) -#define G_IS_VALUE(value) (G_TYPE_IS_VALUE (G_VALUE_TYPE (value))) /* FIXME */ -#define G_VALUE_TYPE(value) (G_TYPE_FROM_CLASS (value)) +#define G_IS_VALUE(value) (G_TYPE_CHECK_VALUE (value)) +#define G_VALUE_HOLDS(value, g_type) (G_TYPE_CHECK_VALUE_TYPE ((value), (g_type))) +#define G_VALUE_TYPE(value) (((GValue*) (value))->g_type) #define G_VALUE_TYPE_NAME(value) (g_type_name (G_VALUE_TYPE (value))) @@ -66,6 +67,8 @@ gboolean g_value_convert (const GValue *src_value, GValue *dest_value); void g_value_reset (GValue *value); void g_value_unset (GValue *value); +gboolean g_value_fits_pointer (const GValue *value); +gpointer g_value_get_as_pointer (const GValue *value); /* --- implementation details --- */ diff --git a/gobject/gvaluetypes.c b/gobject/gvaluetypes.c index d27171c8f..8f5d6175a 100644 --- a/gobject/gvaluetypes.c +++ b/gobject/gvaluetypes.c @@ -260,6 +260,54 @@ value_string_lcopy_value (const GValue *value, return NULL; } +static void +value_pointer_init (GValue *value) +{ + value->data[0].v_pointer = 0; +} + +static void +value_pointer_copy (const GValue *src_value, + GValue *dest_value) +{ + dest_value->data[0].v_pointer = src_value->data[0].v_pointer; +} + +static gpointer +value_pointer_peek_pointer (const GValue *value) +{ + return value->data[0].v_pointer; +} + +static gchar* +value_pointer_collect_value (GValue *value, + guint nth_value, + GType *collect_type, + GTypeCValue *collect_value) +{ + value->data[0].v_pointer = collect_value->v_pointer; + + *collect_type = 0; + return NULL; +} + +static gchar* +value_pointer_lcopy_value (const GValue *value, + guint nth_value, + GType *collect_type, + GTypeCValue *collect_value) +{ + gpointer *pointer_p = collect_value->v_pointer; + + if (!pointer_p) + return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value)); + + *pointer_p = value->data[0].v_pointer; + + *collect_type = 0; + return NULL; +} + /* --- type initialization --- */ void @@ -287,15 +335,16 @@ g_value_types_init (void) /* sync with gtype.c */ value_long0_init, /* value_init */ NULL, /* value_free */ value_long0_copy, /* value_copy */ + NULL, /* value_peek_pointer */ G_VALUE_COLLECT_INT, /* collect_type */ value_int_collect_value, /* collect_value */ G_VALUE_COLLECT_POINTER, /* lcopy_type */ value_char_lcopy_value, /* lcopy_value */ }; info.value_table = &value_table; - type = g_type_register_fundamental (G_TYPE_CHAR, "gchar", &info, &finfo); + type = g_type_register_fundamental (G_TYPE_CHAR, "gchar", &info, &finfo, 0); g_assert (type == G_TYPE_CHAR); - type = g_type_register_fundamental (G_TYPE_UCHAR, "guchar", &info, &finfo); + type = g_type_register_fundamental (G_TYPE_UCHAR, "guchar", &info, &finfo, 0); g_assert (type == G_TYPE_UCHAR); } @@ -306,13 +355,14 @@ g_value_types_init (void) /* sync with gtype.c */ value_long0_init, /* value_init */ NULL, /* value_free */ value_long0_copy, /* value_copy */ + NULL, /* value_peek_pointer */ G_VALUE_COLLECT_INT, /* collect_type */ value_int_collect_value, /* collect_value */ G_VALUE_COLLECT_POINTER, /* lcopy_type */ value_boolean_lcopy_value, /* lcopy_value */ }; info.value_table = &value_table; - type = g_type_register_fundamental (G_TYPE_BOOLEAN, "gboolean", &info, &finfo); + type = g_type_register_fundamental (G_TYPE_BOOLEAN, "gboolean", &info, &finfo, 0); g_assert (type == G_TYPE_BOOLEAN); } @@ -323,15 +373,16 @@ g_value_types_init (void) /* sync with gtype.c */ value_long0_init, /* value_init */ NULL, /* value_free */ value_long0_copy, /* value_copy */ + NULL, /* value_peek_pointer */ G_VALUE_COLLECT_INT, /* collect_type */ value_int_collect_value, /* collect_value */ G_VALUE_COLLECT_POINTER, /* lcopy_type */ value_int_lcopy_value, /* lcopy_value */ }; info.value_table = &value_table; - type = g_type_register_fundamental (G_TYPE_INT, "gint", &info, &finfo); + type = g_type_register_fundamental (G_TYPE_INT, "gint", &info, &finfo, 0); g_assert (type == G_TYPE_INT); - type = g_type_register_fundamental (G_TYPE_UINT, "guint", &info, &finfo); + type = g_type_register_fundamental (G_TYPE_UINT, "guint", &info, &finfo, 0); g_assert (type == G_TYPE_UINT); } @@ -342,15 +393,16 @@ g_value_types_init (void) /* sync with gtype.c */ value_long0_init, /* value_init */ NULL, /* value_free */ value_long0_copy, /* value_copy */ + NULL, /* value_peek_pointer */ G_VALUE_COLLECT_LONG, /* collect_type */ value_long_collect_value, /* collect_value */ G_VALUE_COLLECT_POINTER, /* lcopy_type */ value_long_lcopy_value, /* lcopy_value */ }; info.value_table = &value_table; - type = g_type_register_fundamental (G_TYPE_LONG, "glong", &info, &finfo); + type = g_type_register_fundamental (G_TYPE_LONG, "glong", &info, &finfo, 0); g_assert (type == G_TYPE_LONG); - type = g_type_register_fundamental (G_TYPE_ULONG, "gulong", &info, &finfo); + type = g_type_register_fundamental (G_TYPE_ULONG, "gulong", &info, &finfo, 0); g_assert (type == G_TYPE_ULONG); } @@ -361,13 +413,14 @@ g_value_types_init (void) /* sync with gtype.c */ value_float_init, /* value_init */ NULL, /* value_free */ value_float_copy, /* value_copy */ + NULL, /* value_peek_pointer */ G_VALUE_COLLECT_DOUBLE, /* collect_type */ value_float_collect_value, /* collect_value */ G_VALUE_COLLECT_POINTER, /* lcopy_type */ value_float_lcopy_value, /* lcopy_value */ }; info.value_table = &value_table; - type = g_type_register_fundamental (G_TYPE_FLOAT, "gfloat", &info, &finfo); + type = g_type_register_fundamental (G_TYPE_FLOAT, "gfloat", &info, &finfo, 0); g_assert (type == G_TYPE_FLOAT); } @@ -378,13 +431,14 @@ g_value_types_init (void) /* sync with gtype.c */ value_double_init, /* value_init */ NULL, /* value_free */ value_double_copy, /* value_copy */ + NULL, /* value_peek_pointer */ G_VALUE_COLLECT_DOUBLE, /* collect_type */ value_double_collect_value, /* collect_value */ G_VALUE_COLLECT_POINTER, /* lcopy_type */ value_double_lcopy_value, /* lcopy_value */ }; info.value_table = &value_table; - type = g_type_register_fundamental (G_TYPE_DOUBLE, "gdouble", &info, &finfo); + type = g_type_register_fundamental (G_TYPE_DOUBLE, "gdouble", &info, &finfo, 0); g_assert (type == G_TYPE_DOUBLE); } @@ -395,15 +449,34 @@ g_value_types_init (void) /* sync with gtype.c */ value_string_init, /* value_init */ value_string_free_value, /* value_free */ value_string_copy_value, /* value_copy */ + value_pointer_peek_pointer, /* value_peek_pointer */ G_VALUE_COLLECT_POINTER, /* collect_type */ value_string_collect_value, /* collect_value */ G_VALUE_COLLECT_POINTER, /* lcopy_type */ value_string_lcopy_value, /* lcopy_value */ }; info.value_table = &value_table; - type = g_type_register_fundamental (G_TYPE_STRING, "gstring", &info, &finfo); + type = g_type_register_fundamental (G_TYPE_STRING, "gstring", &info, &finfo, 0); g_assert (type == G_TYPE_STRING); } + + /* G_TYPE_POINTER + */ + { + static const GTypeValueTable value_table = { + value_pointer_init, /* value_init */ + NULL, /* value_free */ + value_pointer_copy, /* value_copy */ + value_pointer_peek_pointer, /* value_peek_pointer */ + G_VALUE_COLLECT_POINTER, /* collect_type */ + value_pointer_collect_value, /* collect_value */ + G_VALUE_COLLECT_POINTER, /* lcopy_type */ + value_pointer_lcopy_value, /* lcopy_value */ + }; + info.value_table = &value_table; + type = g_type_register_fundamental (G_TYPE_POINTER, "gpointer", &info, &finfo, 0); + g_assert (type == G_TYPE_POINTER); + } } @@ -418,7 +491,7 @@ g_value_set_char (GValue *value, } gint8 -g_value_get_char (GValue *value) +g_value_get_char (const GValue *value) { g_return_val_if_fail (G_IS_VALUE_CHAR (value), 0); @@ -435,7 +508,7 @@ g_value_set_uchar (GValue *value, } guint8 -g_value_get_uchar (GValue *value) +g_value_get_uchar (const GValue *value) { g_return_val_if_fail (G_IS_VALUE_UCHAR (value), 0); @@ -452,7 +525,7 @@ g_value_set_boolean (GValue *value, } gboolean -g_value_get_boolean (GValue *value) +g_value_get_boolean (const GValue *value) { g_return_val_if_fail (G_IS_VALUE_BOOLEAN (value), 0); @@ -469,7 +542,7 @@ g_value_set_int (GValue *value, } gint -g_value_get_int (GValue *value) +g_value_get_int (const GValue *value) { g_return_val_if_fail (G_IS_VALUE_INT (value), 0); @@ -486,7 +559,7 @@ g_value_set_uint (GValue *value, } guint -g_value_get_uint (GValue *value) +g_value_get_uint (const GValue *value) { g_return_val_if_fail (G_IS_VALUE_UINT (value), 0); @@ -503,7 +576,7 @@ g_value_set_long (GValue *value, } glong -g_value_get_long (GValue *value) +g_value_get_long (const GValue *value) { g_return_val_if_fail (G_IS_VALUE_LONG (value), 0); @@ -520,7 +593,7 @@ g_value_set_ulong (GValue *value, } gulong -g_value_get_ulong (GValue *value) +g_value_get_ulong (const GValue *value) { g_return_val_if_fail (G_IS_VALUE_ULONG (value), 0); @@ -537,7 +610,7 @@ g_value_set_float (GValue *value, } gfloat -g_value_get_float (GValue *value) +g_value_get_float (const GValue *value) { g_return_val_if_fail (G_IS_VALUE_FLOAT (value), 0); @@ -554,7 +627,7 @@ g_value_set_double (GValue *value, } gdouble -g_value_get_double (GValue *value) +g_value_get_double (const GValue *value) { g_return_val_if_fail (G_IS_VALUE_DOUBLE (value), 0); @@ -572,7 +645,7 @@ g_value_set_string (GValue *value, } gchar* -g_value_get_string (GValue *value) +g_value_get_string (const GValue *value) { g_return_val_if_fail (G_IS_VALUE_STRING (value), NULL); @@ -580,9 +653,26 @@ g_value_get_string (GValue *value) } gchar* -g_value_dup_string (GValue *value) +g_value_dup_string (const GValue *value) { g_return_val_if_fail (G_IS_VALUE_STRING (value), NULL); return g_strdup (value->data[0].v_pointer); } + +void +g_value_set_pointer (GValue *value, + gpointer v_pointer) +{ + g_return_if_fail (G_IS_VALUE_POINTER (value)); + + value->data[0].v_pointer = v_pointer; +} + +gpointer +g_value_get_pointer (GValue *value) +{ + g_return_val_if_fail (G_IS_VALUE_POINTER (value), NULL); + + return value->data[0].v_pointer; +} diff --git a/gobject/gvaluetypes.h b/gobject/gvaluetypes.h index f1429671d..f91329140 100644 --- a/gobject/gvaluetypes.h +++ b/gobject/gvaluetypes.h @@ -8,7 +8,7 @@ * * 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 + * 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 @@ -22,7 +22,7 @@ #define __G_VALUETYPES_H__ -#include <gobject/gvalue.h> +#include <gobject/gvalue.h> #ifdef __cplusplus @@ -31,50 +31,54 @@ extern "C" { /* --- type macros --- */ -#define G_IS_VALUE_CHAR(value) (G_TYPE_CHECK_CLASS_TYPE ((value), G_TYPE_CHAR)) -#define G_IS_VALUE_UCHAR(value) (G_TYPE_CHECK_CLASS_TYPE ((value), G_TYPE_UCHAR)) -#define G_IS_VALUE_BOOLEAN(value) (G_TYPE_CHECK_CLASS_TYPE ((value), G_TYPE_BOOLEAN)) -#define G_IS_VALUE_INT(value) (G_TYPE_CHECK_CLASS_TYPE ((value), G_TYPE_INT)) -#define G_IS_VALUE_UINT(value) (G_TYPE_CHECK_CLASS_TYPE ((value), G_TYPE_UINT)) -#define G_IS_VALUE_LONG(value) (G_TYPE_CHECK_CLASS_TYPE ((value), G_TYPE_LONG)) -#define G_IS_VALUE_ULONG(value) (G_TYPE_CHECK_CLASS_TYPE ((value), G_TYPE_ULONG)) -#define G_IS_VALUE_FLOAT(value) (G_TYPE_CHECK_CLASS_TYPE ((value), G_TYPE_FLOAT)) -#define G_IS_VALUE_DOUBLE(value) (G_TYPE_CHECK_CLASS_TYPE ((value), G_TYPE_DOUBLE)) -#define G_IS_VALUE_STRING(value) (G_TYPE_CHECK_CLASS_TYPE ((value), G_TYPE_STRING)) +#define G_IS_VALUE_CHAR(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_CHAR)) +#define G_IS_VALUE_UCHAR(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_UCHAR)) +#define G_IS_VALUE_BOOLEAN(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_BOOLEAN)) +#define G_IS_VALUE_INT(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_INT)) +#define G_IS_VALUE_UINT(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_UINT)) +#define G_IS_VALUE_LONG(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_LONG)) +#define G_IS_VALUE_ULONG(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_ULONG)) +#define G_IS_VALUE_FLOAT(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_FLOAT)) +#define G_IS_VALUE_DOUBLE(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_DOUBLE)) +#define G_IS_VALUE_STRING(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_STRING)) +#define G_IS_VALUE_POINTER(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_POINTER)) /* --- prototypes --- */ -void g_value_set_char (GValue *value, - gint8 v_char); -gint8 g_value_get_char (GValue *value); -void g_value_set_uchar (GValue *value, - guint8 v_uchar); -guint8 g_value_get_uchar (GValue *value); -void g_value_set_boolean (GValue *value, - gboolean v_boolean); -gboolean g_value_get_boolean (GValue *value); -void g_value_set_int (GValue *value, - gint v_int); -gint g_value_get_int (GValue *value); -void g_value_set_uint (GValue *value, - guint v_uint); -guint g_value_get_uint (GValue *value); -void g_value_set_long (GValue *value, - glong v_long); -glong g_value_get_long (GValue *value); -void g_value_set_ulong (GValue *value, - gulong v_ulong); -gulong g_value_get_ulong (GValue *value); -void g_value_set_float (GValue *value, - gfloat v_float); -gfloat g_value_get_float (GValue *value); -void g_value_set_double (GValue *value, - gdouble v_double); -gdouble g_value_get_double (GValue *value); -void g_value_set_string (GValue *value, - const gchar *v_string); -gchar* g_value_get_string (GValue *value); -gchar* g_value_dup_string (GValue *value); +void g_value_set_char (GValue *value, + gint8 v_char); +gint8 g_value_get_char (const GValue *value); +void g_value_set_uchar (GValue *value, + guint8 v_uchar); +guint8 g_value_get_uchar (const GValue *value); +void g_value_set_boolean (GValue *value, + gboolean v_boolean); +gboolean g_value_get_boolean (const GValue *value); +void g_value_set_int (GValue *value, + gint v_int); +gint g_value_get_int (const GValue *value); +void g_value_set_uint (GValue *value, + guint v_uint); +guint g_value_get_uint (const GValue *value); +void g_value_set_long (GValue *value, + glong v_long); +glong g_value_get_long (const GValue *value); +void g_value_set_ulong (GValue *value, + gulong v_ulong); +gulong g_value_get_ulong (const GValue *value); +void g_value_set_float (GValue *value, + gfloat v_float); +gfloat g_value_get_float (const GValue *value); +void g_value_set_double (GValue *value, + gdouble v_double); +gdouble g_value_get_double (const GValue *value); +void g_value_set_string (GValue *value, + const gchar *v_string); +gchar* g_value_get_string (const GValue *value); +gchar* g_value_dup_string (const GValue *value); +void g_value_set_pointer (GValue *value, + gpointer v_pointer); +gpointer g_value_get_pointer (GValue *value); |