Imported Upstream version 3.4.0
[platform/upstream/python-gobject.git] / gi / pygi-callbacks.c
1 /* -*- Mode: C; c-basic-offset: 4 -*-
2  * vim: tabstop=4 shiftwidth=4 expandtab
3  *
4  *   pygi-callbacks.c: PyGI C Callback Functions and Helpers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19  * USA
20  */
21
22 #include "pygi-private.h"
23
24 static PyGICClosure *global_destroy_notify;
25
26 static void
27 _pygi_destroy_notify_callback_closure (ffi_cif *cif,
28                                        void *result,
29                                        void **args,
30                                        void *data)
31 {
32     PyGICClosure *info = * (void**) (args[0]);
33
34     g_assert (info);
35
36     _pygi_invoke_closure_free (info);
37 }
38
39
40 PyGICClosure*
41 _pygi_destroy_notify_create (void)
42 {
43     if (!global_destroy_notify) {
44
45         PyGICClosure *destroy_notify = g_slice_new0 (PyGICClosure);
46
47         g_assert (destroy_notify);
48
49         GIBaseInfo* glib_destroy_notify = g_irepository_find_by_name (NULL, "GLib", "DestroyNotify");
50         g_assert (glib_destroy_notify != NULL);
51         g_assert (g_base_info_get_type (glib_destroy_notify) == GI_INFO_TYPE_CALLBACK);
52
53         destroy_notify->closure = g_callable_info_prepare_closure ( (GICallableInfo*) glib_destroy_notify,
54                                                                     &destroy_notify->cif,
55                                                                     _pygi_destroy_notify_callback_closure,
56                                                                     NULL);
57
58         global_destroy_notify = destroy_notify;
59     }
60
61     return global_destroy_notify;
62 }
63
64
65 gboolean
66 _pygi_scan_for_callbacks (GIFunctionInfo *function_info,
67                           gboolean       is_method,
68                           guint8        *callback_index,
69                           guint8        *user_data_index,
70                           guint8        *destroy_notify_index)
71 {
72     guint i, n_args;
73
74     *callback_index = G_MAXUINT8;
75     *user_data_index = G_MAXUINT8;
76     *destroy_notify_index = G_MAXUINT8;
77
78     n_args = g_callable_info_get_n_args ( (GICallableInfo *) function_info);
79     for (i = 0; i < n_args; i++) {
80         GIDirection direction;
81         GIArgInfo *arg_info;
82         GITypeInfo *type_info;
83         guint8 destroy, closure;
84         GITypeTag type_tag;
85
86         arg_info = g_callable_info_get_arg ( (GICallableInfo*) function_info, i);
87         type_info = g_arg_info_get_type (arg_info);
88         type_tag = g_type_info_get_tag (type_info);
89
90         if (type_tag == GI_TYPE_TAG_INTERFACE) {
91             GIBaseInfo* interface_info;
92             GIInfoType interface_type;
93
94             interface_info = g_type_info_get_interface (type_info);
95             interface_type = g_base_info_get_type (interface_info);
96             if (interface_type == GI_INFO_TYPE_CALLBACK &&
97                     ! (strcmp (g_base_info_get_namespace ( (GIBaseInfo*) interface_info), "GLib") == 0 &&
98                        (strcmp (g_base_info_get_name ( (GIBaseInfo*) interface_info), "DestroyNotify") == 0 ||
99                        (strcmp (g_base_info_get_name ( (GIBaseInfo*) interface_info), "FreeFunc") == 0)))) {
100                 if (*callback_index != G_MAXUINT8) {
101                     PyErr_Format (PyExc_TypeError, "Function %s.%s has multiple callbacks, not supported",
102                                   g_base_info_get_namespace ( (GIBaseInfo*) function_info),
103                                   g_base_info_get_name ( (GIBaseInfo*) function_info));
104                     g_base_info_unref (interface_info);
105                     return FALSE;
106                 }
107                 *callback_index = i;
108             }
109             g_base_info_unref (interface_info);
110         }
111         destroy = g_arg_info_get_destroy (arg_info);
112         
113         closure = g_arg_info_get_closure (arg_info);
114         direction = g_arg_info_get_direction (arg_info);
115
116         if (destroy > 0 && destroy < n_args) {
117             if (*destroy_notify_index != G_MAXUINT8) {
118                 PyErr_Format (PyExc_TypeError, "Function %s has multiple GDestroyNotify, not supported",
119                               g_base_info_get_name ( (GIBaseInfo*) function_info));
120                 return FALSE;
121             }
122             *destroy_notify_index = destroy;
123         }
124
125         if (closure > 0 && closure < n_args) {
126             if (*user_data_index != G_MAXUINT8) {
127                 PyErr_Format (PyExc_TypeError, "Function %s has multiple user_data arguments, not supported",
128                               g_base_info_get_name ( (GIBaseInfo*) function_info));
129                 return FALSE;
130             }
131             *user_data_index = closure;
132         }
133
134         g_base_info_unref ( (GIBaseInfo*) arg_info);
135         g_base_info_unref ( (GIBaseInfo*) type_info);
136     }
137
138     return TRUE;
139 }
140
141 gboolean
142 _pygi_create_callback (GIBaseInfo  *function_info,
143                        gboolean       is_method,
144                        gboolean       is_constructor,
145                        int            n_args,
146                        Py_ssize_t     py_argc,
147                        PyObject      *py_argv,
148                        guint8         callback_index,
149                        guint8         user_data_index,
150                        guint8         destroy_notify_index,
151                        PyGICClosure **closure_out)
152 {
153     GIArgInfo *callback_arg;
154     GITypeInfo *callback_type;
155     GICallbackInfo *callback_info;
156     GIScopeType scope;
157     gboolean found_py_function;
158     PyObject *py_function;
159     guint8 i, py_argv_pos;
160     PyObject *py_user_data;
161     gboolean allow_none;
162
163     callback_arg = g_callable_info_get_arg ( (GICallableInfo*) function_info, callback_index);
164     scope = g_arg_info_get_scope (callback_arg);
165     allow_none = g_arg_info_may_be_null (callback_arg);
166
167     callback_type = g_arg_info_get_type (callback_arg);
168     g_assert (g_type_info_get_tag (callback_type) == GI_TYPE_TAG_INTERFACE);
169
170     callback_info = (GICallbackInfo*) g_type_info_get_interface (callback_type);
171     g_assert (g_base_info_get_type ( (GIBaseInfo*) callback_info) == GI_INFO_TYPE_CALLBACK);
172
173     /* Find the Python function passed for the callback */
174     found_py_function = FALSE;
175     py_function = Py_None;
176     py_user_data = NULL;
177
178     /* if its a method then we need to skip over 'self' */
179     if (is_method || is_constructor)
180         py_argv_pos = 1;
181     else
182         py_argv_pos = 0;
183
184     for (i = 0; i < n_args && i < py_argc; i++) {
185         if (i == callback_index) {
186             py_function = PyTuple_GetItem (py_argv, py_argv_pos);
187             /* if we allow none then set the closure to NULL and return */
188             if (allow_none && py_function == Py_None) {
189                 *closure_out = NULL;
190                 goto out;
191             }
192             found_py_function = TRUE;
193         } else if (i == user_data_index) {
194             py_user_data = PyTuple_GetItem (py_argv, py_argv_pos);
195         }
196         py_argv_pos++;
197     }
198
199     if (!found_py_function
200             || (py_function == Py_None || !PyCallable_Check (py_function))) {
201         PyErr_Format (PyExc_TypeError, "Error invoking %s.%s: Unexpected value "
202                       "for argument '%s'",
203                       g_base_info_get_namespace ( (GIBaseInfo*) function_info),
204                       g_base_info_get_name ( (GIBaseInfo*) function_info),
205                       g_base_info_get_name ( (GIBaseInfo*) callback_arg));
206         g_base_info_unref ( (GIBaseInfo*) callback_info);
207         g_base_info_unref ( (GIBaseInfo*) callback_type);
208         return FALSE;
209     }
210
211     /** Now actually build the closure **/
212     *closure_out = _pygi_make_native_closure ( (GICallableInfo *) callback_info,
213                                                g_arg_info_get_scope (callback_arg),
214                                                py_function,
215                                                py_user_data);
216 out:
217     g_base_info_unref ( (GIBaseInfo*) callback_info);
218     g_base_info_unref ( (GIBaseInfo*) callback_type);
219
220     return TRUE;
221 }