changeset 455:6864a38b2413

Make pytouhou.lib.sdl cimportable, and convert pytouhou.ui.window.* to extension types.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Mon, 02 Sep 2013 22:16:38 +0200
parents a502887557ac
children cae1ae9de430
files pytouhou/lib/_sdl.pxd pytouhou/lib/sdl.pxd pytouhou/lib/sdl.pyx pytouhou/ui/anmrenderer.pyx pytouhou/ui/gamerunner.pyx pytouhou/ui/music.py pytouhou/ui/music.pyx pytouhou/ui/texture.pyx pytouhou/ui/window.pyx setup.py
diffstat 9 files changed, 163 insertions(+), 234 deletions(-) [+]
line wrap: on
line diff
copy from pytouhou/lib/sdl.pxd
copy to pytouhou/lib/_sdl.pxd
--- a/pytouhou/lib/sdl.pxd
+++ b/pytouhou/lib/_sdl.pxd
@@ -12,7 +12,7 @@
 ## GNU General Public License for more details.
 ##
 
-cdef extern from "SDL.h":
+cdef extern from "SDL.h" nogil:
     ctypedef unsigned int Uint32
     ctypedef unsigned short Uint16
     ctypedef unsigned char Uint8
@@ -24,15 +24,15 @@ cdef extern from "SDL.h":
 
 
 IF UNAME_SYSNAME == "Windows":
-    cdef extern from "SDL_main.h":
+    cdef extern from "SDL_main.h" nogil:
         void SDL_SetMainReady()
 
 
-cdef extern from "SDL_error.h":
+cdef extern from "SDL_error.h" nogil:
     const char *SDL_GetError()
 
 
-cdef extern from "SDL_video.h":
+cdef extern from "SDL_video.h" nogil:
     ctypedef enum SDL_GLattr:
         SDL_GL_CONTEXT_MAJOR_VERSION
         SDL_GL_CONTEXT_MINOR_VERSION
@@ -59,7 +59,7 @@ cdef extern from "SDL_video.h":
     void SDL_SetWindowSize(SDL_Window *window, int w, int h)
 
 
-cdef extern from "SDL_scancode.h":
+cdef extern from "SDL_scancode.h" nogil:
     ctypedef enum SDL_Scancode:
         SDL_SCANCODE_Z
         SDL_SCANCODE_X
@@ -72,7 +72,7 @@ cdef extern from "SDL_scancode.h":
         SDL_SCANCODE_ESCAPE
 
 
-cdef extern from "SDL_events.h":
+cdef extern from "SDL_events.h" nogil:
     ctypedef enum SDL_EventType:
         SDL_KEYDOWN
         SDL_QUIT
@@ -91,22 +91,22 @@ cdef extern from "SDL_events.h":
     int SDL_PollEvent(SDL_Event *event)
 
 
-cdef extern from "SDL_keyboard.h":
+cdef extern from "SDL_keyboard.h" nogil:
     const Uint8 *SDL_GetKeyboardState(int *numkeys)
 
 
-cdef extern from "SDL_timer.h":
+cdef extern from "SDL_timer.h" nogil:
     Uint32 SDL_GetTicks()
     void SDL_Delay(Uint32 ms)
 
 
-cdef extern from "SDL_rect.h":
+cdef extern from "SDL_rect.h" nogil:
     ctypedef struct SDL_Rect:
         int x, y
         int w, h
 
 
-cdef extern from "SDL_surface.h":
+cdef extern from "SDL_surface.h" nogil:
     ctypedef struct SDL_Surface:
         int w, h
         unsigned char *pixels
