Commits

marcus  committed 84e9391

Added basic openal.CaptureDevice implementation.
Fixed threading issues in openal device handling.
Improved msys environment detection and make targets.
Fixed memory leaks in openal.Buffers and openal.Sources.

  • Participants
  • Parent commits 6d536aa
  • Branches pgreloaded

Comments (0)

Files changed (14)

 PYTHON = python
+COMPILER = ''
 # Set this to 0 on releases.
 EXPERIMENTAL = 1
 top_srcdir = `pwd`
 	$(top_srcdir)/src/sdlmixer \
 	$(top_srcdir)/src/sdlttf
 
-
 all: clean build
 
 dist: clean docs
 	@$(PYTHON) setup.py bdist
 
 build:
-	@echo "Running build..."
-	@WITH_EXPERIMENTAL=$(EXPERIMENTAL) $(PYTHON) setup.py build #-c mingw32
+	@if test -n $(COMPILER); then \
+        echo "Running build with $(COMPILER)..."; \
+		WITH_EXPERIMENTAL=$(EXPERIMENTAL) $(PYTHON) setup.py build -c $(COMPILER); \
+	else \
+        echo "Running build"; \
+		WITH_EXPERIMENTAL=$(EXPERIMENTAL) $(PYTHON) setup.py build; \
+	fi
 	@echo "Build finished, invoke 'make install' to install."
 
-clang: clean
-	@echo "Running build with Clang..."
-	@WITH_EXPERIMENTAL=$(EXPERIMENTAL) $(PYTHON) setup.py build -c clang
-	@echo "Build finished, invoke 'make install' to install."
+clang: COMPILER = clang
+clang: all
+
+msys: COMPILER = mingw32
+msys: all
 
 install:
 	@echo "Installing..."
 # purposes only!
 
 buildall: clean
-	@WITH_EXPERIMENTAL=$(EXPERIMENTAL) python2.4 setup.py build
-	@WITH_EXPERIMENTAL=$(EXPERIMENTAL) python2.5 setup.py build
-	@WITH_EXPERIMENTAL=$(EXPERIMENTAL) python2.6 setup.py build
-	@WITH_EXPERIMENTAL=$(EXPERIMENTAL) python3.1 setup.py build
+	@if test -n $(COMPILER); then \
+		WITH_EXPERIMENTAL=$(EXPERIMENTAL) python2.4 setup.py build -c $(COMPILER); \
+		WITH_EXPERIMENTAL=$(EXPERIMENTAL) python2.5 setup.py build -c $(COMPILER); \
+		WITH_EXPERIMENTAL=$(EXPERIMENTAL) python2.6 setup.py build -c $(COMPILER); \
+		WITH_EXPERIMENTAL=$(EXPERIMENTAL) python3.1 setup.py build -c $(COMPILER); \
+	else \
+		WITH_EXPERIMENTAL=$(EXPERIMENTAL) python2.4 setup.py build; \
+		WITH_EXPERIMENTAL=$(EXPERIMENTAL) python2.5 setup.py build; \
+		WITH_EXPERIMENTAL=$(EXPERIMENTAL) python2.6 setup.py build; \
+		WITH_EXPERIMENTAL=$(EXPERIMENTAL) python3.1 setup.py build; \
+	fi
 
-buildallclang: clean
-	@WITH_EXPERIMENTAL=$(EXPERIMENTAL) python2.4 setup.py build -c clang
-	@WITH_EXPERIMENTAL=$(EXPERIMENTAL) python2.5 setup.py build -c clang
-	@WITH_EXPERIMENTAL=$(EXPERIMENTAL) python2.6 setup.py build -c clang
-	@WITH_EXPERIMENTAL=$(EXPERIMENTAL) python3.1 setup.py build -c clang
+buildallclang: COMPILER = clang
+buildallclang: buildall
 
 installall:
 	@WITH_EXPERIMENTAL=$(EXPERIMENTAL) python2.4 setup.py install
    has)?
 
 * openal: check integrity and references for sources, buffers and listeners.
+* openal: check for locking issues on closing a CaptureDevice.
 
 Things to ADD:
 ==============

File config/msysio.py

 def is_msys():
     """Return true if the execution environment is MSYS"""
     
