Commits

Lenard Lindstrom committed bf94911

For Surface.get_view, move surface locking to View object

A surface is locked when an array interface is exported,
not when the View is created. Also, the capsule is created by
the View object when needed, rather than being passed to the
View constructor. This is in preparation to adding the new buffer
interface to View.

  • Participants
  • Parent commits 98e3a61

Comments (0)

Files changed (6)

 #include "pgcompat.h"
 #include "pgview.h"
 
-typedef struct {
-    PyObject_HEAD
-    PyObject *capsule;            /* Wrapped array struct            */
-    PyObject *parent;             /* Object responsible for the view */
-    PgView_Destructor destructor; /* Optional release callback       */
-    PyObject *pydestructor;       /* Python callable destructor      */
-    PyObject *weakrefs;           /* There can be reference cycles   */
-} PgViewObject;
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+#define VIEW_MY_ENDIAN '<'
+#define VIEW_OTHER_ENDIAN '>'
+#else
+#define VIEW_MY_ENDIAN '>'
+#define VIEW_OTHER_ENDIAN '<'
+#endif
 
 static int Pg_GetArrayInterface(PyObject *, PyObject **, PyArrayInterface **);
 static PyObject *Pg_ArrayStructAsDict(PyArrayInterface *);
 /**
  * Helper functions.
  */
+static int
+_view_null_prelude(PyObject *view) {
+    return 0;
+}
+
 static void
-_view_default_destructor(PyObject *view) {
+_view_null_postscript(PyObject *view) {
+    return;
+}
+
+static int
+_view_python_prelude(PyObject *view)
+{
+    PgViewObject *v = (PgViewObject *)view;
+    PyObject *rvalue;
+    int failed = 0;
+    
+    rvalue = PyObject_CallFunctionObjArgs(v->pyprelude, v->parent, 0);
+    if (rvalue) {
+        Py_DECREF(rvalue);
+    }
+    else {
+        failed = -1;
+    }
+    return failed;
+}
+
+static void
+_view_python_postscript(PyObject *view)
+{
     PgViewObject *v = (PgViewObject *)view;
     PyObject *rvalue;
 
-    if (v->pydestructor) {
-        rvalue = PyObject_CallFunctionObjArgs(v->pydestructor,
-                                              v->capsule,
-                                              v->parent,
-                                              0);
-        PyErr_Clear();
-        Py_XDECREF(rvalue);
-    }
+    rvalue = PyObject_CallFunctionObjArgs(v->pypostscript, v->parent, 0);
+    PyErr_Clear();
+    Py_XDECREF(rvalue);
 }
 
 static PyObject *
 _view_new_from_type(PyTypeObject *type,
-                    PyObject *capsule,
+                    PyArrayInterface *inter_p,
                     PyObject *parent,
-                    PgView_Destructor destructor,
-                    PyObject *pydestructor)
+                    PgView_PreludeCallback prelude,
+                    PgView_PostscriptCallback postscript,
+                    PyObject *pyprelude,
+                    PyObject *pypostscript)
 {
-    PgViewObject *self = (PgViewObject *)type->tp_alloc(type, 0);
+    int nd = inter_p->nd;
+    PgViewObject *self;
+    Py_intptr_t *intmem;
+    int i;
 
-    if (!self) {
+    if (inter_p->two != 2) {
+        PyErr_SetString(PyExc_SystemError,
+                        "pygame: _view_new_from_type:"
+                        " array interface two.");
         return 0;
     }
+    if ((inter_p->flags & PAI_ARR_HAS_DESCR) && !inter_p->descr) {
+        PyErr_SetString(PyExc_SystemError,
+                        "pygame: _view_new_from_type:"
+                        " array interface descr");
+        return 0;
+    }
+    
+    intmem = PyMem_New(Py_intptr_t, (inter_p->strides ? 2 : 1) * nd);
+    if (!intmem) {
+        return PyErr_NoMemory();
+    }
+    
+    self = (PgViewObject *)type->tp_alloc(type, 0);
+    if (!self) {
+        PyMem_Free(intmem);
+        return 0;
+    }
+    
     self->weakrefs = 0;
-    Py_INCREF(capsule);
-    self->capsule = capsule;
+    self->inter.two = 2;
+    self->inter.nd = nd;
+    self->inter.typekind = inter_p->typekind;
+    self->inter.itemsize = inter_p->itemsize;
+    self->inter.flags = inter_p->flags;
+    self->inter.shape = intmem;
+    for (i = 0; i < nd; ++i) {
+        intmem[i] = inter_p->shape[i];
+    }
+    if (inter_p->strides) {
+        intmem += nd;
+        self->inter.strides = intmem;
+        for (i = 0; i < nd; ++i) {
+            intmem[i] = inter_p->strides[i];
+        }
+    }
+    else {
+        inter_p->strides = 0;
+    }
+    self->inter.data = inter_p->data;
+    if (inter_p->flags & PAI_ARR_HAS_DESCR) {
+        Py_INCREF(inter_p->descr);
+        self->inter.descr = inter_p->descr;
+    }
+    else {
+        self->inter.descr = 0;
+    }
     if (!parent) {
         parent = Py_None;
     }
     Py_INCREF(parent);
     self->parent = parent;
-    self->destructor = destructor ? destructor : _view_default_destructor;
-    Py_XINCREF(pydestructor);
-    self->pydestructor = pydestructor;
+    self->prelude = _view_null_prelude;
+    if (pyprelude) {
+        Py_INCREF(pyprelude);
+        self->prelude = _view_python_prelude;
+    }
+    else if (prelude) {
+        self->prelude = prelude;
+    }
+    self->pyprelude = pyprelude;
+    self->postscript = _view_null_postscript;
+    if (pypostscript) {
+        Py_INCREF(pypostscript);
+        self->postscript = _view_python_postscript;
+    }
+    else if (postscript) {
+        self->postscript = postscript;
+    }
+    self->pypostscript = pypostscript;
+    self->global_release = 0;
     return (PyObject *)self;
 }
 
     return shapeobj;
 }
 
