# HG changeset patch # User Emmanuel Gil Peyrot # Date 1460665083 -3600 # Node ID 80687f2580018a0f29b152060ecdb5c8416effbb # Parent 5270c34b4c009ff57df5a54fa215fd0ed0b35057 Make sdl.Window inherit from gui.Window, so we can swap implementations. diff --git a/pytouhou/lib/_sdl.pxd b/pytouhou/lib/_sdl.pxd --- 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 diff --git a/pytouhou/lib/gui.pxd b/pytouhou/lib/gui.pxd 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 diff --git a/pytouhou/lib/gui.pyx b/pytouhou/lib/gui.pyx 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 diff --git a/pytouhou/lib/sdl.pxd b/pytouhou/lib/sdl.pxd --- 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 diff --git a/pytouhou/lib/sdl.pyx b/pytouhou/lib/sdl.pyx --- 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() diff --git a/pytouhou/ui/anmrenderer.pyx b/pytouhou/ui/anmrenderer.pyx --- 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__) diff --git a/pytouhou/ui/gamerunner.pyx b/pytouhou/ui/gamerunner.pyx --- 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() diff --git a/pytouhou/ui/opengl/backend.pxd b/pytouhou/ui/opengl/backend.pxd --- 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 diff --git a/pytouhou/ui/opengl/backend.pyx b/pytouhou/ui/opengl/backend.pyx --- 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 diff --git a/pytouhou/ui/opengl/backend_sdl.pyx b/pytouhou/ui/opengl/backend_sdl.pyx 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 diff --git a/pytouhou/ui/sdl/backend.pyx b/pytouhou/ui/sdl/backend.pyx --- 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 diff --git a/pytouhou/ui/window.pxd b/pytouhou/ui/window.pxd --- 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 diff --git a/pytouhou/ui/window.pyx b/pytouhou/ui/window.pyx --- 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()