changeset 635:80687f258001

Make sdl.Window inherit from gui.Window, so we can swap implementations.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Thu, 14 Apr 2016 21:18:03 +0100
parents 5270c34b4c00
children 4fa0a8e7d941
files pytouhou/lib/_sdl.pxd pytouhou/lib/gui.pxd pytouhou/lib/gui.pyx pytouhou/lib/sdl.pxd pytouhou/lib/sdl.pyx pytouhou/ui/anmrenderer.pyx pytouhou/ui/gamerunner.pyx pytouhou/ui/opengl/backend.pxd pytouhou/ui/opengl/backend.pyx pytouhou/ui/opengl/backend_sdl.pyx pytouhou/ui/sdl/backend.pyx pytouhou/ui/window.pxd pytouhou/ui/window.pyx
diffstat 13 files changed, 236 insertions(+), 104 deletions(-) [+]
line wrap: on
line diff
--- a/pytouhou/lib/_sdl.pxd
+++ b/pytouhou/lib/_sdl.pxd
@@ -52,6 +52,7 @@ cdef extern from "SDL_video.h" nogil:
         SDL_WINDOWPOS_CENTERED
         SDL_WINDOW_OPENGL
         SDL_WINDOW_RESIZABLE
+        SDL_WINDOW_FULLSCREEN_DESKTOP
 
     ctypedef struct SDL_Window:
         pass
@@ -67,6 +68,7 @@ cdef extern from "SDL_video.h" nogil:
     void SDL_DestroyWindow(SDL_Window *window)
 
     void SDL_SetWindowSize(SDL_Window *window, int w, int h)
+    int SDL_SetWindowFullscreen(SDL_Window *window, Uint32 flags)
 
 
 cdef extern from "SDL_scancode.h" nogil:
@@ -82,6 +84,13 @@ cdef extern from "SDL_scancode.h" nogil:
         SDL_SCANCODE_LCTRL
         SDL_SCANCODE_ESCAPE
         SDL_SCANCODE_HOME
+        SDL_SCANCODE_RETURN
+        SDL_SCANCODE_F11
+
+
+cdef extern from "SDL_keycode.h" nogil:
+    ctypedef enum SDL_Keymod:
+        KMOD_ALT
 
 
 cdef extern from "SDL_events.h" nogil:
@@ -92,6 +101,7 @@ cdef extern from "SDL_events.h" nogil:
 
     ctypedef struct SDL_Keysym:
         SDL_Scancode scancode
+        Uint16 mod
 
     ctypedef struct SDL_KeyboardEvent:
         Uint32 type
new file mode 100644
--- /dev/null
+++ b/pytouhou/lib/gui.pxd
@@ -0,0 +1,28 @@
+# Events
+cdef int EXIT
+cdef int PAUSE
+cdef int SCREENSHOT
+cdef int RESIZE
+cdef int FULLSCREEN
+cdef int DOWN
+
+# Keystates
+cdef int SHOOT
+cdef int BOMB
+cdef int FOCUS
+# ??
+cdef int UP
+cdef int DOWN
+cdef int LEFT
+cdef int RIGHT
+cdef int SKIP
+
+
+cdef class Window:
+    cdef void create_gl_context(self) except *
+    cdef void present(self) nogil
+    cdef void set_window_size(self, int width, int height) nogil
+    cdef void set_swap_interval(self, int interval) except *
+    cdef list get_events(self)
+    cdef int get_keystate(self) nogil
+    cdef void toggle_fullscreen(self) nogil
new file mode 100644
--- /dev/null
+++ b/pytouhou/lib/gui.pyx
@@ -0,0 +1,45 @@
+# Events
+EXIT = 1
+PAUSE = 2
+SCREENSHOT = 3
+RESIZE = 4
+FULLSCREEN = 5
+DOWN = 6
+
+# Possible keystates.
+SHOOT = 1
+BOMB = 2
+FOCUS = 4
+# ??
+UP = 16
+DOWN = 32
+LEFT = 64
+RIGHT = 128
+SKIP = 256
+
+
+class Error(Exception):
+    pass
+
+
+cdef class Window:
+    cdef void create_gl_context(self) except *:
+        pass
+
+    cdef void present(self) nogil:
+        pass
+
+    cdef void set_window_size(self, int width, int height) nogil:
+        pass
+
+    cdef void set_swap_interval(self, int interval) except *:
+        pass
+
+    cdef list get_events(self):
+        return []
+
+    cdef int get_keystate(self) nogil:
+        return 0
+
+    cdef void toggle_fullscreen(self) nogil:
+        pass
--- a/pytouhou/lib/sdl.pxd
+++ b/pytouhou/lib/sdl.pxd
@@ -12,7 +12,8 @@
 ## GNU General Public License for more details.
 ##
 
