Commits

marcus  committed f8aa195

Fixed sdl.time.add_timer() and sdl.time.remove_timer() behaviour for threaded Python.
Added more sdl.time unittests.
Added freetype.constants module docs.
More math module portage.
Fixed various doc issues.
Removed superfluous freetype constants.

  • Participants
  • Parent commits 11ecf01
  • Branches pgreloaded

Comments (0)

Files changed (25)

 #today_fmt = '%B %d, %Y'
 
 # List of documents that shouldn't be included in the build.
-#unused_docs = []
+unused_docs = [ 'ref/pygame2_base',
+                'ref/pygame2_math_base',
+                'ref/pygame2_midi_base',
+                'ref/pygame2_sdl_base',
+                'ref/pygame2_sdlext_base',
+                'ref/pygame2_sdlgfx_base',
+                'ref/pygame2_sdlimage_base',
+                'ref/pygame2_sdlmixer_base',
+                'ref/pygame2_sdlttf_base',
+                ]
 
 # List of directories, relative to source directory, that shouldn't be searched
 # for source files.

File doc/create_rstref.py

             fp.write ("%s" % self.create_desc_rst (self.description))
             fp.write (".. module:: %s\n" % (name))
             fp.write ("   :synopsis: %s\n\n" % (self.shortdesc))
+        else:
+            fp.write (".. currentmodule:: %s\n\n" % (name))
 
         if len (self.example) > 0:
             fp.write (self.create_example_rst (self.example, True))

File doc/src/freetype_constants.rst

+:mod:`pygame2.freetype.constants` -- Constants for the FreeType extension
+=========================================================================
+
+This module contains the constants used within the :mod:`pygame2.freetype`
+module.
+
+.. module:: pygame2.freetype.constants
+   :synopsis: Constants used within the :mod:`pygame2.freetype` module.
+
+Style Constants
+---------------
+
+Those constants denote the available font styles for the various
+:class:`pygame2.freetype.Font` methods.
+
+.. data:: STYLE_NORMAL
+   
+   Use the default style as given by the font.
+   
+.. data:: STYLE_BOLD
+
+   Use the bold style of the font. If the font does not contain information for
+   bold text and glyph rendering, it will be emulated.
+
+.. data:: STYLE_ITALIC
+
+   Use the italic style of the font. If the font does not contain information
+   for italic text and glyph rendering, it will be emulated.
+
+.. data:: STYLE_UNDERLINE
+
+   Use an underlined style of the font. This will cause the glyphs and texts
+   to be rendered with an additional line beneath the glyph baseline.
+
+Bounding Box Constants
+----------------------
+
+Those constants are used or getting the glyph and text metrics of a specific
+:class:`pygame2.freetype.Font` in the :meth:`pygame2.freetype.Font.get_metrics`
+method.
+
+.. data:: BBOX_EXACT
+
+    Return accurate floating point values for each individual glyph.
+    In contrast to the :data:`BBOX_EXACT_GRIDFIT` constant, this can return
+    different minimum and maximum y extents for each glyph.
+
+.. data:: BBOX_EXACT_GRIDFIT
+
+    Return accurate floating point values aligned to the surrounding drawing
+    grid for each glyph.
+
+.. data:: BBOX_PIXEL
+    
+    Return pixel coordinates (integer values) for each individual glyph.
+    In contrast to the :data:`BBOX_EXACT_GRIDFIT` constant, this can return
+    different minimum and maximum y extents for each glyph.
+
+.. data:: BBOX_PIXEL_GRIDFIT
+
+    Return  pixel coordinates (integer values) aligned to the surrounding
+    drawing grid for each glyph.

File doc/src/freetypebase.xml

       FreeType, namely TTF, Type1, CFF, OpenType, SFNT, PCF, FNT, BDF,
       PFR and Type42 fonts.
 
-      This module is optional, and replaces all of the functionality of the
-      original 'font' module, whilst expanding it. This module depends in no
-      way on the SDL_ttf library.
-
-      You should test that pygame.freetype is initialized before attempting
-      to use the module; if the module is available and loaded, it will be
-      automatically initialized by pygame.init()
-
-      Most of the work done with fonts is done by using the actual Font objects.
-      The module by itself only has routines to initialize itself and create
-      Font objects with pygame.freetype.Font().
-
-      You can load fonts from the system by using the pygame.freetype.SysFont()
-      function. There are a few other functions to help lookup the system fonts.
-
-      Pygame comes with a builtin default font. This can always be accessed by
-      passing None as the font name to the Font constructor.
+      Most of the work done with fonts is done by using the actual :class:`Font`
+      objects. The module by itself only has routines to initialize itself and
+      create :class:`Font` objects.
   </desc>
   <func name="get_error">
-    <call>get_error() -> string</call>
+    <call>get_error() -> str</call>
     <desc>
         Returns the description of the last error which occurred in the
         FreeType library, or None if no errors have occurred.
       This function must be called before trying to use any of the functionality
       of the 'freetype' module. It is safe to call this function more than once.
 
-      Optionally, you may specify a default size for the Glyph cache:
+      Optionally, you may specify a default size for the glyph cache:
       this is the maximum amount of glyphs that will be cached at any
       given time by the module.
       Exceedingly small values will be automatically tuned for performance.
     </desc>
   </func>
 
-
   <class name="Font">
     <constructor>Font (file [, ptsize, style, index]) -> Font</constructor>
     <desc>
         to 0; font loading will fail if the given index is not contained
         in the font.
 
