1 /*
2   pygame - Python Game Library
3   Copyright (C) 2000-2001  Pete Shinners
4   Copyright (C) 2008 Marcus von Appen
5 
6   This library is free software; you can redistribute it and/or
7   modify it under the terms of the GNU Library General Public
8   License as published by the Free Software Foundation; either
9   version 2 of the License, or (at your option) any later version.
10 
11   This library is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   Library General Public License for more details.
15 
16   You should have received a copy of the GNU Library General Public
17   License along with this library; if not, write to the Free
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 
20   Pete Shinners
21   pete@shinners.org
22 */
23 
24 /*
25  *  internal surface locking support for python objects
26  */
27 #define PYGAMEAPI_SURFLOCK_INTERNAL
28 
29 #include "pygame.h"
30 
31 #include "pgcompat.h"
32 
33 static int
34 pgSurface_Lock(pgSurfaceObject *);
35 static int
36 pgSurface_Unlock(pgSurfaceObject *);
37 static int
38 pgSurface_LockBy(pgSurfaceObject *, PyObject *);
39 static int
40 pgSurface_UnlockBy(pgSurfaceObject *, PyObject *);
41 
42 static void
43 _lifelock_dealloc(PyObject *);
44 
45 static void
pgSurface_Prep(pgSurfaceObject * surfobj)46 pgSurface_Prep(pgSurfaceObject *surfobj)
47 {
48     struct pgSubSurface_Data *data = ((pgSurfaceObject *)surfobj)->subsurface;
49     if (data != NULL) {
50         SDL_Surface *surf = pgSurface_AsSurface(surfobj);
51         SDL_Surface *owner = pgSurface_AsSurface(data->owner);
52         pgSurface_LockBy((pgSurfaceObject *)data->owner, (PyObject *)surfobj);
53         surf->pixels = ((char *)owner->pixels) + data->pixeloffset;
54     }
55 }
56 
57 static void
pgSurface_Unprep(pgSurfaceObject * surfobj)58 pgSurface_Unprep(pgSurfaceObject *surfobj)
59 {
60     struct pgSubSurface_Data *data = ((pgSurfaceObject *)surfobj)->subsurface;
61     if (data != NULL) {
62         pgSurface_UnlockBy((pgSurfaceObject *)data->owner,
63                            (PyObject *)surfobj);
64     }
65 }
66 
67 static int
pgSurface_Lock(pgSurfaceObject * surfobj)68 pgSurface_Lock(pgSurfaceObject *surfobj)
69 {
70     return pgSurface_LockBy(surfobj, (PyObject *)surfobj);
71 }
72 
73 static int
pgSurface_Unlock(pgSurfaceObject * surfobj)74 pgSurface_Unlock(pgSurfaceObject *surfobj)
75 {
76     return pgSurface_UnlockBy(surfobj, (PyObject *)surfobj);
77 }
78 
79 static int
pgSurface_LockBy(pgSurfaceObject * surfobj,PyObject * lockobj)80 pgSurface_LockBy(pgSurfaceObject *surfobj, PyObject *lockobj)
81 {
82     PyObject *ref;
83     pgSurfaceObject *surf = (pgSurfaceObject *)surfobj;
84 
85     if (surf->locklist == NULL) {
86         surf->locklist = PyList_New(0);
87         if (surf->locklist == NULL) {
88             return 0;
89         }
90     }
91     ref = PyWeakref_NewRef(lockobj, NULL);
92     if (ref == NULL) {
93         return 0;
94     }
95     if (ref == Py_None) {
96         Py_DECREF(ref);
97         return 0;
98     }
99     if (0 != PyList_Append(surf->locklist, ref)) {
100         Py_DECREF(ref);
101         return 0; /* Exception already set. */
102     }
103     Py_DECREF(ref);
104 
105     if (surf->subsurface != NULL) {
106         pgSurface_Prep(surfobj);
107     }
108     if (SDL_LockSurface(surf->surf) == -1) {
109         PyErr_SetString(PyExc_RuntimeError, "error locking surface");
110         return 0;
111     }
112     return 1;
113 }
114 
115 static int
pgSurface_UnlockBy(pgSurfaceObject * surfobj,PyObject * lockobj)116 pgSurface_UnlockBy(pgSurfaceObject *surfobj, PyObject *lockobj)
117 {
118     pgSurfaceObject *surf = (pgSurfaceObject *)surfobj;
119     int found = 0;
120     int noerror = 1;
121 
122     if (surf->locklist != NULL) {
123         PyObject *item, *ref;
124         Py_ssize_t len = PyList_Size(surf->locklist);
125         while (--len >= 0 && !found) {
126             item = PyList_GetItem(surf->locklist, len);
127             ref = PyWeakref_GetObject(item);
128             if (ref == lockobj) {
129                 if (PySequence_DelItem(surf->locklist, len) == -1) {
130                     return 0;
131                 }
132                 else {
133                     found = 1;
134                 }
135             }
136         }
137 
138         /* Clear dead references */
139         len = PyList_Size(surf->locklist);
140         while (--len >= 0) {
141             item = PyList_GetItem(surf->locklist, len);
142             ref = PyWeakref_GetObject(item);
143             if (ref == Py_None) {
144                 if (PySequence_DelItem(surf->locklist, len) == -1) {
145                     noerror = 0;
146                 }
147                 else {
148                     found++;
149                 }
150             }
151         }
152     }
153 
154     if (!found) {
155         return noerror;
156     }
157 
158     /* Release all found locks. */
159     while (found > 0) {
160         if (surf->surf != NULL) {
161             SDL_UnlockSurface(surf->surf);
162         }
163         if (surf->subsurface != NULL) {
164             pgSurface_Unprep(surfobj);
165         }
166         found--;
167     }
168 
169     return noerror;
170 }
171 
172 static PyTypeObject pgLifetimeLock_Type = {
173     PyVarObject_HEAD_INIT(NULL,0)
174     "SurfLifeLock",                    /* name */
175     sizeof(pgLifetimeLockObject),      /* basic size */
176     0,                                 /* tp_itemsize */
177     _lifelock_dealloc,                 /* tp_dealloc*/
178     0,                                 /* tp_print */
179     NULL,                              /* tp_getattr */
180     NULL,                              /* tp_setattr */
181     NULL,                              /* tp_compare */
182     NULL,                              /* tp_repr */
183     NULL,                              /* tp_as_number */
184     NULL,                              /* tp_as_sequence */
185     NULL,                              /* tp_as_mapping */
186     NULL,                              /* tp_hash */
187     NULL,                              /* tp_call */
188     NULL,                              /* tp_str */
189     NULL,                              /* tp_getattro */
190     NULL,                              /* tp_setattro */
191     NULL,                              /* tp_as_buffer */
192     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_WEAKREFS,
193     NULL,                                     /* tp_doc */
194     NULL,                                     /* tp_traverse */
195     NULL,                                     /* tp_clear */
196     NULL,                                     /* tp_richcompare */
197     offsetof(pgLifetimeLockObject, weakrefs), /* tp_weaklistoffset */
198     NULL,                                     /* tp_iter */
199     NULL,                                     /* tp_iternext */
200     NULL,                                     /* tp_methods */
201     NULL,                                     /* tp_members */
202     NULL,                                     /* tp_getset */
203     NULL,                                     /* tp_base */
204     NULL,                                     /* tp_dict */
205     NULL,                                     /* tp_descr_get */
206     NULL,                                     /* tp_descr_set */
207     0,                                        /* tp_dictoffset */
208     NULL,                                     /* tp_init */
209     NULL,                                     /* tp_alloc */
210     NULL,                                     /* tp_new */
211     NULL,                                     /* tp_free */
212     NULL,                                     /* tp_is_gc */
213     NULL,                                     /* tp_bases */
214     NULL,                                     /* tp_mro */
215     NULL,                                     /* tp_cache */
216     NULL,                                     /* tp_subclasses */
217     NULL,                                     /* tp_weaklist */
218     NULL                                      /* tp_del */
219 };
220 
221 /* lifetimelock object internals */
222 static void
_lifelock_dealloc(PyObject * self)223 _lifelock_dealloc(PyObject *self)
224 {
225     pgLifetimeLockObject *lifelock = (pgLifetimeLockObject *)self;
226 
227     if (lifelock->weakrefs != NULL) {
228         PyObject_ClearWeakRefs(self);
229     }
230 
231     pgSurface_UnlockBy((pgSurfaceObject *)lifelock->surface,
232                        lifelock->lockobj);
233     Py_DECREF(lifelock->surface);
234     PyObject_DEL(self);
235 }
236 
237 static PyObject *
pgSurface_LockLifetime(PyObject * surfobj,PyObject * lockobj)238 pgSurface_LockLifetime(PyObject *surfobj, PyObject *lockobj)
239 {
240     pgLifetimeLockObject *life;
241     if (surfobj == NULL) {
242         return RAISE(pgExc_SDLError, SDL_GetError());
243     }
244 
245     life = PyObject_NEW(pgLifetimeLockObject, &pgLifetimeLock_Type);
246     if (life != NULL) {
247         life->surface = surfobj;
248         life->lockobj = lockobj;
249         life->weakrefs = NULL;
250         Py_INCREF(surfobj);
251         if (!pgSurface_LockBy((pgSurfaceObject *)surfobj, lockobj)) {
252             return NULL;
253         }
254     }
255     return (PyObject *)life;
256 }
257 
258 static PyMethodDef _surflock_methods[] = {{NULL, NULL, 0, NULL}};
259 
260 /*DOC*/ static char _surflock_doc[] =
261     /*DOC*/ "Surface locking support";
262 
MODINIT_DEFINE(surflock)263 MODINIT_DEFINE(surflock)
264 {
265     PyObject *module, *dict, *apiobj;
266     int ecode;
267     static void *c_api[PYGAMEAPI_SURFLOCK_NUMSLOTS];
268 
269     static struct PyModuleDef _module = {PyModuleDef_HEAD_INIT,
270                                          "surflock",
271                                          _surflock_doc,
272                                          -1,
273                                          _surflock_methods,
274                                          NULL,
275                                          NULL,
276                                          NULL,
277                                          NULL};
278 
279     if (PyType_Ready(&pgLifetimeLock_Type) < 0) {
280         MODINIT_ERROR;
281     }
282 
283     /* Create the module and add the functions */
284     module = PyModule_Create(&_module);
285     if (module == NULL) {
286         MODINIT_ERROR;
287     }
288     dict = PyModule_GetDict(module);
289 
290     /* export the c api */
291     c_api[0] = &pgLifetimeLock_Type;
292     c_api[1] = pgSurface_Prep;
293     c_api[2] = pgSurface_Unprep;
294     c_api[3] = pgSurface_Lock;
295     c_api[4] = pgSurface_Unlock;
296     c_api[5] = pgSurface_LockBy;
297     c_api[6] = pgSurface_UnlockBy;
298     c_api[7] = pgSurface_LockLifetime;
299     apiobj = encapsulate_api(c_api, "surflock");
300     if (apiobj == NULL) {
301         DECREF_MOD(module);
302         MODINIT_ERROR;
303     }
304     ecode = PyDict_SetItemString(dict, PYGAMEAPI_LOCAL_ENTRY, apiobj);
305     Py_DECREF(apiobj);
306     if (ecode) {
307         DECREF_MOD(module);
308         MODINIT_ERROR;
309     }
310     MODINIT_RETURN(module);
311 }
312