:author: Alex Holkner
:address: firstname.lastname@example.org \\\\
:date: 29 June 2006
:copyright: Copyright (c) 2006 Alex Holkner.
SDL-ctypes is a wrapper module for the `Simple DirectMedia Layer`_ (SDL) and
its companion libraries SDL_image, SDL_mixer and SDL_ttf.
Example programs ported from the SDL library are in the ``test`` directory.
You can use SDL-ctypes to access all SDL functions and data types.
Pixel and audio buffers can be manipulated using either slices or NumPy_.
SDL can also provide a context for PyOpenGL_ to work with.
.. _Simple DirectMedia Layer: http://www.libsdl.org
.. _NumPy: http://numeric.scipy.org
.. _PyOpenGL: http://pyopengl.sourceforge.net
Versions 1.2.1 through 1.2.11
Unversioned release on 13 April 2002 through 1.2.5
Versions 1.2.6 through 1.2.7
Versions 2.0.7 through 2.0.8
Note that earlier versions are not available through the SDL subversion
If you are familiar with SDL in C, you should find SDL-ctypes
to be fairly intuitive. The interface has been kept as consistent as
possible, while making it simple to use in Python.
The following sections detail the general differences you can expect to
The preferred way to use SDL-ctypes is with::
from SDL import *
This follows the convention of PyOpenGL, and allows you to call SDL
functions in the same way as the C library. The actual package is
composed of several modules such as ``SDL.cdrom``, ``SDL.video``, and
so on. All functions and structures from each module are imported
directly into the SDL namespace for convenience.
The extension libraries to SDL must be imported separately if you want
to use them::
from SDL.image import *
from SDL.mixer import *
from SDL.ttf import *
The API documentation lists core functions in both their originating module
and the SDL module for readability.
Many SDL functions return an error code (the actual code depends on the
function: some return 0 on error, others return -1 on error). SDL-ctypes
hides this detail from the application and will check return codes itself.
When an error code is returned from the SDL library, an ``SDL_Exception``
is raised. The message it contains is the return value of ``SDL_GetError``.
In general this means you can leave out error checking code in your
application, or use try/except clauses where necessary.
Return by reference
Some SDL functions return values by storing the result in a pointer
passed in as a function argument. Since this convention is unwieldly
in Python, these functions have been rewritten. There are three
types of these functions:
* Those whose return value is an error code. In this case SDL-ctypes
handles the error code itself (raising an exception if necessary),
and returns the primary result directly. An example of this is
``SDL_PollEvent``, which has a C prototype of::
int SDL_PollEvent(SDL_Event *event);
This function actually has two behaviours: if ``event`` is NULL the
function returns ``SDL_TRUE`` but does not dequeue the event.
This is implemented in SDL-ctypes as::
SDL_PollEvent() -> bool
If you wish to dequeue and view the event as well, you can use the
SDL_PollEventAndReturn() -> SDL_Event
* Those that return multiple values. An example is ``SDL_GetKeyState``,
which has a C prototype of::
void SDL_GetKeyRepeat(int *delay, int *interval);
In SDL-ctypes, the two results are returned as a tuple::
SDL_GetKeyRepeat() -> (delay, interval)
* Those that fill in an array of values. An example is ``SDL_PeepEvents``,
which has a C prototype of::
int SDL_PeepEvents(SDL_Event *events, int numevents, int action, int
In SDL-ctypes the array of events is returned as a list::
SDL_PeepEvents(numevents, action, mask) -> list of SDL_Event
There are times when you need to access and manipulate large arrays
of data in SDL. For example, ``SDL_SetGammaRamp`` in C takes
three arrays of integers. The ``SDL_Surface.pixels`` attribute in C
is a void pointer to a large array of pixel data.
In SDL-ctypes the ``SDL_array`` abstraction is used to represent
large arrays of data, allowing you to choose which format you would
like the data in.
For example, to access the surface pixels as an instance of ``SDL_array``::
surface = SDL_SetVideoMode(640, 480, 32, 0)
pixels = surface.pixels
``SDL_array`` implements the usual slicing and indexing operators so you
can access the data element-wise::
# Set the third pixel to red
pixels = SDL_MapRGBColor(surface.format, 255, 0, 0)
# Set all pixels to white
white = SDL_MapRGBColor(surface.format, 255, 255, 255)
for i in range(len(pixels)):
pixels[i] = white
# Equivalent to
pixels[:] = [white] * len(pixels)
You can also access the data as a NumPy array, if it is installed::
pixels = surface.pixels.as_numpy()
The resultant array is mapped directly to the SDL buffer, so updates
# Set all pixels to white
pixels[:] = [white] * len(pixels)
# Add one to all pixel values:
Using NumPy is substantially faster than plain Python code.
If you are using another library that uses ctypes, you can pass it
the raw buffer as a pointer::
# Get the ctypes buffer pointer
p = surface.pixels.as_ctypes()
So far all demonstrated operations have been on the pixel type; for
example if using a 32-bit buffer, each element in the array is an
integer representing the complete buffer. You can also access the
raw bytes of the buffer::
bytes = surface.pixels.as_bytes()
If you know what you're doing, you can manipulate the buffer in the
# Zero out the third byte of the buffer.
bytes = 0
You can access the byte buffer as a NumPy array or ctypes pointer in
the same way::
bytes = surface.pixels.as_bytes().as_numpy()
bytes = surface.pixels.as_bytes().as_ctypes()
You have already seen that ``SDL_Surface.pixels`` is an ``SDL_array``.
Some functions also return ``SDL_array``, which can be manipulated
in the same way::
red, green, blue = SDL_GetGammaRamp()
red = red.as_numpy()
red[:] = numpy.zeros(len(red))
Any sequence, ctypes pointer or NumPy array can be passed as an argument
to a function expecting ``SDL_array``::
# red is now a NumPy array; green is still SDL_array; blue is a list
SDL_SetGammaRamp(red, green,  * 256)
Functions accepting SDL_array also accept any sequence, ctypes pointer
or NumPy array of a byte buffer::
SDL_SetGammaRamp(red, green.as_bytes(),  * 256 * 2)
``SDL_RWops`` is a structure in SDL that describes a read/write interface
to an arbitrary object. For example, the ``SDL_Load_BMP_RW`` function
can create an ``SDL_Surface`` from any source.
Several functions from SDL have been included in SDL-ctypes for creating
``SDL_RWops`` from files::
rw = SDL_RWFromFile('sample.bmp', 'rb')
You can also create an SDL_RWops from any Python file-like object that
provides read, write, seek and close methods::
f = open('sample.bmp')
rw = SDL_RWFromObject(f)
For example, you could use this functionality to load a bitmap directly
from a ZIP archive or remote URL.
SDL-ctypes can be used with any SDL shared library from version 2.1.1
onwards. Differences between these versions are handled transparently
where possible (e.g., the difference in layout between structures).
If you call a function or access a structure member that doesn't exist
in the version that was linked at runtime, ``SDL_NotImplementedError``
will be raised.
You can check the version of the runtime library with ``SDL_Linked_Version``.
Tools and process
Releasing a snapshot
1. Increment revision numbers in ``setup.py`` and ``doc/manual.txt``, and
ensure ``CHANGELOG`` is up to date.
2. Update ``doc/index.txt`` and generate ``index.html`` with::
python setup.py doc
3. Generate user documentation (requires ``mkhowto`` from the Python source
and `Python LaTeX Writer
python setup.py manual
4. Generate API documentation (requires `Epydoc
<http://epydoc.sourceforge.net>`_ 3.0 alpha or later)::
python setup.py apidoc
5. Generate snapshot::
python setup.py sdist
How API documentation is generated
EpyDoc generates HTML pages by both parsing source files and by
importing the modules and introspecting them. When EpyDoc is run
on the SDL package it misses most functions, as they are created
procedurally with ``SDL.dll.function``. The parser misses them as
they look like variable assignments, and the introspector misses them
as the underlying function is declared in another module.
The script ``support/prep_doc.py`` creates a package in ``build_doc``
by introspecting the SDL module and writing the function definitions
and their docstrings in the standard form. EpyDoc is then run over
the ``build_doc`` directory to generate useful API documentation.
Run it with::
python support/prep_doc.py build_doc/
(it is also run automatically by setup.py within the ``apidoc`` commmand.)
Detecting changes in SDL revisions
``support/diff_changes.py`` can be used to checkout consecutive releases
of libsdl and diff each version against its predecessor. The results
are written to ``support/diff_1.2.x.txt``, where x is the newer version.
When a new version of SDL is released, add the revision number to
the script and re-run it. Examine the generated report for added or
changed function prototypes and structures, and make the appropriate
changes to SDL-ctypes.