changeset 512:b39ad30c6620

Add a pure SDL backend.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Thu, 05 Dec 2013 01:55:39 +0100
parents 2e8ceaa85d5c
children 5e3e0b09a531
files eosd pytouhou/lib/_sdl.pxd pytouhou/lib/sdl.pxd pytouhou/lib/sdl.pyx pytouhou/ui/gamerenderer.pyx pytouhou/ui/sdl/__init__.py pytouhou/ui/sdl/gamerenderer.pxd pytouhou/ui/sdl/gamerenderer.py pytouhou/ui/sdl/sprite.pxd pytouhou/ui/sdl/sprite.pyx pytouhou/ui/sdl/texture.pxd pytouhou/ui/sdl/texture.pyx pytouhou/ui/sprite.pyx pytouhou/ui/window.pxd pytouhou/ui/window.pyx setup.py
diffstat 15 files changed, 450 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/eosd	Thu Nov 28 18:55:43 2013 +0100
+++ b/eosd	Thu Dec 05 01:55:39 2013 +0100
@@ -49,6 +49,7 @@
 parser.add_argument('--port', metavar='PORT', type=int, default=0, help='Local port to use for netplay')
 parser.add_argument('--remote', metavar='REMOTE', default=None, help='Remote address')
 parser.add_argument('--no-friendly-fire', action='store_false', help='Allow friendly-fire during netplay.')
+parser.add_argument('--backend', metavar='BACKEND', choices=['opengl', 'sdl'], default='opengl', help='Which backend to use (opengl or sdl for now).')
 
 args = parser.parse_args()
 
@@ -56,11 +57,17 @@
 import sys
 import logging
 
+if args.backend == 'opengl':
+    from pytouhou.ui.gamerenderer import GameRenderer
+    opengl = True
+elif args.backend == 'sdl':
+    from pytouhou.ui.sdl.gamerenderer import GameRenderer
+    opengl = False
+
 from pytouhou.lib.sdl import SDL
 from pytouhou.ui.window import Window
 from pytouhou.resource.loader import Loader
 from pytouhou.ui.gamerunner import GameRunner
-from pytouhou.ui.gamerenderer import GameRenderer
 from pytouhou.game.player import GameOver
 from pytouhou.formats.t6rp import T6RP, Level
 from pytouhou.utils.random import Random
@@ -167,7 +174,7 @@
     game_class = GameBossRush if boss_rush else Game
 
     common = Common(resource_loader, characters, continues, stage_num - 1)
-    renderer = GameRenderer(resource_loader, window.use_fixed_pipeline)
+    renderer = GameRenderer(resource_loader, window)
     runner = GameRunner(window, renderer, common, resource_loader, skip_replay, con)
     window.set_runner(runner)
 