-from ._sdl cimport *
+from pytouhou.lib._sdl cimport *
+cimport pytouhou.lib.gui as gui
 
 
 cdef SDL_GLattr GL_CONTEXT_MAJOR_VERSION
@@ -55,14 +56,11 @@ cdef SDL_EventType WINDOWEVENT
 cdef const Uint8 *keyboard_state
 
 
-cdef class Window:
+cdef class Window(gui.Window):
     cdef SDL_Window *window
     cdef SDL_GLContext context
     cdef SDL_Renderer *renderer
-
-    cdef bint gl_create_context(self) except True
-    cdef void present(self) nogil
-    cdef void set_window_size(self, int width, int height) nogil
+    cdef bint is_fullscreen
 
     # The following functions are there for the pure SDL backend.
     cdef bint create_renderer(self, Uint32 flags) except True
@@ -122,8 +120,6 @@ cdef bint img_init(int flags) except Tru
 cdef bint mix_init(int flags) except True
 cdef bint ttf_init() except True
 cdef bint gl_set_attribute(SDL_GLattr attr, int value) except True
-cdef bint gl_set_swap_interval(int interval) except True
-cdef list poll_events()
 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 bint mix_open_audio(int frequency, Uint16 format_, int channels, int chunksize) except True
--- a/pytouhou/lib/sdl.pyx
+++ b/pytouhou/lib/sdl.pyx
@@ -12,6 +12,8 @@
 ## GNU General Public License for more details.
 ##
 
+import pytouhou.lib.gui as gui
+
 from pytouhou.utils.helpers import get_logger
 
 logger = get_logger(__name__)
@@ -53,7 +55,7 @@ QUIT = SDL_QUIT
 WINDOWEVENT = SDL_WINDOWEVENT
 
 
-class SDLError(Exception):
+class SDLError(gui.Error):
     def __init__(self):
         error = SDL_GetError()
         Exception.__init__(self, error.decode())
@@ -96,7 +98,7 @@ class SDL:
         SDL_Quit()
 
 
-cdef class Window:
+cdef class Window(gui.Window):
     def __init__(self, str title, int x, int y, int w, int h, Uint32 flags):
         title_bytes = title.encode()
         self.window = SDL_CreateWindow(title_bytes, x, y, w, h, flags)
@@ -109,7 +111,7 @@ cdef class Window:
         if self.window != NULL:
             SDL_DestroyWindow(self.window)
 
-    cdef bint gl_create_context(self) except True:
+    cdef void create_gl_context(self) except *:
         self.context = SDL_GL_CreateContext(self.window)
         if self.context == NULL:
             raise SDLError()
@@ -123,6 +125,63 @@ cdef class Window:
     cdef void set_window_size(self, int width, int height) nogil:
         SDL_SetWindowSize(self.window, width, height)
 