-        The 'style' argument will set the default style (italic,
+        The *style* argument will set the default style (italic,
         underline, bold) used to draw this font. This style may be
-        overriden on any Font.render() call.
+        overriden on any :meth:`render` call.
     </desc>
     <attr name="name">
       <desc>Read only. Gets the name of the font face.</desc>
     <method name="get_size">
       <call>get_size(text [, style, rotation, ptsize]) -> int, int</call>
       <desc>
-          Gets the size in pixels which 'text' will occupy when rendered
+          Gets the size in pixels which *text* will occupy when rendered
           using this Font. The calculations will take into account the
           font's default style (e.g. underlined fonts take extra height
           for the underline), or the style may be overriden by the
-          'style' parameter.
+          *style* parameter.
 
           Returns a tuple containing the width and height of the text's
           bounding box. 
           
           The calculations are done using the font's default size in points,
           without any rotation, and taking into account fonts which are set
-          to be drawn vertically via the Font.vertical attribute.
+          to be drawn vertically via the :attr:`vertical` attribute.
           Optionally you may specify another point size to use via the
-          'ptsize' argument, or a text rotation via the 'rotation' argument.
+          *ptsize* argument, or a text rotation via the 'rotation' argument.
       </desc>
     </method>
     <method name="get_metrics">
       <call>get_metrics(text, [bbmode, ptsize]) -> [(...), ...]</call>
       <desc>
-        Returns the glyph metrics for each character in 'text'.
+        Returns the glyph metrics for each character in *text*.
 
         The glyph metrics are returned inside a list; each character will
         be represented as a tuple inside the list with the following values:
           (min_x, max_x, min_y, max_y, horizontal_advance)
 
         By default, these values are returned as grid-fitted pixel
-        coordinates (ints). Optionally, one of the following constants
-        (which can be found on the FreeType constants module) may be passed
-        as the bbmode argument:
+        coordinates (ints). Optionally, one of the bounding box constants
+        of the :mod:`pygame2.freetype.constants` module  may be passed
+        as the *bbmode* argument.
 
-            BBOX_EXACT: Return accurate floating point values.
-
-            BBOX_EXACT_GRIDFIT: Return accurate floating point values aligned
-            to the drawing grid.
-
-            BBOX_PIXEL: Return pixel coordinates (ints).
-
-            BBOX_PIXEL_GRIDFIT (default): Return grid-aligned pixel coordinates.
-        
         The calculations are done using the font's default size in points.
         Optionally you may specify another point size to use.
-
       </desc>
     </method>
     <attr name="height">
         :class:`pygame2.sdl.video.Surface` (independently of its bit
         depth), the text will be rendered directly on top of it at the
         passed coordinates, using the given *fgcolor*, and painting
-        the background of the text with the given 'bgcolor', if available. 
+        the background of the text with the given *bgcolor*, if available. 
         The alpha values for both colors are always taken into account.
         The width and height of the rendered text will be returned in a tuple.
 
         Font.vertical attribute.
         
         Optionally you may specify another point size to use via the
-        'ptsize' argument, a text rotation via the 'rotation' argument,
-        or a new text style via the 'style' argument.
+        *ptsize* argument, a text rotation via the *rotation* argument,
+        or a new text style via the *style* argument.
 
-        This function is only available when PyGame 2 has been compiled
+        This function is only available when PyGame2 has been compiled
         with SDL support.
       </desc>
     </method>
         <desc>
             Gets or sets the default style of the Font. This default style
             will be used for all text rendering and size calculations unless
-            overriden specifically in the `render()` or `get_size()` calls.
-            The style value may be a bitwise OR of one or more of the following
-            constants:
+            overriden specifically in the :meth:`render()` or :meth:`get_size()`
+            calls. The style value may be a bitwise OR of one or more of the
+            following constants:
                 
-                STYLE_NONE
-                STYLE_UNDERLINE
-                STYLE_ITALIC
-                STYLE_BOLD
+                * STYLE_NONE
+                * STYLE_UNDERLINE
+                * STYLE_ITALIC
+                * STYLE_BOLD
 
-            These constants may be found on the FreeType constants module.
+            These constants may be found in the
+            :mod:`pygame2.freetype.constants` module.
             Optionally, the default style can be modified or obtained
             accessing the individual style attributes (underline, italic,
             bold).
       <desc>
         Gets or sets whether the font will be underlined when drawing text.
         This default style value will be used for all text rendering and 
-        size calculations unless overriden specifically in the `render()` 
-        or `get_size()` calls, via the 'style' parameter.
+        size calculations unless overriden specifically in the :meth:`render()` 
+        or :meth:`get_size()` calls, via the *style* parameter.
       </desc>
     </attr>
     <attr name="bold">
       <desc>
         Gets or sets whether the font will be bold when drawing text.
         This default style value will be used for all text rendering and 
-        size calculations unless overriden specifically in the `render()` 
-        or `get_size()` calls, via the 'style' parameter.
+        size calculations unless overriden specifically in the :meth:`render()` 
+        or :meth:`get_size()` calls, via the *style* parameter.
       </desc>
     </attr>
     <attr name="italic">
       <desc>
         Gets or sets whether the font will be slanted when drawing text.
         This default style value will be used for all text rendering and 
-        size calculations unless overriden specifically in the `render()` 
-        or `get_size()` calls, via the 'style' parameter.
+        size calculations unless overriden specifically in the :meth:`render()` 
+        or :meth:`get_size()` calls, via the *style* parameter.
       </desc>
     </attr>
     <attr name="fixed_width">