-#if SDL_BYTEORDER == SDL_LIL_ENDIAN
-#define MY_ENDIAN '<'
-#define OTHER_ENDIAN '>'
-#else
-#define MY_ENDIAN '>'
-#define OTHER_ENDIAN '<'
-#endif
-
 static PyObject *
 _typekind_as_str(PyArrayInterface *inter_p)
 {
     return Text_FromFormat("%c%c%i", 
-                           inter_p->flags & PAI_NOTSWAPPED ?
-                           MY_ENDIAN : OTHER_ENDIAN,
+                           inter_p->itemsize > 1 ?
+                               (inter_p->flags & PAI_NOTSWAPPED ?
+                                    VIEW_MY_ENDIAN :
+                                    VIEW_OTHER_ENDIAN) :
+                               '|',
                            inter_p->typekind, inter_p->itemsize);
 }
 
-#undef MY_ENDIAN
-#undef OTHER_ENDIAN
-
-static PyObject *_strides_as_tuple(PyArrayInterface *inter_p)
+static PyObject *
+_strides_as_tuple(PyArrayInterface *inter_p)
 {
     PyObject *stridesobj = PyTuple_New((Py_ssize_t)inter_p->nd);
     PyObject *lengthobj;
     return stridesobj;
 }
 
-static PyObject *_data_as_tuple(PyArrayInterface *inter_p)
+static PyObject *
+_data_as_tuple(PyArrayInterface *inter_p)
 {
     long readonly = (inter_p->flags & PAI_WRITEABLE) == 0;
 
                          PyBool_FromLong(readonly));
 }
 
