1 /* -*- Mode: C; c-basic-offset: 4 -*-
2 * pygtk- Python bindings for the GTK toolkit.
3 * Copyright (C) 1998-2003 James Henstridge
4 * Copyright (C) 2004 Johan Dahlin
5 *
6 * pygmainloop.c: GMainLoop wrapper
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21 * USA
22 */
23
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
27 #include <fcntl.h>
28
29 #include <Python.h>
30 #include <pythread.h>
31 #include <glib.h>
32
33 #include "pygmainloop.h"
34 #include "pygmaincontext.h"
35 #include "pyglib.h"
36 #include "pyglib-private.h"
37
38 static int pipe_fds[2];
39
40 typedef struct {
41 GSource source;
42 GPollFD fd;
43 } PySignalWatchSource;
44
45 #ifdef DISABLE_THREADING
46 static GMainLoop *pyg_current_main_loop = NULL;;
47
48 static inline GMainLoop *
pyg_save_current_main_loop(GMainLoop * main_loop)49 pyg_save_current_main_loop (GMainLoop *main_loop)
50 {
51 GMainLoop *retval = pyg_current_main_loop;
52
53 g_return_val_if_fail(main_loop != NULL, NULL);
54
55 pyg_current_main_loop = g_main_loop_ref(main_loop);
56
57 return retval;
58 }
59
60 static inline void
pyg_restore_current_main_loop(GMainLoop * main_loop)61 pyg_restore_current_main_loop (GMainLoop *main_loop)
62 {
63 if (pyg_current_main_loop != NULL)
64 g_main_loop_unref(pyg_current_main_loop);
65 pyg_current_main_loop = main_loop;
66 }
67
68 static inline GMainLoop *
pyg_get_current_main_loop(void)69 pyg_get_current_main_loop (void)
70 {
71 return pyg_current_main_loop;
72 }
73 #else /* !defined(#ifndef DISABLE_THREADING) */
74
75 static int pyg_current_main_loop_key = -1;
76
77 static inline GMainLoop *
pyg_save_current_main_loop(GMainLoop * main_loop)78 pyg_save_current_main_loop (GMainLoop *main_loop)
79 {
80 GMainLoop *retval;
81
82 g_return_val_if_fail(main_loop != NULL, NULL);
83
84 if (pyg_current_main_loop_key == -1)
85 pyg_current_main_loop_key = PyThread_create_key();
86
87 retval = PyThread_get_key_value(pyg_current_main_loop_key);
88 PyThread_delete_key_value(pyg_current_main_loop_key);
89 PyThread_set_key_value(pyg_current_main_loop_key,
90 g_main_loop_ref(main_loop));
91
92 return retval;
93 }
94
95 static inline void
pyg_restore_current_main_loop(GMainLoop * main_loop)96 pyg_restore_current_main_loop (GMainLoop *main_loop)
97 {
98 GMainLoop *prev;
99
100 g_return_if_fail (pyg_current_main_loop_key != -1);
101
102 prev = PyThread_get_key_value(pyg_current_main_loop_key);
103 if (prev != NULL)
104 g_main_loop_unref(prev);
105 PyThread_delete_key_value(pyg_current_main_loop_key);
106 if (main_loop != NULL)
107 PyThread_set_key_value(pyg_current_main_loop_key, main_loop);
108 }
109
110 static inline GMainLoop *
pyg_get_current_main_loop(void)111 pyg_get_current_main_loop (void)
112 {
113 if (pyg_current_main_loop_key == -1)
114 return NULL;
115 return PyThread_get_key_value(pyg_current_main_loop_key);
116 }
117 #endif /* DISABLE_THREADING */
118
119 static gboolean
pyg_signal_watch_prepare(GSource * source,int * timeout)120 pyg_signal_watch_prepare(GSource *source,
121 int *timeout)
122 {
123 /* Python only invokes signal handlers from the main thread,
124 * so if a thread other than the main thread receives the signal
125 * from the kernel, PyErr_CheckSignals() from that thread will
126 * do nothing.
127 */
128
129 #ifdef HAVE_PYSIGNAL_SETWAKEUPFD
130 return FALSE;
131 #else /* !HAVE_PYSIGNAL_SETWAKEUPFD */
132 /* On Windows g_poll() won't be interrupted by a signal
133 * (AFAIK), so we need the timeout there too, even if there's
134 * only one thread.
135 */
136 #ifndef PLATFORM_WIN32
137 if (!pyglib_threads_enabled())
138 return FALSE;
139 #endif /* PLATFORM_WIN32 */
140
141 /* If we're using 2.5 or an earlier version of python we
142 * will default to a timeout every second, be aware,
143 * this will cause unnecessary wakeups, see
144 * http://bugzilla.gnome.org/show_bug.cgi?id=481569
145 */
146 *timeout = 1000;
147 return FALSE;
148 #endif /* HAVE_PYSIGNAL_SETWAKEUPFD */
149 }
150
151 static gboolean
pyg_signal_watch_check(GSource * source)152 pyg_signal_watch_check(GSource *source)
153 {
154 PyGILState_STATE state;
155 GMainLoop *main_loop;
156
157 #ifdef HAVE_PYSIGNAL_SETWAKEUPFD
158 PySignalWatchSource *real_source = (PySignalWatchSource *)source;
159 GPollFD *poll_fd = &real_source->fd;
160 unsigned char dummy;
161 gssize ret;
162 if (poll_fd->revents & G_IO_IN)
163 ret = read(poll_fd->fd, &dummy, 1);
164 #endif
165
166 state = pyglib_gil_state_ensure();
167
168 main_loop = pyg_get_current_main_loop();
169
170 if (PyErr_CheckSignals() == -1 && main_loop != NULL) {
171 PyErr_SetNone(PyExc_KeyboardInterrupt);
172 g_main_loop_quit(main_loop);
173 }
174
175 pyglib_gil_state_release(state);
176
177 return FALSE;
178 }
179
180 static gboolean
pyg_signal_watch_dispatch(GSource * source,GSourceFunc callback,gpointer user_data)181 pyg_signal_watch_dispatch(GSource *source,
182 GSourceFunc callback,
183 gpointer user_data)
184 {
185 /* We should never be dispatched */
186 g_assert_not_reached();
187 return TRUE;
188 }
189
190 static GSourceFuncs pyg_signal_watch_funcs =
191 {
192 pyg_signal_watch_prepare,
193 pyg_signal_watch_check,
194 pyg_signal_watch_dispatch
195 };
196
197 static GSource *
pyg_signal_watch_new(void)198 pyg_signal_watch_new(void)
199 {
200 GSource *source = g_source_new(&pyg_signal_watch_funcs,
201 sizeof(PySignalWatchSource));
202
203 #ifdef HAVE_PYSIGNAL_SETWAKEUPFD
204 PySignalWatchSource *real_source = (PySignalWatchSource *)source;
205 int flag;
206
207 /* Unfortunately we need to create a new pipe here instead of
208 * reusing the pipe inside the GMainContext.
209 * Ideally an api should be added to GMainContext which allows us
210 * to reuse that pipe which would suit us perfectly fine.
211 * XXX More efficient than a pipe, we could use an eventfd on Linux
212 * kernels that support it.
213 */
214 gint already_piped = (pipe_fds[0] > 0);
215 if (!already_piped) {
216 if (pipe(pipe_fds) < 0)
217 g_error("Cannot create main loop pipe: %s\n",
218 g_strerror(errno));
219
220 /* Make the write end of the fd non blocking */
221 flag = fcntl(pipe_fds[1], F_GETFL, 0);
222 flag |= O_NONBLOCK;
223 fcntl(pipe_fds[1], F_SETFL, flag);
224 }
225
226 real_source->fd.fd = pipe_fds[0];
227 real_source->fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
228 g_source_add_poll(source, &real_source->fd);
229
230 if (!already_piped)
231 PySignal_SetWakeupFd(pipe_fds[1]);
232 #endif
233 return source;
234 }
235
236 PYGLIB_DEFINE_TYPE("glib.MainLoop", PyGMainLoop_Type, PyGMainLoop)
237
238 static int
pyg_main_loop_init(PyGMainLoop * self,PyObject * args,PyObject * kwargs)239 pyg_main_loop_init(PyGMainLoop *self, PyObject *args, PyObject *kwargs)
240 {
241 static char *kwlist[] = { "context", "is_running", NULL };
242 PyObject *py_context = Py_None;
243 int is_running = 0;
244 GMainContext *context;
245
246 if (!PyArg_ParseTupleAndKeywords(args, kwargs,
247 "|Ob:GMainLoop.__init__",
248 kwlist, &py_context, &is_running))
249 return -1;
250
251 if (!PyObject_TypeCheck(py_context, &PyGMainContext_Type) &&
252 py_context != Py_None) {
253 PyErr_SetString(PyExc_TypeError,
254 "context must be a glib.MainContext or None");
255 return -1;
256 }
257
258 if (py_context != Py_None) {
259 context = ((PyGMainContext*)py_context)->context;
260 } else {
261 context = NULL;
262 }
263
264 self->loop = g_main_loop_new(context, is_running);
265
266 self->signal_source = pyg_signal_watch_new();
267 g_source_attach(self->signal_source, context);
268 g_source_unref(self->signal_source);
269
270 return 0;
271 }
272
273 static void
pyg_main_loop_dealloc(PyGMainLoop * self)274 pyg_main_loop_dealloc(PyGMainLoop *self)
275 {
276 if (self->signal_source != NULL) {
277 g_source_destroy(self->signal_source);
278 self->signal_source = NULL;
279 }
280
281 if (self->loop != NULL) {
282 g_main_loop_unref(self->loop);
283 self->loop = NULL;
284 }
285
286 PyObject_Del(self);
287 }
288
289 static PyObject*
pyg_main_loop_richcompare(PyObject * self,PyObject * other,int op)290 pyg_main_loop_richcompare(PyObject *self, PyObject *other, int op)
291 {
292 if (Py_TYPE(self) == Py_TYPE(other) && Py_TYPE(self) == &PyGMainLoop_Type)
293 return _pyglib_generic_ptr_richcompare(((PyGMainLoop*)self)->loop,
294 ((PyGMainLoop*)other)->loop,
295 op);
296 else {
297 Py_INCREF(Py_NotImplemented);
298 return Py_NotImplemented;
299 }
300 }
301
302 static PyObject *
_wrap_g_main_loop_get_context(PyGMainLoop * loop)303 _wrap_g_main_loop_get_context (PyGMainLoop *loop)
304 {
305 return pyg_main_context_new(g_main_loop_get_context(loop->loop));
306 }
307
308 static PyObject *
_wrap_g_main_loop_is_running(PyGMainLoop * self)309 _wrap_g_main_loop_is_running (PyGMainLoop *self)
310 {
311 return PyBool_FromLong(g_main_loop_is_running(self->loop));
312 }
313
314 static PyObject *
_wrap_g_main_loop_quit(PyGMainLoop * self)315 _wrap_g_main_loop_quit (PyGMainLoop *self)
316 {
317 g_main_loop_quit(self->loop);
318
319 Py_INCREF(Py_None);
320 return Py_None;
321 }
322
323 static PyObject *
_wrap_g_main_loop_run(PyGMainLoop * self)324 _wrap_g_main_loop_run (PyGMainLoop *self)
325 {
326 GMainLoop *prev_loop;
327
328 prev_loop = pyg_save_current_main_loop(self->loop);
329
330 pyglib_begin_allow_threads;
331 g_main_loop_run(self->loop);
332 pyglib_end_allow_threads;
333
334 pyg_restore_current_main_loop(prev_loop);
335
336 if (PyErr_Occurred())
337 return NULL;
338
339 Py_INCREF(Py_None);
340 return Py_None;
341 }
342
343 static PyMethodDef _PyGMainLoop_methods[] = {
344 { "get_context", (PyCFunction)_wrap_g_main_loop_get_context, METH_NOARGS },
345 { "is_running", (PyCFunction)_wrap_g_main_loop_is_running, METH_NOARGS },
346 { "quit", (PyCFunction)_wrap_g_main_loop_quit, METH_NOARGS },
347 { "run", (PyCFunction)_wrap_g_main_loop_run, METH_NOARGS },
348 { NULL, NULL, 0 }
349 };
350
351 void
pyglib_mainloop_register_types(PyObject * d)352 pyglib_mainloop_register_types(PyObject *d)
353 {
354 PyGMainLoop_Type.tp_dealloc = (destructor)pyg_main_loop_dealloc;
355 PyGMainLoop_Type.tp_richcompare = pyg_main_loop_richcompare;
356 PyGMainLoop_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
357 PyGMainLoop_Type.tp_methods = _PyGMainLoop_methods;
358 PyGMainLoop_Type.tp_init = (initproc)pyg_main_loop_init;
359 PYGLIB_REGISTER_TYPE(d, PyGMainLoop_Type, "MainLoop");
360 }
361