File doc/src/mask.xml

 
         If an *outputmask* is specified, the output is drawn onto
         *outputmask* and *outputmask* is returned. Otherwise a mask of
-        size :attr:`size` + *mask*.:attr:`size` - (1, 1) is created.
+        size :attr:`size` + :attr:`size` - (1, 1) is created.
       </desc>
     </method>
     <attr name="count">

File doc/src/mathbase.xml

       <call>normalize_ip () -> None</call>
       <desc></desc>
     </method>
+    <method name="slerp">
+      <call>slerp () -> Vector</call>
+      <desc></desc>
+    </method>
   </class>
   
   <class name="Vector2">
     <attr name="y">
       <desc>Gets or sets second element of the :class:`Vector2`.</desc>
     </attr>
+    <method name="rotate">
+      <call>rotate () -> Vector2</call>
+      <desc></desc>
+    </method>
+    <method name="rotate_ip">
+      <call>rotate_ip () -> Vector2</call>
+      <desc></desc>
+    </method>
   </class>
 
   <class name="Vector3">

File doc/src/modules.rst

    pygame2_examples.rst
    pygame2_font.rst
    pygame2_freetype_base.rst
+   pygame2_freetype_constants.rst
    pygame2_math.rst
    pygame2_mask.rst
    pygame2_midi.rst

File doc/src/sdlbase.xml

 
 <module name="pygame2.sdl.base">
   <show>0</show>
-  <alias>pygame2.sdl.base</alias>
+  <alias>pygame2.sdl</alias>
   <short>basic SDL wrapper module</short>
   <desc>
     Basic Pygame2 SDL wrapper functions.
       Initializes one or more SDL subsystems.
 
       In case a specific part of SDL was not initialized using
-      :func:`pygame2.sdl.init`, this function can be used to initialize it at
+      :func:`init`, this function can be used to initialize it at
       a later time.
 
       In contrast to the original SDL library, you do not need to call
-      :func:`pygame2.sdl.init` beforehand to initialize anything. In
-      case it was not done before, :func:`pygame2.sdl.init` will be
+      :func:`init` beforehand to initialize anything. In
+      case it was not done before, :func:`init` will be
       called implicitly with the passed *flags*.
 
       In case an error occured, False will be returned. The detailled

File doc/src/sdlevent.xml

 
       This will block a single or multiple event types from being
       processed by the event system and thus will exactly behave like
-      ``:func:`state`(type, :const:`IGNORE`)``.
+      ``state(type, IGNORE)``.
 
       In case other event types are already blocked, the block for them
       will be reset.

File doc/src/sdlextbase.xml

 
 <module name="pygame2.sdlext.base">
   <show>0</show>
-  <alias>pygame2.sdlext.base</alias>
+  <alias>pygame2.sdlext</alias>
   <short>basic extensions for SDL modules</short>
   <desc>
     Basic extensions for the pygame2 SDL wrapper.

File doc/src/sdlgfxbase.xml

 
 <module name="pygame2.sdlgfx.base">
   <show>0</show>
-  <alias>pygame2.sdlgfx.base</alias>
+  <alias>pygame2.sdlgfx</alias>
   <short>basic SDL_gfx wrapper module</short>
   <desc>
     Basic SDL_gfx library wrapper module

File doc/src/sdlimagebase.xml

 
 <module name="pygame2.sdlimage.base">
   <show>0</show>
-  <alias>pygame2.sdlimage.base</alias>
+  <alias>pygame2.sdlimage</alias>
   <short>basic SDL_image wrapper module</short>
   <desc>
     Basic SDL_image wrapper module

File doc/src/sdlmixerbase.xml

 
 <module name="pygame2.sdlmixer.base">
   <show>0</show>
-  <alias>pygame2.sdlmixer.base</alias>
+  <alias>pygame2.sdlmixer</alias>
   <short>basic SDL_mixer wrapper module</short>
   <desc>
     .. note::

File doc/src/sdltime.xml

       Adds a timer callback to be called periodically.
       
       Adds a timer callback to be called periodically using the specified
-      *interval*. *callable* can be any callable objet, method or function. On
-      invocation, the optional *data* will be passed to the callable.
+      *interval*. *callable* can be any callable objet, method or function.
+      On invocation, the optional *data* will be passed to the callable. If
+      *data* is a sequence, each item of it will be passed as single argument
+      to tha callable.
+            
+      This will return an CObject that acts as unique id for the timer callback.
+
+      .. note::
       
-      This will return an CObject that acts as unique id for the timer callback.
+        Any added timer will be removed automatically on calling :func:`quit`.
     </desc>
   </func>
   <func name="delay">
 
       .. note::
 
-        This does *not* require :meth:`init` to be called before.
+        This does *not* require :func:`init` to be called before.
     </desc>
   </func>
   <func name="get_ticks">
       After calling this function, you should not invoke any class,
       method or function related to the timer subsystem as they are
       likely to fail or might give unpredictable results.
+      
+      .. note::
+      
+        Any timer set by :func:`set_timer` or :func:`add_timer` will be removed
+        automatically on calling :func:`quit`.
     </desc>
   </func>
   <func name="remove_timer">
       Sets a single timer callback to be called periodically using the specified
       *interval* in milliseconds. The timer callback can be reset by passing
       None as *callable* object.
+      
+      .. note::
+      
+        Any set timer will be removed automatically on calling :func:`quit`.
     </desc>
   </func>
   <func name="was_init">

File doc/src/sdlttfbase.xml

 
 <module name="pygame2.sdlttf.base">
   <show>0</show>