+static int
+_tuple_as_ints(PyObject *o,
+               const char *keyword,
+               Py_ssize_t **array,
+               int *length)
+{
+    /* Convert o as a C array of integers and return 1, otherwise
+     * raise a Python exception and return 0. keyword is the function
+     * argument name to use in the exception.
+     */
+    Py_ssize_t i, n;
+    Py_ssize_t *a;
+    
+    if (!PyTuple_Check(o)) {
+        PyErr_Format(PyExc_TypeError,
+                     "Expected a tuple for argument %s: found %s",
+                     keyword, Py_TYPE(o)->tp_name);
+        return 0;
+    }
+    n = PyTuple_GET_SIZE(o);
+    a = PyMem_New(Py_intptr_t, n);
+    for (i = 0; i < n; ++i) {
+        a[i] = PyInt_AsSsize_t(PyTuple_GET_ITEM(o, i));
+        if (a[i] == -1 && PyErr_Occurred() /* conditional && */) {
+            PyMem_Free(a);
+            PyErr_Format(PyExc_TypeError,
+                         "%s tuple has a non-integer at position %d",
+                         keyword, (int)i);
+            return 0;
+        }
+    }
+    *array = a;
+    *length = n;
+    return 1;
+}
+
+static int
+_shape_arg_convert(PyObject *o, void *a)
+{
+    PyArrayInterface *inter_p = (PyArrayInterface *)a;
+    
+    if (!_tuple_as_ints(o, "shape", &(inter_p->shape), &(inter_p->nd))) {
+        return 0;
+    }
+    return 1;
+}
+
+static int
+_typestr_arg_convert(PyObject *o, void *a)
+{
+    /* Due to incompatibilities between the array and new buffer interfaces,
+     * as well as significant unicode changes in Python 3.3, this will
+     * only handle integer types.
+     */
+    PyArrayInterface *inter_p = (PyArrayInterface *)a;
+    int flags = inter_p->flags;
+    char typekind;
+    int itemsize;
+    PyObject *s;
+    const char *typestr;
+    
+    if (PyUnicode_Check(o)) {
+        s = PyUnicode_AsASCIIString(o);
+        if (!s) {
+            return 0;
+        }
+    }
+    else {
+        Py_INCREF(o);
+        s = o;
+    }
+    if (!Bytes_Check(s)) {
+        PyErr_Format(PyExc_TypeError, "Expected a string for typestr: got %s",
+                     Py_TYPE(s)->tp_name);
+        Py_DECREF(s);
+        return 0;
+    }
+    if (Bytes_GET_SIZE(s) != 3) {
+        PyErr_SetString(PyExc_TypeError, "Expected typestr to be length 3");
+        Py_DECREF(s);
+        return 0;
+    }
+    typestr = Bytes_AsString(s);
+    switch (typestr[0]) {
+
+    case VIEW_MY_ENDIAN:
+        flags |= PAI_NOTSWAPPED;
+        break;
+    case VIEW_OTHER_ENDIAN:
+        break;
+    case '|':
+        break;
+    default:
+        PyErr_Format(PyExc_ValueError,
+                     "unknown byteorder character %c in typestr",
+                     typestr[0]);
+        Py_DECREF(s);
+        return 0;
+    }
+    typekind = typestr[1];
+    switch (typekind) {
+
+    case 'i':
+        break;
+    case 'u':
+        break;
+    default:
+        PyErr_Format(PyExc_ValueError,
+                     "unsupported typekind %c in typestr",
+                     typekind);
+        Py_DECREF(s);
+        return 0;
+    }
+    switch (typestr[2]) {
+
+    case '1':
+        itemsize = 1;
+        break;
+    case '2':
+        itemsize = 2;
+        break;
+    case '3':
+        itemsize = 3;
+        break;
+    case '4':
+        itemsize = 4;
+        break;
+    case '6':
+        itemsize = 6;
+        break;
+    case '8':
+        itemsize = 8;
+        break;
+    default:
+        PyErr_Format(PyExc_ValueError,
+                     "unsupported size %c in typestr",
+                     typestr[2]);
+        Py_DECREF(s);
+        return 0;
+    }
+    inter_p->typekind = typekind;
+    inter_p->itemsize = itemsize;
+    inter_p->flags = flags;
+    return 1;
+}
+
+static int
+_strides_arg_convert(PyObject *o, void *a)
+{
+    /* Must be called after the array interface nd field has been filled in.
+     */
+    PyArrayInterface *inter_p = (PyArrayInterface *)a;
+    int n = 0;
+
+    if (o == Py_None) {
+        return 1; /* no strides (optional) given */
+    }
+    if (!_tuple_as_ints(o, "strides", &(inter_p->strides), &n)) {
+        return 0;
+    }
+    if (n != inter_p->nd) {
+        PyErr_SetString(PyExc_TypeError,
+                        "strides and shape tuple lengths differ");
+        return 0;
+    }
+    return 1;
+}
+
+static int
+_data_arg_convert(PyObject *o, void *a)
+{
+    PyArrayInterface *inter_p = (PyArrayInterface *)a;
+    Py_ssize_t address;
+    int readonly;
+    
+    if (!PyTuple_Check(o)) {
+        PyErr_Format(PyExc_TypeError, "expected a tuple for data: got %s",
+                     Py_TYPE(o)->tp_name);
+        return 0;
+    }
+    if (PyTuple_GET_SIZE(o) != 2) {
+        PyErr_SetString(PyExc_TypeError, "expected a length 2 tuple for data");
+        return 0;
+    }
+    address = PyInt_AsSsize_t(PyTuple_GET_ITEM(o, 0));
+    if (address == -1 && PyErr_Occurred()) {
+        PyErr_Clear();
+        PyErr_Format(PyExc_TypeError,
+                     "expected an integer address for data item 0: got %s",
+                     Py_TYPE(PyTuple_GET_ITEM(o, 0))->tp_name);
+        return 0;
+    }
+    readonly = PyObject_IsTrue(PyTuple_GET_ITEM(o, 1));
+    if (readonly == -1) {
+        PyErr_Clear();
+        PyErr_Format(PyExc_TypeError,
+                     "expected a boolean flag for data item 1: got %s",
+                     Py_TYPE(PyTuple_GET_ITEM(o, 0))->tp_name);
+        return 0;
+    }
+    inter_p->data = (void *)address;
+    inter_p->flags |= readonly ? 0 : PAI_WRITEABLE;
+    return 1;
+}
+
+static void
+_free_inter(PyArrayInterface *inter_p)
+{
+    if (inter_p->shape) {
+        PyMem_Free(inter_p->shape);
+    }
+    if (inter_p->strides) {
+        PyMem_Free(inter_p->strides);
+    }
+}
+
 /**
- * Creates a new PgViewObject.
+ * Return a new PgViewObject (Python level constructor).
  */
 static PyObject *
 _view_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
-    PyObject *capsule;
+    PyArrayInterface inter = {0, 0, '\0', 0, 0, 0, 0, 0, 0};
     PyObject *parent = 0;
