Imported Upstream version 3.21.91
[platform/upstream/python-gobject.git] / gi / pygi-signal-closure.c
1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /*
3  * Copyright (c) 2011  Laszlo Pandy <lpandy@src.gnome.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "pygi-signal-closure.h"
20 #include "pygi-value.h"
21 #include "pygi-argument.h"
22 #include "pygi-boxed.h"
23
24 static GISignalInfo *
25 _pygi_lookup_signal_from_g_type (GType g_type,
26                                  const gchar *signal_name)
27 {
28     GIRepository *repository;
29     GIBaseInfo *info;
30     GISignalInfo *signal_info = NULL;
31
32     repository = g_irepository_get_default();
33     info = g_irepository_find_by_gtype (repository, g_type);
34     if (info == NULL)
35         return NULL;
36
37     if (GI_IS_OBJECT_INFO (info))
38         signal_info = g_object_info_find_signal ((GIObjectInfo *) info,
39                                                  signal_name);
40     else if (GI_IS_INTERFACE_INFO (info))
41         signal_info = g_interface_info_find_signal ((GIInterfaceInfo *) info,
42                                                     signal_name);
43
44     g_base_info_unref (info);
45     return signal_info;
46 }
47
48 static void
49 pygi_signal_closure_invalidate(gpointer data,
50                                GClosure *closure)
51 {
52     PyGClosure *pc = (PyGClosure *)closure;
53     PyGILState_STATE state;
54
55     state = PyGILState_Ensure();
56     Py_XDECREF(pc->callback);
57     Py_XDECREF(pc->extra_args);
58     Py_XDECREF(pc->swap_data);
59     PyGILState_Release(state);
60
61     pc->callback = NULL;
62     pc->extra_args = NULL;
63     pc->swap_data = NULL;
64
65     g_base_info_unref (((PyGISignalClosure *) pc)->signal_info);
66     ((PyGISignalClosure *) pc)->signal_info = NULL;
67 }
68
69 static void
70 pygi_signal_closure_marshal(GClosure *closure,
71                             GValue *return_value,
72                             guint n_param_values,
73                             const GValue *param_values,
74                             gpointer invocation_hint,
75                             gpointer marshal_data)
76 {
77     PyGILState_STATE state;
78     PyGClosure *pc = (PyGClosure *)closure;
79     PyObject *params, *ret = NULL;
80     guint i;
81     GISignalInfo *signal_info;
82     gint n_sig_info_args;
83     gint sig_info_highest_arg;
84     GSList *list_item = NULL;
85     GSList *pass_by_ref_structs = NULL;
86
87     state = PyGILState_Ensure();
88
89     signal_info = ((PyGISignalClosure *)closure)->signal_info;
90     n_sig_info_args = g_callable_info_get_n_args(signal_info);
91     /* the first argument to a signal callback is instance,
92        but instance is not counted in the introspection data */
93     sig_info_highest_arg = n_sig_info_args + 1;
94     g_assert_cmpint(sig_info_highest_arg, ==, n_param_values);
95
96     /* construct Python tuple for the parameter values */
97     params = PyTuple_New(n_param_values);
98     for (i = 0; i < n_param_values; i++) {
99         /* swap in a different initial data for connect_object() */
100         if (i == 0 && G_CCLOSURE_SWAP_DATA(closure)) {
101             g_return_if_fail(pc->swap_data != NULL);
102             Py_INCREF(pc->swap_data);
103             PyTuple_SetItem(params, 0, pc->swap_data);
104
105         } else if (i == 0) {
106             PyObject *item = pyg_value_as_pyobject(&param_values[i], FALSE);
107
108             if (!item) {
109                 goto out;
110             }
111             PyTuple_SetItem(params, i, item);
112
113         } else if (i < sig_info_highest_arg) {
114             GIArgInfo arg_info;
115             GITypeInfo type_info;
116             GITypeTag type_tag;
117             GIArgument arg = { 0, };
118             PyObject *item = NULL;
119             gboolean free_array = FALSE;
120             gboolean pass_struct_by_ref = FALSE;
121
122             g_callable_info_load_arg(signal_info, i - 1, &arg_info);
123             g_arg_info_load_type(&arg_info, &type_info);
124
125             arg = _pygi_argument_from_g_value(&param_values[i], &type_info);
126
127             type_tag = g_type_info_get_tag (&type_info);
128             if (type_tag == GI_TYPE_TAG_ARRAY) {
129                 /* Skip the self argument of param_values */
130                 arg.v_pointer = _pygi_argument_to_array (&arg,
131                                                          _pygi_argument_array_length_marshal,
132                                                          (void *)(param_values + 1),
133                                                          signal_info,
134                                                          &type_info,
135                                                          &free_array);
136             }
137
138             /* Hack to ensure struct arguments are passed-by-reference allowing
139              * callback implementors to modify the struct values. This is needed
140              * for keeping backwards compatibility and should be removed in future
141              * versions which support signal output arguments as return values.
142              * See: https://bugzilla.gnome.org/show_bug.cgi?id=735486
143              *
144              * Note the logic here must match the logic path taken in _pygi_argument_to_object.
145              */
146             if (type_tag == GI_TYPE_TAG_INTERFACE) {
147                 GIBaseInfo *info = g_type_info_get_interface (&type_info);
148                 GIInfoType info_type = g_base_info_get_type (info);
149
150                 if (info_type == GI_INFO_TYPE_STRUCT ||
151                         info_type == GI_INFO_TYPE_BOXED ||
152                         info_type == GI_INFO_TYPE_UNION) {
153
154                     GType gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *) info);
155                     gboolean is_foreign = (info_type == GI_INFO_TYPE_STRUCT) &&
156                                           (g_struct_info_is_foreign ((GIStructInfo *) info));
157
158                     if (!is_foreign && !g_type_is_a (gtype, G_TYPE_VALUE) &&
159                             g_type_is_a (gtype, G_TYPE_BOXED)) {
160                         pass_struct_by_ref = TRUE;
161                     }
162                 }
163
164                 g_base_info_unref (info);
165             }
166
167             if (pass_struct_by_ref) {
168                 /* transfer everything will ensure the struct is not copied when wrapped. */
169                 item = _pygi_argument_to_object (&arg, &type_info, GI_TRANSFER_EVERYTHING);
170                 if (item && PyObject_IsInstance (item, (PyObject *) &PyGIBoxed_Type)) {
171                     ((PyGBoxed *)item)->free_on_dealloc = FALSE;
172                     pass_by_ref_structs = g_slist_prepend (pass_by_ref_structs, item);
173                 }
174
175             } else {
176                 item = _pygi_argument_to_object (&arg, &type_info, GI_TRANSFER_NOTHING);
177             }
178
179             if (free_array) {
180                 g_array_free (arg.v_pointer, FALSE);
181             }
182
183             if (item == NULL) {
184                 PyErr_Print ();
185                 goto out;
186             }
187             PyTuple_SetItem(params, i, item);
188         }
189     }
190     /* params passed to function may have extra arguments */
191     if (pc->extra_args) {
192         PyObject *tuple = params;
193         params = PySequence_Concat(tuple, pc->extra_args);
194         Py_DECREF(tuple);
195     }
196     ret = PyObject_CallObject(pc->callback, params);
197     if (ret == NULL) {
198         if (pc->exception_handler)
199             pc->exception_handler(return_value, n_param_values, param_values);
200         else
201             PyErr_Print();
202         goto out;
203     }
204
205     if (G_IS_VALUE(return_value) && pyg_value_from_pyobject(return_value, ret) != 0) {
206         PyErr_SetString(PyExc_TypeError,
207                         "can't convert return value to desired type");
208
209         if (pc->exception_handler)
210             pc->exception_handler(return_value, n_param_values, param_values);
211         else
212             PyErr_Print();
213     }
214     Py_DECREF(ret);
215
216     /* Run through the list of structs which have been passed by reference and
217      * check if they are being held longer than the duration of the callback
218      * execution. This is determined if the ref count is greater than 1.
219      * A single ref is held by the argument list and any more would mean the callback
220      * stored a ref somewhere else. In this case we make an internal copy of
221      * the boxed struct so Python can own the memory to it.
222      */
223     list_item = pass_by_ref_structs;
224     while (list_item) {
225         PyObject *item = list_item->data;
226         if (item->ob_refcnt > 1) {
227             _pygi_boxed_copy_in_place ((PyGIBoxed *)item);
228         }
229         list_item = g_slist_next (list_item);
230     }
231
232  out:
233     g_slist_free (pass_by_ref_structs);
234     Py_DECREF(params);
235     PyGILState_Release(state);
236 }
237
238 GClosure *
239 pygi_signal_closure_new (PyGObject *instance,
240                          GType g_type,
241                          const gchar *signal_name,
242                          PyObject *callback,
243                          PyObject *extra_args,
244                          PyObject *swap_data)
245 {
246     GClosure *closure = NULL;
247     PyGISignalClosure *pygi_closure = NULL;
248     GISignalInfo *signal_info = NULL;
249
250     g_return_val_if_fail(callback != NULL, NULL);
251
252     signal_info = _pygi_lookup_signal_from_g_type (g_type, signal_name);
253     if (signal_info == NULL)
254         return NULL;
255
256     closure = g_closure_new_simple(sizeof(PyGISignalClosure), NULL);
257     g_closure_add_invalidate_notifier(closure, NULL, pygi_signal_closure_invalidate);
258     g_closure_set_marshal(closure, pygi_signal_closure_marshal);
259
260     pygi_closure = (PyGISignalClosure *)closure;
261
262     pygi_closure->signal_info = signal_info;
263     Py_INCREF(callback);
264     pygi_closure->pyg_closure.callback = callback;
265
266     if (extra_args != NULL && extra_args != Py_None) {
267         Py_INCREF(extra_args);
268         if (!PyTuple_Check(extra_args)) {
269             PyObject *tmp = PyTuple_New(1);
270             PyTuple_SetItem(tmp, 0, extra_args);
271             extra_args = tmp;
272         }
273         pygi_closure->pyg_closure.extra_args = extra_args;
274     }
275     if (swap_data) {
276         Py_INCREF(swap_data);
277         pygi_closure->pyg_closure.swap_data = swap_data;
278         closure->derivative_flag = TRUE;
279     }
280
281     return closure;
282 }