-  <alias>pygame2.sdlttf.base</alias>
+  <alias>pygame2.sdlttf</alias>
   <short>basic SDL_ttf wrapper module</short>
   <desc>
     Basic SDL_ttf wrapper module

File src/freetype/ft_constants.c

     DEC_CONST(BBOX_PIXEL);
     DEC_CONST(BBOX_PIXEL_GRIDFIT);
 
-    DEC_CONST(RENDER_NEWBYTEARRAY);
-    DEC_CONST(RENDER_NEWSURFACE);
-    
     MODINIT_RETURN(module);
 
 fail:

File src/freetype/pgfreetype.h

 #define FT_RFLAG_HINTED         (1 << 3)
 #define FT_RFLAG_DEFAULTS       (FT_RFLAG_NONE | FT_RFLAG_HINTED)
 
-
-#define FT_RENDER_NEWBYTEARRAY      0x0
-#define FT_RENDER_NEWSURFACE        0x1
-#define FT_RENDER_EXISTINGSURFACE   0x2
-
 /**********************************************************
  * Global module types
  **********************************************************/

File src/math/mathmod.c

     return ret;
 }
 
+/* C API */
+double*
+VectorCoordsFromObj (PyObject *object, Py_ssize_t *dims)
+{
+    double *coords;
+
+    if (!object || !dims)
+    {
+        PyErr_SetString (PyExc_ValueError, "arguments must not be NULL");
+        return NULL;
+    }
+    if (PyVector_Check (object))
+    {
+        *dims = ((PyVector*)object)->dim;
+        coords = PyMem_New (double, *dims);
+        if (!coords)
+            return NULL;
+        memcpy (coords, ((PyVector*)object)->coords, sizeof (double) * (*dims));
+        return coords;
+    }
+    else if (PySequence_Check (object))
+    {
+        Py_ssize_t i;
+
+        *dims = PySequence_Size (object);
+        if ((*dims) < 2)
+        {
+            PyErr_SetString (PyExc_ValueError,
+                "sequence must be greater than 1");
+            return NULL;
+        }
+        coords = PyMem_New (double, *dims);
+        if (!coords)
+            return NULL;
+
+        for (i = 0; i < (*dims); i++)
+        {
+            if (!DoubleFromSeqIndex (object, i, &(coords[i])))
+            {
+                PyMem_Free (coords);
+                return NULL;
+            }
+        }
+        return coords;
+    }
+    else
+    {
+        PyErr_SetString (PyExc_TypeError,
+            "object must be a Vector or sequence");
+        return NULL;
+    }
+}
+
 #ifdef IS_PYTHON_3
 PyMODINIT_FUNC PyInit_base (void)
 #else
     PyModule_AddObject (mod, "Vector3", (PyObject *) &PyVector3_Type);
 
     /* Export C API */
+    c_api[PYGAME_MATH_FIRSTSLOT] = &VectorCoordsFromObj;
+
     vector_export_capi (c_api);
     vector2_export_capi (c_api);
     vector3_export_capi (c_api);

File src/math/mathmod.h

 
 double _ScalarProduct (const double *coords1, const double *coords2,
     Py_ssize_t size);
+double* VectorCoordsFromObj (PyObject *object, Py_ssize_t *dims);
 
 void vector_export_capi (void **capi);
 void vector2_export_capi (void **capi);

File src/math/pgmath.h

 #endif
 
 #define PYGAME_MATH_FIRSTSLOT 0
-#define PYGAME_MATH_NUMSLOTS 0
+#define PYGAME_MATH_NUMSLOTS 1
 #ifndef PYGAME_MATH_INTERNAL
+#define VectorCoordsFromObj                                             \
+    (*(double*(*)(PyObject*,Py_ssize_t*))PyGameMath_C_API[PYGAME_MATH_FIRSTSLOT+0])
 #endif /* PYGAME_MATH_INTERNAL */
 
 typedef struct

File src/math/vector.c

 static PyObject* _vector_get_epsilon (PyObject *self, void *closure);
 static int _vector_set_epsilon (PyObject *self, PyObject *value, void *closure);
 static PyObject* _vector_get_elements (PyObject *self, void *closure);
-static int _vector_set_elements (PyObject *self, PyObject *value, void *closure);
+static int _vector_set_elements (PyObject *self, PyObject *value,
+    void *closure);
 static PyObject* _vector_get_length (PyVector *self, void *closure);
 static PyObject* _vector_get_length_squared (PyVector *self, void *closure);
 static PyObject* _vector_normalize (PyVector *self);
 static PyObject* _vector_normalize_ip (PyVector *self);
+static PyObject* _vector_slerp (PyVector *self, PyObject *args);
 
 /* Generic math operations for vectors. */
 static PyObject* _vector_generic_math (PyObject *o1, PyObject *o2, int op);
 /* Sequence protocol methods */
 static Py_ssize_t _vector_len (PyVector *self);
 static PyObject* _vector_item (PyVector *self, Py_ssize_t _index);