-    PyObject *pydestructor = 0;
-    char *keywords[] = {"capsule", "parent", "destructor", 0};
-
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO:View", keywords,
-                                     &capsule, &parent, &pydestructor)) {
+    PyObject *pyprelude = 0;
+    PyObject *pypostscript = 0;
+    void *inter_vp = (void *)&inter;
+    PyObject *self = 0;
+    /* The argument evaluation order is important: strides must follow shape. */
+    char *keywords[] = {"shape", "typestr", "data", "strides", "parent",
+                        "prelude", "postscript", 0};
+                        
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O&O&|O&OOO:View", keywords,
+                                     _shape_arg_convert, inter_vp,
+                                     _typestr_arg_convert, inter_vp,
+                                     _data_arg_convert, inter_vp,
+                                     _strides_arg_convert, inter_vp,
+                                     &parent, &pyprelude, &pypostscript)) {
+        _free_inter(&inter);
         return 0;
     }
-    if (pydestructor == Py_None) {
-        pydestructor = 0;
+    if (parent == Py_None) {
+        parent = 0;
     }
-    return _view_new_from_type(type,
-                               capsule,
+    if (pyprelude == Py_None) {
+        pyprelude = 0;
+    }
+    if (pypostscript == Py_None) {
+        pypostscript = 0;
+    }
+    inter.two = 2;
+    self = _view_new_from_type(type,
+                               &inter,
                                parent,
                                0,
-                               pydestructor);
+                               0,
+                               pyprelude,
+                               pypostscript);
+    _free_inter(&inter);
+    return self;
 }
 
 /**
 static void
 _view_dealloc(PgViewObject *self)
 {
-    PgView_Destructor destructor = self->destructor;
-
     /* Guard against recursion */
-    self->destructor = 0;
-    if (!destructor) {
+    if (self->inter.two == 0) {
         return;
     }
-
-    destructor((PyObject *)self);
-    Py_DECREF(self->capsule);
+    self->inter.two = 0;
+    
+    if (self->global_release) {
+        self->postscript((PyObject *)self);
+    }
     Py_DECREF(self->parent);
-    Py_XDECREF(self->pydestructor);
+    Py_XDECREF(self->pyprelude);
+    Py_XDECREF(self->pypostscript);
     if (self->weakrefs) {
         PyObject_ClearWeakRefs((PyObject *)self);
     }
+    PyMem_Free(self->inter.shape);
     Py_TYPE(self)->tp_free(self);
 }
 
 /**** Getter and setter access ****/
+#if PY3
+#define Capsule_New(p) PyCapsule_New((p), 0, 0);
+#else
+#define Capsule_New(p) PyCObject_FromVoidPtr((p), 0);
+#endif
 
 static PyObject *
 _view_get_arraystruct(PgViewObject *self, PyObject *closure)
 {
-    Py_INCREF(self->capsule);
-    return self->capsule;
+    PyObject *capsule = Capsule_New(&self->inter);
+
+    if (capsule && !self->global_release /* conditional && */ ) {
+        if (self->prelude((PyObject *)self)) {
+            Py_DECREF(capsule);
+            capsule = 0;
+        }
+        else {
+            self->global_release = 1;
+        }
+    }
+    return capsule;
 }
 
 static PyObject *
-_view_get_arrayinterface(PyObject *self, PyObject *closure)
+_view_get_arrayinterface(PgViewObject *self, PyObject *closure)
 {
-    PyObject *cobj = 0;
-    PyArrayInterface *inter_p = 0;
-    PyObject *dict;
+    PyObject *dict = Pg_ArrayStructAsDict(&self->inter);
     
-    if (Pg_GetArrayInterface(self, &cobj, &inter_p)) {
-        return 0;
+    if (dict && !self->global_release) {
+        if (self->prelude((PyObject *)self)) {
+            Py_DECREF(dict);
+            dict = 0;
+        }
+        else {
+            self->global_release = 1;
+        }
     }
-    
-    dict = Pg_ArrayStructAsDict(inter_p);
-    Py_DECREF(cobj);
     return dict;
 }
 
 static PyObject *
 _view_repr (PgViewObject *self)
 {
-    return Text_FromFormat("<%s(%p)>", Py_TYPE(self)->tp_name, self->capsule);
+    return Text_FromFormat("<%s(%p)>", Py_TYPE(self)->tp_name, self);
 }
 
 /**
 /**** Public C api ***/
 
 static PyObject *
-PgView_New(PyObject *capsule,
+PgView_New(PyArrayInterface *inter_p,
            PyObject *parent,
-           PgView_Destructor destructor)
+           PgView_PreludeCallback prelude,
+           PgView_PostscriptCallback postscript)
 {
-    if (!capsule) {
-        PyErr_SetString(PyExc_TypeError, "the capsule argument is required");
-        return 0;
-    }
-    return _view_new_from_type(&PgView_Type, capsule, parent, destructor, 0);
-}
-
-static PyObject *
-PgView_GetCapsule(PyObject *view)
-{
-    PyObject *capsule = ((PgViewObject *)view)->capsule;
-
-    Py_INCREF(capsule);
-    return capsule;
-}
-
-static PyObject *
-PgView_GetParent(PyObject *view)
-{
-    PyObject *parent = ((PgViewObject *)view)->parent;
-
-    Py_INCREF(parent);
-    return parent;
+    return _view_new_from_type(&PgView_Type,
+                               inter_p,
+                               parent,
+                               prelude,
+                               postscript,
+                               0,
+                               0);
 }
 
 static int
         DECREF_MOD(module);
         MODINIT_ERROR;
     }