@@ -116,7 +116,7 @@ cdef extern from "SDL_surface.h":
     SDL_Surface *SDL_CreateRGBSurface(Uint32 flags, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
 
 
-cdef extern from "SDL_rwops.h":
+cdef extern from "SDL_rwops.h" nogil:
     ctypedef struct SDL_RWops:
         pass
 
@@ -124,7 +124,7 @@ cdef extern from "SDL_rwops.h":
     int SDL_RWclose(SDL_RWops *context)
 
 
-cdef extern from "SDL_image.h":
+cdef extern from "SDL_image.h" nogil:
     int IMG_INIT_PNG
 
     int IMG_Init(int flags)
@@ -132,7 +132,7 @@ cdef extern from "SDL_image.h":
     SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
 
 
-cdef extern from "SDL_mixer.h":
+cdef extern from "SDL_mixer.h" nogil:
     ctypedef enum:
         MIX_DEFAULT_FORMAT
 
--- a/pytouhou/lib/sdl.pxd
+++ b/pytouhou/lib/sdl.pxd
@@ -12,155 +12,89 @@
 ## GNU General Public License for more details.
 ##
 
-cdef extern from "SDL.h":
-    ctypedef unsigned int Uint32
-    ctypedef unsigned short Uint16
-    ctypedef unsigned char Uint8
-
-    int SDL_INIT_VIDEO
-
-    int SDL_Init(Uint32 flags)
-    void SDL_Quit()
-
-
-IF UNAME_SYSNAME == "Windows":
-    cdef extern from "SDL_main.h":
-        void SDL_SetMainReady()
-
-
-cdef extern from "SDL_error.h":
-    const char *SDL_GetError()
+from _sdl cimport *
 
 
-cdef extern from "SDL_video.h":
-    ctypedef enum SDL_GLattr:
-        SDL_GL_CONTEXT_MAJOR_VERSION
-        SDL_GL_CONTEXT_MINOR_VERSION
-        SDL_GL_DOUBLEBUFFER
-        SDL_GL_DEPTH_SIZE
+cdef Uint32 INIT_VIDEO
+cdef Uint32 INIT_PNG
 
-    ctypedef enum SDL_WindowFlags:
-        SDL_WINDOWPOS_CENTERED
-        SDL_WINDOW_OPENGL
-        SDL_WINDOW_SHOWN
+cdef SDL_GLattr GL_CONTEXT_MAJOR_VERSION
+cdef SDL_GLattr GL_CONTEXT_MINOR_VERSION
+cdef SDL_GLattr GL_DOUBLEBUFFER
+cdef SDL_GLattr GL_DEPTH_SIZE
+
+cdef SDL_WindowFlags WINDOWPOS_CENTERED
+cdef SDL_WindowFlags WINDOW_OPENGL
+cdef SDL_WindowFlags WINDOW_SHOWN
 
-    ctypedef struct SDL_Window:
-        pass
-
-    ctypedef void *SDL_GLContext
+#TODO: should be SDL_Scancode, but Cython doesn’t allow enum for array indexing.
+cdef long SCANCODE_Z
+cdef long SCANCODE_X
+cdef long SCANCODE_LSHIFT
+cdef long SCANCODE_UP
+cdef long SCANCODE_DOWN
+cdef long SCANCODE_LEFT
+cdef long SCANCODE_RIGHT
+cdef long SCANCODE_LCTRL
+cdef long SCANCODE_ESCAPE
 
-    int SDL_GL_SetAttribute(SDL_GLattr attr, int value)
-    SDL_Window *SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags)
-    SDL_GLContext SDL_GL_CreateContext(SDL_Window *window)
-    void SDL_GL_SwapWindow(SDL_Window *window)
-    void SDL_GL_DeleteContext(SDL_GLContext context)
-    void SDL_DestroyWindow(SDL_Window *window)
+cdef SDL_EventType KEYDOWN
+cdef SDL_EventType QUIT
 
-    void SDL_SetWindowSize(SDL_Window *window, int w, int h)
+cdef Uint16 DEFAULT_FORMAT
 
 
-cdef extern from "SDL_scancode.h":
-    ctypedef enum SDL_Scancode:
-        SDL_SCANCODE_Z
-        SDL_SCANCODE_X
-        SDL_SCANCODE_LSHIFT
-        SDL_SCANCODE_UP
-        SDL_SCANCODE_DOWN
-        SDL_SCANCODE_LEFT
-        SDL_SCANCODE_RIGHT
-        SDL_SCANCODE_LCTRL
-        SDL_SCANCODE_ESCAPE
+cdef class Window:
+    cdef SDL_Window *window
+    cdef SDL_GLContext context
+
+    cdef void gl_create_context(self) except *
+    cdef void gl_swap_window(self) nogil
+    cdef void set_window_size(self, int width, int height) nogil
 
 
-cdef extern from "SDL_events.h":
-    ctypedef enum SDL_EventType:
-        SDL_KEYDOWN
-        SDL_QUIT
-
-    ctypedef struct SDL_Keysym:
-        SDL_Scancode scancode
+cdef class Surface:
+    cdef SDL_Surface *surface
 
