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