summaryrefslogtreecommitdiff
path: root/glib/pygmainloop.c
diff options
context:
space:
mode:
Diffstat (limited to 'glib/pygmainloop.c')
-rw-r--r--glib/pygmainloop.c354
1 files changed, 354 insertions, 0 deletions
diff --git a/glib/pygmainloop.c b/glib/pygmainloop.c
new file mode 100644
index 0000000..d9f048c
--- /dev/null
+++ b/glib/pygmainloop.c
@@ -0,0 +1,354 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * pygtk- Python bindings for the GTK toolkit.
+ * Copyright (C) 1998-2003 James Henstridge
+ * Copyright (C) 2004 Johan Dahlin
+ *
+ * pygmainloop.c: GMainLoop wrapper
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <fcntl.h>
+
+#include <Python.h>
+#include <pythread.h>
+#include <glib.h>
+
+#include "pygmainloop.h"
+#include "pygmaincontext.h"
+#include "pyglib.h"
+#include "pyglib-private.h"
+
+static int pipe_fds[2];
+
+typedef struct {
+ GSource source;
+ GPollFD fd;
+} PySignalWatchSource;
+
+#ifdef DISABLE_THREADING
+static GMainLoop *pyg_current_main_loop = NULL;;
+
+static inline GMainLoop *
+pyg_save_current_main_loop (GMainLoop *main_loop)
+{
+ GMainLoop *retval = pyg_current_main_loop;
+
+ g_return_val_if_fail(main_loop != NULL, NULL);
+
+ pyg_current_main_loop = g_main_loop_ref(main_loop);
+
+ return retval;
+}
+
+static inline void
+pyg_restore_current_main_loop (GMainLoop *main_loop)
+{
+ if (pyg_current_main_loop != NULL)
+ g_main_loop_unref(pyg_current_main_loop);
+ pyg_current_main_loop = main_loop;
+}
+
+static inline GMainLoop *
+pyg_get_current_main_loop (void)
+{
+ return pyg_current_main_loop;
+}
+#else /* !defined(#ifndef DISABLE_THREADING) */
+
+static int pyg_current_main_loop_key = -1;
+
+static inline GMainLoop *
+pyg_save_current_main_loop (GMainLoop *main_loop)
+{
+ GMainLoop *retval;
+
+ g_return_val_if_fail(main_loop != NULL, NULL);
+
+ if (pyg_current_main_loop_key == -1)
+ pyg_current_main_loop_key = PyThread_create_key();
+
+ retval = PyThread_get_key_value(pyg_current_main_loop_key);
+ PyThread_delete_key_value(pyg_current_main_loop_key);
+ PyThread_set_key_value(pyg_current_main_loop_key,
+ g_main_loop_ref(main_loop));
+
+ return retval;
+}
+
+static inline void
+pyg_restore_current_main_loop (GMainLoop *main_loop)
+{
+ GMainLoop *prev;
+
+ g_return_if_fail (pyg_current_main_loop_key != -1);
+
+ prev = PyThread_get_key_value(pyg_current_main_loop_key);
+ if (prev != NULL)
+ g_main_loop_unref(prev);
+ PyThread_delete_key_value(pyg_current_main_loop_key);
+ if (main_loop != NULL)
+ PyThread_set_key_value(pyg_current_main_loop_key, main_loop);
+}
+
+static inline GMainLoop *
+pyg_get_current_main_loop (void)
+{
+ if (pyg_current_main_loop_key == -1)
+ return NULL;
+ return PyThread_get_key_value(pyg_current_main_loop_key);
+}
+#endif /* DISABLE_THREADING */
+
+static gboolean
+pyg_signal_watch_prepare(GSource *source,
+ int *timeout)
+{
+ /* Python only invokes signal handlers from the main thread,
+ * so if a thread other than the main thread receives the signal
+ * from the kernel, PyErr_CheckSignals() from that thread will
+ * do nothing.
+ */
+
+#ifdef HAVE_PYSIGNAL_SETWAKEUPFD
+ return FALSE;
+#else /* !HAVE_PYSIGNAL_SETWAKEUPFD */
+ /* On Windows g_poll() won't be interrupted by a signal
+ * (AFAIK), so we need the timeout there too, even if there's
+ * only one thread.
+ */
+#ifndef PLATFORM_WIN32
+ if (!pyglib_threads_enabled())
+ return FALSE;
+#endif /* PLATFORM_WIN32 */
+
+ /* If we're using 2.5 or an earlier version of python we
+ * will default to a timeout every second, be aware,
+ * this will cause unnecessary wakeups, see
+ * http://bugzilla.gnome.org/show_bug.cgi?id=481569
+ */
+ *timeout = 1000;
+ return FALSE;
+#endif /* HAVE_PYSIGNAL_SETWAKEUPFD */
+}
+
+static gboolean
+pyg_signal_watch_check(GSource *source)
+{
+ PyGILState_STATE state;
+ GMainLoop *main_loop;
+
+#ifdef HAVE_PYSIGNAL_SETWAKEUPFD
+ PySignalWatchSource *real_source = (PySignalWatchSource *)source;
+ GPollFD *poll_fd = &real_source->fd;
+ unsigned char dummy;
+ if (poll_fd->revents & G_IO_IN)
+ read(poll_fd->fd, &dummy, 1);
+#endif
+
+ state = pyglib_gil_state_ensure();
+
+ main_loop = pyg_get_current_main_loop();
+
+ if (PyErr_CheckSignals() == -1 && main_loop != NULL) {
+ PyErr_SetNone(PyExc_KeyboardInterrupt);
+ g_main_loop_quit(main_loop);
+ }
+
+ pyglib_gil_state_release(state);
+
+ return FALSE;
+}
+
+static gboolean
+pyg_signal_watch_dispatch(GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ /* We should never be dispatched */
+ g_assert_not_reached();
+ return TRUE;
+}
+
+static GSourceFuncs pyg_signal_watch_funcs =
+{
+ pyg_signal_watch_prepare,
+ pyg_signal_watch_check,
+ pyg_signal_watch_dispatch
+};
+
+static GSource *
+pyg_signal_watch_new(void)
+{
+ GSource *source = g_source_new(&pyg_signal_watch_funcs,
+ sizeof(PySignalWatchSource));
+
+#ifdef HAVE_PYSIGNAL_SETWAKEUPFD
+ PySignalWatchSource *real_source = (PySignalWatchSource *)source;
+ int flag;
+
+ /* Unfortunately we need to create a new pipe here instead of
+ * reusing the pipe inside the GMainContext.
+ * Ideally an api should be added to GMainContext which allows us
+ * to reuse that pipe which would suit us perfectly fine.
+ * XXX More efficient than a pipe, we could use an eventfd on Linux
+ * kernels that support it.
+ */
+ gint already_piped = (pipe_fds[0] > 0);
+ if (!already_piped) {
+ if (pipe(pipe_fds) < 0)
+ g_error("Cannot create main loop pipe: %s\n",
+ g_strerror(errno));
+
+ /* Make the write end of the fd non blocking */
+ flag = fcntl(pipe_fds[1], F_GETFL, 0);
+ flag |= O_NONBLOCK;
+ fcntl(pipe_fds[1], F_SETFL, flag);
+ }
+
+ real_source->fd.fd = pipe_fds[0];
+ real_source->fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
+ g_source_add_poll(source, &real_source->fd);
+
+ if (!already_piped)
+ PySignal_SetWakeupFd(pipe_fds[1]);
+#endif
+ return source;
+}
+
+PYGLIB_DEFINE_TYPE("glib.MainLoop", PyGMainLoop_Type, PyGMainLoop)
+
+static int
+pyg_main_loop_init(PyGMainLoop *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "context", "is_running", NULL };
+ PyObject *py_context = Py_None;
+ int is_running = 0;
+ GMainContext *context;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|Ob:GMainLoop.__init__",
+ kwlist, &py_context, &is_running))
+ return -1;
+
+ if (!PyObject_TypeCheck(py_context, &PyGMainContext_Type) &&
+ py_context != Py_None) {
+ PyErr_SetString(PyExc_TypeError,
+ "context must be a glib.MainContext or None");
+ return -1;
+ }
+
+ if (py_context != Py_None) {
+ context = ((PyGMainContext*)py_context)->context;
+ } else {
+ context = NULL;
+ }
+
+ self->loop = g_main_loop_new(context, is_running);
+
+ self->signal_source = pyg_signal_watch_new();
+ g_source_attach(self->signal_source, context);
+ g_source_unref(self->signal_source);
+
+ return 0;
+}
+
+static void
+pyg_main_loop_dealloc(PyGMainLoop *self)
+{
+ if (self->signal_source != NULL) {
+ g_source_destroy(self->signal_source);
+ self->signal_source = NULL;
+ }
+
+ if (self->loop != NULL) {
+ g_main_loop_unref(self->loop);
+ self->loop = NULL;
+ }
+
+ PyObject_Del(self);
+}
+
+static int
+pyg_main_loop_compare(PyGMainLoop *self, PyGMainLoop *v)
+{
+ if (self->loop == v->loop) return 0;
+ if (self->loop > v->loop) return -1;
+ return 1;
+}
+
+static PyObject *
+_wrap_g_main_loop_get_context (PyGMainLoop *loop)
+{
+ return pyglib_main_context_new(g_main_loop_get_context(loop->loop));
+}
+
+static PyObject *
+_wrap_g_main_loop_is_running (PyGMainLoop *self)
+{
+ return PyBool_FromLong(g_main_loop_is_running(self->loop));
+}
+
+static PyObject *
+_wrap_g_main_loop_quit (PyGMainLoop *self)
+{
+ g_main_loop_quit(self->loop);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_g_main_loop_run (PyGMainLoop *self)
+{
+ GMainLoop *prev_loop;
+
+ prev_loop = pyg_save_current_main_loop(self->loop);
+
+ pyglib_begin_allow_threads;
+ g_main_loop_run(self->loop);
+ pyglib_end_allow_threads;
+
+ pyg_restore_current_main_loop(prev_loop);
+
+ if (PyErr_Occurred())
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef _PyGMainLoop_methods[] = {
+ { "get_context", (PyCFunction)_wrap_g_main_loop_get_context, METH_NOARGS },
+ { "is_running", (PyCFunction)_wrap_g_main_loop_is_running, METH_NOARGS },
+ { "quit", (PyCFunction)_wrap_g_main_loop_quit, METH_NOARGS },
+ { "run", (PyCFunction)_wrap_g_main_loop_run, METH_NOARGS },
+ { NULL, NULL, 0 }
+};
+
+void
+pyglib_mainloop_register_types(PyObject *d)
+{
+ PyGMainLoop_Type.tp_dealloc = (destructor)pyg_main_loop_dealloc;
+ PyGMainLoop_Type.tp_compare = (cmpfunc)pyg_main_loop_compare;
+ PyGMainLoop_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
+ PyGMainLoop_Type.tp_methods = _PyGMainLoop_methods;
+ PyGMainLoop_Type.tp_init = (initproc)pyg_main_loop_init;
+ PYGLIB_REGISTER_TYPE(d, PyGMainLoop_Type, "MainLoop");
+}