-    ctypedef struct SDL_KeyboardEvent:
-        Uint32 type
-        SDL_Keysym keysym
-
-    ctypedef union SDL_Event:
-        Uint32 type
-        SDL_KeyboardEvent key
-
-    int SDL_PollEvent(SDL_Event *event)
+    cdef void blit(self, Surface other) except *
+    cdef void set_alpha(self, Surface alpha_surface) nogil
 
 
-cdef extern from "SDL_keyboard.h":
-    const Uint8 *SDL_GetKeyboardState(int *numkeys)
-
+cdef class Music:
+    cdef Mix_Music *music
 
-cdef extern from "SDL_timer.h":
-    Uint32 SDL_GetTicks()
-    void SDL_Delay(Uint32 ms)
+    cdef void play(self, int loops) nogil
+    cdef void set_loop_points(self, double start, double end) nogil
 
 
-cdef extern from "SDL_rect.h":
-    ctypedef struct SDL_Rect:
-        int x, y
-        int w, h
-
+cdef class Chunk:
+    cdef Mix_Chunk *chunk
 
-cdef extern from "SDL_surface.h":
-    ctypedef struct SDL_Surface:
-        int w, h
-        unsigned char *pixels
-
-    void SDL_FreeSurface(SDL_Surface *surface)
-    int SDL_BlitSurface(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect)
-    SDL_Surface *SDL_CreateRGBSurface(Uint32 flags, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
+    cdef void play(self, int channel, int loops) nogil
+    cdef void set_volume(self, float volume) nogil
 
 
-cdef extern from "SDL_rwops.h":
-    ctypedef struct SDL_RWops:
-        pass
-
-    SDL_RWops *SDL_RWFromConstMem(const void *mem, int size)
-    int SDL_RWclose(SDL_RWops *context)
-
-
-cdef extern from "SDL_image.h":
-    int IMG_INIT_PNG
+cdef void init(Uint32 flags) except *
+cdef void img_init(Uint32 flags) except *
+cdef void mix_init(int flags) except *
 
-    int IMG_Init(int flags)
-    void IMG_Quit()
-    SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
-
-
-cdef extern from "SDL_mixer.h":
-    ctypedef enum:
-        MIX_DEFAULT_FORMAT
-
-    ctypedef struct Mix_Music:
-        pass
+IF UNAME_SYSNAME == "Windows":
+    cdef void set_main_ready()
 
-    ctypedef struct Mix_Chunk:
-        pass
-
-    int Mix_Init(int flags)
-    void Mix_Quit()
-
-    int Mix_OpenAudio(int frequency, Uint16 format_, int channels, int chunksize)
-    void Mix_CloseAudio()
-
-    int Mix_AllocateChannels(int numchans)
-
-    Mix_Music *Mix_LoadMUS(const char *filename)
-    Mix_Chunk *Mix_LoadWAV_RW(SDL_RWops *src, int freesrc)
-
-    void Mix_FreeMusic(Mix_Music *music)
-    void Mix_FreeChunk(Mix_Chunk *chunk)
-
-    int Mix_PlayMusic(Mix_Music *music, int loops)
-    #int Mix_SetLoopPoints(Mix_Music *music, double start, double end)
-
-    int Mix_Volume(int channel, int volume)
-    int Mix_VolumeChunk(Mix_Chunk *chunk, int volume)
-    int Mix_VolumeMusic(int volume)
-
-    int Mix_PlayChannel(int channel, Mix_Chunk *chunk, int loops)
+cdef void quit() nogil
+cdef void img_quit() nogil
+cdef void mix_quit() nogil
+cdef void gl_set_attribute(SDL_GLattr attr, int value) except *
+cdef list poll_events()
+cdef const Uint8* get_keyboard_state() nogil
+cdef Surface load_png(file_)
+cdef Surface create_rgb_surface(int width, int height, int depth, Uint32 rmask=*, Uint32 gmask=*, Uint32 bmask=*, Uint32 amask=*)
+cdef void mix_open_audio(int frequency, Uint16 format_, int channels, int chunksize) except *
+cdef void mix_close_audio() nogil
+cdef void mix_allocate_channels(int numchans) except *
+cdef int mix_volume(int channel, float volume) nogil
+cdef int mix_volume_music(float volume) nogil
+cdef Music load_music(const char *filename)
+cdef Chunk load_chunk(file_)
+cdef Uint32 get_ticks() nogil
+cdef void delay(Uint32 ms) nogil
--- a/pytouhou/lib/sdl.pyx
+++ b/pytouhou/lib/sdl.pyx
@@ -45,56 +45,43 @@ class SDLError(Exception):
 
 
 cdef class Window:
-    cdef SDL_Window *window
-    cdef SDL_GLContext context
-
     def __init__(self, const char *title, int x, int y, int w, int h, Uint32 flags):
         self.window = SDL_CreateWindow(title, x, y, w, h, flags)
         if self.window == NULL:
             raise SDLError(SDL_GetError())
 
-    def destroy_window(self):
-        SDL_DestroyWindow(self.window)
+    def __dealloc__(self):
+        if self.context != NULL:
+            SDL_GL_DeleteContext(self.context)
+        if self.window != NULL:
+            SDL_DestroyWindow(self.window)
 
-    def gl_create_context(self):
+    cdef void gl_create_context(self) except *:
         self.context = SDL_GL_CreateContext(self.window)
         if self.context == NULL:
             raise SDLError(SDL_GetError())
 
-    def gl_swap_window(self):
+    cdef void gl_swap_window(self) nogil:
         SDL_GL_SwapWindow(self.window)
 
-    def gl_delete_context(self):
-        SDL_GL_DeleteContext(self.context)
-
-    def set_window_size(self, width, height):
+    cdef void set_window_size(self, int width, int height) nogil:
         SDL_SetWindowSize(self.window, width, height)
 
 
 cdef class Surface:
-    cdef SDL_Surface *surface
-
     def __dealloc__(self):
         if self.surface != NULL:
             SDL_FreeSurface(self.surface)
 
-    property width:
-        def __get__(self):
-            return self.surface.w
-
-    property height:
-        def __get__(self):
-            return self.surface.h
-
     property pixels:
         def __get__(self):
             return bytes(self.surface.pixels[:self.surface.w * self.surface.h * 4])
 
-    def blit(self, Surface other):
+    cdef void blit(self, Surface other):
         if SDL_BlitSurface(other.surface, NULL, self.surface, NULL) < 0:
             raise SDLError(SDL_GetError())
 
-    def set_alpha(self, Surface alpha_surface):
+    cdef void set_alpha(self, Surface alpha_surface) nogil:
         nb_pixels = self.surface.w * self.surface.h
         image = self.surface.pixels
         alpha = alpha_surface.surface.pixels
@@ -105,73 +92,68 @@ cdef class Surface:
 
 
 cdef class Music:
-    cdef Mix_Music *music
-
     def __dealloc__(self):
         if self.music != NULL:
             Mix_FreeMusic(self.music)
 
-    def play(self, int loops):
+    cdef void play(self, int loops) nogil:
         Mix_PlayMusic(self.music, loops)
 
-    def set_loop_points(self, double start, double end):
+    cdef void set_loop_points(self, double start, double end) nogil:
         #Mix_SetLoopPoints(self.music, start, end)
         pass
 
 
 cdef class Chunk:
-    cdef Mix_Chunk *chunk
-
     def __dealloc__(self):
         if self.chunk != NULL:
             Mix_FreeChunk(self.chunk)
 
-    property volume:
-        def __set__(self, float volume):
-            Mix_VolumeChunk(self.chunk, int(volume * 128))
-
-    def play(self, int channel, int loops):
+    cdef void play(self, int channel, int loops) nogil:
         Mix_PlayChannel(channel, self.chunk, loops)
 
+    cdef void set_volume(self, float volume) nogil:
+        Mix_VolumeChunk(self.chunk, int(volume * 128))
 
-def init(Uint32 flags):
+
+cdef void init(Uint32 flags) except *:
     if SDL_Init(flags) < 0:
         raise SDLError(SDL_GetError())
 
 
-def img_init(Uint32 flags):
+cdef void img_init(Uint32 flags) except *:
     if IMG_Init(flags) != flags:
         raise SDLError(SDL_GetError())
 
 
-def mix_init(int flags):
+cdef void mix_init(int flags) except *:
     if Mix_Init(flags) != flags:
         raise SDLError(SDL_GetError())
 
 
 IF UNAME_SYSNAME == "Windows":
-    def set_main_ready():
+    cdef void set_main_ready():
         SDL_SetMainReady()
 
 
-def quit():
+cdef void quit() nogil:
     SDL_Quit()
 
 
-def img_quit():
+cdef void img_quit() nogil:
     IMG_Quit()
 
 
-def mix_quit():
+cdef void mix_quit() nogil:
     Mix_Quit()
 
 
-def gl_set_attribute(SDL_GLattr attr, int value):
+cdef void gl_set_attribute(SDL_GLattr attr, int value) except *:
     if SDL_GL_SetAttribute(attr, value) < 0:
         raise SDLError(SDL_GetError())
 
 
-def poll_events():
+cdef list poll_events():
     cdef SDL_Event event
     ret = []
     while SDL_PollEvent(&event):
@@ -182,15 +164,11 @@ def poll_events():
     return ret
 
 
-def get_keyboard_state():
-    cdef int numkeys
-    cdef bint k
-    cdef const Uint8 *state
-    state = SDL_GetKeyboardState(&numkeys)
-    return tuple([k is not False for k in state[:numkeys]])
+cdef const Uint8* get_keyboard_state() nogil:
+    return SDL_GetKeyboardState(NULL)
 
 
-def load_png(file_):
+cdef Surface load_png(file_):
     data = file_.read()
     rwops = SDL_RWFromConstMem(<char*>data, len(data))
     surface = Surface()
@@ -201,7 +179,7 @@ def load_png(file_):
     return surface
 
 
-def create_rgb_surface(int width, int height, int depth, Uint32 rmask=0, Uint32 gmask=0, Uint32 bmask=0, Uint32 amask=0):
+cdef Surface create_rgb_surface(int width, int height, int depth, Uint32 rmask=0, Uint32 gmask=0, Uint32 bmask=0, Uint32 amask=0):
     surface = Surface()
     surface.surface = SDL_CreateRGBSurface(0, width, height, depth, rmask, gmask, bmask, amask)
     if surface.surface == NULL:
@@ -209,29 +187,29 @@ def create_rgb_surface(int width, int he
     return surface
 
 
-def mix_open_audio(int frequency, Uint16 format_, int channels, int chunksize):
+cdef void mix_open_audio(int frequency, Uint16 format_, int channels, int chunksize) except *:
     if Mix_OpenAudio(frequency, format_, channels, chunksize) < 0:
         raise SDLError(SDL_GetError())
 
 
-def mix_close_audio():
+cdef void mix_close_audio() nogil:
     Mix_CloseAudio()
 
 
-def mix_allocate_channels(int numchans):
+cdef void mix_allocate_channels(int numchans) except *:
     if Mix_AllocateChannels(numchans) != numchans:
         raise SDLError(SDL_GetError())
 
 
-def mix_volume(int channel, float volume):
+cdef int mix_volume(int channel, float volume) nogil:
     return Mix_Volume(channel, int(volume * 128))
 
 
-def mix_volume_music(float volume):
+cdef int mix_volume_music(float volume) nogil:
     return Mix_VolumeMusic(int(volume * 128))
 
 
-def load_music(const char *filename):
+cdef Music load_music(const char *filename):
     music = Music()
     music.music = Mix_LoadMUS(filename)
     if music.music == NULL:
@@ -239,7 +217,7 @@ def load_music(const char *filename):
     return music
 
 
-def load_chunk(file_):
+cdef Chunk load_chunk(file_):
     cdef SDL_RWops *rwops
     chunk = Chunk()
     data = file_.read()
@@ -250,9 +228,9 @@ def load_chunk(file_):
     return chunk
 
 
-def get_ticks():
+cdef Uint32 get_ticks() nogil:
     return SDL_GetTicks()
 
 
-def delay(Uint32 ms):
+cdef void delay(Uint32 ms) nogil:
     SDL_Delay(ms)
--- a/pytouhou/ui/anmrenderer.pyx
+++ b/pytouhou/ui/anmrenderer.pyx
@@ -24,7 +24,7 @@ from pytouhou.utils.maths cimport perspe
 from .renderer import Renderer
 from .shaders.eosd import GameShader
 
-from pytouhou.lib import sdl
+from pytouhou.lib cimport sdl
 
 
 logger = get_logger(__name__)
--- a/pytouhou/ui/gamerunner.pyx
+++ b/pytouhou/ui/gamerunner.pyx
@@ -12,7 +12,7 @@
 ## GNU General Public License for more details.
 ##
 
-from pytouhou.lib import sdl
+from pytouhou.lib cimport sdl
 
 from pytouhou.lib.opengl cimport \
          (glMatrixMode, glEnable, glDisable, glViewport, glScissor,
@@ -179,7 +179,7 @@ class GameRunner(GameRenderer):
     def render_interface(self):
         elements = []
         interface = self.game.interface
-        interface.labels['framerate'].set_text('%.2ffps' % self.window.clock.get_fps())
+        interface.labels['framerate'].set_text('%.2ffps' % self.window.get_fps())
 
         if self.use_fixed_pipeline:
             glMatrixMode(GL_MODELVIEW)
rename from pytouhou/ui/music.py
rename to pytouhou/ui/music.pyx
--- a/pytouhou/ui/music.py
+++ b/pytouhou/ui/music.pyx
@@ -15,7 +15,7 @@
 
 from os.path import join
 from glob import glob
-from pytouhou.lib import sdl
+from pytouhou.lib cimport sdl
 from pytouhou.utils.helpers import get_logger
 
 logger = get_logger(__name__)
@@ -53,6 +53,7 @@ class MusicPlayer(object):
                 logger.warn(u'No working music file for “%s”, disabling bgm.', globname)
 
     def play(self, index):
+        cdef sdl.Music bgm
         bgm = self.bgms[index]
         if bgm:
             bgm.play(-1)
@@ -75,11 +76,13 @@ class SFXPlayer(object):
     def get_sound(self, name):
         if name not in self.sounds:
             wave_file = self.loader.get_file(name)
-            self.sounds[name] = sdl.load_chunk(wave_file)
-            self.sounds[name].volume = self.volume
+            chunk = sdl.load_chunk(wave_file)
+            chunk.set_volume(self.volume)
+            self.sounds[name] = chunk
         return self.sounds[name]
 
     def play(self, name, volume=None):
+        cdef sdl.Chunk sound
         sound = self.get_sound(name)
         channel = self.get_channel(name)
         if volume:
--- a/pytouhou/ui/texture.pyx
+++ b/pytouhou/ui/texture.pyx
@@ -19,7 +19,7 @@ from pytouhou.lib.opengl cimport \
           glGenTextures, glBindTexture, glTexImage2D, GL_TEXTURE_2D, GLuint,
           glDeleteTextures)
 
-from pytouhou.lib.sdl import load_png, create_rgb_surface
+from pytouhou.lib.sdl cimport load_png, create_rgb_surface
 from pytouhou.formats.thtx import Texture #TODO: perhaps define that elsewhere?
 
 import os
@@ -52,7 +52,7 @@ class TextureManager(object):
 
 cdef decode_png(loader, first_name, secondary_name):
     image_file = load_png(loader.get_file(os.path.basename(first_name)))
-    width, height = image_file.width, image_file.height
+    width, height = image_file.surface.w, image_file.surface.h
 
     # Support only 32 bits RGBA. Paletted surfaces are awful to work with.
     #TODO: verify it doesn’t blow up on big-endian systems.
@@ -61,7 +61,7 @@ cdef decode_png(loader, first_name, seco
 
     if secondary_name:
         alpha_file = load_png(loader.get_file(os.path.basename(secondary_name)))
-        assert (width == alpha_file.width and height == alpha_file.height)
+        assert (width == alpha_file.surface.w and height == alpha_file.surface.h)
 
         new_alpha_file = create_rgb_surface(width, height, 24)
         new_alpha_file.blit(alpha_file)
--- a/pytouhou/ui/window.pyx
+++ b/pytouhou/ui/window.pyx
@@ -13,7 +13,7 @@
 ##
 
 
-from pytouhou.lib import sdl
+from pytouhou.lib cimport sdl
 
 from pytouhou.lib.opengl cimport \
          (glEnable, glHint, glEnableClientState, GL_TEXTURE_2D, GL_BLEND,
@@ -24,8 +24,11 @@ IF USE_GLEW:
     from pytouhou.lib.opengl cimport glewInit
 
 
-class Clock:
-    def __init__(self, fps=None):
+cdef class Clock:
+    cdef long _target_fps, _ref_tick, _ref_frame, _fps_tick, _fps_frame
+    cdef double _rate
+
+    def __init__(self, long fps=-1):
         self._target_fps = 0
         self._ref_tick = 0
         self._ref_frame = 0
@@ -35,17 +38,17 @@ class Clock:
         self.set_target_fps(fps)
 
 
-    def set_target_fps(self, fps):
+    cdef void set_target_fps(self, long fps) nogil:
         self._target_fps = fps
         self._ref_tick = 0
         self._fps_tick = 0
 
 
-    def get_fps(self):
+    cdef double get_fps(self) nogil:
         return self._rate
 
 
-    def tick(self):
+    cdef void tick(self) nogil except *:
         current = sdl.get_ticks()
 
         if not self._ref_tick:
@@ -66,7 +69,7 @@ class Clock:
 
         target_tick = self._ref_tick
         if self._target_fps:
-            target_tick += int(self._ref_frame * 1000 / self._target_fps)
+            target_tick += <long>(self._ref_frame * 1000 / self._target_fps)
 
         if current <= target_tick:
             sdl.delay(target_tick - current)
@@ -76,9 +79,16 @@ class Clock:
 
 
 
-class Window(object):
-    def __init__(self, size=None, double_buffer=True, fps_limit=60,
-                 fixed_pipeline=False, sound=True):
+cdef class Window:
+    cdef sdl.Window win
+    cdef long fps_limit
+    cdef public long width, height
+    cdef public bint use_fixed_pipeline
+    cdef object runner
+    cdef Clock clock
+
+    def __init__(self, size=None, bint double_buffer=True, long fps_limit=-1,
+                 bint fixed_pipeline=False, bint sound=True):
         self.fps_limit = fps_limit
         self.use_fixed_pipeline = fixed_pipeline
         self.runner = None
@@ -95,7 +105,7 @@ class Window(object):
         sdl.gl_set_attribute(sdl.GL_DOUBLEBUFFER, int(double_buffer))
         sdl.gl_set_attribute(sdl.GL_DEPTH_SIZE, 24)
 
-        self.width, self.height = size if size else (640, 480)
+        self.width, self.height = size if size is not None else (640, 480)
 
         self.win = sdl.Window('PyTouhou',
                               sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED,
@@ -125,16 +135,17 @@ class Window(object):
         self.clock = Clock(self.fps_limit)
 
 
-    def set_size(self, width, height):
+    cdef void set_size(self, int width, int height) nogil:
         self.win.set_window_size(width, height)
 
 
-    def set_runner(self, runner):
+    cpdef set_runner(self, runner=None):
         self.runner = runner
-        runner.start()
+        if runner is not None:
+            runner.start()
 
 
-    def run(self):
+    cpdef run(self):
         try:
             while self.run_frame():
                 pass
@@ -142,17 +153,20 @@ class Window(object):
             self.runner.finish()
 
 
-    def run_frame(self):
-        if self.runner:
+    cdef bint run_frame(self) except? False:
+        cdef bint running = False
+        if self.runner is not None:
             running = self.runner.update()
         self.win.gl_swap_window()
         self.clock.tick()
         return running
 
 
+    cpdef double get_fps(self):
+        return self.clock.get_fps()
+
+
     def __dealloc__(self):
-        self.win.gl_delete_context()
-        self.win.destroy_window()
         sdl.mix_close_audio()
         sdl.mix_quit()
         sdl.img_quit()
--- a/setup.py
+++ b/setup.py
@@ -45,8 +45,8 @@ for directory, _, files in os.walk('pyto
                 compile_args = get_arguments('--cflags', SDL_LIBRARIES)
                 link_args = get_arguments('--libs', SDL_LIBRARIES)
             elif extension_name.startswith('pytouhou.ui.'): #XXX
-                compile_args = get_arguments('--cflags', ['gl'])
-                link_args = get_arguments('--libs', ['gl'])
+                compile_args = get_arguments('--cflags', ['gl'] + SDL_LIBRARIES)
+                link_args = get_arguments('--libs', ['gl'] + SDL_LIBRARIES)
             else:
                 compile_args = None
                 link_args = None