-    try:
-        return os.environ['OSTYPE'] == 'msys'
-    except KeyError:
-        return 0
+    if 'OSTYPE' in os.environ:
+        if os.environ['OSTYPE'] == 'msys':
+            return True
+    if 'MSYSCON' in os.environ:
+        return True
+    return False

File doc/src/sdlwm.xml

     <call>set_icon (surface[, mask]) -> None</call>
     <desc>
       Sets the window manager icon to be used by the window.
-      
-      .. todo::
-        
-         Not fully implemented.
     </desc>
   </func>
   <func name="toggle_fullscreen">
                     "src/openal/sources.c",
                     "src/openal/listener.c",
                     "src/openal/context.c",
+                    "src/openal/capturedevice.c",
                     "src/openal/device.c" ],
         depends = [ 'openal' ],
         experimental = True),

File src/openal/buffers.c

             switched = 1;
         }
         alDeleteBuffers (self->count, self->buffers);
+        PyMem_Free (self->buffers);
         if (switched)
             alcMakeContextCurrent (ctxt);
     }
         return NULL;
     }
 
-    if (!PyArg_ParseTuple (args, "lls", &bufnum, &param, &type))
+    if (!PyArg_ParseTuple (args, "lls:get_prop", &bufnum, &param, &type))
     {
         PyErr_Clear ();
-        if (!PyArg_ParseTuple (args, "ll|si", &bufnum, &param, &type, &size))
+        if (!PyArg_ParseTuple (args, "ll|si:get_prop", &bufnum, &param, &type,
+            &size))
             return NULL;
         if (size <= 0)
         {
     if (SetALErrorException (alGetError ()))
     {
         Py_DECREF (buffers);
+        PyMem_Free (buf);
         return NULL;
     }
 
     ((PyBuffers*)buffers)->buffers = buf;
-
-    
     return buffers;
 }
 

File src/openal/capturedevice.c

+/*
+  pygame - Python Game Library
+  Copyright (C) 2010 Marcus von Appen
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 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
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public
+  License along with this library; if not, write to the Free
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+#define PYGAME_OPENALCAPTUREDEVICE_INTERNAL
+
+#include "openalmod.h"
+#include "pgopenal.h"
+
+static PyObject* _capturedevice_new (PyTypeObject *type, PyObject *args,
+    PyObject *kwds);
+static int _capturedevice_init (PyObject *self, PyObject *args, PyObject *kwds);
+static void _capturedevice_dealloc (PyCaptureDevice *self);
+static PyObject* _capturedevice_repr (PyObject *self);
+
+static PyObject* _capturedevice_start (PyObject* self);
+static PyObject* _capturedevice_stop (PyObject* self);
+
+static PyObject* _capturedevice_getsize (PyObject *self, void *closure);
+
+/**
+ */
+static PyMethodDef _capturedevice_methods[] = {
+    { "start", (PyCFunction)_capturedevice_start, METH_NOARGS, NULL },
+    { "stop", (PyCFunction)_capturedevice_start, METH_NOARGS, NULL },
+    { NULL, NULL, 0, NULL }
+};
+
+/**
+ */
+static PyGetSetDef _capturedevice_getsets[] = {
+    { "size", _capturedevice_getsize, NULL, NULL, NULL },
+    { NULL, NULL, NULL, NULL, NULL }
+};
+
+/**
+ */
+PyTypeObject PyCaptureDevice_Type =
+{
+    TYPE_HEAD(NULL, 0)
+    "base.CaptureDevice",       /* tp_name */
+    sizeof (PyCaptureDevice),   /* tp_basicsize */
+    0,                          /* tp_itemsize */
+    (destructor) _capturedevice_dealloc, /* tp_dealloc */
+    0,                          /* tp_print */
+    0,                          /* tp_getattr */
+    0,                          /* tp_setattr */
+    0,                          /* tp_compare */
+    (reprfunc)_capturedevice_repr,     /* tp_repr */
+    0,                          /* tp_as_number */
+    0,                          /* tp_as_sequence */
+    0,                          /* tp_as_mapping */
+    0,                          /* tp_hash */
+    0,                          /* tp_call */
+    0,                          /* tp_str */
+    0,                          /* tp_getattro */
+    0,                          /* tp_setattro */
+    0,                          /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+    0/*DOC_BASE_DEVICE*/,
+    0,                          /* tp_traverse */
+    0,                          /* tp_clear */
+    0,                          /* tp_richcompare */
+    0,                          /* tp_weaklistoffset */
+    0,                          /* tp_iter */
+    0,                          /* tp_iternext */
+    _capturedevice_methods,     /* tp_methods */
+    0,                          /* tp_members */
+    _capturedevice_getsets,     /* tp_getset */
+    0,                          /* tp_base */
+    0,                          /* tp_dict */
+    0,                          /* tp_descr_get */
+    0,                          /* tp_descr_set */
+    0,                          /* tp_dictoffset */
+    (initproc) _capturedevice_init,    /* tp_init */
+    0,                          /* tp_alloc */
+    _capturedevice_new,         /* tp_new */
+    0,                          /* tp_free */
+    0,                          /* tp_is_gc */
+    0,                          /* tp_bases */
+    0,                          /* tp_mro */
+    0,                          /* tp_cache */
+    0,                          /* tp_subclasses */
+    0,                          /* tp_weaklist */
+    0,                          /* tp_del */
+#if PY_VERSION_HEX >= 0x02060000
+    0                           /* tp_version_tag */
+#endif
+};
+
+static PyObject*
+_capturedevice_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    PyCaptureDevice *device = (PyCaptureDevice *)type->tp_alloc (type, 0);
+    if (!device)
+        return NULL;
+    device->size = 0;
+    device->device.device = NULL;
+    return (PyObject*) device;
+}
+
+static void
+_capturedevice_dealloc (PyCaptureDevice *self)
+{
+    if (self->device.device)
+    {
+        Py_BEGIN_ALLOW_THREADS;
+        alcCaptureCloseDevice (self->device.device);
+        Py_END_ALLOW_THREADS;
+    }
+    self->device.device = NULL;
+    ((PyObject*)self)->ob_type->tp_free ((PyObject *) self);
+}
+
+static int
+_capturedevice_init (PyObject *self, PyObject *args, PyObject *kwds)
+{
+    char *name = NULL;
+    ALCdevice *device = NULL;
+    long bufsize, freq, format;
+    
+    if (!PyArg_ParseTuple (args, "slll", &name, &freq, &format, &bufsize))
+    {
+        PyErr_Clear ();
+        if (!PyArg_ParseTuple (args, "lll", &freq, &format, &bufsize))
+            return -1;
+    }
+
+    if (bufsize <= 0)
+    {
+        PyErr_SetString (PyExc_ValueError,
+            "bufsize must not be smaller than 1");
+        return -1;
+    }
+
+    Py_BEGIN_ALLOW_THREADS;
+    device = alcCaptureOpenDevice ((const ALCchar*)name, (ALCuint) freq,
+        (ALCenum)format, (ALCsizei) bufsize);
+    Py_END_ALLOW_THREADS;
+    if (!device)
+    {
+        SetALErrorException (alGetError ());
+        return -1;
+    }
+    ((PyCaptureDevice*)self)->device.device = device;
+    return 0;
+}
+
+static PyObject*
+_capturedevice_repr (PyObject *self)
+{
+    PyObject *retval;
+    const ALCchar *name = alcGetString (PyCaptureDevice_AsDevice (self),
+        ALC_CAPTURE_DEVICE_SPECIFIER);
+    /* CaptureDevice('') == 17 */
+    size_t len = strlen ((const char*) name) + 18;
+    char *str = malloc (len);
+    if (!str)
+        return NULL;
+
+    snprintf (str, len, "CaptureDevice('%s')", (const char*) name);
+    retval = Text_FromUTF8 (str);
+    free (str);
+    return retval;
+}
+
+/* CaptureDevice getters/setters */
+static PyObject*
+_capturedevice_getsize (PyObject *self, void *closure)
+{
+    return PyInt_FromLong ((long) ((PyCaptureDevice*)self)->size);
+}
+
+/* CaptureDevice methods */
+static PyObject*
+_capturedevice_start (PyObject* self)
+{
+    Py_RETURN_NONE;
+}
+
+static PyObject*
+_capturedevice_stop (PyObject* self)
+{
+    Py_RETURN_NONE;
+}
+
+/* C API */
+PyObject*
+PyCaptureDevice_New (const char* name, ALCuint frequency, ALCenum format,
+    ALCsizei bufsize)
+{
+    ALCdevice *dev;
+    PyObject *device = PyCaptureDevice_Type.tp_new (&PyCaptureDevice_Type,
+        NULL, NULL);
+
+    if (!device)
+        return NULL;
+
+    dev = alcCaptureOpenDevice (name, frequency, format, bufsize);
+    if (!dev)
+    {
+        SetALErrorException (alGetError ());
+        Py_DECREF (device);
+        return NULL;
+    }
+    ((PyCaptureDevice*)device)->device.device = dev;
+    return device;
+}
+
+void
+capturedevice_export_capi (void **capi)
+{
+    capi[PYGAME_OPENALDEVICE_FIRSTSLOT] = &PyCaptureDevice_Type;
+    capi[PYGAME_OPENALDEVICE_FIRSTSLOT+1] = (void *)PyCaptureDevice_New;
+}

