diff options
author | James Henstridge <james@daa.com.au> | 2000-06-27 14:39:12 +0000 |
---|---|---|
committer | James Henstridge <jamesh@src.gnome.org> | 2000-06-27 14:39:12 +0000 |
commit | 3857fe92a26226de993b173a478d60bdadf32906 (patch) | |
tree | 3169c3a5114d5242bfbe74e37071f7aac9850bec /gobject/gobjectmodule.c | |
parent | 8a2c8306339d58d69bb50fd1c33194e907aecd2e (diff) | |
download | pygobject-3857fe92a26226de993b173a478d60bdadf32906.tar.gz pygobject-3857fe92a26226de993b173a478d60bdadf32906.tar.xz pygobject-3857fe92a26226de993b173a478d60bdadf32906.zip |
start of gobject wrapper.
2000-06-27 James Henstridge <james@daa.com.au>
* gobjectmodule.c (pygobject_dealloc): start of gobject wrapper.
Diffstat (limited to 'gobject/gobjectmodule.c')
-rw-r--r-- | gobject/gobjectmodule.c | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/gobject/gobjectmodule.c b/gobject/gobjectmodule.c new file mode 100644 index 0000000..de9e26d --- /dev/null +++ b/gobject/gobjectmodule.c @@ -0,0 +1,528 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- */ +#include <glib.h> +#include <gobject/gobject.h> +#include <gobject/gvaluetypes.h> +#include <gobject/genums.h> +#include <Python.h> +#include "ExtensionClass.h" + +static GHashTable *class_hash; + +static GQuark pygobject_wrapper_key = 0; +static GQuark pygobject_ownedref_key = 0; + +typedef struct { + PyObject_HEAD + GObject *obj; + gboolean hasref; /* the GObject owns this reference */ + PyObject *inst_dict; /* the instance dictionary -- must be last */ +} PyGObject; + +#define pygobject_get(v) (((PyGObject *)v)->obj) + +staticforward PyExtensionClass PyGObject_Type; +static void pygobject_dealloc(PyGObject *self); +static PyObject *pygobject_getattr(PyGObject *self, char *attr); +static int pygobject_setattr(PyGObject *self, char *attr, PyObject *val); +static int pygobject_compare(PyGObject *self, PyGObject *v); +static long pygobject_hash(PyGObject *self); +static PyObject *pygobject_repr(PyGObject *self); + +/* -------------- class <-> wrapper manipulation --------------- */ + +void +pygobject_destroy_notify(gpointer user_data) +{ + PyObject *obj = (PyObject *)user_data; + + /* PyGTK_BLOCK_THREADS */ + Py_DECREF(obj); + /* PyGTK_UNBLOCK_THREADS */ +} + +static void +pygobject_register_class(PyObject *dict, const gchar *class_name, + PyExtensionClass *ec, PyExtensionClass *parent) +{ + if (!class_hash) + class_hash = g_hash_table_new(g_str_hash, g_str_equal); + + /* set standard pyobject class functions if they aren't already set */ + if (!ec->tp_dealloc) ec->tp_dealloc = (destructor)pygobject_dealloc; + if (!ec->tp_getattr) ec->tp_getattr = (getattrfunc)pygobject_getattr; + if (!ec->tp_setattr) ec->tp_setattr = (setattrfunc)pygobject_setattr; + if (!ec->tp_compare) ec->tp_compare = (cmpfunc)pygobject_compare; + if (!ec->tp_repr) ec->tp_repr = (reprfunc)pygobject_repr; + if (!ec->tp_hash) ec->tp_hash = (hashfunc)pygobject_hash; + + if (parent) { + PyExtensionClass_ExportSubclassSingle(dict, (char *)class_name, + *ec, *parent); + } else { + PyExtensionClass_Export(dict, (char *)class_name, *ec); + } + + g_hash_table_insert(class_hash, g_strdup(class_name), ec); +} + +void +pygobject_register_wrapper(PyObject *self) +{ + GObject *obj = ((PyGObject *)self)->obj; + + g_object_ref(obj); + g_object_set_qdata(obj, pygobject_wrapper_key, self); +} + +static PyExtensionClass * +pygobject_lookup_class(GType type) +{ + PyExtensionClass *ec; + + /* find the python type for this object. If not found, use parent. */ + while ((ec = g_hash_table_lookup(class_hash, g_type_name(type))) == NULL + && type != 0) + type = g_type_parent(type); + g_assert(ec != NULL); + return ec; +} + +static PyObject * +pygobject_new(GObject *obj) +{ + PyGObject *self; + PyTypeObject *tp; + + if (obj == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + + /* we already have a wrapper for this object -- return it. */ + if ((self = (PyGObject *)g_object_get_qdata(obj, pygobject_wrapper_key))) { + /* if the GObject currently owns the wrapper reference ... */ + if (self->hasref) { + self->hasref = FALSE; + g_object_steal_qdata(obj, pygobject_ownedref_key); + g_object_ref(obj); + } + Py_INCREF(self); + return (PyObject *)self; + } + + tp = (PyTypeObject *)pygobject_lookup_class(G_TYPE_FROM_INSTANCE(obj)); + self = PyObject_NEW(PyGObject, tp); + + if (self == NULL) + return NULL; + self->obj = obj; + g_object_ref(obj); + /* save wrapper pointer so we can access it later */ + g_object_set_qdata(obj, pygobject_wrapper_key, self); + self->inst_dict = PyDict_New(); + + return (PyObject *)self; +} + +/* -------------- GValue marshalling ------------------ */ + +static gint +pyg_enum_get_value(GType enum_type, PyObject *obj, gint *val) +{ + GEnumClass *eclass = G_ENUM_CLASS(g_type_class_ref(enum_type)); + gint res = -1; + + g_return_val_if_fail(val != NULL, -1); + if (!obj) { + *val = 0; + res = 0; + } else if (PyInt_Check(obj)) { + *val = PyInt_AsLong(obj); + res = 0; + } else if (PyString_Check(obj)) { + char *str = PyString_AsString(obj); + GEnumValue *info = g_enum_get_value_by_name(eclass, str); + + if (!info) + info = g_enum_get_value_by_nick(eclass, str); + if (info) { + *val = info->value; + res = 0; + } else { + PyErr_SetString(PyExc_TypeError, "could not convert string"); + res = -1; + } + } else { + PyErr_SetString(PyExc_TypeError,"enum values must be strings or ints"); + res = -1; + } + g_type_class_unref(eclass); + return res; +} + +static gint +pyg_flags_get_value(GType flag_type, PyObject *obj, gint *val) +{ + GFlagsClass *fclass = G_FLAGS_CLASS(g_type_class_ref(flag_type)); + gint res = -1; + + g_return_val_if_fail(val != NULL, -1); + if (!obj) { + *val = 0; + res = 0; + } else if (PyInt_Check(obj)) { + *val = PyInt_AsLong(obj); + res = 0; + } else if (PyString_Check(obj)) { + char *str = PyString_AsString(obj); + GFlagsValue *info = g_flags_get_value_by_name(fclass, str); + + if (!info) + info = g_flags_get_value_by_nick(fclass, str); + if (info) { + *val = info->value; + res = 0; + } else { + PyErr_SetString(PyExc_TypeError, "could not convert string"); + res = -1; + } + } else if (PyTuple_Check(obj)) { + int i, len; + + len = PyTuple_Size(obj); + *val = 0; + res = 0; + for (i = 0; i < len; i++) { + PyObject *item = PyTuple_GetItem(obj, i); + char *str = PyString_AsString(item); + GFlagsValue *info = g_flags_get_value_by_name(fclass, str); + + if (!info) + info = g_flags_get_value_by_nick(fclass, str); + if (info) { + *val |= info->value; + } else { + PyErr_SetString(PyExc_TypeError, "could not convert string"); + res = -1; + break; + } + } + } else { + PyErr_SetString(PyExc_TypeError, + "flag values must be strings, ints or tuples"); + res = -1; + } + g_type_class_unref(fclass); + return res; +} + +static int +pyg_value_from_pyobject(GValue *value, PyObject *obj) +{ + PyObject *tmp; + + if (G_IS_VALUE_CHAR(value)) { + if ((tmp = PyObject_Str(obj))) + g_value_set_char(value, PyString_AsString(tmp)[0]); + else { + PyErr_Clear(); + return -1; + } + Py_DECREF(tmp); + } else if (G_IS_VALUE_UCHAR(value)) { + if ((tmp = PyObject_Str(obj))) + g_value_set_char(value, PyString_AsString(tmp)[0]); + else { + PyErr_Clear(); + return -1; + } + Py_DECREF(tmp); + } else if (G_IS_VALUE_BOOLEAN(value)) { + g_value_set_boolean(value, PyObject_IsTrue(obj)); + } else if (G_IS_VALUE_INT(value)) { + if ((tmp = PyNumber_Int(obj))) + g_value_set_int(value, PyInt_AsLong(tmp)); + else { + PyErr_Clear(); + return -1; + } + Py_DECREF(tmp); + } else if (G_IS_VALUE_UINT(value)) { + if ((tmp = PyNumber_Int(obj))) + g_value_set_uint(value, PyInt_AsLong(tmp)); + else { + PyErr_Clear(); + return -1; + } + Py_DECREF(tmp); + } else if (G_IS_VALUE_LONG(value)) { + if ((tmp = PyNumber_Int(obj))) + g_value_set_long(value, PyInt_AsLong(tmp)); + else { + PyErr_Clear(); + return -1; + } + Py_DECREF(tmp); + } else if (G_IS_VALUE_ULONG(value)) { + if ((tmp = PyNumber_Int(obj))) + g_value_set_ulong(value, PyInt_AsLong(tmp)); + else { + PyErr_Clear(); + return -1; + } + Py_DECREF(tmp); + } else if (G_IS_VALUE_FLOAT(value)) { + if ((tmp = PyNumber_Float(obj))) + g_value_set_float(value, PyFloat_AsDouble(tmp)); + else { + PyErr_Clear(); + return -1; + } + Py_DECREF(tmp); + } else if (G_IS_VALUE_DOUBLE(value)) { + if ((tmp = PyNumber_Float(obj))) + g_value_set_double(value, PyFloat_AsDouble(tmp)); + else { + PyErr_Clear(); + return -1; + } + Py_DECREF(tmp); + } else if (G_IS_VALUE_STRING(value)) { + if ((tmp = PyObject_Str(obj))) + g_value_set_string(value, PyString_AsString(tmp)); + else { + PyErr_Clear(); + return -1; + } + Py_DECREF(tmp); + } else if (G_IS_VALUE_OBJECT(value)) { + PyExtensionClass *ec =pygobject_lookup_class(G_VALUE_TYPE(value)); + if (!ExtensionClassSubclassInstance_Check(obj, ec)) { + return -1; + } + g_value_set_object(value, pygobject_get(obj)); + } else if (G_IS_VALUE_ENUM(value)) { + gint val = 0; + if (pyg_enum_get_value(G_VALUE_TYPE(value), obj, &val) < 0) + return -1; + g_value_set_enum(value, val); + } else if (G_IS_VALUE_FLAGS(value)) { + gint val = 0; + if (pyg_flags_get_value(G_VALUE_TYPE(value), obj, &val) < 0) + return -1; + g_value_set_flags(value, val); + } + return 0; +} + +static PyObject * +pyg_value_as_pyobject(GValue *value) +{ + if (G_IS_VALUE_CHAR(value)) { + gint8 val = g_value_get_char(value); + return PyString_FromStringAndSize((char *)&val, 1); + } else if (G_IS_VALUE_UCHAR(value)) { + guint8 val = g_value_get_uchar(value); + return PyString_FromStringAndSize((char *)&val, 1); + } else if (G_IS_VALUE_INT(value)) { + return PyInt_FromLong(g_value_get_int(value)); + } else if (G_IS_VALUE_UINT(value)) { + return PyInt_FromLong(g_value_get_uint(value)); + } else if (G_IS_VALUE_LONG(value)) { + return PyInt_FromLong(g_value_get_long(value)); + } else if (G_IS_VALUE_ULONG(value)) { + return PyInt_FromLong(g_value_get_ulong(value)); + } else if (G_IS_VALUE_FLOAT(value)) { + return PyFloat_FromDouble(g_value_get_float(value)); + } else if (G_IS_VALUE_DOUBLE(value)) { + return PyFloat_FromDouble(g_value_get_double(value)); + } else if (G_IS_VALUE_STRING(value)) { + return PyString_FromString(g_value_get_string(value)); + } else if (G_IS_VALUE_OBJECT(value)) { + return pygobject_new(g_value_get_object(value)); + } else if (G_IS_VALUE_ENUM(value)) { + return PyInt_FromLong(g_value_get_enum(value)); + } else if (G_IS_VALUE_FLAGS(value)) { + return PyInt_FromLong(g_value_get_flags(value)); + } + PyErr_SetString(PyExc_TypeError, "unknown type"); + return NULL; +} + +/* -------------- PyGObject behaviour ----------------- */ +static void +pygobject_dealloc(PyGObject *self) +{ + GObject *obj = self->obj; + + if (obj && !(((PyExtensionClass *)self->ob_type)->class_flags & + EXTENSIONCLASS_PYSUBCLASS_FLAG)) { + /* save reference to python wrapper if there are still + * references to the GObject in such a way that it will be + * freed when the GObject is destroyed, so is the python + * wrapper, but if a python wrapper can be */ + if (obj->ref_count > 1) { + Py_INCREF(self); /* grab a reference on the wrapper */ + self->hasref = TRUE; + g_object_set_qdata_full(obj, pygobject_ownedref_key, + self, pygobject_destroy_notify); + g_object_unref(obj); + return; + } + if (!self->hasref) /* don't unref the GObject if it owns us */ + g_object_unref(obj); + } + /* subclass_dealloc (ExtensionClass.c) does this for us for python + * subclasses */ + if (self->inst_dict && + !(((PyExtensionClass *)self->ob_type)->class_flags & + EXTENSIONCLASS_PYSUBCLASS_FLAG)) { + Py_DECREF(self->inst_dict); + } + PyMem_DEL(self); +} + +/* standard getattr method */ +static PyObject * +pygobject_getattr(PyGObject *self, char *attr) +{ + ExtensionClassImported; + + return Py_FindAttrString((PyObject *)self, attr); +} + +static int +pygobject_setattr(PyGObject *self, char *attr, PyObject *value) +{ + PyDict_SetItemString(INSTANCE_DICT(self), attr, value); + return 0; +} + +static int +pygobject_compare(PyGObject *self, PyGObject *v) +{ + if (self->obj == v->obj) return 0; + if (self->obj > v->obj) return -1; + return 1; +} + +static long +pygobject_hash(PyGObject *self) +{ + return (long)self->obj; +} + +static PyObject * +pygobject_repr(PyGObject *self) +{ + gchar buf[128]; + + g_snprintf(buf, sizeof(buf), "<%s at %lx>", G_OBJECT_TYPE_NAME(self->obj), + (long)self->obj); + return PyString_FromString(buf); +} + +/* ---------------- PyGObject methods ----------------- */ +static destructor real_subclass_dealloc = NULL; +static void +pygobject_subclass_dealloc(PyGObject *self) +{ + GObject *obj = self->obj; + + if (obj) { + /* save reference to python wrapper if there are still + * references to the GObject in such a way that it will be + * freed when the GObject is destroyed, so is the python + * wrapper, but if a python wrapper can be */ + if (obj->ref_count > 1) { + Py_INCREF(self); /* grab a reference on the wrapper */ + self->hasref = TRUE; + g_object_set_qdata_full(obj, pygobject_ownedref_key, + self, pygobject_destroy_notify); + g_object_unref(obj); + return; + } + if (!self->hasref) /* don't unref the GObject if it owns us */ + g_object_unref(obj); + } + if (real_subclass_dealloc) + (* real_subclass_dealloc)((PyObject *)self); +} +/* more hackery to stop segfaults caused by multi deallocs on a subclass + * (which happens quite regularly in pygobject) */ +static PyObject * +pygobject__class_init__(PyObject *something, PyObject *args) +{ + PyExtensionClass *subclass; + + if (!PyArg_ParseTuple(args, "O:GObject.__class_init__", &subclass)) + return NULL; + g_message("__class_init__ called for %s", subclass->tp_name); + if ((subclass->class_flags & EXTENSIONCLASS_PYSUBCLASS_FLAG) && + subclass->tp_dealloc != (destructor)pygobject_subclass_dealloc) { + real_subclass_dealloc = subclass->tp_dealloc; + subclass->tp_dealloc = (destructor)pygobject_subclass_dealloc; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef pygobject_methods[] = { + { "__class_init__", (PyCFunction)pygobject__class_init__, METH_VARARGS|METH_CLASS_METHOD }, + { NULL, NULL, 0 } +}; + +static PyExtensionClass PyGObject_Type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "GObject", /* tp_name */ + sizeof(PyGObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)pygobject_dealloc, /* tp_dealloc */ + (printfunc)0, /* tp_print */ + (getattrfunc)pygobject_getattr, /* tp_getattr */ + (setattrfunc)pygobject_setattr, /* tp_setattr */ + (cmpfunc)pygobject_compare, /* tp_compare */ + (reprfunc)pygobject_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)pygobject_hash, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)0, /* tp_str */ + (getattrofunc)0, /* tp_getattro */ + (setattrofunc)0, /* tp_setattro */ + /* Space for future expansion */ + 0L, 0L, + NULL, /* Documentation string */ + METHOD_CHAIN(pygobject_methods), + EXTENSIONCLASS_INSTDICT_FLAG, +}; + +/* ---------------- gobject module functions -------------------- */ + +static PyMethodDef pygobject_functions[] = { + { NULL, NULL, 0 } +}; + + +/* ----------------- gobject module initialisation -------------- */ + +DL_EXPORT(void) +initgobject(void) +{ + PyObject *m, *d; + + m = Py_InitModule("gobject", pygobject_functions); + d = PyModule_GetDict(m); + + g_type_init(); + pygobject_register_class(d, "GObject", &PyGObject_Type, NULL); + + pygobject_wrapper_key = g_quark_from_static_string("py-gobject-wrapper"); + pygobject_ownedref_key = g_quark_from_static_string("py-gobject-ownedref"); + + if (PyErr_Occurred()) + Py_FatalError("can't initialise module gobject"); +} |