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