changeset 460:ec327e58b477

Add a context manager to initialize and shut down SDL outside of Window.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Fri, 06 Sep 2013 23:29:19 +0200
parents 6e733ed817bd
children 6af3854ed826
files eosd pytouhou/lib/sdl.pxd pytouhou/lib/sdl.pyx pytouhou/ui/window.pyx setup.py
diffstat 5 files changed, 120 insertions(+), 140 deletions(-) [+]
line wrap: on
line diff
--- a/eosd
+++ b/eosd
@@ -53,6 +53,7 @@ args = parser.parse_args()
 import sys
 import logging
 
+from pytouhou.lib.sdl import SDL
 from pytouhou.ui.window import Window
 from pytouhou.resource.loader import Loader
 from pytouhou.ui.gamerunner import GameRunner
@@ -108,115 +109,116 @@ def main(path, data, stage_num, rank, ch
         sys.stderr.write('Some data files were not found, did you forget the -p option?\n')
         exit(1)
 
-    window = Window(double_buffer=(not single_buffer), fps_limit=fps_limit, fixed_pipeline=fixed_pipeline)
+    with SDL():
+        window = Window(double_buffer=(not single_buffer), fps_limit=fps_limit, fixed_pipeline=fixed_pipeline)
 
-    if stage_num is None:
-        story = True
-        stage_num = 1
-        continues = 3
-    else:
-        story = False
-        continues = 0
+        if stage_num is None:
+            story = True
+            stage_num = 1
+            continues = 3
+        else:
+            story = False
+            continues = 0
 
-    if debug:
-        if not verbosity:
-            verbosity = 'DEBUG'
-        continues = float('inf')
+        if debug:
+            if not verbosity:
+                verbosity = 'DEBUG'
+            continues = float('inf')
 
-    if verbosity:
-        logging.basicConfig(level=logging.__getattribute__(verbosity))
+        if verbosity:
+            logging.basicConfig(level=logging.__getattribute__(verbosity))
 
-    if replay:
-        with open(replay, 'rb') as file:
-            replay = T6RP.read(file)
-        rank = replay.rank
-        character = replay.character
+        if replay:
+            with open(replay, 'rb') as file:
+                replay = T6RP.read(file)
+            rank = replay.rank
+            character = replay.character
 
-    save_keystates = None
-    if save_filename:
-        save_replay = T6RP()
-        save_replay.rank = rank
-        save_replay.character = character
+        save_keystates = None
+        if save_filename:
+            save_replay = T6RP()
+            save_replay.rank = rank
+            save_replay.character = character
 
-    if hints:
-        with open(hints, 'rb') as file:
-            hints = Hint.read(file)
+        if hints:
+            with open(hints, 'rb') as file:
+                hints = Hint.read(file)
 
-    difficulty = 16
-    default_power = [0, 64, 128, 128, 128, 128, 0][stage_num - 1]
-    states = [PlayerState(character=character, power=default_power)]
+        difficulty = 16
+        default_power = [0, 64, 128, 128, 128, 128, 0][stage_num - 1]
+        states = [PlayerState(character=character, power=default_power)]
 
-    game_class = GameBossRush if boss_rush else Game
+        game_class = GameBossRush if boss_rush else Game
 
-    common = Common(resource_loader)
-    runner = GameRunner(window, resource_loader, skip=skip_replay)
-    while True:
-        if replay:
-            level = replay.levels[stage_num - 1]
-            if not level:
-                raise Exception
+        common = Common(resource_loader)
+        runner = GameRunner(window, resource_loader, skip=skip_replay)
+        while True:
+            if replay:
+                level = replay.levels[stage_num - 1]
+                if not level:
+                    raise Exception
 
-            prng = Random(level.random_seed)
+                prng = Random(level.random_seed)
 
-            #TODO: apply the replay to the other players.
-            #TODO: see if the stored score is used or if it’s the one from the previous stage.
-            if stage_num != 1 and stage_num - 2 in replay.levels:
-                previous_level = replay.levels[stage_num - 1]
-                states[0].score = previous_level.score
-                states[0].effective_score = previous_level.score
-            states[0].points = level.point_items
-            states[0].power = level.power
-            states[0].lives = level.lives
-            states[0].bombs = level.bombs
-            difficulty = level.difficulty
-        else:
-            prng = Random()
+                #TODO: apply the replay to the other players.
+                #TODO: see if the stored score is used or if it’s the one from the previous stage.
+                if stage_num != 1 and stage_num - 2 in replay.levels:
+                    previous_level = replay.levels[stage_num - 1]
+                    states[0].score = previous_level.score
+                    states[0].effective_score = previous_level.score
+                states[0].points = level.point_items
+                states[0].power = level.power
+                states[0].lives = level.lives
+                states[0].bombs = level.bombs
+                difficulty = level.difficulty
+            else:
+                prng = Random()
 
-        if save_filename:
-            if not replay:
-                save_replay.levels[stage_num - 1] = level = Level()
-                level.score = states[0].score
-                level.random_seed = prng.seed
-                level.point_items = states[0].points
-                level.power = states[0].power
-                level.lives = states[0].lives
-                level.bombs = states[0].bombs
-                level.difficulty = difficulty
-            save_keystates = []
+            if save_filename:
+                if not replay:
+                    save_replay.levels[stage_num - 1] = level = Level()
+                    level.score = states[0].score
+                    level.random_seed = prng.seed
+                    level.point_items = states[0].points
+                    level.power = states[0].power
+                    level.lives = states[0].lives
+                    level.bombs = states[0].bombs
+                    level.difficulty = difficulty
+                save_keystates = []
 
-        hints_stage = hints.stages[stage_num - 1] if hints else None
+            hints_stage = hints.stages[stage_num - 1] if hints else None
 
-        game = game_class(resource_loader, states, stage_num, rank, difficulty, common, prng=prng, continues=continues, hints=hints_stage)
+            game = game_class(resource_loader, states, stage_num, rank, difficulty, common, prng=prng, continues=continues, hints=hints_stage)
 
-        if not enable_particles:
-            def new_particle(pos, anim, amp, number=1, reverse=False, duration=24):
-                pass
-            game.new_particle = new_particle
+            if not enable_particles:
+                def new_particle(pos, anim, amp, number=1, reverse=False, duration=24):
+                    pass
+                game.new_particle = new_particle
 
-        background = game.background if enable_background else None
-        bgms = game.std.bgms if enable_music else None
+            background = game.background if enable_background else None
+            bgms = game.std.bgms if enable_music else None
 
-        # Main loop
-        runner.load_game(game, background, bgms, replay, save_keystates)
-        window.set_runner(runner)
-        try:
-            window.run()
-            break
-        except NextStage:
-            if not story or stage_num == (7 if boss_rush else 6 if rank > 0 else 5):
+            # Main loop
+            runner.load_game(game, background, bgms, replay, save_keystates)
+            window.set_runner(runner)
+            try:
+                window.run()
                 break
-            stage_num += 1
-            states = [player.state.copy() for player in game.players]  # if player.state.lives >= 0]
-        except GameOver:
-            print('Game over')
-            break
-        finally:
-            if save_filename:
-                last_key = -1
-                for time, key in enumerate(save_keystates):
-                    if key != last_key:
-                        level.keys.append((time, key, 0))
-                    last_key = key
+            except NextStage:
+                if not story or stage_num == (7 if boss_rush else 6 if rank > 0 else 5):
+                    break
+                stage_num += 1
+                states = [player.state.copy() for player in game.players]  # if player.state.lives >= 0]
+            except GameOver:
+                print('Game over')
+                break
+            finally:
+                if save_filename:
+                    last_key = -1
+                    for time, key in enumerate(save_keystates):
+                        if key != last_key:
+                            level.keys.append((time, key, 0))
+                        last_key = key
 
     if save_filename:
         with open(save_filename, 'wb+') as file:
--- a/pytouhou/lib/sdl.pxd
+++ b/pytouhou/lib/sdl.pxd
@@ -15,9 +15,6 @@
 from _sdl cimport *
 
 
-cdef Uint32 INIT_VIDEO
-cdef Uint32 INIT_PNG
-
 cdef SDL_GLattr GL_CONTEXT_MAJOR_VERSION
 cdef SDL_GLattr GL_CONTEXT_MINOR_VERSION
 cdef SDL_GLattr GL_DOUBLEBUFFER
@@ -41,8 +38,6 @@ cdef long SCANCODE_ESCAPE
 cdef SDL_EventType KEYDOWN
 cdef SDL_EventType QUIT
 
-cdef Uint16 DEFAULT_FORMAT
-
 
 cdef class Window:
     cdef SDL_Window *window
@@ -84,21 +79,12 @@ cdef void init(Uint32 flags) except *
 cdef void img_init(Uint32 flags) except *
 cdef void mix_init(int flags) except *
 cdef void ttf_init() except *
-
-IF UNAME_SYSNAME == "Windows":
-    cdef void set_main_ready()
-
-cdef void quit() nogil
-cdef void img_quit() nogil
-cdef void mix_quit() nogil
-cdef void ttf_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
--- a/pytouhou/lib/sdl.pyx
+++ b/pytouhou/lib/sdl.pyx
@@ -12,9 +12,6 @@
 ## GNU General Public License for more details.
 ##
 
-INIT_VIDEO = SDL_INIT_VIDEO
-INIT_PNG = IMG_INIT_PNG
-
 GL_CONTEXT_MAJOR_VERSION = SDL_GL_CONTEXT_MAJOR_VERSION
 GL_CONTEXT_MINOR_VERSION = SDL_GL_CONTEXT_MINOR_VERSION
 GL_DOUBLEBUFFER = SDL_GL_DOUBLEBUFFER
@@ -37,13 +34,37 @@ SCANCODE_ESCAPE = SDL_SCANCODE_ESCAPE
 KEYDOWN = SDL_KEYDOWN
 QUIT = SDL_QUIT
 
-DEFAULT_FORMAT = MIX_DEFAULT_FORMAT
-
 
 class SDLError(Exception):
     pass
 
 
+class SDL(object):
+    def __init__(self, sound=True):
+        self.sound = sound
+
+    def __enter__(self):
+        IF UNAME_SYSNAME == "Windows":
+            SDL_SetMainReady()
+        init(SDL_INIT_VIDEO)
+        img_init(IMG_INIT_PNG)
+        ttf_init()
+
+        if self.sound:
+            mix_init(0)
+            mix_open_audio(44100, MIX_DEFAULT_FORMAT, 2, 4096)
+            mix_allocate_channels(MAX_CHANNELS) #TODO: make it dependent on the SFX number.
+
+    def __exit__(self, *args):
+        if self.sound:
+            Mix_CloseAudio()
+            Mix_Quit()
+
+        TTF_Quit()
+        IMG_Quit()
+        SDL_Quit()
+
+
 cdef class Window:
     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)
