diff --git a/docs/reference/gobject/gobject-sections.txt b/docs/reference/gobject/gobject-sections.txt index f52d95379..36552b1b3 100644 --- a/docs/reference/gobject/gobject-sections.txt +++ b/docs/reference/gobject/gobject-sections.txt @@ -65,6 +65,7 @@ g_type_default_interface_unref g_type_children g_type_interfaces g_type_interface_prerequisites +g_type_interface_instantiatable_prerequisite g_type_set_qdata g_type_get_qdata g_type_query diff --git a/gobject/gclosure.c b/gobject/gclosure.c index 7caf9c4e6..1d1f2f48a 100644 --- a/gobject/gclosure.c +++ b/gobject/gclosure.c @@ -1258,8 +1258,12 @@ static void value_from_ffi_type (GValue *gvalue, gpointer *value) { ffi_arg *int_val = (ffi_arg*) value; + GType type; - switch (g_type_fundamental (G_VALUE_TYPE (gvalue))) + type = G_VALUE_TYPE (gvalue); + +restart: + switch (g_type_fundamental (type)) { case G_TYPE_INT: g_value_set_int (gvalue, (gint) *int_val); @@ -1318,9 +1322,15 @@ value_from_ffi_type (GValue *gvalue, gpointer *value) case G_TYPE_VARIANT: g_value_take_variant (gvalue, *(gpointer*)value); break; + case G_TYPE_INTERFACE: + type = g_type_interface_instantiatable_prerequisite (type); + if (type) + goto restart; + G_GNUC_FALLTHROUGH; default: - g_warning ("value_from_ffi_type: Unsupported fundamental type: %s", - g_type_name (g_type_fundamental (G_VALUE_TYPE (gvalue)))); + g_warning ("value_from_ffi_type: Unsupported fundamental type %s for type %s", + g_type_name (g_type_fundamental (G_VALUE_TYPE (gvalue))), + g_type_name (G_VALUE_TYPE (gvalue))); } } diff --git a/gobject/gtype.c b/gobject/gtype.c index 723675d59..12ad8be28 100644 --- a/gobject/gtype.c +++ b/gobject/gtype.c @@ -1689,6 +1689,54 @@ g_type_interface_prerequisites (GType interface_type, } } +/** + * g_type_interface_instantiatable_prerequisite: + * @interface_type: an interface type + * + * Returns the most specific instantiatable prerequisite of an + * interface type. If the interface type has no instantiatable + * prerequisite, %G_TYPE_INVALID is returned. + * + * See g_type_interface_add_prerequisite() for more information + * about prerequisites. + * + * Returns: the instantiatable prerequisite type or %G_TYPE_INVALID if none + * + * Since: 2.68 + **/ +GType +g_type_interface_instantiatable_prerequisite (GType interface_type) +{ + TypeNode *inode = NULL; + TypeNode *iface; + guint i; + + g_return_val_if_fail (G_TYPE_IS_INTERFACE (interface_type), G_TYPE_INVALID); + + iface = lookup_type_node_I (interface_type); + if (iface == NULL) + return G_TYPE_INVALID; + + G_READ_LOCK (&type_rw_lock); + + for (i = 0; i < IFACE_NODE_N_PREREQUISITES (iface); i++) + { + GType prerequisite = IFACE_NODE_PREREQUISITES (iface)[i]; + TypeNode *node = lookup_type_node_I (prerequisite); + if (node->is_instantiatable) + { + if (!inode || type_node_is_a_L (node, inode)) + inode = node; + } + } + + G_READ_UNLOCK (&type_rw_lock); + + if (inode) + return NODE_TYPE (inode); + else + return G_TYPE_INVALID; +} static IFaceHolder* type_iface_peek_holder_L (TypeNode *iface, @@ -3407,7 +3455,7 @@ g_type_depth (GType type) * @root_type: immediate parent of the returned type * * Given a @leaf_type and a @root_type which is contained in its - * anchestry, return the type that @root_type is the immediate parent + * ancestry, return the type that @root_type is the immediate parent * of. In other words, this function determines the type that is * derived directly from @root_type which is also a base class of * @leaf_type. Given a root type and a leaf type, this function can diff --git a/gobject/gtype.h b/gobject/gtype.h index 89178411f..d839e4e3a 100644 --- a/gobject/gtype.h +++ b/gobject/gtype.h @@ -1300,6 +1300,9 @@ void g_type_interface_add_prerequisite (GType interface_type, GLIB_AVAILABLE_IN_ALL GType*g_type_interface_prerequisites (GType interface_type, guint *n_prerequisites); +GLIB_AVAILABLE_IN_2_68 +GType g_type_interface_instantiatable_prerequisite + (GType interface_type); GLIB_DEPRECATED_IN_2_58 void g_type_class_add_private (gpointer g_class, gsize private_size); diff --git a/gobject/gvalue.c b/gobject/gvalue.c index 468da2e7d..9c6c90d4d 100644 --- a/gobject/gvalue.c +++ b/gobject/gvalue.c @@ -448,6 +448,15 @@ g_value_init_from_instance (GValue *value, } } +static GType +transform_lookup_get_parent_type (GType type) +{ + if (g_type_fundamental (type) == G_TYPE_INTERFACE) + return g_type_interface_instantiatable_prerequisite (type); + + return g_type_parent (type); +} + static GValueTransform transform_func_lookup (GType src_type, GType dest_type) @@ -470,11 +479,11 @@ transform_func_lookup (GType src_type, g_type_value_table_peek (entry.src_type) == g_type_value_table_peek (src_type)) return e->func; } - entry.dest_type = g_type_parent (entry.dest_type); + entry.dest_type = transform_lookup_get_parent_type (entry.dest_type); } while (entry.dest_type); - entry.src_type = g_type_parent (entry.src_type); + entry.src_type = transform_lookup_get_parent_type (entry.src_type); } while (entry.src_type); diff --git a/gobject/tests/binding.c b/gobject/tests/binding.c index 1d3ead481..be6d751c1 100644 --- a/gobject/tests/binding.c +++ b/gobject/tests/binding.c @@ -2,6 +2,47 @@ #include #include +typedef struct { + GTypeInterface g_iface; +} FooInterface; + +GType foo_get_type (void); + +G_DEFINE_INTERFACE (Foo, foo, G_TYPE_OBJECT) + +static void +foo_default_init (FooInterface *iface) +{ +} + +typedef struct { + GObject parent; +} Baa; + +typedef struct { + GObjectClass parent_class; +} BaaClass; + +static void +baa_init_foo (FooInterface *iface) +{ +} + +GType baa_get_type (void); + +G_DEFINE_TYPE_WITH_CODE (Baa, baa, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (foo_get_type (), baa_init_foo)) + +static void +baa_init (Baa *baa) +{ +} + +static void +baa_class_init (BaaClass *class) +{ +} + typedef struct _BindingSource { GObject parent_instance; @@ -10,6 +51,7 @@ typedef struct _BindingSource gint bar; gdouble double_value; gboolean toggle; + gpointer item; } BindingSource; typedef struct _BindingSourceClass @@ -24,7 +66,8 @@ enum PROP_SOURCE_FOO, PROP_SOURCE_BAR, PROP_SOURCE_DOUBLE_VALUE, - PROP_SOURCE_TOGGLE + PROP_SOURCE_TOGGLE, + PROP_SOURCE_OBJECT }; static GType binding_source_get_type (void); @@ -56,6 +99,10 @@ binding_source_set_property (GObject *gobject, source->toggle = g_value_get_boolean (value); break; + case PROP_SOURCE_OBJECT: + source->item = g_value_get_object (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } @@ -87,6 +134,10 @@ binding_source_get_property (GObject *gobject, g_value_set_boolean (value, source->toggle); break; + case PROP_SOURCE_OBJECT: + g_value_set_object (value, source->item); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } @@ -119,6 +170,10 @@ binding_source_class_init (BindingSourceClass *klass) g_param_spec_boolean ("toggle", "Toggle", "Toggle", FALSE, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_SOURCE_OBJECT, + g_param_spec_object ("object", "Object", "Object", + G_TYPE_OBJECT, + G_PARAM_READWRITE)); } static void @@ -133,6 +188,7 @@ typedef struct _BindingTarget gint bar; gdouble double_value; gboolean toggle; + gpointer foo; } BindingTarget; typedef struct _BindingTargetClass @@ -146,7 +202,8 @@ enum PROP_TARGET_BAR, PROP_TARGET_DOUBLE_VALUE, - PROP_TARGET_TOGGLE + PROP_TARGET_TOGGLE, + PROP_TARGET_FOO }; static GType binding_target_get_type (void); @@ -174,6 +231,10 @@ binding_target_set_property (GObject *gobject, target->toggle = g_value_get_boolean (value); break; + case PROP_TARGET_FOO: + target->foo = g_value_get_object (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } @@ -201,6 +262,10 @@ binding_target_get_property (GObject *gobject, g_value_set_boolean (value, target->toggle); break; + case PROP_TARGET_FOO: + g_value_set_object (value, target->foo); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } @@ -228,6 +293,10 @@ binding_target_class_init (BindingTargetClass *klass) g_param_spec_boolean ("toggle", "Toggle", "Toggle", FALSE, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_TARGET_FOO, + g_param_spec_object ("foo", "Foo", "Foo", + foo_get_type (), + G_PARAM_READWRITE)); } static void @@ -757,6 +826,66 @@ binding_fail (void) g_assert_null (binding); } +static gboolean +transform_to_func (GBinding *binding, + const GValue *value_a, + GValue *value_b, + gpointer user_data) +{ + if (g_value_type_compatible (G_VALUE_TYPE (value_a), G_VALUE_TYPE (value_b))) + { + g_value_copy (value_a, value_b); + return TRUE; + } + + if (g_value_type_transformable (G_VALUE_TYPE (value_a), G_VALUE_TYPE (value_b))) + { + if (g_value_transform (value_a, value_b)) + return TRUE; + } + + return FALSE; +} + +static void +binding_interface (void) +{ + BindingSource *source = g_object_new (binding_source_get_type (), NULL); + BindingTarget *target = g_object_new (binding_target_get_type (), NULL); + GObject *baa; + GBinding *binding; + GClosure *transform_to; + + /* binding a generic object property to an interface-valued one */ + binding = g_object_bind_property (source, "object", + target, "foo", + G_BINDING_DEFAULT); + + baa = g_object_new (baa_get_type (), NULL); + g_object_set (source, "object", baa, NULL); + g_object_unref (baa); + + g_binding_unbind (binding); + + /* the same, with a generic marshaller */ + transform_to = g_cclosure_new (G_CALLBACK (transform_to_func), NULL, NULL); + g_closure_set_marshal (transform_to, g_cclosure_marshal_generic); + binding = g_object_bind_property_with_closures (source, "object", + target, "foo", + G_BINDING_DEFAULT, + transform_to, + NULL); + + baa = g_object_new (baa_get_type (), NULL); + g_object_set (source, "object", baa, NULL); + g_object_unref (baa); + + g_binding_unbind (binding); + + g_object_unref (source); + g_object_unref (target); +} + int main (int argc, char *argv[]) { @@ -778,6 +907,7 @@ main (int argc, char *argv[]) g_test_add_func ("/binding/unbind-weak", binding_unbind_weak); g_test_add_func ("/binding/unbind-multiple", binding_unbind_multiple); g_test_add_func ("/binding/fail", binding_fail); + g_test_add_func ("/binding/interface", binding_interface); return g_test_run (); } diff --git a/gobject/tests/signals.c b/gobject/tests/signals.c index 08b54d0fa..120f90b5c 100644 --- a/gobject/tests/signals.c +++ b/gobject/tests/signals.c @@ -135,6 +135,47 @@ static GType flags_type; static guint simple_id; static guint simple2_id; +typedef struct { + GTypeInterface g_iface; +} FooInterface; + +GType foo_get_type (void); + +G_DEFINE_INTERFACE (Foo, foo, G_TYPE_OBJECT) + +static void +foo_default_init (FooInterface *iface) +{ +} + +typedef struct { + GObject parent; +} Baa; + +typedef struct { + GObjectClass parent_class; +} BaaClass; + +static void +baa_init_foo (FooInterface *iface) +{ +} + +GType baa_get_type (void); + +G_DEFINE_TYPE_WITH_CODE (Baa, baa, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (foo_get_type (), baa_init_foo)) + +static void +baa_init (Baa *baa) +{ +} + +static void +baa_class_init (BaaClass *class) +{ +} + typedef struct _Test Test; typedef struct _TestClass TestClass; @@ -257,6 +298,14 @@ test_class_init (TestClass *klass) NULL, G_TYPE_UINT, 0); + g_signal_new ("generic-marshaller-interface-return", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + foo_get_type (), + 0); s = g_signal_new ("va-marshaller-uint-return", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, @@ -754,6 +803,35 @@ test_generic_marshaller_signal_uint_return (void) g_object_unref (test); } +static gpointer +on_generic_marshaller_interface_return (Test *test) +{ + return g_object_new (baa_get_type (), NULL); +} + +static void +test_generic_marshaller_signal_interface_return (void) +{ + Test *test; + guint id; + gpointer retval; + + test = g_object_new (test_get_type (), NULL); + + /* Test return value -30 */ + id = g_signal_connect (test, + "generic-marshaller-interface-return", + G_CALLBACK (on_generic_marshaller_interface_return), + NULL); + g_signal_emit_by_name (test, "generic-marshaller-interface-return", &retval); + g_assert_true (g_type_check_instance_is_a ((GTypeInstance*)retval, foo_get_type ())); + g_object_unref (retval); + + g_signal_handler_disconnect (test, id); + + g_object_unref (test); +} + static const GSignalInvocationHint dont_use_this = { 0, }; static void @@ -1082,6 +1160,7 @@ test_introspection (void) "generic-marshaller-int-return", "va-marshaller-int-return", "generic-marshaller-uint-return", + "generic-marshaller-interface-return", "va-marshaller-uint-return", "variant-changed-no-slot", "variant-changed", @@ -1495,6 +1574,7 @@ main (int argc, g_test_add_func ("/gobject/signals/generic-marshaller-enum-return-unsigned", test_generic_marshaller_signal_enum_return_unsigned); g_test_add_func ("/gobject/signals/generic-marshaller-int-return", test_generic_marshaller_signal_int_return); g_test_add_func ("/gobject/signals/generic-marshaller-uint-return", test_generic_marshaller_signal_uint_return); + g_test_add_func ("/gobject/signals/generic-marshaller-interface-return", test_generic_marshaller_signal_interface_return); g_test_add_func ("/gobject/signals/custom-marshaller", test_custom_marshaller); g_test_add_func ("/gobject/signals/connect", test_connect); g_test_add_func ("/gobject/signals/emission-hook", test_emission_hook); diff --git a/gobject/tests/threadtests.c b/gobject/tests/threadtests.c index 4cba720be..e341a9d67 100644 --- a/gobject/tests/threadtests.c +++ b/gobject/tests/threadtests.c @@ -159,7 +159,7 @@ static void prop_tester_init (PropTester* t) { if (t->name == NULL) - ; /* neds unit test framework initialization: g_test_bug ("race initializing properties"); */ + { } /* needs unit test framework initialization: g_test_bug ("race initializing properties"); */ } static void prop_tester_set_property (GObject *object, diff --git a/gobject/tests/type.c b/gobject/tests/type.c index c5db7e992..30e138a77 100644 --- a/gobject/tests/type.c +++ b/gobject/tests/type.c @@ -40,6 +40,63 @@ foo_default_init (FooInterface *iface) { } +typedef struct { + GTypeInterface g_iface; +} BaaInterface; + +GType baa_get_type (void); + +G_DEFINE_INTERFACE (Baa, baa, G_TYPE_INVALID) + +static void +baa_default_init (BaaInterface *iface) +{ +} + +typedef struct { + GTypeInterface g_iface; +} BooInterface; + +GType boo_get_type (void); + +G_DEFINE_INTERFACE_WITH_CODE (Boo, boo, G_TYPE_INVALID, + g_type_interface_add_prerequisite (g_define_type_id, baa_get_type ())) + +static void +boo_default_init (BooInterface *iface) +{ +} + +typedef struct { + GTypeInterface g_iface; +} BibiInterface; + +GType bibi_get_type (void); + +G_DEFINE_INTERFACE (Bibi, bibi, G_TYPE_INITIALLY_UNOWNED) + +static void +bibi_default_init (BibiInterface *iface) +{ +} + +typedef struct { + GTypeInterface g_iface; +} BozoInterface; + +GType bozo_get_type (void); + +G_DEFINE_INTERFACE_WITH_CODE (Bozo, bozo, G_TYPE_INVALID, + g_type_interface_add_prerequisite (g_define_type_id, foo_get_type ()); + g_type_interface_add_prerequisite (g_define_type_id, bibi_get_type ())) + +static void +bozo_default_init (BozoInterface *iface) +{ +} + + + static void test_interface_prerequisite (void) { @@ -52,6 +109,7 @@ test_interface_prerequisite (void) g_assert_cmpint (n_prereqs, ==, 2); g_assert (prereqs[0] == bar_get_type ()); g_assert (prereqs[1] == G_TYPE_OBJECT); + g_assert (g_type_interface_instantiatable_prerequisite (foo_get_type ()) == G_TYPE_OBJECT); iface = g_type_default_interface_ref (foo_get_type ()); parent = g_type_interface_peek_parent (iface); @@ -59,6 +117,11 @@ test_interface_prerequisite (void) g_type_default_interface_unref (iface); g_free (prereqs); + + g_assert_cmpint (g_type_interface_instantiatable_prerequisite (baa_get_type ()), ==, G_TYPE_INVALID); + g_assert_cmpint (g_type_interface_instantiatable_prerequisite (boo_get_type ()), ==, G_TYPE_INVALID); + + g_assert_cmpint (g_type_interface_instantiatable_prerequisite (bozo_get_type ()), ==, G_TYPE_INITIALLY_UNOWNED); } typedef struct { diff --git a/gobject/tests/value.c b/gobject/tests/value.c index e21b23445..b3ea223f9 100644 --- a/gobject/tests/value.c +++ b/gobject/tests/value.c @@ -261,6 +261,99 @@ test_valuearray_basic (void) g_value_array_free (a2); } +/* We create some dummy objects with this relationship: + * + * GObject TestInterface + * / \ / / + * TestObjectA TestObjectB / + * / \ / + * TestObjectA1 TestObjectA2------- + * + * ie: TestObjectA1 and TestObjectA2 are subclasses of TestObjectA + * and TestObjectB is related to neither. TestObjectA2 and TestObjectB + * implement TestInterface + */ + +typedef GTypeInterface TestInterfaceInterface; +static GType test_interface_get_type (void); +G_DEFINE_INTERFACE (TestInterface, test_interface, G_TYPE_OBJECT) +static void test_interface_default_init (TestInterfaceInterface *iface) { } + +static GType test_object_a_get_type (void); +typedef GObject TestObjectA; typedef GObjectClass TestObjectAClass; +G_DEFINE_TYPE (TestObjectA, test_object_a, G_TYPE_OBJECT) +static void test_object_a_class_init (TestObjectAClass *class) { } +static void test_object_a_init (TestObjectA *a) { } + +static GType test_object_b_get_type (void); +typedef GObject TestObjectB; typedef GObjectClass TestObjectBClass; +static void test_object_b_iface_init (TestInterfaceInterface *iface) { } +G_DEFINE_TYPE_WITH_CODE (TestObjectB, test_object_b, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (test_interface_get_type (), test_object_b_iface_init)) +static void test_object_b_class_init (TestObjectBClass *class) { } +static void test_object_b_init (TestObjectB *b) { } + +static GType test_object_a1_get_type (void); +typedef GObject TestObjectA1; typedef GObjectClass TestObjectA1Class; +G_DEFINE_TYPE (TestObjectA1, test_object_a1, test_object_a_get_type ()) +static void test_object_a1_class_init (TestObjectA1Class *class) { } +static void test_object_a1_init (TestObjectA1 *c) { } + +static GType test_object_a2_get_type (void); +typedef GObject TestObjectA2; typedef GObjectClass TestObjectA2Class; +static void test_object_a2_iface_init (TestInterfaceInterface *iface) { } +G_DEFINE_TYPE_WITH_CODE (TestObjectA2, test_object_a2, test_object_a_get_type (), + G_IMPLEMENT_INTERFACE (test_interface_get_type (), test_object_a2_iface_init)) +static void test_object_a2_class_init (TestObjectA2Class *class) { } +static void test_object_a2_init (TestObjectA2 *b) { } + +static void +test_value_transform_object (void) +{ + GValue src = G_VALUE_INIT; + GValue dest = G_VALUE_INIT; + GObject *object; + guint i, s, d; + GType types[] = { + G_TYPE_OBJECT, + test_interface_get_type (), + test_object_a_get_type (), + test_object_b_get_type (), + test_object_a1_get_type (), + test_object_a2_get_type () + }; + + for (i = 0; i < G_N_ELEMENTS (types); i++) + { + if (!G_TYPE_IS_CLASSED (types[i])) + continue; + + object = g_object_new (types[i], NULL); + + for (s = 0; s < G_N_ELEMENTS (types); s++) + { + if (!G_TYPE_CHECK_INSTANCE_TYPE (object, types[s])) + continue; + + g_value_init (&src, types[s]); + g_value_set_object (&src, object); + + for (d = 0; d < G_N_ELEMENTS (types); d++) + { + g_test_message ("Next: %s object in GValue of %s to GValue of %s", g_type_name (types[i]), g_type_name (types[s]), g_type_name (types[d])); + g_assert_true (g_value_type_transformable (types[s], types[d])); + g_value_init (&dest, types[d]); + g_assert_true (g_value_transform (&src, &dest)); + g_assert_cmpint (g_value_get_object (&dest) != NULL, ==, G_TYPE_CHECK_INSTANCE_TYPE (object, types[d])); + g_value_unset (&dest); + } + g_value_unset (&src); + } + + g_object_unref (object); + } +} + int main (int argc, char *argv[]) { @@ -269,6 +362,7 @@ main (int argc, char *argv[]) g_test_add_func ("/value/basic", test_value_basic); g_test_add_func ("/value/string", test_value_string); g_test_add_func ("/value/array/basic", test_valuearray_basic); + g_test_add_func ("/value/transform-object", test_value_transform_object); return g_test_run (); }