diff options
Diffstat (limited to 'glib/pyglib.c')
-rw-r--r-- | glib/pyglib.c | 577 |
1 files changed, 577 insertions, 0 deletions
diff --git a/glib/pyglib.c b/glib/pyglib.c new file mode 100644 index 0000000..a1bdbb9 --- /dev/null +++ b/glib/pyglib.c @@ -0,0 +1,577 @@ +/* -*- Mode: C; c-set-style: python; 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <Python.h> +#include <pythread.h> +#include "pyglib.h" +#include "pyglib-private.h" +#include "pygmaincontext.h" +#include "pygoptioncontext.h" +#include "pygoptiongroup.h" + +static struct _PyGLib_Functions *_PyGLib_API; +static int pyglib_thread_state_tls_key; +static PyObject *exception_table = NULL; + +static PyTypeObject *_PyGMainContext_Type; +#define PyGMainContext_Type (*_PyGMainContext_Type) + +static PyTypeObject *_PyGOptionGroup_Type; +#define PyGOptionGroup_Type (*_PyGOptionGroup_Type) + +static PyTypeObject *_PyGOptionContext_Type; +#define PyGOptionContext_Type (*_PyGOptionContext_Type) + +void +pyglib_init(void) +{ + PyObject *glib, *cobject; + + glib = PyImport_ImportModule("glib"); + if (!glib) { + if (PyErr_Occurred()) { + PyObject *type, *value, *traceback; + PyObject *py_orig_exc; + PyErr_Fetch(&type, &value, &traceback); + py_orig_exc = PyObject_Repr(value); + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(traceback); + PyErr_Format(PyExc_ImportError, + "could not import glib (error was: %s)", + _PyUnicode_AsString(py_orig_exc)); + Py_DECREF(py_orig_exc); + } else { + PyErr_SetString(PyExc_ImportError, + "could not import glib (no error given)"); + } + return; + } + + cobject = PyObject_GetAttrString(glib, "_PyGLib_API"); + if (cobject && PyCObject_Check(cobject)) + _PyGLib_API = (struct _PyGLib_Functions *) PyCObject_AsVoidPtr(cobject); + else { + PyErr_SetString(PyExc_ImportError, + "could not import glib (could not find _PyGLib_API object)"); + Py_DECREF(glib); + return; + } + + _PyGMainContext_Type = (PyTypeObject*)PyObject_GetAttrString(glib, "MainContext"); + _PyGOptionGroup_Type = (PyTypeObject*)PyObject_GetAttrString(glib, "OptionGroup"); + _PyGOptionContext_Type = (PyTypeObject*)PyObject_GetAttrString(glib, "OptionContext"); +} + +void +pyglib_init_internal(PyObject *api) +{ + _PyGLib_API = (struct _PyGLib_Functions *) PyCObject_AsVoidPtr(api); +} + +gboolean +pyglib_threads_enabled(void) +{ + g_return_val_if_fail (_PyGLib_API != NULL, FALSE); + + return _PyGLib_API->threads_enabled; +} + +PyGILState_STATE +pyglib_gil_state_ensure(void) +{ + g_return_val_if_fail (_PyGLib_API != NULL, PyGILState_LOCKED); + + if (!_PyGLib_API->threads_enabled) + return PyGILState_LOCKED; + + return PyGILState_Ensure(); +} + +void +pyglib_gil_state_release(PyGILState_STATE state) +{ + g_return_if_fail (_PyGLib_API != NULL); + + if (!_PyGLib_API->threads_enabled) + return; + + PyGILState_Release(state); +} + +/** + * pyglib_enable_threads: + * + * Returns: TRUE if threading is enabled, FALSE otherwise. + * + */ +#ifdef DISABLE_THREADING +gboolean +pyglib_enable_threads(void) +{ + PyErr_SetString(PyExc_RuntimeError, + "pyglib threading disabled at compile time"); + return FALSE; +} + +void +_pyglib_notify_on_enabling_threads(PyGLibThreadsEnabledFunc callback) +{ + /* Ignore, threads cannot be enabled. */ +} + +#else + +static GSList *thread_enabling_callbacks = NULL; + +/* Enable threading; note that the GIL must be held by the current + * thread when this function is called + */ +gboolean +pyglib_enable_threads(void) +{ + GSList *callback; + + g_return_val_if_fail (_PyGLib_API != NULL, FALSE); + + if (_PyGLib_API->threads_enabled) + return TRUE; + + PyEval_InitThreads(); + if (!g_threads_got_initialized) + g_thread_init(NULL); + + _PyGLib_API->threads_enabled = TRUE; + pyglib_thread_state_tls_key = PyThread_create_key(); + + for (callback = thread_enabling_callbacks; callback; callback = callback->next) + ((PyGLibThreadsEnabledFunc) callback->data) (); + + g_slist_free(thread_enabling_callbacks); + return TRUE; +} + +void +_pyglib_notify_on_enabling_threads(PyGLibThreadsEnabledFunc callback) +{ + if (callback && !pyglib_threads_enabled()) + thread_enabling_callbacks = g_slist_append(thread_enabling_callbacks, callback); +} +#endif + +int +pyglib_gil_state_ensure_py23 (void) +{ + return PyGILState_Ensure(); +} + +void +pyglib_gil_state_release_py23 (int flag) +{ + PyGILState_Release(flag); +} + +/** + * pyglib_block_threads: + * + */ +void +pyglib_block_threads(void) +{ + g_return_if_fail (_PyGLib_API != NULL); + + if (_PyGLib_API->block_threads != NULL) + (* _PyGLib_API->block_threads)(); +} + +/** + * pyglib_unblock_threads: + * + */ +void +pyglib_unblock_threads(void) +{ + g_return_if_fail (_PyGLib_API != NULL); + if (_PyGLib_API->unblock_threads != NULL) + (* _PyGLib_API->unblock_threads)(); +} + +/** + * pyglib_set_thread_block_funcs: + * + * hooks to register handlers for getting GDK threads to cooperate + * with python threading + */ +void +pyglib_set_thread_block_funcs (PyGLibThreadBlockFunc block_threads_func, + PyGLibThreadBlockFunc unblock_threads_func) +{ + g_return_if_fail (_PyGLib_API != NULL); + + _PyGLib_API->block_threads = block_threads_func; + _PyGLib_API->unblock_threads = unblock_threads_func; +} + + +/** + * pyglib_error_check: + * @error: a pointer to the GError. + * + * Checks to see if the GError has been set. If the error has been + * set, then the glib.GError Python exception will be raised, and + * the GError cleared. + * + * Returns: True if an error was set. + */ +gboolean +pyglib_error_check(GError **error) +{ + PyGILState_STATE state; + PyObject *exc_type; + PyObject *exc_instance; + PyObject *d; + + g_return_val_if_fail(error != NULL, FALSE); + + if (*error == NULL) + return FALSE; + + state = pyglib_gil_state_ensure(); + + exc_type = _PyGLib_API->gerror_exception; + if (exception_table != NULL) + { + PyObject *item; + item = PyDict_GetItem(exception_table, _PyLong_FromLong((*error)->domain)); + if (item != NULL) + exc_type = item; + } + + exc_instance = PyObject_CallFunction(exc_type, "z", (*error)->message); + + if ((*error)->domain) { + PyObject_SetAttrString(exc_instance, "domain", + d=_PyUnicode_FromString(g_quark_to_string((*error)->domain))); + Py_DECREF(d); + } + else + PyObject_SetAttrString(exc_instance, "domain", Py_None); + + PyObject_SetAttrString(exc_instance, "code", + d=_PyLong_FromLong((*error)->code)); + Py_DECREF(d); + + if ((*error)->message) { + PyObject_SetAttrString(exc_instance, "message", + d=_PyUnicode_FromString((*error)->message)); + Py_DECREF(d); + } else { + PyObject_SetAttrString(exc_instance, "message", Py_None); + } + + PyErr_SetObject(_PyGLib_API->gerror_exception, exc_instance); + Py_DECREF(exc_instance); + g_clear_error(error); + + pyglib_gil_state_release(state); + + return TRUE; +} + +/** + * pyglib_gerror_exception_check: + * @error: a standard GLib GError ** output parameter + * + * Checks to see if a GError exception has been raised, and if so + * translates the python exception to a standard GLib GError. If the + * raised exception is not a GError then PyErr_Print() is called. + * + * Returns: 0 if no exception has been raised, -1 if it is a + * valid glib.GError, -2 otherwise. + */ +gboolean +pyglib_gerror_exception_check(GError **error) +{ + PyObject *type, *value, *traceback; + PyObject *py_message, *py_domain, *py_code; + const char *bad_gerror_message; + + PyErr_Fetch(&type, &value, &traceback); + if (type == NULL) + return 0; + PyErr_NormalizeException(&type, &value, &traceback); + if (value == NULL) { + PyErr_Restore(type, value, traceback); + PyErr_Print(); + return -2; + } + if (!value || + !PyErr_GivenExceptionMatches(type, + (PyObject *) _PyGLib_API->gerror_exception)) { + PyErr_Restore(type, value, traceback); + PyErr_Print(); + return -2; + } + Py_DECREF(type); + Py_XDECREF(traceback); + + py_message = PyObject_GetAttrString(value, "message"); + if (!py_message || !_PyUnicode_Check(py_message)) { + bad_gerror_message = "glib.GError instances must have a 'message' string attribute"; + goto bad_gerror; + } + + py_domain = PyObject_GetAttrString(value, "domain"); + if (!py_domain || !_PyUnicode_Check(py_domain)) { + bad_gerror_message = "glib.GError instances must have a 'domain' string attribute"; + Py_DECREF(py_message); + goto bad_gerror; + } + + py_code = PyObject_GetAttrString(value, "code"); + if (!py_code || !_PyLong_Check(py_code)) { + bad_gerror_message = "glib.GError instances must have a 'code' int attribute"; + Py_DECREF(py_message); + Py_DECREF(py_domain); + goto bad_gerror; + } + + g_set_error(error, g_quark_from_string(_PyUnicode_AsString(py_domain)), + _PyLong_AsLong(py_code), _PyUnicode_AsString(py_message)); + + Py_DECREF(py_message); + Py_DECREF(py_code); + Py_DECREF(py_domain); + return -1; + +bad_gerror: + Py_DECREF(value); + g_set_error(error, g_quark_from_static_string("pyglib"), 0, bad_gerror_message); + PyErr_SetString(PyExc_ValueError, bad_gerror_message); + PyErr_Print(); + return -2; +} + +/** + * pyglib_register_exception_for_domain: + * @name: name of the exception + * @error_domain: error domain + * + * Registers a new glib.GError exception subclass called #name for + * a specific #domain. This exception will be raised when a GError + * of the same domain is passed in to pyglib_error_check(). + * + * Returns: the new exception + */ +PyObject * +pyglib_register_exception_for_domain(gchar *name, + gint error_domain) +{ + PyObject *exception; + + exception = PyErr_NewException(name, _PyGLib_API->gerror_exception, NULL); + + if (exception_table == NULL) + exception_table = PyDict_New(); + + PyDict_SetItem(exception_table, + _PyLong_FromLong(error_domain), + exception); + + return exception; +} + +/** + * 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; +} + +/** + * pyg_option_group_transfer_group: + * @group: a GOptionGroup wrapper + * + * This is used to transfer the GOptionGroup to a GOptionContext. After this + * is called, the calle must handle the release of the GOptionGroup. + * + * When #NULL is returned, the GOptionGroup was already transfered. + * + * Returns: Either #NULL or the wrapped GOptionGroup. + */ +GOptionGroup * +pyglib_option_group_transfer_group(PyObject *obj) +{ + PyGOptionGroup *self = (PyGOptionGroup*)obj; + + if (self->is_in_context) + return NULL; + + self->is_in_context = TRUE; + + /* Here we increase the reference count of the PyGOptionGroup, because now + * the GOptionContext holds an reference to us (it is the userdata passed + * to g_option_group_new(). + * + * The GOptionGroup is freed with the GOptionContext. + * + * We set it here because if we would do this in the init method we would + * hold two references and the PyGOptionGroup would never be freed. + */ + Py_INCREF(self); + + return self->group; +} + +/** + * pyglib_option_group_new: + * @group: a GOptionGroup + * + * The returned GOptionGroup can't be used to set any hooks, translation domains + * or add entries. It's only intend is, to use for GOptionContext.add_group(). + * + * Returns: the GOptionGroup wrapper. + */ +PyObject * +pyglib_option_group_new (GOptionGroup *group) +{ + PyGOptionGroup *self; + + self = (PyGOptionGroup *)PyObject_NEW(PyGOptionGroup, + &PyGOptionGroup_Type); + if (self == NULL) + return NULL; + + self->group = group; + self->other_owner = TRUE; + self->is_in_context = FALSE; + + return (PyObject *)self; +} + +/** + * pyglib_option_context_new: + * @context: a GOptionContext + * + * Returns: A new GOptionContext wrapper. + */ +PyObject * +pyglib_option_context_new (GOptionContext *context) +{ + PyGOptionContext *self; + + self = (PyGOptionContext *)PyObject_NEW(PyGOptionContext, + &PyGOptionContext_Type); + if (self == NULL) + return NULL; + + self->context = context; + self->main_group = NULL; + + return (PyObject *)self; +} + +/** + * pyglib_option_context_new: + * @context: a GTimeVal struct + * + * Converts a GTimeVal struct to a python float + * + * Returns: a float representing the timeval + */ +PyObject * +pyglib_float_from_timeval(GTimeVal timeval) +{ + double ret; + ret = (double)timeval.tv_sec + (double)timeval.tv_usec * 0.000001; + return PyFloat_FromDouble(ret); +} + + +/****** Private *****/ + +/** + * _pyglib_destroy_notify: + * @user_data: a PyObject pointer. + * + * A function that can be used as a GDestroyNotify callback that will + * call Py_DECREF on the data. + */ +void +_pyglib_destroy_notify(gpointer user_data) +{ + PyObject *obj = (PyObject *)user_data; + PyGILState_STATE state; + + g_return_if_fail (_PyGLib_API != NULL); + + state = pyglib_gil_state_ensure(); + Py_DECREF(obj); + pyglib_gil_state_release(state); +} + +gboolean +_pyglib_handler_marshal(gpointer user_data) +{ + PyObject *tuple, *ret; + gboolean res; + PyGILState_STATE state; + + g_return_val_if_fail(user_data != NULL, FALSE); + + state = pyglib_gil_state_ensure(); + + tuple = (PyObject *)user_data; + ret = PyObject_CallObject(PyTuple_GetItem(tuple, 0), + PyTuple_GetItem(tuple, 1)); + if (!ret) { + PyErr_Print(); + res = FALSE; + } else { + res = PyObject_IsTrue(ret); + Py_DECREF(ret); + } + + pyglib_gil_state_release(state); + + return res; +} + + |