+    cdef void set_swap_interval(self, int interval) except *:
+        if SDL_GL_SetSwapInterval(interval) < 0:
+            raise SDLError()
+
+    cdef list get_events(self):
+        cdef SDL_Event event
+        ret = []
+        while SDL_PollEvent(&event):
+            if event.type == SDL_KEYDOWN:
+                scancode = event.key.keysym.scancode
+                if scancode == SDL_SCANCODE_ESCAPE:
+                    ret.append((gui.PAUSE, None))
+                elif scancode in (SDL_SCANCODE_P, SDL_SCANCODE_HOME):
+                    ret.append((gui.SCREENSHOT, None))
+                elif scancode == SDL_SCANCODE_DOWN:
+                    ret.append((gui.DOWN, None))
+                elif scancode == SDL_SCANCODE_F11:
+                    ret.append((gui.FULLSCREEN, None))
+                elif scancode == SDL_SCANCODE_RETURN:
+                    mod = event.key.keysym.mod
+                    if mod & KMOD_ALT:
+                        ret.append((gui.FULLSCREEN, None))
+            elif event.type == SDL_QUIT:
+                ret.append((gui.EXIT, None))
+            elif event.type == SDL_WINDOWEVENT:
+                if event.window.event == SDL_WINDOWEVENT_RESIZED:
+                    ret.append((gui.RESIZE, (event.window.data1, event.window.data2)))
+        return ret
+
+    cdef int get_keystate(self) nogil:
+        cdef int keystate = 0
+        cdef const Uint8 *keys = keyboard_state
+        if keys[SCANCODE_Z]:
+            keystate |= 1
+        if keys[SCANCODE_X]:
+            keystate |= 2
+        if keys[SCANCODE_LSHIFT]:
+            keystate |= 4
+        if keys[SCANCODE_UP]:
+            keystate |= 16
+        if keys[SCANCODE_DOWN]:
+            keystate |= 32
+        if keys[SCANCODE_LEFT]:
+            keystate |= 64
+        if keys[SCANCODE_RIGHT]:
+            keystate |= 128
+        if keys[SCANCODE_LCTRL]:
+            keystate |= 256
+        return keystate
+
+    cdef void toggle_fullscreen(self) nogil:
+        ret = SDL_SetWindowFullscreen(self.window, 0 if self.is_fullscreen else SDL_WINDOW_FULLSCREEN_DESKTOP)
+        if ret == -1:
+            with gil:
+                raise SDLError()
+        self.is_fullscreen = not self.is_fullscreen
+
     # The following functions are there for the pure SDL backend.
     cdef bint create_renderer(self, Uint32 flags) except True:
         self.renderer = SDL_CreateRenderer(self.window, -1, flags)
