Files
RGSX/__main__.py

567 lines
29 KiB
Python
Raw Normal View History

import pygame# type: ignore
2025-07-06 19:47:21 +02:00
import os
os.environ["SDL_FBDEV"] = "/dev/fb0"
import asyncio
import platform
import logging
import requests
import config
from config import logger
from display import init_display, draw_loading_screen, draw_error_screen, draw_platform_grid, draw_progress_screen, draw_controls, draw_gradient, draw_virtual_keyboard, draw_popup_result_download, draw_extension_warning, draw_pause_menu, draw_controls_help, draw_game_list, draw_history_list, draw_clear_history_dialog, draw_confirm_dialog, draw_redownload_game_cache_dialog, draw_popup, THEME_COLORS, draw_music_popup
from network import test_internet, download_rom, check_extension_before_download, extract_zip, check_for_updates
2025-07-07 02:48:05 +02:00
from controls import handle_controls, validate_menu_state
2025-07-06 19:47:21 +02:00
from controls_mapper import load_controls_config, map_controls, draw_controls_mapping, ACTIONS
from utils import play_random_music, load_sources, detect_non_pc
2025-07-07 02:48:05 +02:00
from history import load_history
from config import OTA_data_ZIP
2025-07-06 19:47:21 +02:00
# Configuration du logging
log_dir = "/userdata/roms/ports/RGSX/logs"
log_file = os.path.join(log_dir, "RGSX.log")
try:
os.makedirs(log_dir, exist_ok=True)
logging.basicConfig(
filename=log_file,
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
except Exception as e:
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logging.error(f"Échec de la configuration du logging dans {log_file}: {str(e)}")
logger = logging.getLogger(__name__)
# Initialisation de Pygame
2025-07-06 19:47:21 +02:00
pygame.init()
config.init_font()
2025-07-06 19:47:21 +02:00
pygame.joystick.init()
pygame.mouse.set_visible(True)
# Détection du système
2025-07-06 19:47:21 +02:00
config.is_non_pc = detect_non_pc()
# Initialisation des polices
try:
2025-07-10 22:32:35 +02:00
config.font = pygame.font.Font("/userdata/roms/ports/RGSX/assets/Pixel-UniCode.ttf", 36)
config.title_font = pygame.font.Font("/userdata/roms/ports/RGSX/assets/Pixel-UniCode.ttf", 48)
config.search_font = pygame.font.Font("/userdata/roms/ports/RGSX/assets/Pixel-UniCode.ttf", 48)
config.progress_font = pygame.font.Font("/userdata/roms/ports/RGSX/assets/Pixel-UniCode.ttf", 36)
config.small_font = pygame.font.Font("/userdata/roms/ports/RGSX/assets/Pixel-UniCode.ttf", 28)
2025-07-06 19:47:21 +02:00
logger.debug("Police Pixel-UniCode chargée")
except:
config.font = pygame.font.SysFont("arial", 48)
config.title_font = pygame.font.SysFont("arial", 60)
config.search_font = pygame.font.SysFont("arial", 60)
config.progress_font = pygame.font.SysFont("arial", 36)
config.small_font = pygame.font.SysFont("arial", 28)
2025-07-06 19:47:21 +02:00
logger.debug("Police Arial chargée")
# Initialisation de lécran
screen = init_display()
pygame.display.set_caption("RGSX")
# Afficher un écran de chargement initial
draw_gradient(screen, THEME_COLORS["background_top"], THEME_COLORS["background_bottom"])
loading_text = config.font.render("Initialisation...", True, (255, 255, 255))
text_rect = loading_text.get_rect(center=(config.screen_width // 2, config.screen_height // 2))
screen.blit(loading_text, text_rect)
pygame.display.flip()
logger.debug("Écran de chargement initial affiché")
2025-07-06 19:47:21 +02:00
# Mise à jour de la résolution dans config
config.screen_width, config.screen_height = pygame.display.get_surface().get_size()
logger.debug(f"Résolution réelle : {config.screen_width}x{config.screen_height}")
# Initialisation des variables de grille
config.current_page = 0
config.selected_platform = 0
config.selected_key = (0, 0)
config.transition_state = "none"
2025-07-07 02:48:05 +02:00
# Chargement de l'historique
config.history = load_history()
logger.debug(f"Historique chargé: {len(config.history)} entrées")
2025-07-06 19:47:21 +02:00
# Vérification et chargement de la configuration des contrôles
config.controls_config = load_controls_config()
if not config.controls_config:
config.menu_state = "controls_mapping"
else:
config.menu_state = "loading"
# Initialisation du gamepad
joystick = None
if pygame.joystick.get_count() > 0:
joystick = pygame.joystick.Joystick(0)
joystick.init()
logger.debug("Gamepad initialisé")
# Initialisation de pygame.mixer
2025-07-06 19:47:21 +02:00
pygame.mixer.init()
# Jouer la première musique au démarrage
play_random_music()
2025-07-06 19:47:21 +02:00
# Boucle principale
async def main():
logger.debug("Début main")
running = True
loading_step = "none"
sources = []
config.last_state_change_time = 0
config.debounce_delay = 50
config.update_triggered = False
last_redraw_time = pygame.time.get_ticks()
clock = pygame.time.Clock()
# Variables pour la progression simulée
check_ota_start_time = None
load_sources_start_time = None
SIMULATED_CHECK_OTA_DURATION = 5.0
SIMULATED_LOAD_SOURCES_DURATION = 3.0
2025-07-06 19:47:21 +02:00
while running:
clock.tick(60)
2025-07-06 19:47:21 +02:00
if config.update_triggered:
logger.debug("Mise à jour déclenchée, arrêt de la boucle principale")
break
current_time = pygame.time.get_ticks()
current_time_sec = current_time / 1000.0
2025-07-06 19:47:21 +02:00
# Forcer redraw toutes les 100 ms dans download_progress
if config.menu_state == "download_progress" and current_time - last_redraw_time >= 100:
config.needs_redraw = True
last_redraw_time = current_time
# Gestion du popup timer
delta_time = current_time - config.last_frame_time
config.last_frame_time = current_time
if config.menu_state == "restart_popup" and config.popup_timer > 0:
config.popup_timer -= delta_time
config.needs_redraw = True
if config.popup_timer <= 0:
config.menu_state = validate_menu_state(config.previous_menu_state)
config.popup_message = ""
config.popup_timer = 0
config.needs_redraw = True
logger.debug(f"Fermeture automatique du popup, retour à {config.menu_state}")
2025-07-06 19:47:21 +02:00
# Gestion des événements
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
config.menu_state = "confirm_exit"
config.confirm_selection = 0
config.needs_redraw = True
logger.debug("Événement QUIT détecté, passage à confirm_exit")
continue
elif event.type == pygame.USEREVENT + 1:
logger.debug("Fin de la musique actuelle, passage à la suivante")
play_random_music()
2025-07-06 19:47:21 +02:00
start_config = config.controls_config.get("start", {})
if start_config and (
(event.type == pygame.KEYDOWN and start_config.get("type") == "key" and event.key == start_config.get("value")) or
(event.type == pygame.JOYBUTTONDOWN and start_config.get("type") == "button" and event.button == start_config.get("value")) or
(event.type == pygame.JOYAXISMOTION and start_config.get("type") == "axis" and event.axis == start_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == start_config.get("value")[1]) or
2025-07-07 02:48:05 +02:00
(event.type == pygame.JOYHATMOTION and start_config.get("type") == "hat" and event.value == tuple(start_config.get("value"))) or
2025-07-06 19:47:21 +02:00
(event.type == pygame.MOUSEBUTTONDOWN and start_config.get("type") == "mouse" and event.button == start_config.get("value"))
):
2025-07-07 02:48:05 +02:00
if config.menu_state not in ["pause_menu", "controls_help", "controls_mapping", "history", "confirm_clear_history"]:
2025-07-06 19:47:21 +02:00
config.previous_menu_state = config.menu_state
config.menu_state = "pause_menu"
config.selected_option = 0
2025-07-06 19:47:21 +02:00
config.needs_redraw = True
logger.debug(f"Ouverture menu pause depuis {config.previous_menu_state}")
continue
2025-07-06 19:47:21 +02:00
if config.menu_state == "pause_menu":
handle_controls(event, sources, joystick, screen)
config.needs_redraw = True
logger.debug(f"Événement transmis à handle_controls dans pause_menu: {event.type}")
2025-07-06 19:47:21 +02:00
continue
if config.menu_state == "controls_help":
cancel_config = config.controls_config.get("cancel", {})
if (
(event.type == pygame.KEYDOWN and cancel_config and event.key == cancel_config.get("value")) or
2025-07-07 02:48:05 +02:00
(event.type == pygame.JOYBUTTONDOWN and cancel_config and cancel_config.get("type") == "button" and event.button == cancel_config.get("value")) or
(event.type == pygame.JOYAXISMOTION and cancel_config and cancel_config.get("type") == "axis" and event.axis == cancel_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == cancel_config.get("value")[1]) or
(event.type == pygame.JOYHATMOTION and cancel_config and cancel_config.get("type") == "hat" and event.value == tuple(cancel_config.get("value")))
2025-07-06 19:47:21 +02:00
):
2025-07-07 02:48:05 +02:00
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
2025-07-06 19:47:21 +02:00
config.menu_state = "pause_menu"
config.needs_redraw = True
logger.debug("Controls_help: Annulation, retour à pause_menu")
continue
2025-07-07 02:48:05 +02:00
if config.menu_state == "confirm_clear_history":
handle_controls(event, sources, joystick, screen)
2025-07-07 02:48:05 +02:00
config.needs_redraw = True
logger.debug(f"Événement transmis à handle_controls dans confirm_clear_history: {event.type}")
continue
if config.menu_state == "redownload_game_cache":
handle_controls(event, sources, joystick, screen)
config.needs_redraw = True
logger.debug(f"Événement transmis à handle_controls dans redownload_game_cache: {event.type}")
continue
2025-07-07 02:48:05 +02:00
if config.menu_state in ["platform", "game", "error", "confirm_exit", "download_progress", "download_result", "extension_warning", "history"]:
2025-07-06 19:47:21 +02:00
action = handle_controls(event, sources, joystick, screen)
config.needs_redraw = True
if action == "quit":
running = False
logger.debug("Action quit détectée, arrêt de l'application")
elif action == "download" and config.menu_state == "game" and config.filtered_games:
game = config.filtered_games[config.current_game]
game_name = game[0] if isinstance(game, (list, tuple)) else game
platform = config.platforms[config.current_platform]
url = game[1] if isinstance(game, (list, tuple)) and len(game) > 1 else None
if url:
logger.debug(f"Vérification de l'extension pour {game_name}, URL: {url}")
is_supported, message, is_zip_non_supported = check_extension_before_download(url, platform, game_name)
if not is_supported:
config.pending_download = (url, platform, game_name, is_zip_non_supported)
config.menu_state = "extension_warning"
config.extension_confirm_selection = 0
config.needs_redraw = True
logger.debug(f"Extension non reconnue, passage à extension_warning pour {game_name}")
else:
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported))
config.download_tasks[task] = (task, url, game_name, platform)
config.menu_state = "download_progress"
config.pending_download = None
2025-07-06 19:47:21 +02:00
config.needs_redraw = True
logger.debug(f"Téléchargement démarré pour {game_name}, passage à download_progress")
2025-07-07 02:48:05 +02:00
elif action == "redownload" and config.menu_state == "history" and config.history:
entry = config.history[config.current_history_item]
platform = entry["platform"]
game_name = entry["game_name"]
for game in config.games:
if game[0] == game_name and config.platforms[config.current_platform] == platform:
url = game[1]
is_supported, message, is_zip_non_supported = check_extension_before_download(url, platform, game_name)
if not is_supported:
config.pending_download = (url, platform, game_name, is_zip_non_supported)
config.menu_state = "extension_warning"
config.extension_confirm_selection = 0
config.needs_redraw = True
logger.debug(f"Extension non reconnue pour retéléchargement, passage à extension_warning pour {game_name}")
else:
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported))
config.download_tasks[task] = (task, url, game_name, platform)
config.menu_state = "download_progress"
config.pending_download = None
2025-07-07 02:48:05 +02:00
config.needs_redraw = True
logger.debug(f"Retéléchargement démarré pour {game_name}, passage à download_progress")
break
2025-07-06 19:47:21 +02:00
# Gestion des téléchargements
if config.download_tasks:
for task_id, (task, url, game_name, platform) in list(config.download_tasks.items()):
if task.done():
try:
success, message = await task
config.download_result_message = message
config.download_result_error = not success
config.download_result_start_time = pygame.time.get_ticks()
config.menu_state = "download_result"
config.download_progress.clear()
config.pending_download = None
2025-07-06 19:47:21 +02:00
config.needs_redraw = True
del config.download_tasks[task_id]
logger.debug(f"Téléchargement terminé: {game_name}, succès={success}, message={message}")
except Exception as e:
config.download_result_message = f"Erreur lors du téléchargement : {str(e)}"
config.download_result_error = True
config.download_result_start_time = pygame.time.get_ticks()
config.menu_state = "download_result"
config.download_progress.clear()
config.pending_download = None
2025-07-06 19:47:21 +02:00
config.needs_redraw = True
del config.download_tasks[task_id]
logger.error(f"Erreur dans tâche de téléchargement: {str(e)}")
# Gestion de la fin du popup download_result
if config.menu_state == "download_result" and current_time - config.download_result_start_time > 3000:
2025-07-07 02:48:05 +02:00
config.menu_state = config.previous_menu_state if config.previous_menu_state in ["platform", "game", "history"] else "game"
config.download_progress.clear()
config.pending_download = None
2025-07-06 19:47:21 +02:00
config.needs_redraw = True
logger.debug(f"Fin popup download_result, retour à {config.menu_state}")
# Gestion de l'état loading
if config.menu_state == "loading":
logger.debug(f"Étape chargement : {loading_step}")
if loading_step == "none":
loading_step = "init_sources"
config.current_loading_system = "Chargement des sources..."
config.loading_progress = 0.0
config.needs_redraw = True
load_sources_start_time = current_time_sec
logger.debug(f"Étape chargement : {loading_step}, progress={config.loading_progress}")
elif loading_step == "init_sources":
if load_sources_start_time is None:
load_sources_start_time = current_time_sec
# Simuler la progression pour init_sources
elapsed = current_time_sec - load_sources_start_time
progress = min(0.0 + (5.0 * elapsed / SIMULATED_LOAD_SOURCES_DURATION), 5.0)
config.loading_progress = progress
2025-07-07 02:48:05 +02:00
config.needs_redraw = True
logger.debug(f"Progression simulée init_sources : {config.loading_progress}%")
2025-07-06 19:47:21 +02:00
# Exécuter load_sources
sources = load_sources()
if not sources:
config.menu_state = "error"
config.error_message = "Échec du chargement de sources.json"
2025-07-06 19:47:21 +02:00
config.needs_redraw = True
logger.debug("Erreur : Échec du chargement de sources.json")
2025-07-06 19:47:21 +02:00
else:
loading_step = "test_internet"
config.current_loading_system = "Test de connexion..."
config.loading_progress = 5.0
load_sources_start_time = None
2025-07-06 19:47:21 +02:00
config.needs_redraw = True
logger.debug(f"Étape chargement : {loading_step}, progress={config.loading_progress}")
2025-07-06 19:47:21 +02:00
elif loading_step == "test_internet":
logger.debug("Exécution de test_internet()")
if test_internet():
loading_step = "check_ota"
config.current_loading_system = "Vérification des mises à jour..."
config.loading_progress = 5.0
check_ota_start_time = current_time_sec
2025-07-06 19:47:21 +02:00
config.needs_redraw = True
logger.debug(f"Étape chargement : {loading_step}, progress={config.loading_progress}")
else:
config.menu_state = "error"
config.error_message = "Pas de connexion Internet. Vérifiez votre réseau."
config.needs_redraw = True
logger.debug(f"Erreur : {config.error_message}")
2025-07-06 19:47:21 +02:00
elif loading_step == "check_ota":
if check_ota_start_time is None:
check_ota_start_time = current_time_sec
# Simuler la progression pour check_ota
elapsed = current_time_sec - check_ota_start_time
progress = min(5.0 + (25.0 * elapsed / SIMULATED_CHECK_OTA_DURATION), 30.0)
config.loading_progress = progress
config.needs_redraw = True
logger.debug(f"Progression simulée check_ota : {config.loading_progress}%")
# Exécuter check_for_updates
2025-07-06 19:47:21 +02:00
success, message = await check_for_updates()
logger.debug(f"Résultat de check_for_updates : success={success}, message={message}")
if not success:
config.menu_state = "error"
config.error_message = message
config.needs_redraw = True
logger.debug(f"Erreur OTA : {message}")
else:
loading_step = "check_data"
config.current_loading_system = "Téléchargement des jeux et images..."
config.loading_progress = 30.0
check_ota_start_time = None
2025-07-06 19:47:21 +02:00
config.needs_redraw = True
logger.debug(f"Étape chargement : {loading_step}, progress={config.loading_progress}")
2025-07-06 19:47:21 +02:00
elif loading_step == "check_data":
games_data_dir = "/userdata/roms/ports/RGSX/games"
is_data_empty = not os.path.exists(games_data_dir) or not any(os.scandir(games_data_dir))
2025-07-06 19:47:21 +02:00
if is_data_empty:
config.current_loading_system = "Téléchargement du Dossier Data initial..."
config.loading_progress = 30.0
2025-07-06 19:47:21 +02:00
config.needs_redraw = True
logger.debug("Dossier Data vide, début du téléchargement du ZIP")
2025-07-06 19:47:21 +02:00
try:
zip_path = "/userdata/roms/ports/RGSX.zip"
headers = {'User-Agent': 'Mozilla/5.0'}
with requests.get(OTA_data_ZIP, stream=True, headers=headers, timeout=30) as response:
response.raise_for_status()
total_size = int(response.headers.get('content-length', 0))
logger.debug(f"Taille totale du ZIP : {total_size} octets")
downloaded = 0
os.makedirs(os.path.dirname(zip_path), exist_ok=True)
with open(zip_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
downloaded += len(chunk)
config.download_progress[OTA_data_ZIP] = {
"downloaded_size": downloaded,
"total_size": total_size,
"status": "Téléchargement",
"progress_percent": (downloaded / total_size * 100) if total_size > 0 else 0
}
config.loading_progress = 30.0 + (40.0 * downloaded / total_size) if total_size > 0 else 30.0
2025-07-06 19:47:21 +02:00
config.needs_redraw = True
await asyncio.sleep(0)
logger.debug(f"ZIP téléchargé : {zip_path}")
config.current_loading_system = "Extraction du Dossier Data initial..."
config.loading_progress = 70.0
2025-07-06 19:47:21 +02:00
config.needs_redraw = True
dest_dir = "/userdata/roms/ports/RGSX"
success, message = extract_zip(zip_path, dest_dir, OTA_data_ZIP)
if success:
logger.debug(f"Extraction réussie : {message}")
config.loading_progress = 70.0
2025-07-06 19:47:21 +02:00
config.needs_redraw = True
else:
raise Exception(f"Échec de l'extraction : {message}")
2025-07-06 19:47:21 +02:00
except Exception as e:
logger.error(f"Erreur lors du téléchargement/extraction du Dossier Data : {str(e)}")
config.menu_state = "error"
config.error_message = f"Échec du téléchargement/extraction du Dossier Data : {str(e)}"
config.needs_redraw = True
loading_step = "load_sources"
if os.path.exists(zip_path):
os.remove(zip_path)
continue
if os.path.exists(zip_path):
os.remove(zip_path)
logger.debug(f"Fichier ZIP {zip_path} supprimé")
2025-07-06 19:47:21 +02:00
loading_step = "load_sources"
config.current_loading_system = "Chargement des systèmes..."
config.loading_progress = 70.0
load_sources_start_time = current_time_sec
2025-07-06 19:47:21 +02:00
config.needs_redraw = True
logger.debug(f"Étape chargement : {loading_step}, progress={config.loading_progress}")
2025-07-06 19:47:21 +02:00
else:
loading_step = "load_sources"
config.current_loading_system = "Chargement des systèmes..."
config.loading_progress = 70.0
load_sources_start_time = current_time_sec
2025-07-06 19:47:21 +02:00
config.needs_redraw = True
logger.debug(f"Dossier Data non vide, passage à {loading_step}")
2025-07-06 19:47:21 +02:00
elif loading_step == "load_sources":
if load_sources_start_time is None:
load_sources_start_time = current_time_sec
# Simuler la progression pour load_sources
elapsed = current_time_sec - load_sources_start_time
progress = min(70.0 + (30.0 * elapsed / SIMULATED_LOAD_SOURCES_DURATION), 100.0)
config.loading_progress = progress
config.needs_redraw = True
logger.debug(f"Progression simulée load_sources : {config.loading_progress}%")
# Exécuter load_sources
2025-07-06 19:47:21 +02:00
sources = load_sources()
if not sources:
config.menu_state = "error"
config.error_message = "Échec du chargement de sources.json"
config.needs_redraw = True
logger.debug("Erreur : Échec du chargement de sources.json")
else:
config.menu_state = "platform"
config.loading_progress = 0.0
config.current_loading_system = ""
load_sources_start_time = None
2025-07-06 19:47:21 +02:00
config.needs_redraw = True
logger.debug(f"Fin chargement, passage à platform, progress={config.loading_progress}")
# Gestion de l'état de transition
if config.transition_state == "to_game":
config.transition_progress += 1
if config.transition_progress >= config.transition_duration:
config.menu_state = "game"
config.transition_state = "idle"
config.transition_progress = 0.0
config.needs_redraw = True
logger.debug("Transition terminée, passage à game")
# Affichage
if config.needs_redraw:
draw_gradient(screen, THEME_COLORS["background_top"], THEME_COLORS["background_bottom"])
if config.menu_state == "controls_mapping":
draw_controls_mapping(screen, ACTIONS[0], None, False, 0.0)
elif config.menu_state == "loading":
draw_loading_screen(screen)
elif config.menu_state == "error":
draw_error_screen(screen)
elif config.menu_state == "platform":
draw_platform_grid(screen)
elif config.menu_state == "game":
if not config.search_mode:
draw_game_list(screen)
if config.search_mode:
draw_game_list(screen)
draw_virtual_keyboard(screen)
elif config.menu_state == "download_progress":
draw_progress_screen(screen)
elif config.menu_state == "download_result":
draw_popup_result_download(screen, config.download_result_message, config.download_result_error)
elif config.menu_state == "confirm_exit":
draw_confirm_dialog(screen)
elif config.menu_state == "extension_warning":
draw_extension_warning(screen)
elif config.menu_state == "pause_menu":
draw_pause_menu(screen, config.selected_option)
elif config.menu_state == "controls_help":
draw_controls_help(screen, config.previous_menu_state)
elif config.menu_state == "history":
draw_history_list(screen)
elif config.menu_state == "confirm_clear_history":
draw_clear_history_dialog(screen)
elif config.menu_state == "redownload_game_cache":
draw_redownload_game_cache_dialog(screen)
elif config.menu_state == "restart_popup":
draw_popup(screen)
else:
config.menu_state = "platform"
draw_platform_grid(screen)
config.needs_redraw = True
logger.error(f"État de menu non valide détecté: {config.menu_state}, retour à platform")
draw_controls(screen, config.menu_state)
draw_music_popup(screen)
pygame.display.flip()
config.needs_redraw = False
# Gestion de l'état controls_mapping
if config.menu_state == "controls_mapping":
try:
success = map_controls(screen)
logger.debug(f"map_controls terminé, succès={success}")
if success:
config.controls_config = load_controls_config()
config.menu_state = "loading"
config.needs_redraw = True
else:
config.menu_state = "error"
config.error_message = "Échec du mappage des contrôles"
config.needs_redraw = True
logger.debug("Échec du mappage, passage à l'état error")
except Exception as e:
logger.error(f"Erreur lors de l'appel de map_controls : {str(e)}")
config.menu_state = "error"
config.error_message = f"Erreur dans map_controls: {str(e)}"
config.needs_redraw = True
2025-07-06 19:47:21 +02:00
clock.tick(60)
await asyncio.sleep(0.01)
pygame.mixer.music.stop()
pygame.quit()
logger.debug("Application terminée")
if platform.system() == "Emscripten":
asyncio.ensure_future(main())
else:
if __name__ == "__main__":
asyncio.run(main())