summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn (J5) Palmieri <johnp@redhat.com>2012-02-21 15:13:42 +0100
committerMartin Pitt <martinpitt@gnome.org>2012-03-27 18:23:30 +0200
commitd03696c1aaa7e66f8f16554cf4a4b97addb5aea1 (patch)
treea5cef3747d0cc285a951af923dd5018233c6a880
parentdb7e1d078db16b6f11dee51aa97525c451346632 (diff)
downloadpygobject-d03696c1aaa7e66f8f16554cf4a4b97addb5aea1.tar.gz
Add a ccallback type which is used to invoke callbacks passed to a vfunc
Used when overriding methods like gtk_container_forall wich pass in a callback that needs to be executed on internal children: def do_forall(self, callback, userdata): callback(self.custom_child, userdata) https://bugzilla.gnome.org/show_bug.cgi?id=644926 Co-authored-by: Tomeu Vizoso <tomeu.vizoso@collabora.co.uk> Co-authored-by: Simon Schampijer <simon@laptop.org> Signed-off-by: Martin Pitt <martinpitt@gnome.org>
-rw-r--r--gi/Makefile.am2
-rw-r--r--gi/gimodule.c1
-rw-r--r--gi/module.py5
-rw-r--r--gi/pygi-argument.c12
-rw-r--r--gi/pygi-cache.c28
-rw-r--r--gi/pygi-cache.h9
-rw-r--r--gi/pygi-ccallback.c100
-rw-r--r--gi/pygi-ccallback.h41
-rw-r--r--gi/pygi-closure.c50
-rw-r--r--gi/pygi-invoke-state-struct.h2
-rw-r--r--gi/pygi-invoke.c73
-rw-r--r--gi/pygi-invoke.h3
-rw-r--r--gi/pygi-private.h1
-rw-r--r--gi/pygi.h10
-rw-r--r--tests/test_gi.py16
15 files changed, 312 insertions, 41 deletions
diff --git a/gi/Makefile.am b/gi/Makefile.am
index 0974b4ff..c51d551f 100644
--- a/gi/Makefile.am
+++ b/gi/Makefile.am
@@ -54,6 +54,8 @@ _gi_la_SOURCES = \
pygi-boxed.h \
pygi-closure.c \
pygi-closure.h \
+ pygi-ccallback.c \
+ pygi-ccallback.h \
pygi-callbacks.c \
pygi-callbacks.h \
pygi.h \
diff --git a/gi/gimodule.c b/gi/gimodule.c
index 1961c173..6ccd87fe 100644
--- a/gi/gimodule.c
+++ b/gi/gimodule.c
@@ -492,6 +492,7 @@ PYGLIB_MODULE_START(_gi, "_gi")
_pygi_info_register_types (module);
_pygi_struct_register_types (module);
_pygi_boxed_register_types (module);
+ _pygi_ccallback_register_types (module);
_pygi_argument_init();
api = PYGLIB_CPointer_WrapPointer ( (void *) &CAPI, "gi._API");
diff --git a/gi/module.py b/gi/module.py
index 6f2bb5b6..70ed6f17 100644
--- a/gi/module.py
+++ b/gi/module.py
@@ -42,8 +42,10 @@ from ._gi import \
ConstantInfo, \
StructInfo, \
UnionInfo, \
+ CallbackInfo, \
Struct, \
Boxed, \
+ CCallback, \
enum_add, \
enum_register_new_gtype_and_add, \
flags_add, \
@@ -155,6 +157,9 @@ class IntrospectionModule(object):
if not issubclass(parent, interface))
bases = (parent,) + interfaces
metaclass = GObjectMeta
+ elif isinstance(info, CallbackInfo):
+ bases = (CCallback,)
+ metaclass = GObjectMeta
elif isinstance(info, InterfaceInfo):
bases = (_gobject.GInterface,)
metaclass = GObjectMeta
diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c
index 894f60bb..64602f2c 100644
--- a/gi/pygi-argument.c
+++ b/gi/pygi-argument.c
@@ -1547,17 +1547,7 @@ _pygi_argument_to_object (GIArgument *arg,
switch (info_type) {
case GI_INFO_TYPE_CALLBACK:
{
- /* There is no way we can support a callback return
- * as we are never sure if the callback was set from C
- * or Python. API that return callbacks are broken
- * so we print a warning and send back a None
- */
-
- g_warning ("You are trying to use an API which returns a callback."
- "Callback returns can not be supported. Returning None instead.");
- object = Py_None;
- Py_INCREF (object);
- break;
+ g_assert_not_reached();
}
case GI_INFO_TYPE_BOXED:
case GI_INFO_TYPE_STRUCT:
diff --git a/gi/pygi-cache.c b/gi/pygi-cache.c
index 5b107e1c..5a653c2e 100644
--- a/gi/pygi-cache.c
+++ b/gi/pygi-cache.c
@@ -1233,6 +1233,7 @@ _arg_name_list_generate (PyGICallableCache *callable_cache)
arg_cache = callable_cache->args_cache[i];
if (arg_cache->meta_type != PYGI_META_ARG_TYPE_CHILD &&
+ arg_cache->meta_type != PYGI_META_ARG_TYPE_CLOSURE &&
(arg_cache->direction == PYGI_DIRECTION_FROM_PYTHON ||
arg_cache->direction == PYGI_DIRECTION_BIDIRECTIONAL)) {
@@ -1331,8 +1332,24 @@ _args_cache_generate (GICallableInfo *callable_info,
gboolean is_caller_allocates = FALSE;
gssize py_arg_index = -1;
- arg_info =
- g_callable_info_get_arg (callable_info, i);
+ arg_info = g_callable_info_get_arg (callable_info, i);
+
+ if (g_arg_info_get_closure (arg_info) == i) {
+
+ arg_cache = _arg_cache_alloc ();
+ callable_cache->args_cache[arg_index] = arg_cache;
+
+ arg_cache->arg_name = g_base_info_get_name ((GIBaseInfo *) arg_info);
+ arg_cache->direction = PYGI_DIRECTION_FROM_PYTHON;
+ arg_cache->meta_type = PYGI_META_ARG_TYPE_CLOSURE;
+ arg_cache->c_arg_index = i;
+
+ callable_cache->n_from_py_args++;
+
+ g_base_info_unref ( (GIBaseInfo *)arg_info);
+
+ continue;
+ }
/* For vfuncs and callbacks our marshalling directions
are reversed */
@@ -1430,7 +1447,7 @@ arg_err:
}
PyGICallableCache *
-_pygi_callable_cache_new (GICallableInfo *callable_info)
+_pygi_callable_cache_new (GICallableInfo *callable_info, gboolean is_ccallback)
{
PyGICallableCache *cache;
GIInfoType type = g_base_info_get_type ( (GIBaseInfo *)callable_info);
@@ -1454,7 +1471,10 @@ _pygi_callable_cache_new (GICallableInfo *callable_info)
} else if (type == GI_INFO_TYPE_VFUNC) {
cache->function_type = PYGI_FUNCTION_TYPE_VFUNC;
} else if (type == GI_INFO_TYPE_CALLBACK) {
- cache->function_type = PYGI_FUNCTION_TYPE_CALLBACK;
+ if (is_ccallback)
+ cache->function_type = PYGI_FUNCTION_TYPE_CCALLBACK;
+ else
+ cache->function_type = PYGI_FUNCTION_TYPE_CALLBACK;
} else {
cache->function_type = PYGI_FUNCTION_TYPE_METHOD;
}
diff --git a/gi/pygi-cache.h b/gi/pygi-cache.h
index 510987bc..6e5e0ab7 100644
--- a/gi/pygi-cache.h
+++ b/gi/pygi-cache.h
@@ -68,7 +68,8 @@ typedef enum {
PYGI_META_ARG_TYPE_PARENT,
PYGI_META_ARG_TYPE_CHILD,
PYGI_META_ARG_TYPE_CHILD_NEEDS_UPDATE,
- PYGI_META_ARG_TYPE_CHILD_WITH_PYARG
+ PYGI_META_ARG_TYPE_CHILD_WITH_PYARG,
+ PYGI_META_ARG_TYPE_CLOSURE,
} PyGIMetaArgType;
/*
@@ -82,7 +83,8 @@ typedef enum {
PYGI_FUNCTION_TYPE_METHOD,
PYGI_FUNCTION_TYPE_CONSTRUCTOR,
PYGI_FUNCTION_TYPE_VFUNC,
- PYGI_FUNCTION_TYPE_CALLBACK
+ PYGI_FUNCTION_TYPE_CALLBACK,
+ PYGI_FUNCTION_TYPE_CCALLBACK,
} PyGIFunctionType;
/*
@@ -187,7 +189,8 @@ struct _PyGICallableCache
void _pygi_arg_cache_clear (PyGIArgCache *cache);
void _pygi_callable_cache_free (PyGICallableCache *cache);
-PyGICallableCache *_pygi_callable_cache_new (GICallableInfo *callable_info);
+PyGICallableCache *_pygi_callable_cache_new (GICallableInfo *callable_info,
+ gboolean is_ccallback);
G_END_DECLS
diff --git a/gi/pygi-ccallback.c b/gi/pygi-ccallback.c
new file mode 100644
index 00000000..bf3ec8a2
--- /dev/null
+++ b/gi/pygi-ccallback.c
@@ -0,0 +1,100 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * vim: tabstop=4 shiftwidth=4 expandtab
+ *
+ * Copyright (C) 2011 John (J5) Palmieri <johnp@redhat.com>, Red Hat, Inc.
+ *
+ * pygi-boxed-closure.c: wrapper to handle GClosure box types with C callbacks.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#include "pygi-private.h"
+
+#include <pygobject.h>
+#include <girepository.h>
+#include <pyglib-python-compat.h>
+
+
+static PyObject *
+_ccallback_call(PyGICCallback *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *result;
+ GCallback *func;
+
+ if (self->cache == NULL) {
+ self->cache = _pygi_callable_cache_new (self->info, TRUE);
+ if (self->cache == NULL)
+ return NULL;
+ }
+
+ result = pygi_callable_info_invoke( (GIBaseInfo *) self->info,
+ args,
+ kwargs,
+ self->cache,
+ self->callback,
+ self->user_data);
+ return result;
+}
+
+PYGLIB_DEFINE_TYPE("gi.CCallback", PyGICCallback_Type, PyGICCallback);
+
+PyObject *
+_pygi_ccallback_new (GCallback callback,
+ gpointer user_data,
+ GIScopeType scope,
+ GIFunctionInfo *info,
+ GDestroyNotify destroy_notify)
+{
+ PyGICCallback *self;
+
+ if (!callback) {
+ Py_RETURN_NONE;
+ }
+
+ self = (PyGICCallback *) PyGICCallback_Type.tp_alloc (&PyGICCallback_Type, 0);
+ if (self == NULL) {
+ return NULL;
+ }
+
+ self->callback = (GCallback) callback;
+ self->user_data = user_data;
+ self->scope = scope;
+ self->destroy_notify_func = destroy_notify;
+ self->info = g_base_info_ref( (GIBaseInfo *) info);
+
+ return (PyObject *) self;
+}
+
+static void
+_ccallback_dealloc (PyGICCallback *self)
+{
+ g_base_info_unref ( (GIBaseInfo *)self->info);
+}
+
+void
+_pygi_ccallback_register_types (PyObject *m)
+{
+ Py_TYPE(&PyGICCallback_Type) = &PyType_Type;
+ PyGICCallback_Type.tp_flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE);
+ PyGICCallback_Type.tp_dealloc = (destructor) _ccallback_dealloc;
+ PyGICCallback_Type.tp_call = (ternaryfunc) _ccallback_call;
+
+
+ if (PyType_Ready (&PyGICCallback_Type))
+ return;
+ if (PyModule_AddObject (m, "CCallback", (PyObject *) &PyGICCallback_Type))
+ return;
+}
diff --git a/gi/pygi-ccallback.h b/gi/pygi-ccallback.h
new file mode 100644
index 00000000..c7960922
--- /dev/null
+++ b/gi/pygi-ccallback.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * vim: tabstop=4 shiftwidth=4 expandtab
+ *
+ * Copyright (C) 2011 John (J5) Palmieri <johnp@redhat.com>, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#ifndef __PYGI_CCLOSURE_H__
+#define __PYGI_CCLOSURE_H__
+
+#include <Python.h>
+
+G_BEGIN_DECLS
+
+extern PyTypeObject PyGICCallback_Type;
+
+PyObject * _pygi_ccallback_new (GCallback callback,
+ gpointer user_data,
+ GIScopeType scope,
+ GIFunctionInfo *info,
+ GDestroyNotify destroy_notify);
+
+void _pygi_ccallback_register_types (PyObject *m);
+
+G_END_DECLS
+
+#endif /* __PYGI_CCLOSURE_H__ */
diff --git a/gi/pygi-closure.c b/gi/pygi-closure.c
index 6fc08fb6..c89be644 100644
--- a/gi/pygi-closure.c
+++ b/gi/pygi-closure.c
@@ -155,7 +155,8 @@ _pygi_closure_convert_ffi_arguments (GICallableInfo *callable_info, void **args)
g_args[i].v_double = * (double *) args[i];
g_base_info_unref (interface);
break;
- } else if (interface_type == GI_INFO_TYPE_STRUCT) {
+ } else if (interface_type == GI_INFO_TYPE_STRUCT ||
+ interface_type == GI_INFO_TYPE_CALLBACK) {
g_args[i].v_pointer = * (gpointer *) args[i];
g_base_info_unref (interface);
break;
@@ -167,6 +168,7 @@ _pygi_closure_convert_ffi_arguments (GICallableInfo *callable_info, void **args)
case GI_TYPE_TAG_GHASH:
case GI_TYPE_TAG_GLIST:
case GI_TYPE_TAG_GSLIST:
+ case GI_TYPE_TAG_VOID:
g_args[i].v_pointer = * (gpointer *) args[i];
break;
default:
@@ -188,6 +190,9 @@ _pygi_closure_convert_arguments (GICallableInfo *callable_info, void **args,
int n_in_args = 0;
int n_out_args = 0;
int i;
+ int user_data_arg = -1;
+ int destroy_notify_arg = -1;
+
GIArgument *g_args = NULL;
*py_args = NULL;
@@ -200,6 +205,10 @@ _pygi_closure_convert_arguments (GICallableInfo *callable_info, void **args,
g_args = _pygi_closure_convert_ffi_arguments (callable_info, args);
for (i = 0; i < n_args; i++) {
+ /* Special case callbacks and skip over userdata and Destroy Notify */
+ if (i == user_data_arg || i == destroy_notify_arg)
+ continue;
+
GIArgInfo *arg_info = g_callable_info_get_arg (callable_info, i);
GIDirection direction = g_arg_info_get_direction (arg_info);
@@ -220,6 +229,45 @@ _pygi_closure_convert_arguments (GICallableInfo *callable_info, void **args,
value = user_data;
Py_INCREF (value);
}
+ } else if (direction == GI_DIRECTION_IN &&
+ arg_tag == GI_TYPE_TAG_INTERFACE) {
+ /* Handle callbacks as a special case */
+ GIBaseInfo *info;
+ GIInfoType info_type;
+
+ info = g_type_info_get_interface (arg_type);
+ info_type = g_base_info_get_type (info);
+
+ arg = (GIArgument*) &g_args[i];
+
+ if (info_type == GI_INFO_TYPE_CALLBACK) {
+ gpointer user_data = NULL;
+ GDestroyNotify destroy_notify = NULL;
+ GIScopeType scope = g_arg_info_get_scope(arg_info);
+
+ user_data_arg = g_arg_info_get_closure(arg_info);
+ destroy_notify_arg = g_arg_info_get_destroy(arg_info);
+
+ if (user_data_arg != -1)
+ user_data = g_args[user_data_arg].v_pointer;
+
+ if (destroy_notify_arg != -1)
+ user_data = (GDestroyNotify) g_args[destroy_notify_arg].v_pointer;
+
+ value = _pygi_ccallback_new(arg->v_pointer,
+ user_data,
+ scope,
+ (GIFunctionInfo *) info,
+ destroy_notify);
+ } else
+ value = _pygi_argument_to_object (arg, arg_type, transfer);
+
+ g_base_info_unref (info);
+ if (value == NULL) {
+ g_base_info_unref (arg_type);
+ g_base_info_unref (arg_info);
+ goto error;
+ }
} else {
if (direction == GI_DIRECTION_IN)
arg = (GIArgument*) &g_args[i];
diff --git a/gi/pygi-invoke-state-struct.h b/gi/pygi-invoke-state-struct.h
index a4072b78..f5a3d3e5 100644
--- a/gi/pygi-invoke-state-struct.h
+++ b/gi/pygi-invoke-state-struct.h
@@ -37,6 +37,8 @@ typedef struct _PyGIInvokeState
GError *error;
gboolean failed;
+
+ gpointer user_data;
} PyGIInvokeState;
G_END_DECLS
diff --git a/gi/pygi-invoke.c b/gi/pygi-invoke.c
index 42cdd014..cabdab94 100644
--- a/gi/pygi-invoke.c
+++ b/gi/pygi-invoke.c
@@ -29,7 +29,8 @@
static inline gboolean
_invoke_callable (PyGIInvokeState *state,
PyGICallableCache *cache,
- GICallableInfo *callable_info)
+ GICallableInfo *callable_info,
+ GCallback function_ptr)
{
GError *error;
gint retval;
@@ -48,6 +49,17 @@ _invoke_callable (PyGIInvokeState *state,
cache->n_to_py_args,
&state->return_arg,
&error);
+ else if (g_base_info_get_type (callable_info) == GI_INFO_TYPE_CALLBACK)
+ retval = g_callable_info_invoke (callable_info,
+ function_ptr,
+ state->in_args,
+ cache->n_from_py_args,
+ state->out_args,
+ cache->n_to_py_args,
+ &state->return_arg,
+ FALSE,
+ FALSE,
+ &error);
else
retval = g_function_info_invoke ( callable_info,
state->in_args,
@@ -149,10 +161,12 @@ _py_args_combine_and_check_length (const gchar *function_name,
GSList *l;
n_py_args = PyTuple_GET_SIZE (py_args);
- n_py_kwargs = PyDict_Size (py_kwargs);
+ if (py_kwargs == NULL)
+ n_py_kwargs = 0;
+ else
+ n_py_kwargs = PyDict_Size (py_kwargs);
n_expected_args = g_slist_length (arg_name_list);
-
if (n_py_kwargs == 0 && n_py_args == n_expected_args) {
return py_args;
}
@@ -170,7 +184,9 @@ _py_args_combine_and_check_length (const gchar *function_name,
return NULL;
}
- if (!_check_for_unexpected_kwargs (function_name, arg_name_hash, py_kwargs)) {
+ if (n_py_kwargs > 0 && !_check_for_unexpected_kwargs (function_name,
+ arg_name_hash,
+ py_kwargs)) {
Py_DECREF (py_args);
return NULL;
}
@@ -191,7 +207,7 @@ _py_args_combine_and_check_length (const gchar *function_name,
PyObject *py_arg_item, *kw_arg_item = NULL;
const gchar *arg_name = l->data;
- if (arg_name != NULL) {
+ if (n_py_kwargs > 0 && arg_name != NULL) {
/* NULL means this argument has no keyword name */
/* ex. the first argument to a method or constructor */
kw_arg_item = PyDict_GetItemString (py_kwargs, arg_name);
@@ -400,7 +416,10 @@ _invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache)
state->args[i] = &(state->in_args[in_count]);
in_count++;
- if (arg_cache->meta_type > 0)
+ if (arg_cache->meta_type == PYGI_META_ARG_TYPE_CLOSURE) {
+ state->args[i]->v_pointer = state->user_data;
+ continue;
+ } else if (arg_cache->meta_type != PYGI_META_ARG_TYPE_PARENT)
continue;
if (arg_cache->py_arg_index >= state->n_py_in_args) {
@@ -611,34 +630,44 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache)
}
PyObject *
-_wrap_g_callable_info_invoke (PyGIBaseInfo *self,
- PyObject *py_args,
- PyObject *kwargs)
+pygi_callable_info_invoke (GIBaseInfo *info, PyObject *py_args,
+ PyObject *kwargs, PyGICallableCache *cache,
+ GCallback function_ptr, gpointer user_data)
{
PyGIInvokeState state = { 0, };
PyObject *ret = NULL;
- if (self->cache == NULL) {
- self->cache = _pygi_callable_cache_new (self->info);
- if (self->cache == NULL)
- return NULL;
- }
-
- if (!_invoke_state_init_from_callable_cache (&state, self->cache, py_args, kwargs))
+ if (!_invoke_state_init_from_callable_cache (&state, cache, py_args, kwargs))
goto err;
- if (!_invoke_marshal_in_args (&state, self->cache))
+ if (cache->function_type == PYGI_FUNCTION_TYPE_CCALLBACK)
+ state.user_data = user_data;
+
+ if (!_invoke_marshal_in_args (&state, cache))
goto err;
- if (!_invoke_callable (&state, self->cache, self->info))
+ if (!_invoke_callable (&state, cache, info, function_ptr))
goto err;
- pygi_marshal_cleanup_args_from_py_marshal_success (&state, self->cache);
+ pygi_marshal_cleanup_args_from_py_marshal_success (&state, cache);
- ret = _invoke_marshal_out_args (&state, self->cache);
+ ret = _invoke_marshal_out_args (&state, cache);
if (ret)
- pygi_marshal_cleanup_args_to_py_marshal_success (&state, self->cache);
+ pygi_marshal_cleanup_args_to_py_marshal_success (&state, cache);
err:
- _invoke_state_clear (&state, self->cache);
+ _invoke_state_clear (&state, cache);
return ret;
}
+
+PyObject *
+_wrap_g_callable_info_invoke (PyGIBaseInfo *self, PyObject *py_args,
+ PyObject *kwargs)
+{
+ if (self->cache == NULL) {
+ self->cache = _pygi_callable_cache_new (self->info, FALSE);
+ if (self->cache == NULL)
+ return NULL;
+ }
+
+ return pygi_callable_info_invoke (self->info, py_args, kwargs, self->cache, NULL, NULL);
+}
diff --git a/gi/pygi-invoke.h b/gi/pygi-invoke.h
index 0aa75806..051bc8d6 100644
--- a/gi/pygi-invoke.h
+++ b/gi/pygi-invoke.h
@@ -30,6 +30,9 @@
#include "pygi-invoke-state-struct.h"
G_BEGIN_DECLS
+PyObject *pygi_callable_info_invoke (GIBaseInfo *info, PyObject *py_args,
+ PyObject *kwargs, PyGICallableCache *cache,
+ GCallback function_ptr, gpointer user_data);
PyObject *_wrap_g_callable_info_invoke (PyGIBaseInfo *self, PyObject *py_args,
PyObject *kwargs);
diff --git a/gi/pygi-private.h b/gi/pygi-private.h
index 28e7997b..29ec131b 100644
--- a/gi/pygi-private.h
+++ b/gi/pygi-private.h
@@ -26,6 +26,7 @@
#include "pygi-type.h"
#include "pygi-foreign.h"
#include "pygi-closure.h"
+#include "pygi-ccallback.h"
#include "pygi-callbacks.h"
#include "pygi-property.h"
#include "pygi-signal-closure.h"
diff --git a/gi/pygi.h b/gi/pygi.h
index ef652410..121eae59 100644
--- a/gi/pygi.h
+++ b/gi/pygi.h
@@ -55,6 +55,16 @@ typedef struct {
gsize size;
} PyGIBoxed;
+typedef struct {
+ PyObject_HEAD
+ GCallback callback;
+ GIFunctionInfo *info;
+ gpointer user_data;
+ GIScopeType scope;
+ GDestroyNotify destroy_notify_func;
+ PyGICallableCache *cache;
+} PyGICCallback;
+
typedef PyObject * (*PyGIArgOverrideToGIArgumentFunc) (PyObject *value,
GIInterfaceInfo *interface_info,
GITransfer transfer,
diff --git a/tests/test_gi.py b/tests/test_gi.py
index 76c0aef0..2c591b9a 100644
--- a/tests/test_gi.py
+++ b/tests/test_gi.py
@@ -1690,6 +1690,22 @@ class TestPythonGObject(unittest.TestCase):
GIMarshallingTests.SubSubObject.do_method_deep_hierarchy(sub_sub_sub_object, 5)
self.assertEqual(sub_sub_sub_object.props.int, 5)
+ def test_callback_in_vfunc(self):
+ class SubObject(GIMarshallingTests.Object):
+ def __init__(self):
+ GObject.GObject.__init__(self)
+ self.worked = False
+
+ def do_vfunc_with_callback(self, callback):
+ self.worked = callback(42) == 42
+
+ _object = SubObject()
+ _object.call_vfunc_with_callback()
+ self.assertTrue(_object.worked == True)
+ _object.worked = False
+ _object.call_vfunc_with_callback()
+ self.assertTrue(_object.worked == True)
+
class TestMultiOutputArgs(unittest.TestCase):