-#if PYGAMEAPI_VIEW_NUMSLOTS != 6
+#if PYGAMEAPI_VIEW_NUMSLOTS != 4
 #error export slot count mismatch
 #endif
     c_api[0] = &PgView_Type;
     c_api[1] = PgView_New;
-    c_api[2] = PgView_GetCapsule;
-    c_api[3] = PgView_GetParent;
-    c_api[4] = Pg_GetArrayInterface;
-    c_api[5] = Pg_ArrayStructAsDict;
+    c_api[2] = Pg_GetArrayInterface;
+    c_api[3] = Pg_ArrayStructAsDict;
     apiobj = encapsulate_api(c_api, "_view");
     if (apiobj == NULL) {
         DECREF_MOD(module);
                          start, stop, step, slicelength)
 #endif
 
+/* Python 2.4 (PEP 353) ssize_t */
+#if PY_VERSION_HEX < 0x02050000
+#define PyInt_AsSsize_t PyInt_AsLong
+#endif
+
 #endif /* #if !defined(PGCOMPAT_H) */
 
 #include "pgarrinter.h"
 
-typedef void (*PgView_Destructor)(PyObject *view);
+typedef int (*PgView_PreludeCallback)(PyObject *);
+typedef void (*PgView_PostscriptCallback)(PyObject *);
 
-#define PYGAMEAPI_VIEW_NUMSLOTS 6
+typedef struct PgViewObject_s {
+    PyObject_HEAD
+    PyArrayInterface inter;
+    PyObject *parent;                     /* Object responsible for the view  */
+    PgView_PreludeCallback prelude;       /* Lock callback                    */
+    PgView_PostscriptCallback postscript; /* Release callback                 */
+    PyObject *pyprelude;                  /* Python lock callable             */
+    PyObject *pypostscript;               /* Python release callback          */
+    int global_release;                   /* dealloc callback flag            */
+    PyObject *weakrefs;                   /* There can be reference cycles    */
+} PgViewObject;
+
+#define PYGAMEAPI_VIEW_NUMSLOTS 4
 #define PYGAMEAPI_VIEW_FIRSTSLOT 0
 
 #if !(defined(PYGAMEAPI_VIEW_INTERNAL) || defined(NO_PYGAME_C_API))
 static void *PgVIEW_C_API[PYGAMEAPI_VIEW_NUMSLOTS];
 