@@ -157,11 +178,6 @@ cdef void ttf_init() except *:
         raise SDLError(SDL_GetError())
 
 
-IF UNAME_SYSNAME == "Windows":
-    cdef void set_main_ready():
-        SDL_SetMainReady()
-
-
 cdef void quit() nogil:
     SDL_Quit()
 
@@ -222,10 +238,6 @@ cdef void mix_open_audio(int frequency, 
         raise SDLError(SDL_GetError())
 
 
-cdef void mix_close_audio() nogil:
-    Mix_CloseAudio()
-
-
 cdef void mix_allocate_channels(int numchans) except *:
     if Mix_AllocateChannels(numchans) != numchans:
         raise SDLError(SDL_GetError())
--- a/pytouhou/ui/window.pyx
+++ b/pytouhou/ui/window.pyx
@@ -81,14 +81,6 @@ cdef class Window:
         self.use_fixed_pipeline = fixed_pipeline
         self.runner = None
 
-        IF UNAME_SYSNAME == "Windows":
-            sdl.set_main_ready()
-        sdl.init(sdl.INIT_VIDEO)
-        sdl.img_init(sdl.INIT_PNG)
-        sdl.ttf_init()
-        if sound:
-            sdl.mix_init(0)
-
         sdl.gl_set_attribute(sdl.GL_CONTEXT_MAJOR_VERSION, 2)
         sdl.gl_set_attribute(sdl.GL_CONTEXT_MINOR_VERSION, 1)
         sdl.gl_set_attribute(sdl.GL_DOUBLEBUFFER, int(double_buffer))
@@ -116,11 +108,6 @@ cdef class Window:
             glEnableClientState(GL_VERTEX_ARRAY)
             glEnableClientState(GL_TEXTURE_COORD_ARRAY)
 
-        # Initialize sound
-        if sound:
-            sdl.mix_open_audio(44100, sdl.DEFAULT_FORMAT, 2, 4096)
-            sdl.mix_allocate_channels(26) #TODO: make it dependent on the SFX number.
-
         self.clock = Clock(self.fps_limit)
 
 
@@ -153,11 +140,3 @@ cdef class Window:
 
     cpdef double get_fps(self):
         return self.clock.get_fps()
-
-
-    def __dealloc__(self):
-        sdl.mix_close_audio()
-        sdl.mix_quit()
-        sdl.ttf_quit()
-        sdl.img_quit()
-        sdl.quit()
--- a/setup.py
+++ b/setup.py
@@ -79,6 +79,7 @@ setup(name='PyTouhou',
                             compiler_directives={'infer_types': True,
                                                  'infer_types.verbose': True},
                             compile_time_env={'MAX_TEXTURES': 1024,
+                                              'MAX_CHANNELS': 26,
                                               'USE_GLEW': is_windows}),
       scripts=['eosd', 'anmviewer'],
       **extra)