#!/usr/bin/env python3 # Launch PGFW games in the `games/` folder import argparse, os, pathlib, pygame, sys import lib.pgfw.pgfw as pgfw from games.ibitfit.electric_sieve.ElectricSieve import ElectricSieve class Playzing(pgfw.Game): def __init__(self): """ Create logo sprite, clear screen, and subscribe to events. """ pgfw.Game.__init__(self) # Assign types to configuration values self.get_configuration().type_declarations.add_chart({ "display": { "int-list": "clear" }, "logo": { "path": ["fire", "text"], "int": ["margin", "restart", "delay"], "bool": "trail" } }) # Clear screen to black self.get_display_surface().fill(self.configuration.get("display", "clear")) # Subscribe to PGFW command events self.subscribe(self.respond) # Load sprites for logo parts self.logo_text = pgfw.Sprite(self) self.logo_text.load_from_path(self.get_resource(self.configuration.get("logo", "text")), True) self.logo_fire = pgfw.Sprite(self) self.logo_fire.load_from_path(self.get_resource(self.configuration.get("logo", "fire")), True) # Register logo animation functions and begin animation self.register(self.animate_logo) self.register(self.reveal_text, interval=40) self.play(self.animate_logo, play_once=True) def animate_logo(self): """ Reset the logo animation to its beginning and start animating. """ # Place the logo screen_rect = self.get_display_surface().get_rect() margin = self.configuration.get("logo", "margin") self.logo_text.location.center = screen_rect.center self.logo_text.move(-self.logo_fire.location.w + margin / 2) # Clear the screen self.get_display_surface().fill(self.configuration.get("display", "clear")) # Place the fire at the left side of the logo text self.logo_fire.location.midleft = self.logo_text.location.midleft # Close the draw clip for the logo completely self.logo_text_clip = self.logo_text.location.copy() self.logo_text_clip.width = 0 # Queue the text reveal to start self.play(self.reveal_text, delay=self.configuration.get("logo", "delay")) def reveal_text(self): """ Move the fire right, opening the logo text clip rect until it's big enough to show all the text. Queue the animation to restart once finished. """ self.logo_fire.move(10) self.logo_text_clip.width = self.logo_fire.location.left - self.logo_text.location.left limit = self.logo_text.location.right + self.configuration.get("logo", "margin") / 2 if self.logo_fire.location.left > limit: self.logo_fire.location.left = limit self.halt(self.reveal_text) self.play(self.animate_logo, delay=self.configuration.get("logo", "restart"), play_once=True) def respond(self, event): """ Respond to all PGFW commands. If an any button is pressed, launch the iBitFit game. """ if self.delegate.compare(event, "any"): os.chdir(pathlib.Path("games/ibitfit")) print(f"current working dir is {os.getcwd()}") ibitfit = ElectricSieve() ibitfit.display.set_screen(dimensions=ibitfit.configuration.get("display", "dimensions")) ibitfit.run() os.chdir(pathlib.Path("../..")) self.display.set_screen(dimensions=self.configuration.get("display", "dimensions")) print(f"current working dir is {os.getcwd()}") def update(self): pgfw.Animation.update(self) # Erase the center of the screen if enabled if not self.configuration.get("logo", "trail"): erase_rect = self.logo_text.location.copy() erase_rect.width = self.logo_fire.location.right - self.logo_text.location.left self.get_display_surface().fill(self.configuration.get("display", "clear"), erase_rect) # Draw the logo text clipped to the current state of the animation, then remove the clip self.get_display_surface().set_clip(self.logo_text_clip) self.logo_text.update() self.get_display_surface().set_clip(None) # Draw the fire self.logo_fire.update() if __name__ == "__main__": # Parse command line arguments parser = argparse.ArgumentParser() parser.add_argument( "--keep-working-directory", action="store_true", help=""" Keep using the current directory as the working directory. The script expects the games to be in the games/ folder, so they can be imported as Python modules. Changing the directory to anything other than the launcher project's root folder will break it under normal circumstances, so use with care. """) parser.add_argument( "--kms", action="store_true", help=""" Use SDL 2's KMS video driver. For use on systems without a windowing system (like Linux in only console mode). See https://wiki.libsdl.org/SDL2/FAQUsingSDL#how_do_i_choose_a_specific_video_driver """) parser.add_argument( "--framebuffer", action="store_true", help=""" Use SDL 1.2's framebuffer video driver. For use on older systems without a windowing system that aren't using KMS. This won't work with SDL 2 or Pygame 2. See https://wiki.libsdl.org/SDL2/FAQUsingSDL#how_do_i_choose_a_specific_video_driver """) parser.add_argument( "--ignore-hangup", action="store_true", help=""" Ignore hangup signals. Enabling this may be necessary for running the launcher as a systemd service. See https://stackoverflow.com/questions/57205271/how-to-display-pygame-framebuffer-using-systemd-service """) arguments = parser.parse_args() # If not keeping the working directory, try to move into the same directory as where this program is stored. Use the location of the program # that launched this process to determine the path. if not arguments.keep_working_directory: target = os.path.dirname(sys.argv[0]) if not pathlib.Path(os.getcwd()).samefile(target): try: os.chdir(target) except: print(f""" Warning: detected that the working directory is not the same as the launcher project's root directory and could not change to to detected directory {target} """) if arguments.kms: # Use the KMS video driver. This works for newer versions of Raspberry Pi with the KMS overlay enabled, SDL 2, and Pygame 2. os.putenv("SDL_VIDEODRIVER", "kmsdrm") elif arguments.framebuffer: # Use the framebuffer display. This only works with Pygame 1.9.6 (and SDL 1.2). os.putenv("SDL_VIDEODRIVER", "fbcon") os.putenv("SDL_FBDEV", "/dev/fb0") # Run the launcher playzing = Playzing() playzing.run()