-typedef PyObject *(*_pgview_new_t)(PyObject *capsule,
+typedef PyObject *(*_pgview_new_t)(PyArrayInterface *inter_p,
                                    PyObject *parent,
-                                   PgView_Destructor dest);
+                                   PgView_PreludeCallback prelude,
+                                   PgView_PostscriptCallback postscript);
 typedef PyObject *(*_pgview_get_t)(PyObject *view);
 typedef int *(*_pg_getarrayinterface_t)(PyObject *obj,
                                         PyObject **cobj_p,
 
 #define PgView_Type (*(PyTypeObject*)PgVIEW_C_API[0])
 #define PgView_New (*(_pgview_new_t)PgVIEW_C_API[1])
-#define PgView_GetCapsule (*(_pgview_get_t)PgVIEW_C_API[2])
-#define PgView_GetParent (*(_pgview_get_t)PgVIEW_C_API[3])
-#define Pg_GetArrayInterface (*(_pg_getarrayinterface_t)PgVIEW_C_API[4])
-#define Pg_ArrayStructAsDict (*(_pg_arraystructasdict_t)PgVIEW_C_API[5])
+#define Pg_GetArrayInterface (*(_pg_getarrayinterface_t)PgVIEW_C_API[2])
+#define Pg_ArrayStructAsDict (*(_pg_arraystructasdict_t)PgVIEW_C_API[3])
 #define PgView_Check(x) ((x)->ob_type == (PgView_Type))
 #define import_pygame_view() \
     _IMPORT_PYGAME_MODULE(_view, VIEW, PgVIEW_C_API)
 
 #endif /* #if !(defined(PYGAMEAPI_VIEW_INTERNAL) || ... */
 
+#define PgView_GET_PYARRAYINTERFACE(o) (&(((PgViewObject *)(o))->inter))
+#define PgView_GET_PARENT(o) (((PgViewObject *)(o))->parent)
+
 #define PG_VIEW_HEADER
 
 #endif /* #if !defined(PG_VIEW_HEADER) */
 static PyObject *surf_get_pixels_address (PyObject *self,
                                           PyObject *closure);
 static int surf_view_kind(PyObject *obj, void *view_kind_vptr);
-#if PY3
-static void surf_arraystruct_capsule_destr(PyObject *capsule);
-#endif
-static void surf_view_destr(PyObject *view);
+static int _surf_view_prelude(PyObject *view);
+static void _surf_view_postscript(PyObject *view);
 static PyObject *_raise_get_view_ndim_error(int bitsize, SurfViewKind kind);
 
 
 {
 #   define SURF_GET_VIEW_MAXDIM 3
     const int lilendian = (SDL_BYTEORDER == SDL_LIL_ENDIAN);
-    const int maxdim = SURF_GET_VIEW_MAXDIM; 
-    typedef struct {
-        PyArrayInterface inter;
-        Py_intptr_t shape_mem[SURF_GET_VIEW_MAXDIM];
-        Py_intptr_t strides_mem[SURF_GET_VIEW_MAXDIM];
-    } Interface;
+    const int maxdim = SURF_GET_VIEW_MAXDIM;
+    Py_intptr_t shape[SURF_GET_VIEW_MAXDIM];
+    Py_intptr_t strides[SURF_GET_VIEW_MAXDIM];
+    PyArrayInterface inter = {0, 0, 'u', 0,
+                              PAI_ALIGNED | PAI_NOTSWAPPED | PAI_WRITEABLE,
+                              shape, strides, 0, 0};
 #   undef SURF_GET_VIEW_MAXDIM
     SurfViewKind view_kind = VIEWKIND_2D;
     int ndim = maxdim;
-    PyObject *capsule;
-    Interface *allocation;
-    PyArrayInterface *inter;
     SDL_Surface *surface = PySurface_AsSurface(self);
     int pixelsize;
     int itemsize = 0;
-    Py_intptr_t *shape;
-    Py_intptr_t *strides;
     Uint8 *startpixel;
     Uint32 mask = 0;
     int pixelstep;
     
-    PyObject *view;
     char *keywords[] = {"kind", 0};
 
     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&", keywords,
         return RAISE(PyExc_SDLError, "display Surface quit");
     }
 
-    allocation = PyMem_New(Interface, 1);
-    if (!allocation) {
-        return PyErr_NoMemory();
-    }
-    inter = (PyArrayInterface *)allocation;
-    inter->shape = allocation->shape_mem;
-    inter->strides = allocation->strides_mem;
-#if PY3
-    capsule = PyCapsule_New(inter, 0, surf_arraystruct_capsule_destr);
-#else
-    capsule = PyCObject_FromVoidPtr(inter, PyMem_Free);
-#endif
-    if (!capsule) {
-        PyMem_Free(inter);
-        return 0;
-    }
-    view = PgView_New(capsule, self, surf_view_destr);
-    Py_DECREF(capsule);
-    if (!view) {
-        return 0;
-    }
-    if (!PySurface_LockBy(self, view)) {
-        Py_DECREF(view);
-        return 0;
-    }
-
     startpixel = surface->pixels;
     pixelsize = surface->format->BytesPerPixel;
-    inter->two = 2;
-    inter->typekind = 'u';
-    inter->flags = PAI_ALIGNED | PAI_NOTSWAPPED | PAI_WRITEABLE;
-    inter->descr = 0;
-    shape = inter->shape;
-    strides = inter->strides;
     shape[0] = (Py_intptr_t)surface->w;
     shape[1] = (Py_intptr_t)surface->h;
     strides[0] = (Py_intptr_t)pixelsize;
         ndim = 2;
         itemsize = pixelsize;
         if (strides[1] == shape[0] * pixelsize) {
-            inter->flags |= PAI_CONTIGUOUS;
+            inter.flags |= PAI_CONTIGUOUS;
         }
         break;
     case VIEWKIND_3D:
         if (pixelsize < 3) {
-            Py_DECREF(view);
             return _raise_get_view_ndim_error(pixelsize * 8, view_kind);
         }
         ndim = 3;
             startpixel += lilendian ? 0 : (pixelsize - 1);
         }
         else {
-            Py_DECREF(view);
             return RAISE(PyExc_ValueError,
                          "unsupport colormasks for 3D reference array");
         }
         mask = surface->format->Amask;
         break;
     default:
-        Py_DECREF(view);
         PyErr_Format(PyExc_SystemError,
                      "pygame bug in surf_get_view:"
                      " unrecognized view kind %d", (int)view_kind);
             startpixel += lilendian ? 3 : 0;
             break;
         default:
-            Py_DECREF(view);
             return RAISE(PyExc_ValueError,
                          "unsupported colormasks for alpha reference array");
         }
     }
     if (ndim < 3) {
-        inter->flags |= PAI_FORTRAN;
+        inter.flags |= PAI_FORTRAN;
     }
-    inter->nd = ndim;
-    inter->itemsize = itemsize;
-    inter->data = startpixel;
-
-    return view;
+    inter.nd = ndim;
+    inter.itemsize = itemsize;
+    inter.data = startpixel;
+    inter.two = 2;
+    
+    return PgView_New(&inter, self, _surf_view_prelude, _surf_view_postscript);
 }
 
 static int
     return 1;
 }
 
-#if PY3
-static void
-surf_arraystruct_capsule_destr(PyObject *capsule)
+static int
+_surf_view_prelude(PyObject *view)
 {
-    PyMem_Free(PyCapsule_GetPointer(capsule, 0));
+    PyObject *surf = PgView_GET_PARENT(view);
+    
+    return PySurface_LockBy(surf, view) ? 0 : -1;
 }