@@ -289,23 +348,6 @@ cdef bint gl_set_attribute(SDL_GLattr at
     if SDL_GL_SetAttribute(attr, value) < 0:
         raise SDLError()
 
-cdef bint gl_set_swap_interval(int interval) except True:
-    if SDL_GL_SetSwapInterval(interval) < 0:
-        raise SDLError()
-
-
-cdef list poll_events():
-    cdef SDL_Event event
-    ret = []
-    while SDL_PollEvent(&event):
-        if event.type == SDL_KEYDOWN:
-            ret.append((event.type, event.key.keysym.scancode))
-        elif event.type == SDL_QUIT:
-            ret.append((event.type,))
-        elif event.type == SDL_WINDOWEVENT:
-            ret.append((event.type, event.window.event, event.window.data1, event.window.data2))
-    return ret
-
 
 cdef Surface load_png(file_):
     data = file_.read()
--- 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 cimport sdl
+cimport pytouhou.lib.sdl as sdl
 
 
 logger = get_logger(__name__)
--- a/pytouhou/ui/gamerunner.pyx
+++ b/pytouhou/ui/gamerunner.pyx
@@ -14,7 +14,7 @@
 
 cimport cython
 
-from pytouhou.lib cimport sdl
+from pytouhou.lib.gui cimport EXIT, PAUSE, SCREENSHOT, RESIZE, FULLSCREEN
 
 from .window cimport Window, Runner
 from .music import BGMPlayer, SFXPlayer
@@ -120,42 +120,22 @@ cdef class GameRunner(Runner):
 
         if self.background is not None:
             self.background.update(self.game.frame)
-        for event in sdl.poll_events():
-            type_ = event[0]
-            if type_ == sdl.KEYDOWN:
-                scancode = event[1]
-                if scancode == sdl.SCANCODE_ESCAPE:
-                    return False #TODO: implement the pause.
-                elif scancode == sdl.SCANCODE_P or scancode == sdl.SCANCODE_HOME:
-                    capture = True
-            elif type_ == sdl.QUIT:
+        for event, args in self.window.get_events():
+            if event == EXIT:
                 return False
-            elif type_ == sdl.WINDOWEVENT:
-                event_ = event[1]
-                if event_ == sdl.WINDOWEVENT_RESIZED:
-                    self.set_renderer_size(event[2], event[3])
-                    if self.window is not None:
-                        self.window.set_size(event[2], event[3])
+            elif event == PAUSE:
+                return False  # TODO: implement the pause.
+            elif event == FULLSCREEN:
+                self.window.toggle_fullscreen()
+            elif event == SCREENSHOT:
+                capture = True
+            elif event == RESIZE:
+                width, height = args
+                self.set_renderer_size(width, height)
+                if self.window is not None:
+                    self.window.set_size(width, height)
         if self.replay_level is None:
-            #TODO: allow user settings
-            keys = sdl.keyboard_state
-            keystate = 0
-            if keys[sdl.SCANCODE_Z]:
-                keystate |= 1
-            if keys[sdl.SCANCODE_X]:
-                keystate |= 2
-            if keys[sdl.SCANCODE_LSHIFT]:
-                keystate |= 4
-            if keys[sdl.SCANCODE_UP]:
-                keystate |= 16
-            if keys[sdl.SCANCODE_DOWN]:
-                keystate |= 32
-            if keys[sdl.SCANCODE_LEFT]:
-                keystate |= 64
-            if keys[sdl.SCANCODE_RIGHT]:
-                keystate |= 128
-            if keys[sdl.SCANCODE_LCTRL]:
-                keystate |= 256
+            keystate = self.window.get_keystate()
         else:
             try:
                 keystate = self.keys.next()
--- a/pytouhou/ui/opengl/backend.pxd
+++ b/pytouhou/ui/opengl/backend.pxd
@@ -1,7 +1,6 @@
-from pytouhou.lib.sdl cimport SDL_GLprofile
 from pytouhou.lib.opengl cimport GLenum_mode
 
-cdef SDL_GLprofile profile
+cdef str profile
 cdef int major
 cdef int minor
 cdef int double_buffer
--- a/pytouhou/ui/opengl/backend.pyx
+++ b/pytouhou/ui/opengl/backend.pyx
@@ -1,6 +1,6 @@
-from pytouhou.lib import sdl
-from pytouhou.lib cimport sdl
-from pytouhou.lib.sdl cimport Window
+cimport pytouhou.lib.gui as gui
+from pytouhou.lib.gui import Error as GUIError
+from .backend_sdl import create_sdl_window
 
 from pytouhou.lib.opengl cimport \
          (glEnable, glHint, glEnableClientState, GL_TEXTURE_2D, GL_BLEND,
@@ -27,10 +27,7 @@ def init(options):
 
     flavor = options['flavor']
     assert flavor in ('core', 'es', 'compatibility', 'legacy')
-    profile = (sdl.GL_CONTEXT_PROFILE_CORE if flavor == 'core' else
-               sdl.GL_CONTEXT_PROFILE_ES if flavor == 'es' else
-               sdl.GL_CONTEXT_PROFILE_COMPATIBILITY)
-
+    profile = flavor
     version = str(options['version'])
     assert len(version) == 3 and version[1] == '.'
     major = int(version[0])
@@ -91,34 +88,11 @@ cdef bint discover_features() except Tru
 def create_window(title, x, y, width, height, swap_interval):
     '''Create a window (using SDL) and an OpenGL context.'''
 
-    sdl.gl_set_attribute(sdl.GL_CONTEXT_PROFILE_MASK, profile)
-    sdl.gl_set_attribute(sdl.GL_CONTEXT_MAJOR_VERSION, major)
-    sdl.gl_set_attribute(sdl.GL_CONTEXT_MINOR_VERSION, minor)
-    sdl.gl_set_attribute(sdl.GL_RED_SIZE, 8)
-    sdl.gl_set_attribute(sdl.GL_GREEN_SIZE, 8)
-    sdl.gl_set_attribute(sdl.GL_BLUE_SIZE, 8)
-    sdl.gl_set_attribute(sdl.GL_DEPTH_SIZE, 24 if is_legacy else 0)
-    if double_buffer >= 0:
-        sdl.gl_set_attribute(sdl.GL_DOUBLEBUFFER, double_buffer)
-
-    flags = sdl.WINDOW_SHOWN | sdl.WINDOW_OPENGL
-
-    # Legacy contexts don’t support our required extensions for scaling.
-    if not is_legacy:
-        flags |= sdl.WINDOW_RESIZABLE
-
-    window = Window(title, x, y, width, height, flags)
-    window.gl_create_context()
-
+    cdef gui.Window window
+    window = create_sdl_window(title, x, y, width, height)
+    window.create_gl_context()
     discover_features()
 
-    # If we can’t use scaling but have previously created a resizable window,
-    # recreate it unresizable.
-    if not use_scaled_rendering and flags & sdl.WINDOW_RESIZABLE:
-        flags &= ~sdl.WINDOW_RESIZABLE
-        window = Window(title, x, y, width, height, flags)
-        window.gl_create_context()
-
     if use_debug_group:
         glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "OpenGL initialisation")
 
@@ -144,8 +118,8 @@ def create_window(title, x, y, width, he
 
     if swap_interval is not None:
         try:
-            sdl.gl_set_swap_interval(swap_interval)
-        except sdl.SDLError:
+            window.set_swap_interval(swap_interval)
+        except GUIError:
             # The OpenGL context doesn’t support setting the swap interval,
             # we’ll probably fallback to SDL_Delay-based clocking.
             pass
new file mode 100644
--- /dev/null
+++ b/pytouhou/ui/opengl/backend_sdl.pyx
@@ -0,0 +1,41 @@
+from .backend cimport profile, major, minor, double_buffer, is_legacy
+
+cimport pytouhou.lib.sdl as sdl
+
+
+def create_sdl_window(title, x, y, width, height):
+    '''Create a window (using SDL) and an OpenGL context.'''
+
+    profile_mask = (sdl.GL_CONTEXT_PROFILE_CORE if profile == 'core' else
+                    sdl.GL_CONTEXT_PROFILE_ES if profile == 'es' else
+                    sdl.GL_CONTEXT_PROFILE_COMPATIBILITY)
+
+    sdl.gl_set_attribute(sdl.GL_CONTEXT_PROFILE_MASK, profile_mask)
+    sdl.gl_set_attribute(sdl.GL_CONTEXT_MAJOR_VERSION, major)
+    sdl.gl_set_attribute(sdl.GL_CONTEXT_MINOR_VERSION, minor)
+    sdl.gl_set_attribute(sdl.GL_RED_SIZE, 8)
+    sdl.gl_set_attribute(sdl.GL_GREEN_SIZE, 8)
+    sdl.gl_set_attribute(sdl.GL_BLUE_SIZE, 8)
+    sdl.gl_set_attribute(sdl.GL_DEPTH_SIZE, 24 if is_legacy else 0)
+    if double_buffer >= 0:
+        sdl.gl_set_attribute(sdl.GL_DOUBLEBUFFER, double_buffer)
+
+    flags = sdl.WINDOW_SHOWN | sdl.WINDOW_OPENGL
+
+    # Legacy contexts don’t support our required extensions for scaling.
+    if not is_legacy:
+        flags |= sdl.WINDOW_RESIZABLE
+
+    window = sdl.Window(title, x, y, width, height, flags)
+    #window.create_gl_context()
+
+    #discover_features()
+
+    ## If we can’t use scaling but have previously created a resizable window,
+    ## recreate it unresizable.
+    #if not use_scaled_rendering and flags & sdl.WINDOW_RESIZABLE:
+    #    flags &= ~sdl.WINDOW_RESIZABLE
+    #    window = sdl.Window(title, x, y, width, height, flags)
+    #    window.create_gl_context()
+
+    return window
--- a/pytouhou/ui/sdl/backend.pyx
+++ b/pytouhou/ui/sdl/backend.pyx
@@ -1,4 +1,4 @@
-from pytouhou.lib cimport sdl
+cimport pytouhou.lib.sdl as sdl
 from pytouhou.lib.sdl cimport Window
 
 
--- a/pytouhou/ui/window.pxd
+++ b/pytouhou/ui/window.pxd
@@ -1,4 +1,4 @@
-from pytouhou.lib cimport sdl
+cimport pytouhou.lib.gui as gui
 
 
 cdef class Clock:
@@ -19,7 +19,7 @@ cdef class Runner:
 
 
 cdef class Window:
-    cdef sdl.Window win
+    cdef gui.Window win
     cdef Runner runner
     cdef Clock clock
     cdef int frame, frameskip
@@ -30,3 +30,6 @@ cdef class Window:
     cpdef run(self)
     cdef bint run_frame(self) except -1
     cdef double get_fps(self) nogil
+    cdef list get_events(self)
+    cdef int get_keystate(self) nogil
+    cdef void toggle_fullscreen(self) nogil
--- a/pytouhou/ui/window.pyx
+++ b/pytouhou/ui/window.pyx
@@ -14,6 +14,8 @@
 
 cimport cython
 
+cimport pytouhou.lib.sdl as sdl
+
 
 cdef class Clock:
     def __init__(self, long fps=-1):
@@ -130,3 +132,15 @@ cdef class Window:
 
     cdef double get_fps(self) nogil:
         return self.clock.fps
+
+
+    cdef list get_events(self):
+        return self.win.get_events()
+
+
+    cdef int get_keystate(self) nogil:
+        return self.win.get_keystate()
+
+
+    cdef void toggle_fullscreen(self) nogil:
+        self.win.toggle_fullscreen()