diff options
Diffstat (limited to 'glib')
-rw-r--r-- | glib/Makefile.am | 10 | ||||
-rw-r--r-- | glib/glibmodule.c | 21 | ||||
-rw-r--r-- | glib/pyglib.c | 34 | ||||
-rw-r--r-- | glib/pyglib.h | 13 | ||||
-rw-r--r-- | glib/pygmaincontext.c | 133 | ||||
-rw-r--r-- | glib/pygmaincontext.h | 40 | ||||
-rw-r--r-- | glib/pygmainloop.c | 383 | ||||
-rw-r--r-- | glib/pygmainloop.h | 36 |
8 files changed, 657 insertions, 13 deletions
diff --git a/glib/Makefile.am b/glib/Makefile.am index f0160d8..e32d24d 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -28,10 +28,12 @@ libpyglib_2_0_la_SOURCES = \ _glib_la_CFLAGS = $(GLIB_CFLAGS) _glib_la_LDFLAGS = $(common_ldflags) -export-symbols-regex init_glib _glib_la_LIBADD = $(GLIB_LIBS) libpyglib-2.0.la -_glib_la_SOURCES = \ - glibmodule.c \ - pygspawn.c \ - pygspawn.h +_glib_la_SOURCES = \ + glibmodule.c \ + pygspawn.c \ + pygspawn.h \ + pygmaincontext.c \ + pygmainloop.c if PLATFORM_WIN32 _glib_la_CFLAGS += -DPLATFORM_WIN32 diff --git a/glib/glibmodule.c b/glib/glibmodule.c index 25ded22..6892d1a 100644 --- a/glib/glibmodule.c +++ b/glib/glibmodule.c @@ -29,8 +29,10 @@ #include <glib.h> #include "pyglib.h" -#include "pygspawn.h" #include "pyglib-private.h" +#include "pygmaincontext.h" +#include "pygmainloop.h" +#include "pygspawn.h" #define PYGLIB_MAJOR_VERSION PYGOBJECT_MAJOR_VERSION #define PYGLIB_MINOR_VERSION PYGOBJECT_MINOR_VERSION @@ -343,13 +345,11 @@ pyg_source_remove(PyObject *self, PyObject *args) return PyBool_FromLong(g_source_remove(tag)); } -#ifdef FIXME static PyObject * -pyg_main_context_default(PyObject *unused) +pyglib_main_context_default(PyObject *unused) { - return pyg_main_context_new(g_main_context_default()); + return pyglib_main_context_new(g_main_context_default()); } -#endif struct _PyGChildData { PyObject *func; @@ -565,6 +565,8 @@ pyg_set_prgname(PyObject *self, PyObject *args) static PyMethodDef pyglib_functions[] = { { "spawn_async", (PyCFunction)pyglib_spawn_async, METH_VARARGS|METH_KEYWORDS }, + { "main_context_default", + (PyCFunction)pyglib_main_context_default, METH_NOARGS }, { "idle_add", (PyCFunction)pyg_idle_add, METH_VARARGS|METH_KEYWORDS }, @@ -598,10 +600,8 @@ static PyMethodDef pyglib_functions[] = { (PyCFunction)pyg_set_prgname, METH_VARARGS }, { "main_depth", (PyCFunction)pyg_main_depth, METH_NOARGS }, -#if 0 - { "main_context_default", - (PyCFunction)pyg_main_context_default, METH_NOARGS }, -#endif + + { NULL, NULL, 0 } }; @@ -675,4 +675,7 @@ init_glib(void) pyg_register_error(d); pyg_register_version_tuples(d); pyg_spawn_register_types(d); + + pyglib_mainloop_register_types(d); + pyglib_maincontext_register_types(d); } diff --git a/glib/pyglib.c b/glib/pyglib.c index 57bc095..3f7ebbd 100644 --- a/glib/pyglib.c +++ b/glib/pyglib.c @@ -27,10 +27,14 @@ #include <pythread.h> #include "pyglib.h" #include "pyglib-private.h" +#include "pygmaincontext.h" static struct _PyGLib_Functions *_PyGLib_API; static int pyglib_thread_state_tls_key; +static PyTypeObject *_PyGMainContext_Type; +#define PyGMainContext_Type (*_PyGMainContext_Type) + void pyglib_init(void) { @@ -63,6 +67,8 @@ pyglib_init(void) "could not import glib (could not find _PyGLib_API object)"); Py_DECREF(glib); } + + _PyGMainContext_Type = (PyTypeObject*)PyObject_GetAttrString(glib, "MainContext"); } void @@ -71,6 +77,12 @@ pyglib_init_internal(PyObject *api) _PyGLib_API = (struct _PyGLib_Functions *) PyCObject_AsVoidPtr(api); } +gboolean +pyglib_threads_enabled(void) +{ + return _PyGLib_API->threads_enabled; +} + PyGILState_STATE pyglib_gil_state_ensure(void) { @@ -255,3 +267,25 @@ bad_gerror: PyErr_Print(); return -2; } + +/** + * pyglib_main_context_new: + * @context: a GMainContext. + * + * Creates a wrapper for a GMainContext. + * + * Returns: the GMainContext wrapper. + */ +PyObject * +pyglib_main_context_new(GMainContext *context) +{ + PyGMainContext *self; + + self = (PyGMainContext *)PyObject_NEW(PyGMainContext, + &PyGMainContext_Type); + if (self == NULL) + return NULL; + + self->context = g_main_context_ref(context); + return (PyObject *)self; +} diff --git a/glib/pyglib.h b/glib/pyglib.h index 30b5f74..b2235d3 100644 --- a/glib/pyglib.h +++ b/glib/pyglib.h @@ -35,6 +35,19 @@ void pyglib_gil_state_release(PyGILState_STATE state); gboolean pyglib_enable_threads(void); gboolean pyglib_error_check(GError **error); gboolean pyglib_gerror_exception_check(GError **error); +gboolean pyglib_threads_enabled(void); +PyObject *pyglib_main_context_new(GMainContext *context); + +#define pyglib_begin_allow_threads \ + G_STMT_START { \ + PyThreadState *_save = NULL; \ + if (pyglib_threads_enabled()) \ + _save = PyEval_SaveThread(); + +#define pyglib_end_allow_threads \ + if (pyglib_threads_enabled()) \ + PyEval_RestoreThread(_save); \ + } G_STMT_END G_END_DECLS diff --git a/glib/pygmaincontext.c b/glib/pygmaincontext.c new file mode 100644 index 0000000..c969af5 --- /dev/null +++ b/glib/pygmaincontext.c @@ -0,0 +1,133 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * pygtk- Python bindings for the GTK toolkit. + * Copyright (C) 1998-2003 James Henstridge + * + * pygmaincontext.c: GMainContext 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <Python.h> +#include <pythread.h> +#include <glib.h> +#include "pygmaincontext.h" +#include "pyglib.h" +#include "pyglib-private.h" + +static int +pyg_main_context_init(PyGMainContext *self) +{ + self->context = g_main_context_new(); + return 0; +} + +static void +pyg_main_context_dealloc(PyGMainContext *self) +{ + if (self->context != NULL) { + g_main_context_unref(self->context); + self->context = NULL; + } + + PyObject_Del(self); +} + +static int +pyg_main_context_compare(PyGMainContext *self, PyGMainContext *v) +{ + if (self->context == v->context) return 0; + if (self->context > v->context) return -1; + return 1; +} + +static PyObject * +_wrap_g_main_context_iteration (PyGMainContext *self, PyObject *args) +{ + gboolean ret, may_block = TRUE; + + if (!PyArg_ParseTuple(args, "|i:GMainContext.iteration", + &may_block)) + return NULL; + + pyglib_begin_allow_threads; + ret = g_main_context_iteration(self->context, may_block); + pyglib_end_allow_threads; + + return PyBool_FromLong(ret); +} + +static PyObject * +_wrap_g_main_context_pending (PyGMainContext *self) +{ + return PyBool_FromLong(g_main_context_pending(self->context)); +} + +static PyMethodDef _PyGMainContext_methods[] = { + { "iteration", (PyCFunction)_wrap_g_main_context_iteration, METH_VARARGS }, + { "pending", (PyCFunction)_wrap_g_main_context_pending, METH_NOARGS }, + { NULL, NULL, 0 } +}; + +PyTypeObject PyGMainContext_Type = { + PyObject_HEAD_INIT(NULL) + 0, + "glib.MainContext", + sizeof(PyGMainContext), + 0, + /* methods */ + (destructor)pyg_main_context_dealloc, + (printfunc)0, + (getattrfunc)0, + (setattrfunc)0, + (cmpfunc)pyg_main_context_compare, + (reprfunc)0, + 0, + 0, + 0, + (hashfunc)0, + (ternaryfunc)0, + (reprfunc)0, + (getattrofunc)0, + (setattrofunc)0, + 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + NULL, + (traverseproc)0, + (inquiry)0, + (richcmpfunc)0, + 0, + (getiterfunc)0, + (iternextfunc)0, + _PyGMainContext_methods, + 0, + 0, + NULL, + NULL, + (descrgetfunc)0, + (descrsetfunc)0, + 0, + (initproc)pyg_main_context_init, +}; + +void +pyglib_maincontext_register_types(PyObject *d) +{ + PYGLIB_REGISTER_TYPE(d, PyGMainContext_Type, "MainContext"); +} diff --git a/glib/pygmaincontext.h b/glib/pygmaincontext.h new file mode 100644 index 0000000..58ffbf0 --- /dev/null +++ b/glib/pygmaincontext.h @@ -0,0 +1,40 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * pyglib - Python bindings for GLib toolkit. + * Copyright (C) 1998-2003 James Henstridge + * 2004-2008 Johan Dahlin + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifndef __PYG_MAINCONTEXT_H__ +#define __PYG_MAINCONTEXT_H__ + +#include <Python.h> +#include <glib.h> + +typedef struct { + PyObject_HEAD + GMainContext *context; +} PyGMainContext; + +extern PyTypeObject PyGMainContext_Type; + +PyObject * pyglib_main_context_new(GMainContext *context); + +void pyglib_maincontext_register_types(PyObject *d); + +#endif /* __PYG_MAINCONTEXT_H__ */ + diff --git a/glib/pygmainloop.c b/glib/pygmainloop.c new file mode 100644 index 0000000..04653de --- /dev/null +++ b/glib/pygmainloop.c @@ -0,0 +1,383 @@ +/* -*- 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <Python.h> +#include <pythread.h> +#include <glib.h> + +#include "pygmainloop.h" +#include "pygmaincontext.h" +#include "pyglib.h" +#include "pyglib-private.h" + + +typedef struct { + GSource source; + int fds[2]; + 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) +{ +#ifdef HAVE_PYSIGNAL_SETWAKEUPFD + PySignalWatchSource *real_source = (PySignalWatchSource *)source; +#endif + + /* 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. + */ + + /* 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 + +#ifdef HAVE_PYSIGNAL_SETWAKEUPFD + if (real_source->fds[0] != 0) + return FALSE; + + /* 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. + */ + if (pipe(real_source->fds) < 0) + g_error("Cannot create main loop pipe: %s\n", + g_strerror(errno)); + + real_source->fd.fd = real_source->fds[0]; + real_source->fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR; + g_source_add_poll(source, &real_source->fd); + + PySignal_SetWakeupFd(real_source->fds[1]); + +#else /* !HAVE_PYSIGNAL_SETWAKEUPFD */ + /* 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; +#endif /* HAVE_PYSIGNAL_SETWAKEUPFD */ + + return FALSE; +} + +static gboolean +pyg_signal_watch_check(GSource *source) +{ + PyGILState_STATE state; + GMainLoop *main_loop; + + 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 void +pyg_signal_watch_finalize(GSource *source) +{ + PySignalWatchSource *real_source = (PySignalWatchSource*)source; + + if (source != NULL) { + if (real_source->fds[0] != 0) + close(real_source->fds[0]); + + if (real_source->fds[1] != 0) + close(real_source->fds[1]); + } +} + +static GSourceFuncs pyg_signal_watch_funcs = +{ + pyg_signal_watch_prepare, + pyg_signal_watch_check, + pyg_signal_watch_dispatch, + pyg_signal_watch_finalize +}; + +static GSource * +pyg_signal_watch_new(void) +{ + return g_source_new(&pyg_signal_watch_funcs, sizeof(PySignalWatchSource)); +} + +static int +pyg_main_loop_new(PyGMainLoop *self, PyObject *args, PyObject *kwargs) +{ + + static char *kwlist[] = { "context", "is_running", NULL }; + PyObject *py_context = Py_None; + int is_running; + 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.GMainContext 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); + + 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 } +}; + +PyTypeObject PyGMainLoop_Type = { + PyObject_HEAD_INIT(NULL) + 0, + "glib.MainLoop", + sizeof(PyGMainLoop), + 0, + /* methods */ + (destructor)pyg_main_loop_dealloc, + (printfunc)0, + (getattrfunc)0, + (setattrfunc)0, + (cmpfunc)pyg_main_loop_compare, + (reprfunc)0, + 0, + 0, + 0, + (hashfunc)0, + (ternaryfunc)0, + (reprfunc)0, + (getattrofunc)0, + (setattrofunc)0, + 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + NULL, + (traverseproc)0, + (inquiry)0, + (richcmpfunc)0, + 0, + (getiterfunc)0, + (iternextfunc)0, + _PyGMainLoop_methods, + 0, + 0, + NULL, + NULL, + (descrgetfunc)0, + (descrsetfunc)0, + 0, + (initproc)pyg_main_loop_new, +}; + +void +pyglib_mainloop_register_types(PyObject *d) +{ + PYGLIB_REGISTER_TYPE(d, PyGMainLoop_Type, "MainLoop"); +} diff --git a/glib/pygmainloop.h b/glib/pygmainloop.h new file mode 100644 index 0000000..cdb44db --- /dev/null +++ b/glib/pygmainloop.h @@ -0,0 +1,36 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * pyglib - Python bindings for GLib toolkit. + * Copyright (C) 1998-2003 James Henstridge + * 2004-2008 Johan Dahlin + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifndef __PYG_MAINLOOP_H__ +#define __PYG_MAINLOOP_H__ + +typedef struct { + PyObject_HEAD + GMainLoop *loop; + GSource *signal_source; +} PyGMainLoop; + +extern PyTypeObject PyGMainLoop_Type; + +void pyglib_mainloop_register_types(PyObject *d); + +#endif /* __PYG_MAINLOOP_H__ */ + |