@@ -250,7 +257,7 @@
 with SDL():
     window = Window(double_buffer=(not args.single_buffer),
                     fps_limit=args.fps_limit,
-                    fixed_pipeline=args.fixed_pipeline)
+                    fixed_pipeline=args.fixed_pipeline, opengl=opengl)
 
     main(window, args.path, tuple(args.data), args.stage, args.rank,
          args.character, args.replay, args.save_replay, args.skip_replay,
--- a/pytouhou/lib/_sdl.pxd	Thu Nov 28 18:55:43 2013 +0100
+++ b/pytouhou/lib/_sdl.pxd	Thu Dec 05 01:55:39 2013 +0100
@@ -42,7 +42,6 @@
     ctypedef enum SDL_WindowFlags:
         SDL_WINDOWPOS_CENTERED
         SDL_WINDOW_OPENGL
-        SDL_WINDOW_SHOWN
         SDL_WINDOW_RESIZABLE
 
     ctypedef struct SDL_Window:
@@ -192,3 +191,35 @@
     TTF_Font *TTF_OpenFont(const char *filename, int ptsize)
     void TTF_CloseFont(TTF_Font *font)
     SDL_Surface *TTF_RenderUTF8_Blended(TTF_Font *font, const char *text, SDL_Color fg)
+
+
+cdef extern from "SDL_blendmode.h" nogil:
+    ctypedef enum SDL_BlendMode:
+        SDL_BLENDMODE_NONE
+        SDL_BLENDMODE_BLEND
+        SDL_BLENDMODE_ADD
+        SDL_BLENDMODE_MOD
+
+
+cdef extern from "SDL_render.h" nogil:
+    ctypedef struct SDL_Renderer:
+        pass
+
+    ctypedef struct SDL_Texture:
+        pass
+
+    ctypedef struct SDL_Point:
+        pass
+
+    SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, int index, Uint32 flags)
+    void SDL_RenderPresent(SDL_Renderer *renderer)
+    int SDL_RenderClear(SDL_Renderer *renderer)
+    SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *surface)
+    int SDL_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect, const SDL_Rect *dstrect)
+    int SDL_RenderCopyEx(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect, const SDL_Rect *dstrect, double angle, const SDL_Point *center, bint flip)
+    int SDL_RenderSetClipRect(SDL_Renderer *renderer, const SDL_Rect *rect)
+    int SDL_RenderSetViewport(SDL_Renderer *renderer, const SDL_Rect *rect)
+
+    int SDL_SetTextureColorMod(SDL_Texture *texture, Uint8 r, Uint8 g, Uint8 b)
+    int SDL_SetTextureAlphaMod(SDL_Texture *texture, Uint8 alpha)
+    int SDL_SetTextureBlendMode(SDL_Texture *texture, SDL_BlendMode blend_mode)
--- a/pytouhou/lib/sdl.pxd	Thu Nov 28 18:55:43 2013 +0100
+++ b/pytouhou/lib/sdl.pxd	Thu Dec 05 01:55:39 2013 +0100
@@ -46,11 +46,37 @@
 cdef class Window:
     cdef SDL_Window *window
     cdef SDL_GLContext context
+    cdef SDL_Renderer *renderer
 
     cdef void gl_create_context(self) except *
-    cdef void gl_swap_window(self) nogil
+    cdef void present(self) nogil
     cdef void set_window_size(self, int width, int height) nogil
 
+    # The following functions are there for the pure SDL backend.
+    cdef void create_renderer(self, Uint32 flags)
+    cdef void render_clear(self)
+    cdef void render_copy(self, Texture texture, Rect srcrect, Rect dstrect)
+    cdef void render_copy_ex(self, Texture texture, Rect srcrect, Rect dstrect, double angle, bint flip)
+    cdef void render_set_clip_rect(self, Rect rect)
+    cdef void render_set_viewport(self, Rect rect)
+    cdef Texture create_texture_from_surface(self, Surface surface)
+
+
+cdef class Texture:
+    cdef SDL_Texture *texture
+
+    cpdef set_color_mod(self, Uint8 r, Uint8 g, Uint8 b)
+    cpdef set_alpha_mod(self, Uint8 alpha)
+    cpdef set_blend_mode(self, SDL_BlendMode blend_mode)
+
+
+cdef class Rect:
+    cdef SDL_Rect rect
+
+
+cdef class Color:
+    cdef SDL_Color color
+
 
 cdef class Surface:
     cdef SDL_Surface *surface
--- a/pytouhou/lib/sdl.pyx	Thu Nov 28 18:55:43 2013 +0100
+++ b/pytouhou/lib/sdl.pyx	Thu Dec 05 01:55:39 2013 +0100
@@ -24,7 +24,6 @@
 
 WINDOWPOS_CENTERED = SDL_WINDOWPOS_CENTERED
 WINDOW_OPENGL = SDL_WINDOW_OPENGL
-WINDOW_SHOWN = SDL_WINDOW_SHOWN
 WINDOW_RESIZABLE = SDL_WINDOW_RESIZABLE
 
 SCANCODE_Z = SDL_SCANCODE_Z
@@ -96,12 +95,86 @@
         if self.context == NULL:
             raise SDLError(SDL_GetError())
 
