Imported Upstream version 3.7.3
[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, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18  * USA
19  */
20
21 #include "pygi-private.h"
22
23 /* Copied from glib */
24 static void
25 canonicalize_key (gchar *key)
26 {
27     gchar *p;
28
29     for (p = key; *p != 0; p++)
30     {
31         gchar c = *p;
32
33         if (c != '-' &&
34             (c < '0' || c > '9') &&
35             (c < 'A' || c > 'Z') &&
36             (c < 'a' || c > 'z'))
37                 *p = '-';
38     }
39 }
40
41 static GISignalInfo *
42 _pygi_lookup_signal_from_g_type (GType g_type,
43                                  const gchar *signal_name)
44 {
45     GIRepository *repository;
46     GIBaseInfo *info;
47     GType parent;
48
49     repository = g_irepository_get_default();
50     info = g_irepository_find_by_gtype (repository, g_type);
51     if (info != NULL) {
52         GISignalInfo *signal_info;
53         signal_info = g_object_info_find_signal ((GIObjectInfo *) info, signal_name);
54         g_base_info_unref (info);
55         if (signal_info != NULL)
56             return signal_info;
57     }
58
59     parent = g_type_parent (g_type);
60     if (parent > 0)
61         return _pygi_lookup_signal_from_g_type (parent, signal_name);
62
63     return NULL;
64 }
65
66 static void
67 pygi_signal_closure_invalidate(gpointer data,
68                                GClosure *closure)
69 {
70     PyGClosure *pc = (PyGClosure *)closure;
71     PyGILState_STATE state;
72
73     state = PyGILState_Ensure();
74     Py_XDECREF(pc->callback);
75     Py_XDECREF(pc->extra_args);
76     Py_XDECREF(pc->swap_data);
77     PyGILState_Release(state);
78
79     pc->callback = NULL;
80     pc->extra_args = NULL;
81     pc->swap_data = NULL;
82
83     g_base_info_unref (((PyGISignalClosure *) pc)->signal_info);
84     ((PyGISignalClosure *) pc)->signal_info = NULL;
85 }
86
87 static void
88 pygi_signal_closure_marshal(GClosure *closure,
89                             GValue *return_value,
90                             guint n_param_values,
91                             const GValue *param_values,
92                             gpointer invocation_hint,
93                             gpointer marshal_data)
94 {
95     PyGILState_STATE state;
96     PyGClosure *pc = (PyGClosure *)closure;
97     PyObject *params, *ret = NULL;
98     guint i;
99     GISignalInfo *signal_info;
100     gint n_sig_info_args;
101     gint sig_info_highest_arg;
102
103     state = PyGILState_Ensure();
104
105     signal_info = ((PyGISignalClosure *)closure)->signal_info;
106     n_sig_info_args = g_callable_info_get_n_args(signal_info);
107     /* the first argument to a signal callback is instance,
108        but instance is not counted in the introspection data */
109     sig_info_highest_arg = n_sig_info_args + 1;
110     g_assert_cmpint(sig_info_highest_arg, ==, n_param_values);
111
112     /* construct Python tuple for the parameter values */
113     params = PyTuple_New(n_param_values);
114     for (i = 0; i < n_param_values; i++) {
115         /* swap in a different initial data for connect_object() */
116         if (i == 0 && G_CCLOSURE_SWAP_DATA(closure)) {
117             g_return_if_fail(pc->swap_data != NULL);
118             Py_INCREF(pc->swap_data);
119             PyTuple_SetItem(params, 0, pc->swap_data);
120
121         } else if (i == 0) {
122             PyObject *item = pyg_value_as_pyobject(&param_values[i], FALSE);
123
124             if (!item) {
125                 goto out;
126             }
127             PyTuple_SetItem(params, i, item);
128
129         } else if (i < sig_info_highest_arg) {
130             GIArgInfo arg_info;
131             GITypeInfo type_info;
132             GITransfer transfer;
133             GIArgument arg = { 0, };
134             PyObject *item = NULL;
135             gboolean free_array = FALSE;
136
137             g_callable_info_load_arg(signal_info, i - 1, &arg_info);
138             g_arg_info_load_type(&arg_info, &type_info);
139             transfer = g_arg_info_get_ownership_transfer(&arg_info);
140
141             arg = _pygi_argument_from_g_value(&param_values[i], &type_info);
142             
143             if (g_type_info_get_tag (&type_info) == GI_TYPE_TAG_ARRAY) {
144                 arg.v_pointer = _pygi_argument_to_array (&arg, NULL, NULL,
145                                                          &type_info, &free_array);
146             }
147             
148             item = _pygi_argument_to_object (&arg, &type_info, transfer);
149             
150             if (free_array) {
151                 g_array_free (arg.v_pointer, FALSE);
152             }
153             
154
155             if (item == NULL) {
156                 goto out;
157             }
158             PyTuple_SetItem(params, i, item);
159         }
160     }
161     /* params passed to function may have extra arguments */
162     if (pc->extra_args) {
163         PyObject *tuple = params;
164         params = PySequence_Concat(tuple, pc->extra_args);
165         Py_DECREF(tuple);
166     }
167     ret = PyObject_CallObject(pc->callback, params);
168     if (ret == NULL) {
169         if (pc->exception_handler)
170             pc->exception_handler(return_value, n_param_values, param_values);
171         else
172             PyErr_Print();
173         goto out;
174     }
175
176     if (return_value && pyg_value_from_pyobject(return_value, ret) != 0) {
177         PyErr_SetString(PyExc_TypeError,
178                         "can't convert return value to desired type");
179
180         if (pc->exception_handler)
181             pc->exception_handler(return_value, n_param_values, param_values);
182         else
183             PyErr_Print();
184     }
185     Py_DECREF(ret);
186
187  out:
188     Py_DECREF(params);
189     PyGILState_Release(state);
190 }
191
192 GClosure *
193 pygi_signal_closure_new_real (PyGObject *instance,
194                               const gchar *sig_name,
195                               PyObject *callback,
196                               PyObject *extra_args,
197                               PyObject *swap_data)
198 {
199     GClosure *closure = NULL;
200     PyGISignalClosure *pygi_closure = NULL;
201     GType g_type;
202     GISignalInfo *signal_info = NULL;
203     char *signal_name = g_strdup (sig_name);
204
205     g_return_val_if_fail(callback != NULL, NULL);
206
207     canonicalize_key(signal_name);
208
209     g_type = pyg_type_from_object ((PyObject *)instance);
210     signal_info = _pygi_lookup_signal_from_g_type (g_type, signal_name);
211
212     if (signal_info == NULL)
213         goto out;
214
215     closure = g_closure_new_simple(sizeof(PyGISignalClosure), NULL);
216     g_closure_add_invalidate_notifier(closure, NULL, pygi_signal_closure_invalidate);
217     g_closure_set_marshal(closure, pygi_signal_closure_marshal);
218
219     pygi_closure = (PyGISignalClosure *)closure;
220
221     pygi_closure->signal_info = signal_info;
222     Py_INCREF(callback);
223     pygi_closure->pyg_closure.callback = callback;
224
225     if (extra_args != NULL && extra_args != Py_None) {
226         Py_INCREF(extra_args);
227         if (!PyTuple_Check(extra_args)) {
228             PyObject *tmp = PyTuple_New(1);
229             PyTuple_SetItem(tmp, 0, extra_args);
230             extra_args = tmp;
231         }
232         pygi_closure->pyg_closure.extra_args = extra_args;
233     }
234     if (swap_data) {
235         Py_INCREF(swap_data);
236         pygi_closure->pyg_closure.swap_data = swap_data;
237         closure->derivative_flag = TRUE;
238     }
239
240 out:
241     g_free (signal_name);
242
243     return closure;
244 }