-#endif
 
 static void
-surf_view_destr(PyObject *view)
+_surf_view_postscript(PyObject *view)
 {
-    PyObject *surf = PgView_GetParent(view);
+    PyObject *surf = PgView_GET_PARENT(view);
 
     PySurface_UnlockBy(surf, view);
-    Py_DECREF(surf);
 }
 
 static PyObject *

test/_view_test.py

 import sys
 import re
 import weakref
+import gc
 if __name__ == '__main__':
     import os
     pkg_dir = os.path.split(os.path.abspath(__file__))[0]
 from pygame._view import View
 
 class ViewTest(unittest.TestCase):
+    view_keywords = {'shape': (5, 4, 3),
+                     'typestr': '|u1',
+                     'data': (0, True),
+                     'strides': (4, 20, 1)}
+
     def test___array_struct___property(self):
-        c = []
-        v = View(c)
-        self.assert_(v.__array_struct__ is c)
+        kwds = self.view_keywords
+        v = View(**kwds)
+        d = pygame._view.get_array_interface(v)
+        self.assertEqual(len(d), 5)
+        self.assertEqual(d['version'], 3)
+        self.assertEqual(d['shape'], kwds['shape'])
+        self.assertEqual(d['typestr'], kwds['typestr'])
+        self.assertEqual(d['data'], kwds['data'])
+        self.assertEqual(d['strides'], kwds['strides'])
+
+    def test___array_interface___property(self):
+        kwds = self.view_keywords
+        v = View(**kwds)
+        d = v.__array_interface__
+        self.assertEqual(len(d), 5)
+        self.assertEqual(d['version'], 3)
+        self.assertEqual(d['shape'], kwds['shape'])
+        self.assertEqual(d['typestr'], kwds['typestr'])
+        self.assertEqual(d['data'], kwds['data'])
+        self.assertEqual(d['strides'], kwds['strides'])
 
     def test_parent_property(self):
-        c = []
         p = []
-        v = View(c, p)
+        v = View(parent=p, **self.view_keywords)
         self.assert_(v.parent is p)
+    
+    def test_prelude(self):
+        def callback(parent):
+            success.append(parent is p)
+
+        class MyException(Exception):
+            pass
+
+        def raise_exception(parent):
+            raise MyException("Just a test.")
+
+        p = []
         
-    def test_destructor(self):
-        def d(capsule, parent):
-            success.append(capsule is c)
-            success.append(parent is None)
+        # For array interface
+        success = []
+        v = View(parent=p, prelude=callback, **self.view_keywords)
+        self.assertEqual(len(success), 0)
+        d = v.__array_interface__
+        self.assertEqual(len(success), 1)
+        self.assertTrue(success[0])
+        d = v.__array_interface__
+        self.assertEqual(len(success), 1)
+        d = v = None
+        gc.collect()
+        self.assertEqual(len(success), 1)
+
+        # For array struct
+        success = []
+        v = View(parent=p, prelude=callback, **self.view_keywords)
+        self.assertEqual(len(success), 0)
+        c = v.__array_struct__
+        self.assertEqual(len(success), 1)
+        self.assertTrue(success[0])
+        c = v.__array_struct__
+        self.assertEqual(len(success), 1)
+        c = v = None
+        gc.collect()
+        self.assertEqual(len(success), 1)
         
+        # Callback raises an exception
+        v = View(prelude=raise_exception, **self.view_keywords)
+        self.assertRaises(MyException, lambda : v.__array_struct__)
+
+    def test_postscript(self):
+        def callback(parent):
+            success.append(parent is p)
+
+        p = []
+        
+        # For array interface
         success = []
-        c = []
-        view_id = id(View(c, destructor=d))
-        self.assert_(success)
-        self.assert_(success[0])
-        self.assert_(success[1])
+        v = View(parent=p, postscript=callback, **self.view_keywords)
+        self.assertEqual(len(success), 0)
+        d = v.__array_interface__
+        self.assertEqual(len(success), 0)
+        d = v.__array_interface__
+        self.assertEqual(len(success), 0)
+        d = v = None
+        gc.collect()
+        self.assertEqual(len(success), 1)
+        self.assertTrue(success[0])
+
+        # For array struct
+        success = []
+        v = View(parent=p, postscript=callback, **self.view_keywords)
+        self.assertEqual(len(success), 0)
+        c = v.__array_struct__
+        self.assertEqual(len(success), 0)
+        c = v.__array_struct__
+        self.assertEqual(len(success), 0)
+        c = v = None
+        gc.collect()
+        self.assertEqual(len(success), 1)
+        self.assertTrue(success[0])
 
     def test_weakref(self):
-        v = View([])
+        v = View(**self.view_keywords)
         weak_v = weakref.ref(v)
         self.assert_(weak_v() is v)
-        del v
+        v = None
+        gc.collect()
         self.assert_(weak_v() is None)
 
     def test_gc(self):
         """refcount agnostic check that contained objects are freed"""