-    cdef void gl_swap_window(self) nogil:
-        SDL_GL_SwapWindow(self.window)
+    cdef void present(self) nogil:
+        if self.renderer == NULL:
+            SDL_GL_SwapWindow(self.window)
+        else:
+            SDL_RenderPresent(self.renderer)
 
     cdef void set_window_size(self, int width, int height) nogil:
         SDL_SetWindowSize(self.window, width, height)
 
+    # The following functions are there for the pure SDL backend.
+    cdef void create_renderer(self, Uint32 flags):
+        self.renderer = SDL_CreateRenderer(self.window, -1, flags)
+        if self.renderer == NULL:
+            raise SDLError(SDL_GetError())
+
+    cdef void render_clear(self):
+        ret = SDL_RenderClear(self.renderer)
+        if ret == -1:
+            raise SDLError(SDL_GetError())
+
+    cdef void render_copy(self, Texture texture, Rect srcrect, Rect dstrect):
+        ret = SDL_RenderCopy(self.renderer, texture.texture, &srcrect.rect, &dstrect.rect)
+        if ret == -1:
+            raise SDLError(SDL_GetError())
+
+    cdef void render_copy_ex(self, Texture texture, Rect srcrect, Rect dstrect, double angle, bint flip):
+        ret = SDL_RenderCopyEx(self.renderer, texture.texture, &srcrect.rect, &dstrect.rect, angle, NULL, flip)
+        if ret == -1:
+            raise SDLError(SDL_GetError())
+
+    cdef void render_set_clip_rect(self, Rect rect):
+        ret = SDL_RenderSetClipRect(self.renderer, &rect.rect)
+        if ret == -1:
+            raise SDLError(SDL_GetError())
+
+    cdef void render_set_viewport(self, Rect rect):
+        ret = SDL_RenderSetViewport(self.renderer, &rect.rect)
+        if ret == -1:
+            raise SDLError(SDL_GetError())
+
+    cdef Texture create_texture_from_surface(self, Surface surface):
+        texture = Texture()
+        texture.texture = SDL_CreateTextureFromSurface(self.renderer, surface.surface)
+        if texture.texture == NULL:
+            raise SDLError(SDL_GetError())
+        return texture
+
+
+cdef class Texture:
+    cpdef set_color_mod(self, Uint8 r, Uint8 g, Uint8 b):
+        ret = SDL_SetTextureColorMod(self.texture, r, g, b)
+        if ret == -1:
+            raise SDLError(SDL_GetError())
+
+    cpdef set_alpha_mod(self, Uint8 alpha):
+        ret = SDL_SetTextureAlphaMod(self.texture, alpha)
+        if ret == -1:
+            raise SDLError(SDL_GetError())
+
+    cpdef set_blend_mode(self, SDL_BlendMode blend_mode):
+        ret = SDL_SetTextureBlendMode(self.texture, blend_mode)
+        if ret == -1:
+            raise SDLError(SDL_GetError())
+
+
+cdef class Rect:
+    def __init__(self, int x, int y, int w, int h):
+        self.rect.x = x
+        self.rect.y = y
+        self.rect.w = w
+        self.rect.h = h
+
+
+cdef class Color:
+    def __init__(self, Uint8 b, Uint8 g, Uint8 r, Uint8 a=255):
+        self.color.r = r
+        self.color.g = g
+        self.color.b = b
+        self.color.a = a
+
 
 cdef class Surface:
     def __dealloc__(self):
--- a/pytouhou/ui/gamerenderer.pyx	Thu Nov 28 18:55:43 2013 +0100
+++ b/pytouhou/ui/gamerenderer.pyx	Thu Dec 05 01:55:39 2013 +0100
@@ -32,8 +32,8 @@
 
 
 cdef class GameRenderer(Renderer):
-    def __init__(self, resource_loader, use_fixed_pipeline):
-        self.use_fixed_pipeline = use_fixed_pipeline #XXX
+    def __init__(self, resource_loader, window):
+        self.use_fixed_pipeline = window.use_fixed_pipeline #XXX
 
         Renderer.__init__(self, resource_loader)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/sdl/gamerenderer.pxd	Thu Dec 05 01:55:39 2013 +0100