File src/openal/device.c

 _device_dealloc (PyDevice *self)
 {
     if (self->device)
+    {
+        Py_BEGIN_ALLOW_THREADS;
         alcCloseDevice (self->device);
+        Py_END_ALLOW_THREADS;
+    }
     self->device = NULL;
     ((PyObject*)self)->ob_type->tp_free ((PyObject *) self);
 }
     if (!PyArg_ParseTuple (args, "|s", &name))
         return -1;
     
+    Py_BEGIN_ALLOW_THREADS;
     device = alcOpenDevice (name);
+    Py_END_ALLOW_THREADS;
     if (!device)
     {
         SetALErrorException (alGetError ());

File src/openal/listener.c

         return NULL;
     }
 
-    if (!PyArg_ParseTuple (args, "ls", &param, &type))
+    if (!PyArg_ParseTuple (args, "ls:get_prop", &param, &type))
     {
         PyErr_Clear ();
-        if (!PyArg_ParseTuple (args, "l|si", &param, &type, &size))
+        if (!PyArg_ParseTuple (args, "l|si:get_prop", &param, &type, &size))
             return NULL;
         if (size <= 0)
         {

File src/openal/openalmod.c

     PyDevice_Type.tp_new = PyType_GenericNew;
     if (PyType_Ready (&PyDevice_Type) < 0)
         goto fail;
+    PyCaptureDevice_Type.tp_base = &PyDevice_Type;
+    if (PyType_Ready (&PyCaptureDevice_Type) < 0)
+        goto fail;
     PyContext_Type.tp_new = PyType_GenericNew;
     if (PyType_Ready (&PyContext_Type) < 0)
         goto fail;
         goto fail;
     
     ADD_OBJ_OR_FAIL (mod, "Device", PyDevice_Type, fail);
+    ADD_OBJ_OR_FAIL (mod, "CaptureDevice", PyCaptureDevice_Type, fail);
     ADD_OBJ_OR_FAIL (mod, "Context", PyContext_Type, fail);
     ADD_OBJ_OR_FAIL (mod, "Buffers", PyBuffers_Type, fail);
     ADD_OBJ_OR_FAIL (mod, "Sources", PySources_Type, fail);
     ADD_OBJ_OR_FAIL (mod, "Listener", PyListener_Type, fail);
 
     device_export_capi (c_api);
+    capturedevice_export_capi (c_api);
     context_export_capi (c_api);
     buffers_export_capi (c_api);
     sources_export_capi (c_api);

File src/openal/openalmod.h

 
 #define PYGAME_OPENAL_INTERNAL
 #define PYGAME_OPENALDEVICE_INTERNAL
+#define PYGAME_OPENALCAPTUREDEVICE_INTERNAL
 #define PYGAME_OPENALCONTEXT_INTERNAL
 #define PYGAME_OPENALBUFFERS_INTERNAL
 #define PYGAME_OPENALSOURCES_INTERNAL
 #define PyDevice_Check(x) (PyObject_TypeCheck (x, &PyDevice_Type))
 PyObject* PyDevice_New (const char *name);
 
+extern PyTypeObject PyCaptureDevice_Type;
+#define PyCaptureDevice_Check(x) (PyObject_TypeCheck (x, &PyCaptureDevice_Type))
+PyObject* PyCaptureDevice_New (const char* name, ALCuint frequency,
+    ALCenum format, ALCsizei bufsize);
+
 extern PyTypeObject PyContext_Type;
 #define PyContext_Check(x) (PyObject_TypeCheck (x, &PyContext_Type))
 
 #define CLEAR_ERROR_STATE() alGetError()
 
 void device_export_capi (void **capi);
+void capturedevice_export_capi (void **capi);
 void context_export_capi (void **capi);
 void buffers_export_capi (void **capi);
 void sources_export_capi (void **capi);

File src/openal/pgopenal.h

 {
     PyObject_HEAD
     ALCdevice *device;
-    ALuint    *buffers;
-    int        bufsize;
 } PyDevice;
 #define PyDevice_AsDevice(x) (((PyDevice*)x)->device)
 #define PYGAME_OPENALDEVICE_FIRSTSLOT                   \
 
 typedef struct
 {
+    PyDevice device;
+    ALCsizei size;
+} PyCaptureDevice;
+#define PyCaptureDevice_AsDevice(x) (((PyCaptureDevice*)x)->device.device)
+#define PYGAME_OPENALCAPTUREDEVICE_FIRSTSLOT                   \
+    (PYGAME_OPENALDEVICE_FIRSTSLOT + PYGAME_OPENALDEVICE_NUMSLOTS)
+#define PYGAME_OPENALCAPTUREDEVICE_NUMSLOTS 2
+#ifndef PYGAME_OPENALCAPTUREDEVICE_INTERNAL
+#define PyCaptureDevice_Type                                            \
+    (*(PyTypeObject*)PyGameOpenAL_C_API[PYGAME_OPENALCAPTUREDEVICE_FIRSTSLOT+0])
+#define PyCaptureDevice_Check(x)                                        \
+    (PyObject_TypeCheck(x,                                              \
+        (PyTypeObject*)PyGameOpenAL_C_API[PYGAME_OPENALCAPTUREDEVICE_FIRSTSLOT+0]))
+#define PyCaptureDevice_New                                             \
+    (*(PyObject*(*)(const char*,ALCuint,ALCenum,ALCsizei))PyGameOpenAL_C_API[PYGAME_OPENALCAPTUREDEVICE_FIRSTSLOT+1])
+#endif /* PYGAME_OPENALCAPTUREDEVICE_INTERNAL */
+
+typedef struct
+{
     PyObject_HEAD
     ALCcontext *context;
     PyObject   *device;
         return (ret);                                                   \
     }
 #define PYGAME_OPENALCONTEXT_FIRSTSLOT                                  \
-    (PYGAME_OPENALDEVICE_FIRSTSLOT + PYGAME_OPENALDEVICE_NUMSLOTS)
+    (PYGAME_OPENALCAPTUREDEVICE_FIRSTSLOT + PYGAME_OPENALCAPTUREDEVICE_NUMSLOTS)
 #define PYGAME_OPENALCONTEXT_NUMSLOTS 1
 #ifndef PYGAME_OPENALCONTEXT_INTERNAL
 #define PyContext_Type                                                  \

File src/openal/sources.c

 static PyObject* _sources_pause (PyObject *self, PyObject *args);
 static PyObject* _sources_stop (PyObject *self, PyObject *args);
 static PyObject* _sources_rewind (PyObject *self, PyObject *args);
+static PyObject* _sources_queuebuffers (PyObject *self, PyObject *args);
+static PyObject* _sources_unqueuebuffers (PyObject *self, PyObject *args);
 
 static PyObject* _sources_getcount (PyObject* self, void *closure);
 static PyObject* _sources_getsources (PyObject* self, void *closure);
     { "pause", _sources_pause, METH_O, NULL },
     { "stop", _sources_stop, METH_O, NULL },
     { "rewind", _sources_rewind, METH_O, NULL },
+    { "queue_buffers", _sources_queuebuffers, METH_VARARGS, NULL },
+    { "unqueue_buffers", _sources_unqueuebuffers, METH_VARARGS, NULL },
     { NULL, NULL, 0, NULL }
 };
 
             switched = 1;
         }
         alDeleteSources (self->count, self->sources);