-static int _vector_ass_item (PyVector *self, Py_ssize_t _index, PyObject *value);
+static int _vector_ass_item (PyVector *self, Py_ssize_t _index,
+    PyObject *value);
 static PyObject* _vector_slice (PyVector *self, Py_ssize_t ilow,
     Py_ssize_t ihigh);
 static int _vector_ass_slice (PyVector *self, Py_ssize_t ilow, Py_ssize_t ihigh,
 /**
  * Methods for the PyVector.
  */
-static PyMethodDef _vector_methods[] = {
+static PyMethodDef _vector_methods[] =
+{
     { "normalize", (PyCFunction) _vector_normalize, METH_NOARGS,
       DOC_BASE_VECTOR_NORMALIZE },
     { "normalize_ip", (PyCFunction) _vector_normalize_ip, METH_NOARGS,
       DOC_BASE_VECTOR_NORMALIZE_IP },
+    { "slerp", (PyCFunction) _vector_slerp, METH_VARARGS,
+      DOC_BASE_VECTOR_SLERP },
     { NULL, NULL, 0, NULL },
 };
 
     (binaryfunc)0,                    /* nb_inplace_or;        __ior__ */
     (binaryfunc) _vector_floor_div,   /* nb_floor_divide;         __floor__ */
     (binaryfunc) _vector_div,         /* nb_true_divide;          __truediv__ */
-    (binaryfunc) _vector_inplace_floor_div, /* nb_inplace_floor_divide; __ifloor__ */
-    (binaryfunc) _vector_inplace_div, /* nb_inplace_true_divide;  __itruediv__ */
+    (binaryfunc) _vector_inplace_floor_div, /* nb_inplace_floor_divide; */
+    (binaryfunc) _vector_inplace_div, /* nb_inplace_true_divide; */
 #if PY_VERSION_HEX >= 0x02050000
     (unaryfunc)0,                     /* nb_index */
 #endif
 {
     PyVector *v = (PyVector*) self;
     /* TODO */
-    return Text_FromFormat ("Vector%dd()", v->dim);
+    return Text_FromFormat ("Vector%d()", v->dim);
 }
 
 /* Vector getters/setters */
 static PyObject*
 _vector_get_length (PyVector *self, void *closure)
 {
-    double length_squared = _ScalarProduct(self->coords, self->coords,
+    double length_squared = _ScalarProduct (self->coords, self->coords,
         self->dim);
     return PyFloat_FromDouble (sqrt (length_squared));
 }
 static PyObject*
 _vector_get_length_squared (PyVector *self, void *closure)
 {
-    double length_squared = _ScalarProduct(self->coords, self->coords,
+    double length_squared = _ScalarProduct (self->coords, self->coords,
         self->dim);
     return PyFloat_FromDouble (length_squared);
 }
     double length;
     PyVector *ret;
     
-    length = sqrt (_ScalarProduct(self->coords, self->coords, self->dim));
+    length = sqrt (_ScalarProduct (self->coords, self->coords, self->dim));
     if (length == 0)
     {
         PyErr_SetString (PyExc_ZeroDivisionError,
 }
 
 static PyObject*
+_vector_slerp (PyVector *self, PyObject *args)
+{
+    Py_ssize_t i, otherdim;
+    PyObject *other;
+    PyVector *ret;
+    double *othercoords;
+    double angle, t, length1, length2, f0, f1, f2;
+
+    if (!PyArg_ParseTuple(args, "Od:slerp", &other, &t))
+        return NULL;
+
+    if (!IsVectorCompatible (other))
+    {
+        PyErr_SetString (PyExc_TypeError, "other must be a vector compatible");
+        return NULL;
+    }
+
+    othercoords = VectorCoordsFromObj (other, &otherdim);
+    if (!othercoords)
+        return NULL;
+    if (otherdim != self->dim)
+    {
+        /* TODO: is it okay to fail here? */
+        PyErr_SetString (PyExc_TypeError, "other must have the same dimension");
+        PyMem_Free (othercoords);
+        return NULL;
+    }
+
+    if (fabs (t) > 1)
+    {
+        PyErr_SetString(PyExc_ValueError, "t must be in range [-1, 1]");
+        PyMem_Free (othercoords);
+        return NULL;
+    }
+    
+    length1 = sqrt(_ScalarProduct (self->coords, self->coords, self->dim));
+    length2 = sqrt(_ScalarProduct (othercoords, othercoords, self->dim));
+
+    if ((length1 < self->epsilon) || (length2 < self->epsilon))
+    {
+        PyErr_SetString (PyExc_ZeroDivisionError,
+            "can not use slerp with zero-Vector");
+        PyMem_Free (othercoords);
+        return NULL;
+    }
+    angle = acos (_ScalarProduct (self->coords, othercoords, self->dim) /
+        (length1 * length2));
+
+    if (t < 0)
+    {
+        angle -= 2 * M_PI;
+        t = -t;
+    }
+
+    if (self->coords[0] * othercoords[1] < self->coords[1] * othercoords[0])
+        angle *= -1;
+
+    ret = (PyVector *) PyVector_NewSpecialized (self->dim);
+    if (!ret)
+    {
+        PyMem_Free (othercoords);
+        return NULL;
+    }
+    f0 = ((length2 - length1) * t + length1) / sin (angle);
+    f1 = sin (angle * (1 - t)) / length1;
+    f2 = sin (angle * t) / length2;
+    for (i = 0; i < self->dim; ++i)
+        ret->coords[i] = (self->coords[i] * f1 + othercoords[i] * f2) * f0;
+
+    PyMem_Free (othercoords);
+    return (PyObject*) ret;
+}
+
+
+static PyObject*
 _vector_generic_math (PyObject *o1, PyObject *o2, int op)
 {
     PyVector *v, *retval;
         if (otherdim > dim)
         {
             PyErr_SetString (PyExc_TypeError,
-                "right-hand argument must not have more dimensions than left-hand argument");
+                "right op must not have more dimensions than left op");
             return NULL;
         }
     }

File src/math/vector2.c

 static PyObject* _vector2_get_y (PyObject *self, void *closure);
 static int _vector2_set_y (PyObject *self, PyObject *value, void *closure);
 
+static void _do_rotate (double *dst_coords, double *src_coords, double angle,
+    double epsilon);
+
+static PyObject* _vector2_rotate (PyObject *self, PyObject *args);
+static PyObject* _vector2_rotate_ip (PyObject *self, PyObject *args);
+
 /**
  * Methods for the PyVector2.
  */
-static PyMethodDef _vector2_methods[] = {
+static PyMethodDef _vector2_methods[] =
+{
+    { "rotate", _vector2_rotate, METH_VARARGS, DOC_BASE_VECTOR2_ROTATE },
+    { "rotate_ip", _vector2_rotate_ip, METH_VARARGS,
+      DOC_BASE_VECTOR2_ROTATE_IP },
     { NULL, NULL, 0, NULL },
 };
 
     return 0;
 }
 
-/* Vector getters/setters */
+/* Vector2 getters/setters */
 
 /**
  * x = Vector2.x
     return 0;
 }
 
+/* Vector2 methods */
+
+static void
+_do_rotate (double *dst_coords, double *src_coords, double angle,
+    double epsilon)
+{
+    /* make sure angle is in range [0, 360) */
+    angle = fmod (angle, 360.);
+    if (angle < 0)
+        angle += 360.;
+
+    /* special-case rotation by 0, 90, 180 and 270 degrees */
+    if (fmod (angle + epsilon, 90.) < 2 * epsilon)
+    {
+        switch ((int)((angle + epsilon) / 90))
+        {
+        case 0: /* 0 degrees */
+            dst_coords[0] = src_coords[0];
+            dst_coords[1] = src_coords[1];
+            break;
+        case 1: /* 90 degrees */
+            dst_coords[0] = -src_coords[1];
+            dst_coords[1] = src_coords[0];
+            break;
+        case 2: /* 180 degrees */
+            dst_coords[0] = -src_coords[0];
+            dst_coords[1] = -src_coords[1];
+            break;
+        case 3: /* 270 degrees */
+            dst_coords[0] = src_coords[1];
+            dst_coords[1] = -src_coords[0];
+            break;
+        default:
+            /* this should NEVER happen and means a bug in the code */
+            PyErr_SetString (PyExc_RuntimeError,
+               "Please report this bug in vector2_do_rotate to the developers");
+            break;
+        }
+    }
+    else
+    {
+        double sinv, cosv;
+
+        angle = DEG2RAD (angle);
+        sinv = sin (angle);
+        cosv = cos (angle);
+
+        dst_coords[0] = cosv * src_coords[0] - sinv * src_coords[1];
+        dst_coords[1] = sinv * src_coords[0] + cosv * src_coords[1];
+    }
+}
+
+static PyObject*
+_vector2_rotate (PyObject *self, PyObject *args)
+{
+    double angle;
+    PyVector *v = (PyVector *) self;
+    PyVector *ret;
+
+    if (!PyArg_ParseTuple (args, "d:rotate", &angle))
+        return NULL;
+
+    ret = (PyVector*) PyVector2_New (0., 0.);
+    if (!ret)
+        return NULL;
+    _do_rotate (ret->coords, v->coords, angle, v->epsilon);
+    return (PyObject*)ret;
+}
+
+static PyObject*
+_vector2_rotate_ip (PyObject *self, PyObject *args)
+{
+    double angle, tmp[2];
+    PyVector *v = (PyVector *) self;
+
+    if (!PyArg_ParseTuple (args, "d:rotate_ip", &angle))
+        return NULL;
+
+    tmp[0] = v->coords[0];
+    tmp[1] = v->coords[1];
+    _do_rotate (v->coords, tmp, angle, v->epsilon);
+    Py_RETURN_NONE;
+}
 
 /* C API */
 PyObject*

File src/math/vector3.c

 /**
  * Methods for the PyVector3.
  */
-static PyMethodDef _vector3_methods[] = {
+static PyMethodDef _vector3_methods[] =
+{
     { NULL, NULL, 0, NULL },
 };
 

File src/sdl/timemod.c

 
 typedef struct
 {
-    SDL_TimerID  id;
-    PyObject    *callable;
-    PyObject    *param;
+    SDL_TimerID    id;
+    PyObject      *callable;
+    PyObject      *param;
+#ifdef WITH_THREAD
+    PyThreadState *thread;
+#endif
 } _TimerData;
 
 typedef struct {
 
 static Uint32 _sdl_timercallback (Uint32 interval);
 static Uint32 _sdl_timerfunc (Uint32 interval, void *param);
-static void _free_timerdata (void *data);
+static void _free_timerdata (_TimerData *data);
+static void _remove_alltimers (_SDLTimerState *mod);
 
 static PyObject* _sdl_timeinit (PyObject *self);
 static PyObject* _sdl_timewasinit (PyObject *self);
 static Uint32
 _sdl_timerfunc (Uint32 interval, void *param)
 {
-    _TimerData *timerdata;
-    PyObject *result, *val, *timer;
-    Uint32 retval;
-    _SDLTimerState *state = SDLTIMER_STATE;
+    _TimerData *timerdata = (_TimerData*) param;
+    PyObject *result, *val;
+    Uint32 retval = 0;
+    _SDLTimerState *state;
+
+#ifdef WITH_THREAD
+    PyThreadState* oldstate;
+    PyEval_AcquireLock ();
+    oldstate = PyThreadState_Swap (timerdata->thread);
+#endif
+    state = SDLTIMER_STATE;
     
-    timer = (PyObject*) param;
-    timerdata = (_TimerData*) PyCObject_AsVoidPtr (timer);
-
     val = PyLong_FromUnsignedLong (interval);
     if (timerdata->param)
-        result = PyObject_CallFunctionObjArgs (timerdata->callable, val,
-            timerdata->param);
+        result = PyObject_CallObject (timerdata->callable, timerdata->param);
     else
-        result = PyObject_CallObject (timerdata->callable, val);
-
+        result = PyObject_CallObject (timerdata->callable, NULL);
+    
     Py_DECREF (val);
-
+    if (!result)
+    {
+        PyErr_Warn (PyExc_RuntimeError, "timer callback failed:"); 
+        PyErr_Print ();
+        PyErr_Clear ();
+        return 0;
+    }
+    
     if (!Uint32FromObj (result, &retval))
     {
-        Py_ssize_t pos;
-
+        /* Wrong signature, ignore the callback */
+        PyErr_Clear ();
         Py_XDECREF (result);
-        pos = PySequence_Index (state->timerlist, timer);
-        PySequence_DelItem (state->timerlist, pos);
-
-        /* Wrong signature, remove the callback */
-        PyErr_SetString (PyExc_ValueError,
-            "callback must return a positive integer");
-        return 0;
+        PyErr_Warn (PyExc_ValueError,
+            "timer callback return value must be a positive integer"); 
+        retval = 0;
+    }
+    else
+    {
+        Py_XDECREF (result);
     }
 
-    Py_XDECREF (result);
+#ifdef WITH_THREAD
+    PyThreadState_Swap (oldstate);
+    PyEval_ReleaseLock ();
+#endif
     return retval;
 }
 
 static void
-_free_timerdata (void *data)
+_free_timerdata (_TimerData *data)
 {
-    _TimerData *t = (_TimerData*) data;
     if (!data)
         return;
+    
+    if (data->id)
+        SDL_RemoveTimer (data->id);
+    data->id = NULL;
 
-    if (t->id)
-        SDL_RemoveTimer(t->id);
+    Py_XDECREF (data->callable);
+    Py_XDECREF (data->param);
+#ifdef WITH_THREAD
+    PyThreadState_Clear (data->thread);
+    PyThreadState_Delete (data->thread);
+#endif
+    PyMem_Free (data);
+}
 
-    Py_XDECREF (t->callable);
-    Py_XDECREF (t->param);
-    PyMem_Free (t);
+static void
+_remove_alltimers (_SDLTimerState *state)
+{
+    Py_ssize_t pos, count;
+    PyObject *val;
+    _TimerData *timerdata;
+    
+    if (!state || state->timerlist == NULL)
+        return;
+    
+    /* Clean up all timers */
+    count = PyList_GET_SIZE (state->timerlist);
+    for (pos = 0; pos < count; pos++)
+    {
+        val = PyList_GET_ITEM (state->timerlist, pos);
+        timerdata = (_TimerData*) PyCObject_AsVoidPtr (val);
+        _free_timerdata (timerdata);
+    }
+    Py_XDECREF (state->timerlist);
+    state->timerlist = PyList_New (0);
 }
 
 static PyObject*
         SDL_SetTimer (0, NULL);
         state->timerhook = NULL;
     }
+    _remove_alltimers (state);
     Py_XDECREF (state->timerlist);
+    state->timerlist = NULL;
 
     if (SDL_WasInit (SDL_INIT_TIMER))
         SDL_QuitSubSystem (SDL_INIT_TIMER);
     PyObject *retval, *func, *data = NULL;
     _SDLTimerState *state = SDLTIMER_MOD_STATE (self);
 
+    ASSERT_TIME_INIT (NULL);
+    
     if (!state->timerlist)
     {
         state->timerlist = PyList_New (0);
         PyErr_SetString (PyExc_TypeError, "timer callback must be callable");
         return NULL;
     }
-
+    
     timerdata = PyMem_New (_TimerData, 1);
     if (!timerdata)
         return NULL;
+    
+    if (data)
+    {
+        if (!PySequence_Check (data))
+        {
+            /* Pack data */
+            PyObject *tuple = PyTuple_New (1);
+            if (!tuple)
+                return NULL;
+            PyTuple_SET_ITEM (tuple, 0, data);
+            Py_INCREF (data);
+            data = tuple;
+        }
+        else
+        {
+            Py_XINCREF (data);
+        }
+    }
+    Py_INCREF (func);
 
-    Py_INCREF (func);
-    Py_XINCREF (data);
     timerdata->callable = func;
     timerdata->param = data;
+    timerdata->id = NULL;
 
-    retval = PyCObject_FromVoidPtr (timerdata, _free_timerdata);
-    id = SDL_AddTimer (interval, _sdl_timerfunc, retval);
+#ifdef WITH_THREAD
+    PyEval_InitThreads ();
+    timerdata->thread = PyThreadState_New (PyThreadState_Get ()->interp);
+#endif
+
+    retval = PyCObject_FromVoidPtr (timerdata, NULL);
+    id = SDL_AddTimer (interval, _sdl_timerfunc, timerdata);
     if (!id)
     {
         Py_DECREF (retval);
     if (PyList_Append (state->timerlist, retval) == -1)
     {
         Py_DECREF (retval); /* Takes care of freeing. */
+        SDL_RemoveTimer (id);
         return NULL;
     }
-    Py_DECREF (retval); /* Decrease incremented refcount  */
     return retval;
 }
 
 static PyObject*
 _sdl_removetimer (PyObject *self, PyObject *args)
 {
-    _TimerData *timerdata, *idobj;
+    _TimerData *timerdata = NULL, *idobj;
     int found = 0;
     Py_ssize_t pos, count;
     PyObject *val, *cobj;
     _SDLTimerState *state = SDLTIMER_MOD_STATE (self);
     
+    ASSERT_TIME_INIT (NULL);
+    
     if (!PyArg_ParseTuple (args, "O:remove_timer", &cobj))
         return NULL;
 
     }
 
     idobj = (_TimerData*) PyCObject_AsVoidPtr (cobj);
+    if (!idobj || idobj->id == NULL)
+    {
+        PyErr_SetString (PyExc_ValueError, "timer already removed");
+        return NULL;
+    }
+    
     count = PyList_GET_SIZE (state->timerlist);
     for (pos = 0; pos < count; pos++)
     {
         val = PyList_GET_ITEM (state->timerlist, pos);
         timerdata = (_TimerData*) PyCObject_AsVoidPtr (val);
-        if (timerdata != idobj)
+        if (timerdata->id != idobj->id)
             continue;
         found = 1;
-        if (!SDL_RemoveTimer (timerdata->id))
-            Py_RETURN_FALSE;
+        break;
     }
     if (!found)
     {
         return NULL;
     }
     
-    timerdata->id = NULL;
-    PySequence_DelItem (state->timerlist, pos);
-    Py_RETURN_TRUE;
+    if (PySequence_DelItem (state->timerlist, pos) == -1)
+        return NULL;
+    _free_timerdata (timerdata);
+    Py_RETURN_NONE;
 }
 
 static int
 _timer_clear (PyObject *mod)
 {
     _SDLTimerState *state = SDLTIMER_MOD_STATE (mod);
+    
+    _remove_alltimers (state);
     Py_CLEAR (state->timerhook);
     Py_CLEAR (state->timerlist);
     return 0;

File test/sdl_time_test.py

 
 class SDLTimeTest (unittest.TestCase):
 
-    def todo_test_pygame2_sdl_time_add_timer(self):
+    def test_pygame2_sdl_time_add_timer(self):
 
         # __doc__ (as of 2010-01-06) for pygame2.sdl.time.add_timer:
 
         # 
         # This will return an CObject that acts as unique id for the timer callback.
         setargs = []
+        
         def _timercb (l, arg1, arg2):
-            print ("CB")
             l.append (arg1)
             l.append (arg2)
             return 10
         
+        self.assertRaises (pygame2.Error, sdltime.add_timer, _timercb)
+        
         sdltime.init ()
-        sdltime.add_timer (10, _timercb, (setargs, "Hello", "World"))
+        tobj = sdltime.add_timer (10, _timercb, (setargs, "Hello", "World"))
+        self.assert_ (tobj != None)
         t1 = t2 = sdltime.get_ticks ()
         while (t2 - t1 < 100):
             sdltime.delay (1)
             t2 = sdltime.get_ticks ()
-
+        self.assert_ (len (setargs) > 10)
         sdltime.quit ()
+        
+        self.assertRaises (pygame2.Error, sdltime.add_timer, _timercb)
 
     def test_pygame2_sdl_time_delay(self):
 
             sdltime.delay (20)
             last = time.time ()
             delaytime = (last - prev) * 1000
-            self.assert_ (15 < delaytime < 25)
+            self.assert_ (10 < delaytime < 50)
 
     def test_pygame2_sdl_time_get_ticks(self):
 
         # likely to fail or might give unpredictable results.
         self.assertEqual (sdltime.quit (), None)
 
-    def todo_test_pygame2_sdl_time_remove_timer(self):
+    def test_pygame2_sdl_time_remove_timer(self):
 
         # __doc__ (as of 2010-01-06) for pygame2.sdl.time.remove_timer:
 
         # 
         # Removes a previously added timer callback and throws an exception, if the
         # passed object is not a matching timer object.
-
-        self.fail() 
+        
+        def _timercb (flag):
+            flag.append (1)
+            return 10;
+        
+        self.assertRaises (pygame2.Error, sdltime.remove_timer, _timercb)
+        sdltime.init ()
+        self.assertRaises (TypeError, sdltime.remove_timer, None)
+        
+        flag = []
+        tobj = sdltime.add_timer (10, _timercb, (flag,))
+        self.assert_ (tobj != None)
+        t1 = t2 = sdltime.get_ticks ()
+        while (t2 - t1 < 50):
+            sdltime.delay (1)
+            t2 = sdltime.get_ticks ()
+        self.assertTrue (len (flag) != 0)
+        sdltime.remove_timer (tobj)
+        self.assertRaises (ValueError, sdltime.remove_timer, tobj)
+        
+        flag = []
+        tobj = sdltime.add_timer (10, _timercb, (flag,))
+        sdltime.remove_timer (tobj)
+        self.assertRaises (ValueError, sdltime.remove_timer, tobj)
+        self.assert_ (tobj != None)
+        t1 = t2 = sdltime.get_ticks ()
+        while (t2 - t1 < 50):
+            sdltime.delay (1)
+            t2 = sdltime.get_ticks ()
+        self.assert_ (len (flag) == 0)
+        sdltime.quit ()
+        self.assertRaises (pygame2.Error, sdltime.remove_timer, _timercb)
 
     def todo_test_pygame2_sdl_time_set_timer(self):