@@ -0,0 +1,15 @@
+from pytouhou.game.game cimport Game
+from .texture cimport TextureManager
+from pytouhou.ui.window cimport Window
+
+cdef class GameRenderer:
+    cdef Window window
+    cdef TextureManager texture_manager
+    #cdef FontManager font_manager
+    cdef long x, y, width, height
+
+    cdef public size #XXX
+
+    cdef void render_game(self, Game game) except *
+    cdef void render_text(self, texts) except *
+    cdef void render_interface(self, interface, game_boss) except *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/sdl/gamerenderer.py	Thu Dec 05 01:55:39 2013 +0100
@@ -0,0 +1,136 @@
+# -*- encoding: utf-8 -*-
+##
+## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published
+## by the Free Software Foundation; version 3 only.
+##
+## This program 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 General Public License for more details.
+##
+
+from itertools import chain
+
+from pytouhou.lib.sdl import Rect
+from .sprite import get_sprite_rendering_data
+
+from pytouhou.utils.helpers import get_logger
+logger = get_logger(__name__)
+
+
+class GameRenderer(object):
+    def __init__(self, resource_loader, window):
+        self.window = window
+        self.texture_manager = TextureManager(resource_loader, self.window.win)
+
+
+    def load_textures(self, anms):
+        self.texture_manager.load(anms)
+
+
+    def load_background(self, background):
+        if background is not None:
+            logger.error('Background rendering unavailable in the SDL backend.')
+
+
+    def start(self, common):
+        pass
+
+
+    def render(self, game):
+        self.render_game(game)
+        self.render_text(game.texts + game.native_texts)
+        self.render_interface(game.interface, game.boss)
+
+
+    def render_game(self, game):
+        x, y = game.interface.game_pos
+        self.window.win.render_set_viewport(Rect(x, y, game.width, game.height))
+        self.window.win.render_set_clip_rect(Rect(x, -y, game.width, game.height))
+
+        if game is not None:
+            if game.spellcard_effect is not None:
+                self.render_elements([game.spellcard_effect])
+            else:
+                self.window.win.render_clear()
+
+            self.render_elements([enemy for enemy in game.enemies if enemy.visible])
+            self.render_elements(game.effects)
+            self.render_elements(chain(game.players_bullets,
+                                       game.lasers_sprites(),
+                                       game.players,
+                                       game.msg_sprites()))
+            self.render_elements(chain(game.bullets, game.lasers,
+                                       game.cancelled_bullets, game.items,
+                                       game.labels))
+
+
+    def render_interface(self, interface, boss):
+        interface.labels['framerate'].set_text('%.2ffps' % self.window.clock.get_fps())
+
+        self.window.win.render_set_viewport(Rect(0, 0, interface.width, interface.height))
+        self.window.win.render_set_clip_rect(Rect(0, 0, interface.width, interface.height))
+
+        items = [item for item in interface.items if item.anmrunner and item.anmrunner.running]
+        labels = interface.labels.values()
+
+        if items:
+            # Redraw all the interface
+            self.render_elements(items)
+        else:
+            # Redraw only changed labels
+            labels = [label for label in labels if label.changed]
+
+        self.render_elements(interface.level_start)
+
+        if boss:
+            self.render_elements(interface.boss_items)
+
+        self.render_elements(labels)
+        for label in labels:
+            label.changed = False
+
+
+    def render_elements(self, elements):
+        nb_vertices = 0
+
+        objects = chain(*[element.objects for element in elements])
+        for element in objects:
+            if nb_vertices >= MAX_ELEMENTS - 4:
+                break
+
+            sprite = element.sprite
+            if sprite and sprite.visible:
+                ox, oy = element.x, element.y
+                blendfunc, (vertices, uvs, colors, rotation, flip) = get_sprite_rendering_data(sprite)
+
+                # Pack data in buffer
+                x, y, width, height = vertices
+                left, right, bottom, top = uvs
+                r, g, b, a = colors #TODO: use it.
+
+                #XXX
+                texture_width = 256
+                texture_height = 256
+
+                source = Rect(left * texture_width, bottom * texture_height, (right - left) * texture_width, (top - bottom) * texture_height)
+                dest = Rect(ox + x, oy + y, width, height)
+
+                texture = sprite.anm.texture
+                texture.set_color_mod(r, g, b)
+                texture.set_alpha_mod(a)
+                texture.set_blend_mode(2 if blendfunc else 1)
+
+                if rotation or flip:
+                    self.window.win.render_copy_ex(texture, source, dest, rotation, flip)
+                else:
+                    self.window.win.render_copy(texture, source, dest)
+
+                nb_vertices += 4
+
+
+    def render_text(self, texts):
+        pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/sdl/sprite.pxd	Thu Dec 05 01:55:39 2013 +0100
@@ -0,0 +1,3 @@
+from pytouhou.game.sprite cimport Sprite
+
+cpdef object get_sprite_rendering_data(Sprite sprite)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/sdl/sprite.pyx	Thu Dec 05 01:55:39 2013 +0100
@@ -0,0 +1,61 @@
+# -*- encoding: utf-8 -*-
+##
+## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published
+## by the Free Software Foundation; version 3 only.
+##
+## This program 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 General Public License for more details.
+##
+
+
+from libc.math cimport M_PI as pi
+
+
+cpdef object get_sprite_rendering_data(Sprite sprite):
+    cdef double x, y, tx, ty, tw, th, sx, sy, rz, tox, toy
+
+    if not sprite.changed:
+        return sprite._rendering_data
+
+    x = 0
+    y = 0
+
+    tx, ty, tw, th = sprite.texcoords
+    sx, sy = sprite.rescale
+    width = sprite.width_override or (tw * sx)
+    height = sprite.height_override or (th * sy)
+
+    rz = sprite.rotations_3d[2]
+    if sprite.automatic_orientation:
+        rz += pi/2. - sprite.angle
+    elif sprite.force_rotation:
+        rz += sprite.angle
+
+    if sprite.allow_dest_offset:
+        x += sprite.dest_offset[0]
+        y += sprite.dest_offset[1]
+    if not sprite.corner_relative_placement: # Reposition
+        x -= width / 2
+        y -= height / 2
+
+    size = sprite.anm.size
+    x_1 = 1 / <double>size[0]
+    y_1 = 1 / <double>size[1]
+    tox, toy = sprite.texoffsets
+    uvs = (tx * x_1 + tox,
+           (tx + tw) * x_1 + tox,
+           ty * y_1 + toy,
+           (ty + th) * y_1 + toy)
+
+    key = sprite.blendfunc
+    r, g, b = sprite.color
+    values = (x, y, width, height), uvs, (r, g, b, sprite.alpha), -rz * 180 / pi, sprite.mirrored
+    sprite._rendering_data = key, values
+    sprite.changed = False
+
+    return sprite._rendering_data
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/sdl/texture.pxd	Thu Dec 05 01:55:39 2013 +0100
@@ -0,0 +1,15 @@
+#from pytouhou.lib.sdl cimport Font
+from pytouhou.lib.sdl cimport Surface, Window
+
+cdef class TextureManager:
+    cdef object loader
+    cdef Window window
+
+    cdef void load(self, dict anms) except *
+    cdef load_texture(self, Surface texture)
+
+#cdef class FontManager:
+#    cdef Font font
+#    cdef object renderer, texture_class
+#
+#    cdef load(self, list labels)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytouhou/ui/sdl/texture.pyx	Thu Dec 05 01:55:39 2013 +0100
@@ -0,0 +1,63 @@
+# -*- encoding: utf-8 -*-
+##
+## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published
+## by the Free Software Foundation; version 3 only.
+##
+## This program 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 General Public License for more details.
+##
+
+from pytouhou.lib.sdl cimport load_png, create_rgb_surface
+
+import os
+
+
+cdef class TextureManager:
+    def __init__(self, loader, window):
+        self.loader = loader
+        self.window = window
+
+
+    cdef void load(self, dict anms):
+        for anm in sorted(anms.values(), key=is_ascii):
+            for entry in anm:
+                if not hasattr(entry, 'texture'):
+                    texture = decode_png(self.loader, entry.first_name, entry.secondary_name)
+                #elif not isinstance(entry.texture, self.texture_class):
+                #    texture = entry.texture
+                entry.texture = self.load_texture(texture)
+        anms.clear()
+
+
+    cdef load_texture(self, Surface surface):
+        return self.window.create_texture_from_surface(surface)
+
+
+def is_ascii(anm):
+    return anm[0].first_name.endswith('ascii.png')
+
+
+cdef Surface decode_png(loader, first_name, secondary_name):
+    image_file = load_png(loader.get_file(os.path.basename(first_name)))
+    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.
+    new_image = create_rgb_surface(width, height, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)
+    new_image.blit(image_file)
+
+    if secondary_name:
+        alpha_file = load_png(loader.get_file(os.path.basename(secondary_name)))
+        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)
+
+        new_image.set_alpha(new_alpha_file)
+
+    return new_image
--- a/pytouhou/ui/sprite.pyx	Thu Nov 28 18:55:43 2013 +0100
+++ b/pytouhou/ui/sprite.pyx	Thu Dec 05 01:55:39 2013 +0100
@@ -20,7 +20,6 @@
 
 
 cpdef object get_sprite_rendering_data(Sprite sprite):