+        PyMem_Free (self->sources);
         if (switched)
             alcMakeContextCurrent (ctxt);
     }
         return NULL;
     }
 
-    if (!PyArg_ParseTuple (args, "lls", &bufnum, &param, &type))
+    if (!PyArg_ParseTuple (args, "lls:get_prop", &bufnum, &param, &type))
     {
         PyErr_Clear ();
-        if (!PyArg_ParseTuple (args, "ll|si", &bufnum, &param, &type, &size))
+        if (!PyArg_ParseTuple (args, "ll|si:get_prop", &bufnum, &param, &type,
+            &size))
             return NULL;
         if (size <= 0)
         {
     return _sources_action (self, args, REWIND);
 }
 
+static PyObject*
+_sources_queuebuffers (PyObject *self, PyObject *args)
+{
+    PyObject *buffers;
+    long bufnum;
+    
+    if (!CONTEXT_IS_CURRENT (((PySources*)self)->context))
+    {
+        PyErr_SetString (PyExc_PyGameError, "source context is not current");
+        return NULL;
+    }
+    
+    if (!PyArg_ParseTuple (args, "lO:queue_buffers", &bufnum, &buffers))
+        return NULL;
+    
+    if (bufnum < 0 || bufnum > ((PySources*)self)->count)
+    {
+        PyErr_SetString (PyExc_ValueError, "source index out of range");
+        return NULL;
+    }
+    if (!PyBuffers_Check (buffers))
+    {
+        PyErr_SetString (PyExc_TypeError, "argument must be a Buffers object");
+        return NULL;
+    }
+    
+    CLEAR_ERROR_STATE ();
+    alSourceQueueBuffers ((ALuint) bufnum, ((PyBuffers*)buffers)->count,
+        PyBuffers_AsBuffers(buffers));
+    if (SetALErrorException (alGetError ()))
+        return NULL;
+    
+    Py_RETURN_NONE;
+}
+
+static PyObject*
+_sources_unqueuebuffers (PyObject *self, PyObject *args)
+{
+    PyObject *buffers;
+    long bufnum;
+    
+    if (!CONTEXT_IS_CURRENT (((PySources*)self)->context))
+    {
+        PyErr_SetString (PyExc_PyGameError, "source context is not current");
+        return NULL;
+    }
+    
+    if (!PyArg_ParseTuple (args, "lO:unqueue_buffers", &bufnum, &buffers))
+        return NULL;
+    
+    if (bufnum < 0 || bufnum > ((PySources*)self)->count)
+    {
+        PyErr_SetString (PyExc_ValueError, "source index out of range");
+        return NULL;
+    }
+    if (!PyBuffers_Check (buffers))
+    {
+        PyErr_SetString (PyExc_TypeError, "argument must be a Buffers object");
+        return NULL;
+    }
+    
+    CLEAR_ERROR_STATE ();
+    alSourceUnqueueBuffers ((ALuint) bufnum, ((PyBuffers*)buffers)->count,
+        PyBuffers_AsBuffers(buffers));
+    if (SetALErrorException (alGetError ()))
+        return NULL;
+    
+    Py_RETURN_NONE;
+}
 /* C API */
 PyObject*
 PySources_New (PyObject *context, ALsizei count)
     if (SetALErrorException (alGetError ()))
     {
         Py_DECREF (sources);
+        PyMem_Free (buf);
         return NULL;
     }
 

File src/sdl/wmmod.c

 {
     PyObject *surface;
     PyObject *mask = NULL;
-    SDL_Surface *sfmask = NULL;
+    Uint8 *maskbuf = NULL;
+    Py_ssize_t masklen = 0;
+    SDL_Surface *sdlsurface;
 
     ASSERT_VIDEO_INIT(NULL);
 
         PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
         return NULL;
     }
-
+    sdlsurface = PySDLSurface_AsSDLSurface (surface);
+    
     if (mask)
     {
-        PyErr_SetString (PyExc_NotImplementedError,
-            "icon masks are not supported yet");
+        int w;
+        if (PyObject_AsReadBuffer (mask, (const void**)&maskbuf,
+            &masklen) == -1)
+            return -1;
+        w = (int) round ((masklen * 1.f) / sdlsurface->h);
+        if ((w * 8) != sdlsurface->w)
+        {
+            PyErr_SetString (PyExc_ValueError,
+                "mask buffer does not match size");
+            return NULL;
+        }
     }
-    /* TODO: support the mask */
-    SDL_WM_SetIcon (((PySDLSurface*)surface)->surface, NULL);
+    SDL_WM_SetIcon (((PySDLSurface*)surface)->surface, maskbuf);
     Py_RETURN_NONE;
 }