Start work on game installer and launcher actually working.

This commit is contained in:
Storm Dragon
2026-04-21 14:47:09 -04:00
parent b83b995cf0
commit 2a8265a3b6
4 changed files with 386 additions and 2 deletions
+74 -2
View File
@@ -22,6 +22,9 @@ from dataclasses import dataclass
from pathlib import Path
INSTALL_GAME_RUNNER = Path("/home/stormux/.local/.functions/install_game.sh")
@dataclass(frozen=True)
class GameEntry:
section: str
@@ -69,6 +72,28 @@ def build_game_command(entry):
return f"STORMUX_LAUNCH_SCRIPT={launchPath!r} startx"
return shlex.quote(str(entry.installScript))
def build_game_install_command(entry):
return f"{shlex.quote(str(INSTALL_GAME_RUNNER))} {shlex.quote(str(entry.installScript))}"
def build_game_uninstall_command(entry):
if entry.launchScript is None:
return ""
launchPath = shlex.quote(str(entry.launchScript))
gameName = shlex.quote(entry.name)
return f"rm -f {launchPath}; echo {gameName} uninstalled"
def build_game_actions(entry):
actions = []
if entry.isInstalled:
actions.append(("Play", build_game_command(entry)))
actions.append(("Uninstall", build_game_uninstall_command(entry)))
else:
actions.append(("Install", build_game_install_command(entry)))
return actions
class VoicedMenu:
def __init__(self, title="Stormux Game Menu"):
self.title = title
@@ -113,6 +138,7 @@ class VoicedMenu:
# Load downloadable games registry
self.downloadable_games = self.load_downloadable_games()
self.savedMenuState = None
def load_downloadable_games(self):
"""Load downloadable games registry from JSON file"""
@@ -641,6 +667,49 @@ class VoicedMenu:
self.menuSections[sectionName].append((name, command))
def save_menu_state(self):
"""Save the current menu state so a temporary submenu can restore it."""
self.savedMenuState = {
"sectionNames": self.sectionNames.copy(),
"currentSection": self.currentSection,
"currentItemIndices": self.currentItemIndices.copy(),
"menuSections": {
sectionName: items.copy()
for sectionName, items in self.menuSections.items()
},
}
def restore_saved_menu(self):
"""Restore the menu state saved before entering a temporary submenu."""
if self.savedMenuState is None:
return
self.sectionNames = self.savedMenuState["sectionNames"]
self.currentSection = self.savedMenuState["currentSection"]
self.currentItemIndices = self.savedMenuState["currentItemIndices"]
self.menuSections = self.savedMenuState["menuSections"]
self.savedMenuState = None
self.draw_menu()
self.announce_current_section()
time.sleep(0.5)
self.announce_current_item(interrupt=False)
def show_action_menu(self, sectionName, actions):
"""Replace the current menu with a temporary action submenu."""
self.save_menu_state()
self.sectionNames = [sectionName]
self.currentSection = 0
self.currentItemIndices = {sectionName: 0}
self.menuSections = {sectionName: actions + [("Back", self.restore_saved_menu)]}
self.draw_menu()
self.announce_current_section()
time.sleep(0.5)
self.announce_current_item(interrupt=False)
def show_game_actions(self, gameEntry):
"""Show available actions for a game."""
self.show_action_menu(gameEntry.name, build_game_actions(gameEntry))
def speak(self, text, interrupt=True):
"""Speak the given text with option to interrupt existing speech"""
if self.speechClient is None:
@@ -1103,8 +1172,11 @@ if __name__ == "__main__":
menu = VoicedMenu(title="Stormux Gaming Menu")
for gameEntry in discover_game_entries():
label = gameEntry.name if gameEntry.isInstalled else f"{gameEntry.name} (install)"
menu.add_item(gameEntry.section, label, build_game_command(gameEntry))
menu.add_item(
gameEntry.section,
gameEntry.name,
lambda entry=gameEntry: menu.show_game_actions(entry),
)
menu.add_section("Emulators")
menu.add_item("Emulators", "Apple 2e", "/home/stormux/.local/bin/apple_2e.py")