-    cdef Matrix vertmat
     cdef double tx, ty, tw, th, sx, sy, rx, ry, rz, tox, toy
     cdef object tmp1, tmp2
 
--- a/pytouhou/ui/window.pxd	Thu Nov 28 18:55:43 2013 +0100
+++ b/pytouhou/ui/window.pxd	Thu Dec 05 01:55:39 2013 +0100
@@ -1,5 +1,4 @@
 from pytouhou.lib cimport sdl
-from .gamerenderer cimport GameRenderer
 
 
 cdef class Clock:
--- a/pytouhou/ui/window.pyx	Thu Nov 28 18:55:43 2013 +0100
+++ b/pytouhou/ui/window.pyx	Thu Dec 05 01:55:39 2013 +0100
@@ -102,8 +102,9 @@
 
             flags |= sdl.WINDOW_OPENGL
 
-        if not self.use_fixed_pipeline:
-            flags |= sdl.WINDOW_RESIZABLE
+            #TODO: implement it in the SDL backend too.
+            if not self.use_fixed_pipeline:
+                flags |= sdl.WINDOW_RESIZABLE
 
         self.win = sdl.Window('PyTouhou',
                               sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED,
@@ -126,6 +127,8 @@
                 glEnableClientState(GL_COLOR_ARRAY)
                 glEnableClientState(GL_VERTEX_ARRAY)
                 glEnableClientState(GL_TEXTURE_COORD_ARRAY)
+        else:
+            self.win.create_renderer(0)
 
         self.clock = Clock(self.fps_limit)
 
@@ -152,7 +155,7 @@
         cdef bint running = False
         if self.runner is not None:
             running = self.runner.update()
-        self.win.gl_swap_window()
+        self.win.present()
         self.clock.tick()
         return running
 
--- a/setup.py	Thu Nov 28 18:55:43 2013 +0100
+++ b/setup.py	Thu Dec 05 01:55:39 2013 +0100
@@ -37,11 +37,14 @@
 for directory, _, files in os.walk('pytouhou'):
     package = directory.replace(os.path.sep, '.')
     packages.append(package)
-    if package not in ('pytouhou.game', 'pytouhou.lib', 'pytouhou.ui', 'pytouhou.utils'):
+    if package not in ('pytouhou.game', 'pytouhou.lib', 'pytouhou.ui', 'pytouhou.utils', 'pytouhou.ui.sdl'):
         continue
     if package == 'pytouhou.ui':
         compile_args = get_arguments('--cflags', ['gl'] + SDL_LIBRARIES)
         link_args = get_arguments('--libs', ['gl'] + SDL_LIBRARIES)
+    elif package == 'pytouhou.ui.sdl':
+        compile_args = get_arguments('--cflags', SDL_LIBRARIES)
+        link_args = get_arguments('--libs', SDL_LIBRARIES)
     else:
         compile_args = None
         link_args = None