-        def d(capsule, parent):
+        def prelude_callback(parent):
             return r[0]
+        def postscript_callback(parent):
+            return r[1]
         class Obj(object):
             pass
-        c = Obj()
         p = Obj()
-        r = [Obj()]
-        weak_c = weakref.ref(c)
+        r = [Obj(), Obj()]
         weak_p = weakref.ref(p)
         weak_r0 = weakref.ref(r[0])
-        weak_d = weakref.ref(d)
-        v = View(c, p, d)
+        weak_r1 = weakref.ref(r[1])
+        weak_prelude = weakref.ref(prelude_callback)
+        weak_postscript = weakref.ref(postscript_callback)
+        v = View(parent=p,
+                 prelude=prelude_callback,
+                 postscript=postscript_callback,
+                 **self.view_keywords)
         weak_v = weakref.ref(v)
-        del c, p, d
-        self.assert_(weak_c() is not None)
-        self.assert_(weak_p() is not None)
-        self.assert_(weak_d() is not None)
-        del v
-        self.assert_(weak_v() is None)
-        self.assert_(weak_c() is None)
-        self.assert_(weak_p() is None)
-        self.assert_(weak_d() is None)
-        self.assert_(weak_r0() is not None)
-        del r[0]
-        self.assert_(weak_r0() is None)
+        p = prelude_callback = postscript_callback = None
+        gc.collect()
+        self.assertTrue(weak_p() is not None)
+        self.assertTrue(weak_prelude() is not None)
+        self.assertTrue(weak_postscript() is not None)
+        v = None
+        gc.collect()
+        self.assertTrue(weak_v() is None)
+        self.assertTrue(weak_p() is None)
+        self.assertTrue(weak_prelude() is None)
+        self.assertTrue(weak_postscript() is None)
+        self.assertTrue(weak_r0() is not None)
+        self.assertTrue(weak_r1() is not None)
+        r = None
+        gc.collect()
+        self.assertTrue(weak_r0() is None)
+        self.assertTrue(weak_r1() is None)
         
     def test_c_api(self):
         api = pygame._view._PYGAME_C_API
         self.assert_(isinstance(api, type(pygame.base._PYGAME_C_API)))
 
     def test_repr(self):
-        z = 0
+        v = View(**self.view_keywords)
         cname = re.findall(r"'([^']+)'", repr(View))[0]
-        oname, ovalue = re.findall(r"<([^)]+)\(([^)]+)\)>", repr(View(0)))[0]
+        oname, ovalue = re.findall(r"<([^)]+)\(([^)]+)\)>", repr(v))[0]
         self.assertEqual(oname, cname)
-        self.assertEqual(id(z), int(ovalue, 16))
+        self.assertEqual(id(v), int(ovalue, 16))
 
     def test_subclassing(self):
         class MyView(View):
             def __repr__(self):
                 return "*%s*" % (View.__repr__(self),)
-        v = MyView(0)
-        self.assertEqual(v.__array_struct__, 0)
+        v = MyView(parent=0, **self.view_keywords)
+        self.assertEqual(v.parent, 0)
         r = repr(v)
         self.assertEqual(r[:2], '*<')
         self.assertEqual(r[-2:], '>*')
 
-
-class GetArrayInterfaceTest(unittest.TestCase):
-
-    def test_get_array_interface(self):
-        surf = pygame.Surface((7, 11), 0, 32)
-        d = pygame._view.get_array_interface(surf.get_view("2"))
-        self.assertEqual(len(d), 5)
-        self.assertEqual(d['version'], 3)
-        if pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN:
-            byteorder = '<'
-        else:
-            byteorder = '>'
-        self.assertEqual(d['typestr'], byteorder + 'u4')
-        self.assertEqual(d['shape'], (7, 11))
-        self.assertEqual(d['strides'], (4, 28))
-        self.assertEqual(d['data'], (surf._pixels_address, False))
-
-    def test___array_interface__(self):
-        surf = pygame.Surface((7, 11), 0, 32)
-        d = surf.get_view("2").__array_interface__
-        self.assertEqual(len(d), 5)
-        self.assertEqual(d['version'], 3)
-        if pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN:
-            byteorder = '<'
-        else:
-            byteorder = '>'
-        self.assertEqual(d['typestr'], byteorder + 'u4')
-        self.assertEqual(d['shape'], (7, 11))
-        self.assertEqual(d['strides'], (4, 28))
-        self.assertEqual(d['data'], (surf._pixels_address, False))
-
 if __name__ == '__main__':
     unittest.main()

test/surface_test.py

         s = pygame.Surface((2, 4), 0, 32)
         self.assert_(not s.get_locked())
         v = s.get_view()
+        self.assert_(not s.get_locked())
+        c = v.__array_interface__
         self.assert_(s.get_locked())
-        del v
+        c = None
+        gc.collect()
+        self.assert_(s.get_locked())
+        v = None
+        gc.collect()
         self.assert_(not s.get_locked())
 
         # Check invalid view kind values.