Compare commits

...

10 Commits

Author SHA1 Message Date
skymike03
08f3e64d2a v2.4.0.2 (2026.01.14)
- correct some bugs/errors on display and logging functionality; update language files with new options
2026-01-14 20:25:47 +01:00
RGS
4968af2da9 Update image source in README.md 2026-01-07 14:48:30 +01:00
skymike03
920914b374 v2.4.0.1 (2026.01.07 bis)
- remove windowed mode (useless)
2026-01-07 14:36:00 +01:00
skymike03
a326cb0b67 Merge branch 'main' of https://github.com/RetroGameSets/RGSX 2026-01-07 14:25:18 +01:00
skymike03
c9fdf93221 v2.4.0.0 (2026.01.07)
- add performance mode to disable some 3d effects for a better experience on low-end devices
- multi screen support (choose default screen in Pause>Display menu )
- update windows launcher for retrobat
- ignore some useless warnings
- Merge pull request [#34](https://github.com/RetroGameSets/RGSX/issues/34) from aaronson2012/start-menu-fix
(feat: enable circular navigation for pause menu options)
2026-01-07 14:25:15 +01:00
RGS
184a8c64fe Update troubleshooting steps for app crashes 2026-01-03 16:37:17 +01:00
RGS
9a2e4ce0db Merge pull request #34 from aaronson2012/start-menu-fix
feat: enable circular navigation for pause menu options
2025-12-06 13:06:31 +01:00
Jacob Christie
73eceeb777 feat: enable circular navigation for pause menu options 2025-12-06 05:26:16 -06:00
RGS
2fcc4ca6df Update README to include troubleshooting link
Added a link to the troubleshooting section in the README.
2025-11-25 19:54:03 +01:00
RGS
2ed889540b Enhance troubleshooting section in README
Updated troubleshooting solutions for controls and games visibility issues.
2025-11-25 19:50:20 +01:00
15 changed files with 874 additions and 251 deletions

View File

@@ -1,11 +1,11 @@
# 🎮 Retro Game Sets Xtra (RGSX)
**[Discord Support](https://discord.gg/Vph9jwg3VV)** • **[Installation](#-installation)** • **[French Documentation](https://github.com/RetroGameSets/RGSX/blob/main/README_FR.md)**
**[Discord Support](https://discord.gg/Vph9jwg3VV)** • **[Installation](#-installation)** • **[French Documentation](https://github.com/RetroGameSets/RGSX/blob/main/README_FR.md)****[Troubleshoot / Common Errors](https://github.com/RetroGameSets/RGSX#%EF%B8%8F-troubleshooting)** •
A free, user-friendly ROM downloader for Batocera, Knulli, and RetroBat with multi-source support.
<p align="center">
<img width="69%" alt="platform menu" src="https://github.com/user-attachments/assets/4464b57b-06a8-45e9-a411-cc12b421545a" />
<img width="69%" alt="main" src="https://github.com/user-attachments/assets/a98f1189-9a50-4cc3-b588-3f85245640d8" />
<img width="30%" alt="controls help" src="https://github.com/user-attachments/assets/38cac7e6-14f2-4e83-91da-0679669822ee" />
</p>
<p align="center">
@@ -201,11 +201,12 @@ RGSX includes a web interface that launched automatically when using RGSX for re
| Issue | Solution |
|-------|----------|
| Controls not working | Delete `/saves/ports/rgsx/controls.json` + restart |
| Games not showing | Pause Menu > Games > Update Game Cache |
| Download stuck | Check API keys in `/saves/ports/rgsx/` |
| App crashes | Check `/roms/ports/RGSX/logs/RGSX.log` |
| Controls not working | Delete `/saves/ports/rgsx/controls.json` + restart app, you can try delete /roms/ports/RGSX/assets/controls/xx.json too |
| No games ? | Pause Menu > Games > Update Game Cache |
| Missing systems on the list? | RGSX read es_systems.cfg to show only supported systems, if you want all systems : Pause Menu > Games > Show unsupported systems |
| App crashes | Check `/roms/ports/RGSX/logs/RGSX.log` or `/roms/windows/logs/Retrobat_RGSX_log.txt` |
| Layout change not applied | Restart RGSX after changing layout |
| Downloading BIOS file is ok but you can't download any games? | Activate custom DNS on Pause Menu> Settings and reboot , server can be blocked by your ISP. check any threat/website protection on your router too, especially on ASUS one|
**Need help?** Share logs from `/roms/ports/RGSX/logs/` on [Discord](https://discord.gg/Vph9jwg3VV).

View File

@@ -1,5 +1,11 @@
import os
import platform
import warnings
# Ignorer le warning de deprecation de pkg_resources dans pygame
warnings.filterwarnings("ignore", category=UserWarning, module="pygame.pkgdata")
warnings.filterwarnings("ignore", message="pkg_resources is deprecated")
# Ne pas forcer SDL_FBDEV ici; si déjà défini par l'environnement, on le garde
try:
if "SDL_FBDEV" in os.environ:
@@ -170,7 +176,6 @@ config.init_footer_font()
# Mise à jour de la résolution dans config
config.screen_width, config.screen_height = pygame.display.get_surface().get_size()
logger.debug(f"Resolution d'ecran : {config.screen_width}x{config.screen_height}")
print(f"Resolution ecran validee: {config.screen_width}x{config.screen_height}")
# Afficher un premier écran de chargement immédiatement pour éviter un écran noir
@@ -270,7 +275,6 @@ logger.debug(f"Historique de téléchargement : {len(config.history)} entrées")
# Chargement des jeux téléchargés
config.downloaded_games = load_downloaded_games()
logger.debug(f"Jeux téléchargés : {sum(len(v) for v in config.downloaded_games.values())} jeux")
# Vérification et chargement de la configuration des contrôles (après mises à jour et détection manette)
config.controls_config = load_controls_config()
@@ -296,6 +300,9 @@ try:
if config.controls_config:
summary = {}
for action, mapping in config.controls_config.items():
# Vérifier que mapping est bien un dictionnaire
if not isinstance(mapping, dict):
continue
mtype = mapping.get("type")
val = None
if mtype == "key":
@@ -335,9 +342,6 @@ def start_web_server():
global web_server_process
try:
web_server_script = os.path.join(config.APP_FOLDER, "rgsx_web.py")
logger.info(f"Tentative de démarrage du serveur web...")
logger.info(f"Script: {web_server_script}")
logger.info(f"Fichier existe: {os.path.exists(web_server_script)}")
if not os.path.exists(web_server_script):
logger.warning(f"Script serveur web introuvable: {web_server_script}")
@@ -378,7 +382,6 @@ def start_web_server():
logger.info(f"✅ Serveur web démarré (PID: {web_server_process.pid})")
logger.info(f"🌐 Serveur accessible sur http://localhost:5000")
logger.info(f"📝 Logs de démarrage: {web_server_log}")
# Attendre un peu pour voir si le processus crash immédiatement
import time
@@ -441,7 +444,6 @@ async def main():
# Démarrer le worker de la queue de téléchargement
queue_worker_thread = threading.Thread(target=download_queue_worker, daemon=True)
queue_worker_thread.start()
logger.info("Worker de la queue de téléchargement démarré")
running = True
loading_step = "none"
@@ -1229,6 +1231,7 @@ async def main():
config.loading_progress = 20.0
config.needs_redraw = True
logger.debug(f"Étape chargement : {loading_step}, progress={config.loading_progress}")
continue # Passer immédiatement à check_ota
else:
config.menu_state = "error"
config.error_message = _("error_no_internet")
@@ -1258,6 +1261,7 @@ async def main():
config.loading_progress = 50.0
config.needs_redraw = True
logger.debug(f"Étape chargement : {loading_step}, progress={config.loading_progress}")
continue # Passer immédiatement à check_data
elif loading_step == "check_data":
is_data_empty = not os.path.exists(config.GAMES_FOLDER) or not any(os.scandir(config.GAMES_FOLDER))
if is_data_empty:
@@ -1265,6 +1269,7 @@ async def main():
config.loading_progress = 30.0
config.needs_redraw = True
logger.debug("Dossier Data vide, début du téléchargement du ZIP")
sources_zip_url = None # Initialiser pour éviter les erreurs
try:
zip_path = os.path.join(config.SAVE_FOLDER, "data_download.zip")
headers = {'User-Agent': 'Mozilla/5.0'}
@@ -1370,7 +1375,9 @@ async def main():
config.loading_progress = 80.0
config.needs_redraw = True
logger.debug(f"Dossier Data non vide, passage à {loading_step}")
continue # Passer immédiatement à load_sources
elif loading_step == "load_sources":
logger.debug(f"Étape chargement : {loading_step}, progress={config.loading_progress}")
sources = load_sources()
config.menu_state = "platform"
config.loading_progress = 100.0

View File

@@ -13,7 +13,7 @@ except Exception:
pygame = None # type: ignore
# Version actuelle de l'application
app_version = "2.3.3.3"
app_version = "2.4.0.2"
def get_application_root():

View File

@@ -1658,12 +1658,14 @@ def handle_controls(event, sources, joystick, screen):
config.needs_redraw = True
logger.debug(f"Start: retour à {config.menu_state} depuis pause_menu")
elif is_input_matched(event, "up"):
config.selected_option = max(0, config.selected_option - 1)
# Menu racine hiérarchique: nombre dynamique (langue + catégories)
total = getattr(config, 'pause_menu_total_options', 7)
config.selected_option = (config.selected_option - 1) % total
config.needs_redraw = True
elif is_input_matched(event, "down"):
# Menu racine hiérarchique: nombre dynamique (langue + catégories)
total = getattr(config, 'pause_menu_total_options', 7)
config.selected_option = min(total - 1, config.selected_option + 1)
config.selected_option = (config.selected_option + 1) % total
config.needs_redraw = True
elif is_input_matched(event, "confirm"):
if config.selected_option == 0: # Games submenu
@@ -1753,7 +1755,8 @@ def handle_controls(event, sources, joystick, screen):
# Sous-menu Display
elif config.menu_state == "pause_display_menu":
sel = getattr(config, 'pause_display_selection', 0)
total = 6 # layout, font size, footer font size, font family, allow unknown extensions, back
# layout, font size, footer font size, font family, monitor, light, allow unknown extensions, back (8)
total = 8
if is_input_matched(event, "up"):
config.pause_display_selection = (sel - 1) % total
config.needs_redraw = True
@@ -1848,8 +1851,37 @@ def handle_controls(event, sources, joystick, screen):
config.needs_redraw = True
except Exception as e:
logger.error(f"Erreur changement font family: {e}")
# 4 allow unknown extensions
elif sel == 4 and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")):
# 4 monitor selection
elif sel == 4 and (is_input_matched(event, "left") or is_input_matched(event, "right")):
try:
from rgsx_settings import get_display_monitor, set_display_monitor, get_available_monitors
monitors = get_available_monitors()
num_monitors = len(monitors)
if num_monitors > 1:
current = get_display_monitor()
new_monitor = (current - 1) % num_monitors if is_input_matched(event, "left") else (current + 1) % num_monitors
set_display_monitor(new_monitor)
config.popup_message = _("display_monitor_restart_required") if _ else "Restart required to apply monitor change"
config.popup_timer = 3000
else:
config.popup_message = _("display_monitor_single_only") if _ else "Only one monitor detected"
config.popup_timer = 2000
config.needs_redraw = True
except Exception as e:
logger.error(f"Erreur changement moniteur: {e}")
# 5 light mode toggle
elif sel == 5 and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")):
try:
from rgsx_settings import get_light_mode, set_light_mode
current = get_light_mode()
new_val = set_light_mode(not current)
config.popup_message = _("display_light_mode_enabled") if new_val else _("display_light_mode_disabled")
config.popup_timer = 2000
config.needs_redraw = True
except Exception as e:
logger.error(f"Erreur toggle light mode: {e}")
# 6 allow unknown extensions
elif sel == 6 and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")):
try:
current = get_allow_unknown_extensions()
new_val = set_allow_unknown_extensions(not current)
@@ -1858,8 +1890,8 @@ def handle_controls(event, sources, joystick, screen):
config.needs_redraw = True
except Exception as e:
logger.error(f"Erreur toggle allow_unknown_extensions: {e}")
# 5 back
elif sel == 5 and is_input_matched(event, "confirm"):
# 7 back
elif sel == 7 and is_input_matched(event, "confirm"):
config.menu_state = "pause_menu"
config.last_state_change_time = pygame.time.get_ticks()
config.needs_redraw = True
@@ -2051,14 +2083,15 @@ def handle_controls(event, sources, joystick, screen):
config.needs_redraw = True
logger.debug("Retour au menu pause depuis controls_help")
# Menu Affichage (layout, police, unsupported)
# Menu Affichage (layout, police, moniteur, mode écran, unsupported, extensions, filtres)
elif config.menu_state == "display_menu":
sel = getattr(config, 'display_menu_selection', 0)
num_options = 7 # Layout, Font, Monitor, Mode, Unsupported, Extensions, Filters
if is_input_matched(event, "up"):
config.display_menu_selection = (sel - 1) % 5
config.display_menu_selection = (sel - 1) % num_options
config.needs_redraw = True
elif is_input_matched(event, "down"):
config.display_menu_selection = (sel + 1) % 5
config.display_menu_selection = (sel + 1) % num_options
config.needs_redraw = True
elif is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm"):
# 0: layout change
@@ -2104,8 +2137,40 @@ def handle_controls(event, sources, joystick, screen):
except Exception as e:
logger.error(f"Erreur init polices: {e}")
config.needs_redraw = True
# 2: toggle unsupported
elif sel == 2 and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")):
# 2: monitor selection (new)
elif sel == 2 and (is_input_matched(event, "left") or is_input_matched(event, "right")):
try:
from rgsx_settings import get_display_monitor, set_display_monitor, get_available_monitors
monitors = get_available_monitors()
num_monitors = len(monitors)
if num_monitors > 1:
current = get_display_monitor()
new_monitor = (current - 1) % num_monitors if is_input_matched(event, "left") else (current + 1) % num_monitors
set_display_monitor(new_monitor)
config.needs_redraw = True
# Informer l'utilisateur qu'un redémarrage est nécessaire
config.popup_message = _("display_monitor_restart_required")
config.popup_timer = 3000
else:
config.popup_message = _("display_monitor_single_only")
config.popup_timer = 2000
config.needs_redraw = True
except Exception as e:
logger.error(f"Erreur changement moniteur: {e}")
# 3: fullscreen/windowed toggle (new)
elif sel == 3 and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")):
try:
from rgsx_settings import get_display_fullscreen, set_display_fullscreen
current = get_display_fullscreen()
new_val = set_display_fullscreen(not current)
config.needs_redraw = True
# Informer l'utilisateur qu'un redémarrage est nécessaire
config.popup_message = _("display_mode_restart_required")
config.popup_timer = 3000
except Exception as e:
logger.error(f"Erreur toggle fullscreen: {e}")
# 4: toggle unsupported (was 2)
elif sel == 4 and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")):
try:
current = get_show_unsupported_platforms()
new_val = set_show_unsupported_platforms(not current)
@@ -2115,8 +2180,8 @@ def handle_controls(event, sources, joystick, screen):
config.needs_redraw = True
except Exception as e:
logger.error(f"Erreur toggle unsupported: {e}")
# 3: toggle allow unknown extensions
elif sel == 3 and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")):
# 5: toggle allow unknown extensions (was 3)
elif sel == 5 and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")):
try:
current = get_allow_unknown_extensions()
new_val = set_allow_unknown_extensions(not current)
@@ -2125,8 +2190,8 @@ def handle_controls(event, sources, joystick, screen):
config.needs_redraw = True
except Exception as e:
logger.error(f"Erreur toggle allow_unknown_extensions: {e}")
# 4: open filter platforms menu
elif sel == 4 and (is_input_matched(event, "confirm") or is_input_matched(event, "right")):
# 6: open filter platforms menu (was 4)
elif sel == 6 and (is_input_matched(event, "confirm") or is_input_matched(event, "right")):
# Remember return target so the filter menu can go back to display
config.filter_return_to = "display_menu"
config.menu_state = "filter_platforms"

View File

@@ -3,12 +3,21 @@
import pygame # type: ignore
import os
import io
import platform
import random
import config
from utils import truncate_text_middle, wrap_text, load_system_image, truncate_text_end
from utils import (truncate_text_middle, wrap_text, load_system_image, truncate_text_end,
check_web_service_status, check_custom_dns_status, load_api_keys,
_get_dest_folder_name, find_file_with_or_without_extension)
import logging
import math
from history import load_history, is_game_downloaded
from language import _, get_size_units, get_speed_unit
from language import _, get_size_units, get_speed_unit, get_available_languages, get_language_name
from rgsx_settings import (load_rgsx_settings, get_light_mode, get_show_unsupported_platforms,
get_allow_unknown_extensions, get_display_monitor, get_display_fullscreen,
get_available_monitors, get_font_family, get_sources_mode,
get_hide_premium_systems, get_symlink_option)
from game_filters import GameFilters
logger = logging.getLogger(__name__)
@@ -225,26 +234,113 @@ THEME_COLORS = {
# Général, résolution, overlay
def init_display():
"""Initialise l'écran et les ressources globales."""
"""Initialise l'écran et les ressources globales.
Supporte la sélection de moniteur en plein écran.
Compatible Windows et Linux (Batocera).
"""
global OVERLAY
logger.debug("Initialisation de l'écran")
display_info = pygame.display.Info()
screen_width = display_info.current_w
screen_height = display_info.current_h
screen = pygame.display.set_mode((screen_width, screen_height))
# Charger les paramètres d'affichage
settings = load_rgsx_settings()
logger.debug(f"Settings chargés: display={settings.get('display', {})}")
target_monitor = settings.get("display", {}).get("monitor", 0)
# Vérifier les variables d'environnement (priorité sur les settings)
env_display = os.environ.get("RGSX_DISPLAY")
if env_display is not None:
try:
target_monitor = int(env_display)
logger.debug(f"Override par RGSX_DISPLAY: monitor={target_monitor}")
except ValueError:
pass
# Configurer SDL pour utiliser le bon moniteur
# Cette variable d'environnement doit être définie AVANT la création de la fenêtre
os.environ["SDL_VIDEO_FULLSCREEN_HEAD"] = str(target_monitor)
# Obtenir les informations d'affichage
num_displays = 1
try:
num_displays = pygame.display.get_num_displays()
except Exception:
pass
# S'assurer que le moniteur cible existe
if target_monitor >= num_displays:
logger.warning(f"Monitor {target_monitor} not available, using monitor 0")
target_monitor = 0
# Obtenir la résolution du moniteur cible
try:
if hasattr(pygame.display, 'get_desktop_sizes') and num_displays > 1:
desktop_sizes = pygame.display.get_desktop_sizes()
if target_monitor < len(desktop_sizes):
screen_width, screen_height = desktop_sizes[target_monitor]
else:
display_info = pygame.display.Info()
screen_width = display_info.current_w
screen_height = display_info.current_h
else:
display_info = pygame.display.Info()
screen_width = display_info.current_w
screen_height = display_info.current_h
except Exception as e:
logger.error(f"Error getting display info: {e}")
display_info = pygame.display.Info()
screen_width = display_info.current_w
screen_height = display_info.current_h
# Créer la fenêtre en plein écran
flags = pygame.FULLSCREEN
# Sur Linux/Batocera, utiliser SCALED pour respecter la résolution forcée d'EmulationStation
if platform.system() == "Linux":
flags |= pygame.SCALED
# Sur certains systèmes Windows, NOFRAME aide pour le multi-écran
elif platform.system() == "Windows":
flags |= pygame.NOFRAME
try:
screen = pygame.display.set_mode((screen_width, screen_height), flags, display=target_monitor)
except TypeError:
# Anciennes versions de pygame ne supportent pas le paramètre display=
screen = pygame.display.set_mode((screen_width, screen_height), flags)
except Exception as e:
logger.error(f"Error creating display on monitor {target_monitor}: {e}")
screen = pygame.display.set_mode((screen_width, screen_height), flags)
config.screen_width = screen_width
config.screen_height = screen_height
config.current_monitor = target_monitor
# Initialisation de OVERLAY avec effet glassmorphism
OVERLAY = pygame.Surface((screen_width, screen_height), pygame.SRCALPHA)
OVERLAY.fill((5, 10, 20, 160)) # Bleu très foncé semi-transparent pour effet verre
logger.debug(f"Écran initialisé avec résolution : {screen_width}x{screen_height}")
logger.debug(f"Écran initialisé: {screen_width}x{screen_height} sur moniteur {target_monitor}")
return screen
# Fond d'écran dégradé
def draw_gradient(screen, top_color, bottom_color):
"""Dessine un fond dégradé vertical avec des couleurs vibrantes et texture de grain."""
def draw_gradient(screen, top_color, bottom_color, light_mode=None):
"""Dessine un fond dégradé vertical avec des couleurs vibrantes et texture de grain.
En mode light, utilise une couleur unie pour de meilleures performances."""
if light_mode is None:
light_mode = get_light_mode()
height = screen.get_height()
width = screen.get_width()
if light_mode:
# Mode light: couleur unie (moyenne des deux couleurs)
avg_color = (
(top_color[0] + bottom_color[0]) // 2,
(top_color[1] + bottom_color[1]) // 2,
(top_color[2] + bottom_color[2]) // 2
)
screen.fill(avg_color)
return
top_color = pygame.Color(*top_color)
bottom_color = pygame.Color(*bottom_color)
@@ -256,7 +352,6 @@ def draw_gradient(screen, top_color, bottom_color):
# Ajouter une texture de grain subtile pour plus de profondeur
grain_surface = pygame.Surface((width, height), pygame.SRCALPHA)
import random
random.seed(42) # Seed fixe pour cohérence
for _ in range(width * height // 200): # Réduire la densité pour performance
x = random.randint(0, width - 1)
@@ -266,15 +361,23 @@ def draw_gradient(screen, top_color, bottom_color):
screen.blit(grain_surface, (0, 0))
def draw_shadow(surface, rect, offset=6, alpha=120):
"""Dessine une ombre portée pour un rectangle."""
def draw_shadow(surface, rect, offset=6, alpha=120, light_mode=None):
"""Dessine une ombre portée pour un rectangle. Désactivé en mode light."""
if light_mode is None:
light_mode = get_light_mode()
if light_mode:
return None # Pas d'ombre en mode light
shadow = pygame.Surface((rect.width + offset, rect.height + offset), pygame.SRCALPHA)
pygame.draw.rect(shadow, (0, 0, 0, alpha), (0, 0, rect.width + offset, rect.height + offset), border_radius=15)
return shadow
def draw_glow_effect(screen, rect, color, intensity=80, size=10):
"""Dessine un effet de glow autour d'un rectangle."""
def draw_glow_effect(screen, rect, color, intensity=80, size=10, light_mode=None):
"""Dessine un effet de glow autour d'un rectangle. Désactivé en mode light."""
if light_mode is None:
light_mode = get_light_mode()
if light_mode:
return # Pas de glow en mode light
glow = pygame.Surface((rect.width + size * 2, rect.height + size * 2), pygame.SRCALPHA)
for i in range(size):
alpha = int(intensity * (1 - i / size))
@@ -284,55 +387,67 @@ def draw_glow_effect(screen, rect, color, intensity=80, size=10):
screen.blit(glow, (rect.x - size, rect.y - size))
# Nouvelle fonction pour dessiner un bouton stylisé
def draw_stylized_button(screen, text, x, y, width, height, selected=False):
"""Dessine un bouton moderne avec effet de survol, ombre et bordure arrondie."""
# Ombre portée subtile
shadow_surf = pygame.Surface((width + 6, height + 6), pygame.SRCALPHA)
pygame.draw.rect(shadow_surf, THEME_COLORS["shadow"], (3, 3, width, height), border_radius=12)
screen.blit(shadow_surf, (x - 3, y - 3))
def draw_stylized_button(screen, text, x, y, width, height, selected=False, light_mode=None):
"""Dessine un bouton moderne avec effet de survol, ombre et bordure arrondie.
En mode light, utilise un style simplifié pour de meilleures performances."""
if light_mode is None:
light_mode = get_light_mode()
button_color = THEME_COLORS["button_hover"] if selected else THEME_COLORS["button_idle"]
button_surface = pygame.Surface((width, height), pygame.SRCALPHA)
# Fond avec dégradé subtil pour bouton sélectionné
if selected:
# Créer le dégradé
for i in range(height):
ratio = i / height
brightness = 1 + 0.2 * ratio
r = min(255, int(button_color[0] * brightness))
g = min(255, int(button_color[1] * brightness))
b = min(255, int(button_color[2] * brightness))
alpha = button_color[3] if len(button_color) > 3 else 255
rect = pygame.Rect(0, i, width, 1)
pygame.draw.rect(button_surface, (r, g, b, alpha), rect)
# Appliquer les coins arrondis avec un masque
mask_surface = pygame.Surface((width, height), pygame.SRCALPHA)
pygame.draw.rect(mask_surface, (255, 255, 255, 255), (0, 0, width, height), border_radius=12)
button_surface.blit(mask_surface, (0, 0), special_flags=pygame.BLEND_RGBA_MIN)
if light_mode:
# Mode light: bouton simple sans effets
pygame.draw.rect(screen, button_color[:3], (x, y, width, height), border_radius=8)
if selected:
# Bordure simple pour indiquer la sélection
pygame.draw.rect(screen, THEME_COLORS["neon"], (x, y, width, height), width=2, border_radius=8)
else:
pygame.draw.rect(button_surface, button_color, (0, 0, width, height), border_radius=12)
# Reflet en haut
highlight = pygame.Surface((width - 4, height // 3), pygame.SRCALPHA)
highlight.fill(THEME_COLORS["highlight"])
button_surface.blit(highlight, (2, 2))
# Bordure
pygame.draw.rect(button_surface, THEME_COLORS["border"], (0, 0, width, height), 2, border_radius=12)
if selected:
# Effet glow doux pour sélection
glow_surface = pygame.Surface((width + 16, height + 16), pygame.SRCALPHA)
for i in range(6):
alpha = int(40 * (1 - i / 6))
pygame.draw.rect(glow_surface, (*THEME_COLORS["glow"][:3], alpha),
(i, i, width + 16 - i*2, height + 16 - i*2), border_radius=15)
screen.blit(glow_surface, (x - 8, y - 8))
screen.blit(button_surface, (x, y))
# Mode normal avec tous les effets
# Ombre portée subtile
shadow_surf = pygame.Surface((width + 6, height + 6), pygame.SRCALPHA)
pygame.draw.rect(shadow_surf, THEME_COLORS["shadow"], (3, 3, width, height), border_radius=12)
screen.blit(shadow_surf, (x - 3, y - 3))
button_surface = pygame.Surface((width, height), pygame.SRCALPHA)
# Fond avec dégradé subtil pour bouton sélectionné
if selected:
# Créer le dégradé
for i in range(height):
ratio = i / height
brightness = 1 + 0.2 * ratio
r = min(255, int(button_color[0] * brightness))
g = min(255, int(button_color[1] * brightness))
b = min(255, int(button_color[2] * brightness))
alpha = button_color[3] if len(button_color) > 3 else 255
rect = pygame.Rect(0, i, width, 1)
pygame.draw.rect(button_surface, (r, g, b, alpha), rect)
# Appliquer les coins arrondis avec un masque
mask_surface = pygame.Surface((width, height), pygame.SRCALPHA)
pygame.draw.rect(mask_surface, (255, 255, 255, 255), (0, 0, width, height), border_radius=12)
button_surface.blit(mask_surface, (0, 0), special_flags=pygame.BLEND_RGBA_MIN)
else:
pygame.draw.rect(button_surface, button_color, (0, 0, width, height), border_radius=12)
# Reflet en haut
highlight = pygame.Surface((width - 4, height // 3), pygame.SRCALPHA)
highlight.fill(THEME_COLORS["highlight"])
button_surface.blit(highlight, (2, 2))
# Bordure
pygame.draw.rect(button_surface, THEME_COLORS["border"], (0, 0, width, height), 2, border_radius=12)
if selected:
# Effet glow doux pour sélection
glow_surface = pygame.Surface((width + 16, height + 16), pygame.SRCALPHA)
for i in range(6):
alpha = int(40 * (1 - i / 6))
pygame.draw.rect(glow_surface, (*THEME_COLORS["glow"][:3], alpha),
(i, i, width + 16 - i*2, height + 16 - i*2), border_radius=15)
screen.blit(glow_surface, (x - 8, y - 8))
screen.blit(button_surface, (x, y))
# Vérifier si le texte dépasse la largeur disponible
text_surface = config.font.render(text, True, THEME_COLORS["text"])
@@ -1721,49 +1836,46 @@ def draw_extension_warning(screen):
def draw_controls(screen, menu_state, current_music_name=None, music_popup_start_time=0):
"""Affiche les contrôles contextuels en bas de l'écran selon le menu_state."""
# Import local de la fonction de traduction pour éviter les conflits de scope
from language import _ as i18n
# Mapping des contrôles par menu_state
controls_map = {
"platform": [
("history", i18n("controls_action_history")),
("confirm", i18n("controls_confirm_select")),
("start", i18n("controls_action_start")),
("history", _("controls_action_history")),
("confirm", _("controls_confirm_select")),
("start", _("controls_action_start")),
],
"game": [
("confirm", i18n("controls_confirm_select")),
("clear_history", i18n("controls_action_queue")),
(("page_up", "page_down"), i18n("controls_pages")),
("filter", i18n("controls_filter_search")),
("history", i18n("controls_action_history")),
("confirm", _("controls_confirm_select")),
("clear_history", _("controls_action_queue")),
(("page_up", "page_down"), _("controls_pages")),
("filter", _("controls_filter_search")),
("history", _("controls_action_history")),
],
"history": [
("confirm", i18n("history_game_options_title")),
("clear_history", i18n("controls_action_clear_history")),
("history", i18n("controls_action_close_history")),
("cancel", i18n("controls_cancel_back")),
("confirm", _("history_game_options_title")),
("clear_history", _("controls_action_clear_history")),
("history", _("controls_action_close_history")),
("cancel", _("controls_cancel_back")),
],
"scraper": [
("confirm", i18n("controls_confirm_select")),
("cancel", i18n("controls_cancel_back")),
("confirm", _("controls_confirm_select")),
("cancel", _("controls_cancel_back")),
],
"error": [
("confirm", i18n("controls_confirm_select")),
("confirm", _("controls_confirm_select")),
],
"confirm_exit": [
("confirm", i18n("controls_confirm_select")),
("cancel", i18n("controls_cancel_back")),
("confirm", _("controls_confirm_select")),
("cancel", _("controls_cancel_back")),
],
"extension_warning": [
("confirm", i18n("controls_confirm_select")),
("confirm", _("controls_confirm_select")),
],
}
# Récupérer les contrôles pour ce menu, sinon affichage par défaut
controls_list = controls_map.get(menu_state, [
("confirm", i18n("controls_confirm_select")),
("cancel", i18n("controls_cancel_back")),
("confirm", _("controls_confirm_select")),
("cancel", _("controls_cancel_back")),
])
# Construire les lignes avec icônes
@@ -1777,7 +1889,7 @@ def draw_controls(screen, menu_state, current_music_name=None, music_popup_start
# Si aucun joystick, afficher la touche entre crochets
if not getattr(config, 'joystick', True):
start_button = f"[{start_button}]"
start_text = i18n("controls_action_start")
start_text = _("controls_action_start")
control_parts.append(f"RGSX v{config.app_version} - {start_button} : {start_text}")
# Afficher le nom du joystick s'il est détecté
@@ -1785,7 +1897,7 @@ def draw_controls(screen, menu_state, current_music_name=None, music_popup_start
device_name = getattr(config, 'controller_device_name', '') or ''
if device_name:
try:
joy_label = i18n("footer_joystick")
joy_label = _("footer_joystick")
except Exception:
joy_label = "Joystick: {0}"
if isinstance(joy_label, str) and "{0}" in joy_label:
@@ -1840,7 +1952,7 @@ def draw_controls(screen, menu_state, current_music_name=None, music_popup_start
combined_surf = pygame.Surface((max_width, 50), pygame.SRCALPHA)
x_pos = 10
for action_tuple in all_controls:
_, actions, label = action_tuple
ignored, actions, label = action_tuple
try:
surf = _render_icons_line(actions, label, max_width - x_pos - 10, config.tiny_font, THEME_COLORS["text"], icon_size=scaled_icon_size, icon_gap=scaled_icon_gap, icon_text_gap=scaled_icon_text_gap)
if x_pos + surf.get_width() > max_width - 10:
@@ -1855,7 +1967,7 @@ def draw_controls(screen, menu_state, current_music_name=None, music_popup_start
final_surf.blit(combined_surf, (0, 0), (0, 0, x_pos - 10, 50))
icon_surfs.append(final_surf)
elif line_data[0] == "icons" and len(line_data) == 3:
_, actions, label = line_data
ignored, actions, label = line_data
try:
surf = _render_icons_line(actions, label, max_width, config.tiny_font, THEME_COLORS["text"], icon_size=scaled_icon_size, icon_gap=scaled_icon_gap, icon_text_gap=scaled_icon_text_gap)
icon_surfs.append(surf)
@@ -1893,7 +2005,6 @@ def draw_language_menu(screen):
- Bloc (titre + liste de langues) centré verticalement.
- Gestion d'overflow: réduit légèrement la hauteur/espacement si nécessaire.
"""
from language import get_available_languages, get_language_name
screen.blit(OVERLAY, (0, 0))
@@ -2033,15 +2144,30 @@ def draw_menu_instruction(screen, instruction_text, last_button_bottom=None):
logger.error(f"Erreur draw_menu_instruction: {e}")
def draw_display_menu(screen):
"""Affiche le sous-menu Affichage (layout, taille de police, systèmes non supportés)."""
"""Affiche le sous-menu Affichage (layout, taille de police, systèmes non supportés, moniteur)."""
screen.blit(OVERLAY, (0, 0))
# États actuels
layout_str = f"{getattr(config, 'GRID_COLS', 3)}x{getattr(config, 'GRID_ROWS', 4)}"
font_scale = config.accessibility_settings.get("font_scale", 1.0)
from rgsx_settings import get_show_unsupported_platforms, get_allow_unknown_extensions
show_unsupported = get_show_unsupported_platforms()
allow_unknown = get_allow_unknown_extensions()
# Monitor info
current_monitor = get_display_monitor()
is_fullscreen = get_display_fullscreen()
monitors = get_available_monitors()
num_monitors = len(monitors)
# Construire le label du moniteur
if num_monitors > 1:
monitor_info = monitors[current_monitor] if current_monitor < num_monitors else monitors[0]
monitor_label = f"{_('display_monitor')}: {monitor_info['name']} ({monitor_info['resolution']})"
else:
monitor_label = f"{_('display_monitor')}: {_('display_monitor_single')}"
# Label mode écran
fullscreen_label = f"{_('display_mode')}: {_('display_fullscreen') if is_fullscreen else _('display_windowed')}"
# Compter les systèmes non supportés actuellement masqués
unsupported_list = getattr(config, "unsupported_platforms", []) or []
@@ -2054,11 +2180,13 @@ def draw_display_menu(screen):
else:
unsupported_label = _("menu_show_unsupported_all_displayed")
# Libellés
# Libellés - ajout des options moniteur et mode écran
options = [
f"{_('display_layout')}: {layout_str}",
_("accessibility_font_size").format(f"{font_scale:.1f}"),
unsupported_label,
monitor_label,
fullscreen_label,
unsupported_label,
_("menu_allow_unknown_ext_on") if allow_unknown else _("menu_allow_unknown_ext_off"),
_("menu_filter_platforms"),
]
@@ -2277,10 +2405,6 @@ def draw_pause_controls_menu(screen, selected_index):
draw_menu_instruction(screen, text, last_button_bottom)
def draw_pause_display_menu(screen, selected_index):
from rgsx_settings import (
get_allow_unknown_extensions,
get_font_family
)
# Layout label
layouts = [(3,3),(3,4),(4,3),(4,4)]
try:
@@ -2309,6 +2433,17 @@ def draw_pause_display_menu(screen, selected_index):
fam_label = family_map.get(current_family, current_family)
font_family_txt = f"{_('submenu_display_font_family') if _ else 'Font'}: < {fam_label} >"
# Monitor selection
current_monitor = get_display_monitor()
monitors = get_available_monitors()
num_monitors = len(monitors)
if num_monitors > 1:
monitor_info = monitors[current_monitor] if current_monitor < num_monitors else monitors[0]
monitor_value = f"{monitor_info['name']} ({monitor_info['resolution']})"
else:
monitor_value = _('display_monitor_single') if _ else "Single monitor"
monitor_txt = f"{_('display_monitor') if _ else 'Monitor'}: < {monitor_value} >"
# Allow unknown extensions
allow_unknown = get_allow_unknown_extensions()
status_unknown = _('status_on') if allow_unknown else _('status_off')
@@ -2317,17 +2452,28 @@ def draw_pause_display_menu(screen, selected_index):
raw_unknown_label = raw_unknown_label.split('{status}')[0].rstrip(' :')
unknown_txt = f"{raw_unknown_label}: < {status_unknown} >"
# Light mode (performance)
light_mode = get_light_mode()
light_status = _('status_on') if light_mode else _('status_off')
light_txt = f"{_('display_light_mode') if _ else 'Light mode'}: < {light_status} >"
back_txt = _("menu_back") if _ else "Back"
options = [layout_txt, font_txt, footer_font_txt, font_family_txt, unknown_txt, back_txt]
_draw_submenu_generic(screen, _("menu_display"), options, selected_index)
# Build options list - same for all platforms
# layout, font, footer, family, monitor, light, unknown, back (8)
options = [layout_txt, font_txt, footer_font_txt, font_family_txt, monitor_txt, light_txt, unknown_txt, back_txt]
instruction_keys = [
"instruction_display_layout",
"instruction_display_font_size",
"instruction_display_footer_font_size",
"instruction_display_font_family",
"instruction_display_monitor",
"instruction_display_light_mode",
"instruction_display_unknown_ext",
"instruction_generic_back",
]
_draw_submenu_generic(screen, _("menu_display"), options, selected_index)
key = instruction_keys[selected_index] if 0 <= selected_index < len(instruction_keys) else None
if key:
button_height = int(config.screen_height * 0.045)
@@ -2342,7 +2488,6 @@ def draw_pause_display_menu(screen, selected_index):
draw_menu_instruction(screen, _(key), last_button_bottom)
def draw_pause_games_menu(screen, selected_index):
from rgsx_settings import get_sources_mode, get_show_unsupported_platforms, get_hide_premium_systems
mode = get_sources_mode()
source_label = _("games_source_rgsx") if mode == "rgsx" else _("games_source_custom")
source_txt = f"{_('menu_games_source_prefix')}: < {source_label} >"
@@ -2415,8 +2560,6 @@ def draw_pause_games_menu(screen, selected_index):
draw_menu_instruction(screen, text, last_button_bottom)
def draw_pause_settings_menu(screen, selected_index):
from rgsx_settings import get_symlink_option
from utils import check_web_service_status, check_custom_dns_status
# Music
if config.music_enabled:
music_name = config.current_music_name or ""
@@ -2486,7 +2629,6 @@ def draw_pause_settings_menu(screen, selected_index):
def draw_pause_api_keys_status(screen):
screen.blit(OVERLAY, (0,0))
from utils import load_api_keys
keys = load_api_keys()
title = _("api_keys_status_title") if _ else "API Keys Status"
# Préparer données avec masquage partiel des clés (afficher 4 premiers et 2 derniers caractères si longueur > 10)
@@ -2590,7 +2732,6 @@ def draw_pause_api_keys_status(screen):
def draw_filter_platforms_menu(screen):
"""Affiche le menu de filtrage des plateformes (afficher/masquer)."""
from rgsx_settings import load_rgsx_settings
screen.blit(OVERLAY, (0, 0))
settings = load_rgsx_settings()
hidden = set(settings.get("hidden_platforms", [])) if isinstance(settings, dict) else set()
@@ -3135,8 +3276,6 @@ def show_toast(message, duration=2000):
config.toast_start_time = pygame.time.get_ticks()
def draw_history_game_options(screen):
"""Affiche le menu d'options pour un jeu de l'historique."""
import os
from utils import _get_dest_folder_name, find_file_with_or_without_extension
screen.blit(OVERLAY, (0, 0))
@@ -3243,8 +3382,6 @@ def draw_history_game_options(screen):
def draw_history_show_folder(screen):
"""Affiche le chemin complet du fichier téléchargé."""
import os
from utils import _get_dest_folder_name
screen.blit(OVERLAY, (0, 0))
@@ -3789,7 +3926,6 @@ def draw_filter_menu_choice(screen):
def draw_filter_advanced(screen):
"""Affiche l'écran de filtrage avancé"""
from game_filters import GameFilters
screen.blit(OVERLAY, (0, 0))
@@ -4069,7 +4205,6 @@ def draw_filter_advanced(screen):
def draw_filter_priority_config(screen):
"""Affiche l'écran de configuration de la priorité des régions pour One ROM per game"""
from game_filters import GameFilters
screen.blit(OVERLAY, (0, 0))
@@ -4087,7 +4222,6 @@ def draw_filter_priority_config(screen):
# Initialiser le filtre si nécessaire
if not hasattr(config, 'game_filter_obj'):
from game_filters import GameFilters
from rgsx_settings import load_game_filters
config.game_filter_obj = GameFilters()
filter_dict = load_game_filters()

View File

@@ -65,6 +65,17 @@
"menu_accessibility": "Barrierefreiheit",
"menu_display": "Anzeige",
"display_layout": "Anzeigelayout",
"display_monitor": "Monitor",
"display_monitor_single": "Einzelner Monitor",
"display_monitor_single_only": "Nur ein Monitor erkannt",
"display_monitor_restart_required": "Neustart erforderlich um Monitor zu ändern",
"display_mode": "Anzeigemodus",
"display_fullscreen": "Vollbild",
"display_windowed": "Fenster",
"display_mode_restart_required": "Neustart erforderlich für Modusänderung",
"display_light_mode": "Performance-Modus",
"display_light_mode_enabled": "Performance-Modus aktiviert - Effekte deaktiviert",
"display_light_mode_disabled": "Performance-Modus deaktiviert - Effekte aktiviert",
"menu_redownload_cache": "Spieleliste aktualisieren",
"menu_music_enabled": "Musik aktiviert: {0}",
"menu_music_disabled": "Musik deaktiviert",
@@ -196,6 +207,9 @@
"instruction_display_font_size": "Schriftgröße für bessere Lesbarkeit anpassen",
"instruction_display_footer_font_size": "Fußzeilen-Textgröße anpassen (Version & Steuerelemente)",
"instruction_display_font_family": "Zwischen verfügbaren Schriftarten wechseln",
"instruction_display_monitor": "Monitor für RGSX-Anzeige auswählen",
"instruction_display_mode": "Zwischen Vollbild und Fenstermodus wechseln",
"instruction_display_light_mode": "Performance-Modus für bessere FPS aktivieren",
"instruction_display_show_unsupported": "Nicht in es_systems.cfg definierte Systeme anzeigen/ausblenden",
"instruction_display_unknown_ext": "Warnung für in es_systems.cfg fehlende Dateiendungen an-/abschalten",
"instruction_display_hide_premium": "Systeme ausblenden, die Premiumzugang erfordern über API: {providers}",

View File

@@ -65,6 +65,17 @@
"menu_accessibility": "Accessibility",
"menu_display": "Display",
"display_layout": "Display layout",
"display_monitor": "Monitor",
"display_monitor_single": "Single monitor",
"display_monitor_single_only": "Only one monitor detected",
"display_monitor_restart_required": "Restart required to apply monitor change",
"display_mode": "Screen mode",
"display_fullscreen": "Fullscreen",
"display_windowed": "Windowed",
"display_mode_restart_required": "Restart required to apply screen mode",
"display_light_mode": "Performance mode",
"display_light_mode_enabled": "Performance mode enabled - effects disabled",
"display_light_mode_disabled": "Performance mode disabled - effects enabled",
"menu_redownload_cache": "Update games list",
"menu_music_enabled": "Music enabled: {0}",
"menu_music_disabled": "Music disabled",
@@ -198,6 +209,9 @@
"instruction_display_font_size": "Adjust text scale for readability",
"instruction_display_footer_font_size": "Adjust footer text scale (version & controls display)",
"instruction_display_font_family": "Switch between available font families",
"instruction_display_monitor": "Select which monitor to display RGSX on",
"instruction_display_mode": "Toggle between fullscreen and windowed mode",
"instruction_display_light_mode": "Enable performance mode for better FPS on low-end devices",
"instruction_display_show_unsupported": "Show/hide systems not defined in es_systems.cfg",
"instruction_display_unknown_ext": "Enable/disable warning for file extensions absent from es_systems.cfg",
"instruction_display_hide_premium": "Hide systems requiring premium access via API: {providers}",

View File

@@ -65,6 +65,17 @@
"menu_accessibility": "Accesibilidad",
"menu_display": "Pantalla",
"display_layout": "Distribución",
"display_monitor": "Monitor",
"display_monitor_single": "Monitor único",
"display_monitor_single_only": "Solo un monitor detectado",
"display_monitor_restart_required": "Reinicio necesario para cambiar de monitor",
"display_mode": "Modo de pantalla",
"display_fullscreen": "Pantalla completa",
"display_windowed": "Ventana",
"display_mode_restart_required": "Reinicio necesario para cambiar el modo",
"display_light_mode": "Modo rendimiento",
"display_light_mode_enabled": "Modo rendimiento activado - efectos desactivados",
"display_light_mode_disabled": "Modo rendimiento desactivado - efectos activados",
"menu_redownload_cache": "Actualizar lista de juegos",
"menu_music_enabled": "Música activada: {0}",
"menu_music_disabled": "Música desactivada",
@@ -198,6 +209,9 @@
"instruction_display_font_size": "Ajustar tamaño del texto para mejor legibilidad",
"instruction_display_footer_font_size": "Ajustar el tamaño del texto del pie de página (versión y controles)",
"instruction_display_font_family": "Cambiar entre familias de fuentes disponibles",
"instruction_display_monitor": "Seleccionar monitor para mostrar RGSX",
"instruction_display_mode": "Alternar entre pantalla completa y ventana",
"instruction_display_light_mode": "Activar modo rendimiento para mejores FPS",
"instruction_display_show_unsupported": "Mostrar/ocultar sistemas no definidos en es_systems.cfg",
"instruction_display_unknown_ext": "Activar/desactivar aviso para extensiones no presentes en es_systems.cfg",
"instruction_display_hide_premium": "Ocultar sistemas que requieren acceso premium vía API: {providers}",

View File

@@ -65,6 +65,17 @@
"menu_accessibility": "Accessibilité",
"menu_display": "Affichage",
"display_layout": "Disposition",
"display_monitor": "Écran",
"display_monitor_single": "Écran unique",
"display_monitor_single_only": "Un seul écran détecté",
"display_monitor_restart_required": "Redémarrage requis pour changer d'écran",
"display_mode": "Mode d'affichage",
"display_fullscreen": "Plein écran",
"display_windowed": "Fenêtré",
"display_mode_restart_required": "Redémarrage requis pour changer le mode",
"display_light_mode": "Mode performance",
"display_light_mode_enabled": "Mode performance activé - effets désactivés",
"display_light_mode_disabled": "Mode performance désactivé - effets activés",
"menu_redownload_cache": "Mettre à jour la liste des jeux",
"menu_support": "Support",
"menu_quit": "Quitter",
@@ -198,6 +209,9 @@
"instruction_display_font_size": "Ajuster la taille du texte pour la lisibilité",
"instruction_display_footer_font_size": "Ajuster la taille du texte du pied de page (version et contrôles)",
"instruction_display_font_family": "Basculer entre les polices disponibles",
"instruction_display_monitor": "Sélectionner l'écran pour afficher RGSX",
"instruction_display_mode": "Basculer entre plein écran et fenêtré",
"instruction_display_light_mode": "Activer le mode performance pour de meilleurs FPS",
"instruction_display_show_unsupported": "Afficher/masquer systèmes absents de es_systems.cfg",
"instruction_display_unknown_ext": "Avertir ou non pour extensions absentes de es_systems.cfg",
"instruction_display_hide_premium": "Masquer les systèmes nécessitant un accès premium via API: {providers}",
@@ -250,6 +264,8 @@
"history_option_extract_archive": "Extraire l'archive",
"history_option_open_file": "Ouvrir le fichier",
"history_option_scraper": "Récupérer métadonnées",
"history_option_remove_from_queue": "Retirer de la file d'attente",
"history_option_cancel_download": "Annuler le téléchargement",
"history_option_delete_game": "Supprimer le jeu",
"history_option_error_info": "Détails de l'erreur",
"history_option_retry": "Retenter le téléchargement",

View File

@@ -65,7 +65,16 @@
"menu_accessibility": "Accessibilità",
"menu_display": "Schermo",
"display_layout": "Layout schermo",
"menu_redownload_cache": "Aggiorna elenco giochi",
"display_monitor": "Monitor",
"display_monitor_single": "Monitor singolo",
"display_monitor_single_only": "Rilevato un solo monitor",
"display_monitor_restart_required": "Riavvio necessario per cambiare monitor",
"display_mode": "Modalità schermo",
"display_fullscreen": "Schermo intero",
"display_windowed": "Finestra",
"display_mode_restart_required": "Riavvio necessario per cambiare modalità", "display_light_mode": "Modalità performance",
"display_light_mode_enabled": "Modalità performance attivata - effetti disattivati",
"display_light_mode_disabled": "Modalità performance disattivata - effetti attivati", "menu_redownload_cache": "Aggiorna elenco giochi",
"menu_music_enabled": "Musica attivata: {0}",
"menu_music_disabled": "Musica disattivata",
"menu_restart": "Riavvia RGSX",
@@ -195,6 +204,9 @@
"instruction_display_font_size": "Regolare dimensione testo per leggibilità",
"instruction_display_footer_font_size": "Regola dimensione testo piè di pagina (versione e controlli)",
"instruction_display_font_family": "Cambiare famiglia di font disponibile",
"instruction_display_monitor": "Selezionare monitor per visualizzare RGSX",
"instruction_display_mode": "Alternare tra schermo intero e finestra",
"instruction_display_light_mode": "Attivare modalità performance per FPS migliori",
"instruction_display_show_unsupported": "Mostrare/nascondere sistemi non definiti in es_systems.cfg",
"instruction_display_unknown_ext": "Attivare/disattivare avviso per estensioni assenti in es_systems.cfg",
"instruction_display_hide_premium": "Nascondere sistemi che richiedono accesso premium via API: {providers}",
@@ -247,6 +259,8 @@
"history_option_extract_archive": "Estrai archivio",
"history_option_open_file": "Apri file",
"history_option_scraper": "Scraper metadati",
"history_option_remove_from_queue": "Rimuovi dalla coda",
"history_option_cancel_download": "Annulla download",
"history_option_delete_game": "Elimina gioco",
"history_option_error_info": "Dettagli errore",
"history_option_retry": "Riprova download",

View File

@@ -65,6 +65,17 @@
"menu_accessibility": "Acessibilidade",
"menu_display": "Exibição",
"display_layout": "Layout de exibição",
"display_monitor": "Monitor",
"display_monitor_single": "Monitor único",
"display_monitor_single_only": "Apenas um monitor detectado",
"display_monitor_restart_required": "Reinício necessário para mudar de monitor",
"display_mode": "Modo de tela",
"display_fullscreen": "Tela cheia",
"display_windowed": "Janela",
"display_mode_restart_required": "Reinício necessário para mudar o modo",
"display_light_mode": "Modo performance",
"display_light_mode_enabled": "Modo performance ativado - efeitos desativados",
"display_light_mode_disabled": "Modo performance desativado - efeitos ativados",
"menu_redownload_cache": "Atualizar lista de jogos",
"menu_music_enabled": "Música ativada: {0}",
"menu_music_disabled": "Música desativada",
@@ -197,6 +208,9 @@
"instruction_display_font_size": "Ajustar tamanho do texto para legibilidade",
"instruction_display_footer_font_size": "Ajustar tamanho do texto do rodapé (versão e controles)",
"instruction_display_font_family": "Alternar entre famílias de fontes disponíveis",
"instruction_display_monitor": "Selecionar monitor para exibir RGSX",
"instruction_display_mode": "Alternar entre tela cheia e janela",
"instruction_display_light_mode": "Ativar modo performance para melhor FPS",
"instruction_display_show_unsupported": "Mostrar/ocultar sistemas não definidos em es_systems.cfg",
"instruction_display_unknown_ext": "Ativar/desativar aviso para extensões ausentes em es_systems.cfg",
"instruction_display_hide_premium": "Ocultar sistemas que exigem acesso premium via API: {providers}",

View File

@@ -404,7 +404,6 @@ def test_internet():
]
for test_url in test_urls:
logger.debug(f"Test connexion HTTP vers {test_url}")
try:
response = requests.get(test_url, timeout=5, allow_redirects=True)
if response.status_code == 200:

View File

@@ -49,6 +49,8 @@ def load_rgsx_settings():
"""Charge tous les paramètres depuis rgsx_settings.json."""
from config import RGSX_SETTINGS_PATH
#logger.debug(f"Chargement des settings depuis: {RGSX_SETTINGS_PATH}")
default_settings = {
"language": "en",
"music_enabled": True,
@@ -58,7 +60,10 @@ def load_rgsx_settings():
},
"display": {
"grid": "3x4",
"font_family": "pixel"
"font_family": "pixel",
"monitor": 0,
"fullscreen": True,
"light_mode": False
},
"symlink": {
"enabled": False,
@@ -78,13 +83,17 @@ def load_rgsx_settings():
if os.path.exists(RGSX_SETTINGS_PATH):
with open(RGSX_SETTINGS_PATH, 'r', encoding='utf-8') as f:
settings = json.load(f)
#logger.debug(f"Settings JSON chargé: display={settings.get('display', {})}")
# Fusionner avec les valeurs par défaut pour assurer la compatibilité
for key, value in default_settings.items():
if key not in settings:
settings[key] = value
return settings
else:
logger.warning(f"Fichier settings non trouvé: {RGSX_SETTINGS_PATH}")
except Exception as e:
print(f"Erreur lors du chargement de rgsx_settings.json: {str(e)}")
logger.error(f"Erreur chargement settings: {e}")
return default_settings
@@ -307,6 +316,92 @@ def set_display_grid(cols: int, rows: int):
save_rgsx_settings(settings)
return cols, rows
# ----------------------- Monitor/Display settings ----------------------- #
def get_display_monitor(settings=None):
"""Retourne l'index du moniteur configuré (par défaut 0 = principal)."""
if settings is None:
settings = load_rgsx_settings()
return settings.get("display", {}).get("monitor", 0)
def set_display_monitor(monitor_index: int):
"""Définit et sauvegarde l'index du moniteur à utiliser."""
settings = load_rgsx_settings()
disp = settings.setdefault("display", {})
disp["monitor"] = max(0, int(monitor_index))
save_rgsx_settings(settings)
return disp["monitor"]
def get_display_fullscreen(settings=None):
"""Retourne True si le mode plein écran est activé."""
if settings is None:
settings = load_rgsx_settings()
return settings.get("display", {}).get("fullscreen", True)
def set_display_fullscreen(fullscreen: bool):
"""Définit et sauvegarde le mode plein écran."""
settings = load_rgsx_settings()
disp = settings.setdefault("display", {})
disp["fullscreen"] = bool(fullscreen)
save_rgsx_settings(settings)
return disp["fullscreen"]
def get_light_mode(settings=None):
"""Retourne True si le mode léger (performance) est activé."""
if settings is None:
settings = load_rgsx_settings()
return settings.get("display", {}).get("light_mode", False)
def set_light_mode(enabled: bool):
"""Définit et sauvegarde le mode léger (performance)."""
settings = load_rgsx_settings()
disp = settings.setdefault("display", {})
disp["light_mode"] = bool(enabled)
save_rgsx_settings(settings)
return disp["light_mode"]
def get_available_monitors():
"""Retourne la liste des moniteurs disponibles avec leurs informations.
Compatible Windows, Linux (Batocera), et autres plateformes.
Retourne une liste de dicts: [{"index": 0, "name": "Monitor 1", "resolution": "1920x1080"}, ...]
"""
monitors = []
try:
import pygame
if not pygame.display.get_init():
pygame.display.init()
num_displays = pygame.display.get_num_displays()
for i in range(num_displays):
try:
# Essayer d'obtenir le mode desktop pour ce display
mode = pygame.display.get_desktop_sizes()[i] if hasattr(pygame.display, 'get_desktop_sizes') else None
if mode:
width, height = mode
else:
# Fallback: utiliser la résolution actuelle si disponible
info = pygame.display.Info()
width, height = info.current_w, info.current_h
monitors.append({
"index": i,
"name": f"Monitor {i + 1}",
"resolution": f"{width}x{height}"
})
except Exception as e:
# Si on ne peut pas obtenir les infos, ajouter quand même le moniteur
monitors.append({
"index": i,
"name": f"Monitor {i + 1}",
"resolution": "Unknown"
})
except Exception as e:
logger.error(f"Error getting monitors: {e}")
# Fallback: au moins un moniteur
monitors = [{"index": 0, "name": "Monitor 1 (Default)", "resolution": "Auto"}]
return monitors if monitors else [{"index": 0, "name": "Monitor 1 (Default)", "resolution": "Auto"}]
def get_font_family(settings=None):
if settings is None:
settings = load_rgsx_settings()

View File

@@ -1,3 +1,3 @@
{
"version": "2.3.3.3"
"version": "2.4.0.2"
}

View File

@@ -1,149 +1,385 @@
@echo off
setlocal EnableDelayedExpansion
:: Fichier de log
if not exist "%CD%\logs" MD "%CD%\logs"
set "LOG_FILE=%CD%\logs\Retrobat_RGSX_log.txt"
:: Fichier de log (chemin absolu pour fiabilité)
:: Détecter la racine (ROOT_DIR) d'abord pour construire un chemin stable
set CURRENT_DIR=%CD%
pushd "%CURRENT_DIR%\..\.."
set "ROOT_DIR=%CD%"
popd
if not exist "%ROOT_DIR%\roms\windows\logs" MD "%ROOT_DIR%\roms\windows\logs"
set "LOG_FILE=%ROOT_DIR%\roms\windows\logs\Retrobat_RGSX_log.txt"
:: =============================================================================
:: RGSX Retrobat Launcher v1.3
:: =============================================================================
:: Usage: "RGSX Retrobat.bat" [options]
:: --display=N Launch on display N (0=primary, 1=secondary, etc.)
:: --windowed Launch in windowed mode instead of fullscreen
:: --help Show this help
:: =============================================================================
:: Ajouter un horodatage au début du log
echo [%DATE% %TIME%] Script start >> "%LOG_FILE%"
:: Configuration des couleurs (codes ANSI)
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
set "ESC=%%b"
)
:: Afficher un message de démarrage
:: Couleurs
set "GREEN=[92m"
set "YELLOW=[93m"
set "RED=[91m"
set "CYAN=[96m"
set "RESET=[0m"
set "BOLD=[1m"
:: =============================================================================
:: Traitement des arguments
:: =============================================================================
set "DISPLAY_NUM="
set "WINDOWED_MODE="
set "CONFIG_FILE="
:parse_args
if "%~1"=="" goto :args_done
if /i "%~1"=="--help" goto :show_help
if /i "%~1"=="-h" goto :show_help
if /i "%~1"=="--windowed" (
set "WINDOWED_MODE=1"
shift
goto :parse_args
)
:: Check for --display=N format
echo %~1 | findstr /r "^--display=" >nul
if !ERRORLEVEL! EQU 0 (
for /f "tokens=2 delims==" %%a in ("%~1") do set "DISPLAY_NUM=%%a"
shift
goto :parse_args
)
shift
goto :parse_args
:show_help
echo.
echo %ESC%%CYAN%RGSX Retrobat Launcher - Help%ESC%%RESET%
echo.
echo Usage: "RGSX Retrobat.bat" [options]
echo.
echo Options:
echo --display=N Launch on display N (0=primary, 1=secondary, etc.)
echo --windowed Launch in windowed mode instead of fullscreen
echo --help, -h Show this help
echo.
echo Examples:
echo "RGSX Retrobat.bat" Launch on primary display
echo "RGSX Retrobat.bat" --display=1 Launch on secondary display (TV)
echo "RGSX Retrobat.bat" --windowed Launch in windowed mode
echo.
echo You can also create shortcuts with different display settings.
echo.
pause
exit /b 0
:args_done
:: URL de telechargement Python
set "PYTHON_ZIP_URL=https://github.com/RetroGameSets/RGSX/raw/main/windows/python.zip"
:: Obtenir le chemin du script de maniere fiable
set "SCRIPT_DIR=%~dp0"
set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%"
:: Detecter le repertoire racine
for %%I in ("%SCRIPT_DIR%\..\.." ) do set "ROOT_DIR=%%~fI"
:: Configuration des logs
set "LOG_DIR=%ROOT_DIR%\roms\windows\logs"
if not exist "%LOG_DIR%" mkdir "%LOG_DIR%"
set "LOG_FILE=%LOG_DIR%\Retrobat_RGSX_log.txt"
set "LOG_BACKUP=%LOG_DIR%\Retrobat_RGSX_log.old.txt"
:: Rotation des logs avec backup
if exist "%LOG_FILE%" (
for %%A in ("%LOG_FILE%") do (
if %%~zA GTR 100000 (
if exist "%LOG_BACKUP%" del /q "%LOG_BACKUP%"
move /y "%LOG_FILE%" "%LOG_BACKUP%" >nul 2>&1
echo [%DATE% %TIME%] Log rotated - previous log saved as .old.txt > "%LOG_FILE%"
)
)
)
:: =============================================================================
:: Ecran d'accueil
:: =============================================================================
cls
echo Running __main__.py for RetroBat...
echo [%DATE% %TIME%] Running __main__.py for RetroBat >> "%LOG_FILE%"
echo.
echo %ESC%%CYAN% ____ ____ ______ __ %ESC%%RESET%
echo %ESC%%CYAN% ^| _ \ / ___^/ ___\ \/ / %ESC%%RESET%
echo %ESC%%CYAN% ^| ^|_) ^| ^| _\___ \\ / %ESC%%RESET%
echo %ESC%%CYAN% ^| _ ^<^| ^|_^| ^|___) / \ %ESC%%RESET%
echo %ESC%%CYAN% ^|_^| \_\\____^|____/_/\_\ %ESC%%RESET%
echo.
echo %ESC%%BOLD% RetroBat Launcher v1.3%ESC%%RESET%
echo --------------------------------
if "!DISPLAY_NUM!" NEQ "0" (
echo %ESC%%CYAN%Display: !DISPLAY_NUM!%ESC%%RESET%
)
if "!WINDOWED_MODE!"=="1" (
echo %ESC%%CYAN%Mode: Windowed%ESC%%RESET%
)
echo.
:: Définir les chemins relatifs et les convertir en absolus
set CURRENT_DIR=%CD%
set PYTHON_EXE=python.exe
:: Debut du log
echo [%DATE% %TIME%] ========================================== >> "%LOG_FILE%"
echo [%DATE% %TIME%] RGSX Launcher v1.3 started >> "%LOG_FILE%"
echo [%DATE% %TIME%] Display: !DISPLAY_NUM!, Windowed: !WINDOWED_MODE! >> "%LOG_FILE%"
echo [%DATE% %TIME%] ========================================== >> "%LOG_FILE%"
:: Détecter le répertoire racine en remontant de deux niveaux depuis le script
pushd "%CURRENT_DIR%\..\.."
set "ROOT_DIR=%CD%"
popd
:: Définir le chemin du script principal selon les spécifications
:: Configuration des chemins
set "PYTHON_DIR=%ROOT_DIR%\system\tools\Python"
set "PYTHON_EXE=%PYTHON_DIR%\python.exe"
set "MAIN_SCRIPT=%ROOT_DIR%\roms\ports\RGSX\__main__.py"
set "ZIP_FILE=%ROOT_DIR%\roms\windows\python.zip"
:: Definir le chemin du script de mise à jour de la gamelist Windows
set "UPDATE_GAMELIST_SCRIPT=%ROOT_DIR%\roms\ports\RGSX\update_gamelist_windows.py"
:: Exporter RGSX_ROOT pour le script Python
set "RGSX_ROOT=%ROOT_DIR%"
:: Convertir les chemins relatifs en absolus avec pushd/popd
pushd "%ROOT_DIR%\system\tools\Python"
set "PYTHON_EXE_FULL=%ROOT_DIR%\system\tools\Python\!PYTHON_EXE!"
set "PYTHONW_EXE_FULL=%ROOT_DIR%\system\tools\Python\pythonw.exe"
popd
:: Logger les chemins
echo [%DATE% %TIME%] System info: >> "%LOG_FILE%"
echo [%DATE% %TIME%] ROOT_DIR: %ROOT_DIR% >> "%LOG_FILE%"
echo [%DATE% %TIME%] PYTHON_EXE: %PYTHON_EXE% >> "%LOG_FILE%"
echo [%DATE% %TIME%] MAIN_SCRIPT: %MAIN_SCRIPT% >> "%LOG_FILE%"
echo [%DATE% %TIME%] RGSX_ROOT: %RGSX_ROOT% >> "%LOG_FILE%"
:: Afficher et logger les variables
:: =============================================================================
:: Verification Python
:: =============================================================================
echo %ESC%%YELLOW%[1/3]%ESC%%RESET% Checking Python environment...
echo [%DATE% %TIME%] Step 1/3: Checking Python >> "%LOG_FILE%"
echo ROOT_DIR : %ROOT_DIR% >> "%LOG_FILE%"
echo CURRENT_DIR : !CURRENT_DIR! >> "%LOG_FILE%"
echo ROOT_DIR : !ROOT_DIR! >> "%LOG_FILE%"
echo PYTHON_EXE_FULL : !PYTHON_EXE_FULL! >> "%LOG_FILE%"
echo MAIN_SCRIPT : !MAIN_SCRIPT! >> "%LOG_FILE%"
echo UPDATE_GAMELIST_SCRIPT : !UPDATE_GAMELIST_SCRIPT! >> "%LOG_FILE%"
:: Vérifier si l'exécutable Python existe
echo Checking python.exe...
echo [%DATE% %TIME%] Checking python.exe at !PYTHON_EXE_FULL! >> "%LOG_FILE%"
if not exist "!PYTHON_EXE_FULL!" (
echo python.exe not found in system/tools. Preparing to extract..
echo [%DATE% %TIME%] python.exe not found in system/tools. Preparing to extract.. >> "%LOG_FILE%"
if not exist "%PYTHON_EXE%" (
echo %ESC%%YELLOW%^> Python not found, installing...%ESC%%RESET%
echo [%DATE% %TIME%] Python not found, starting installation >> "%LOG_FILE%"
:: Créer le dossier Python s'il n'existe pas
set "TOOLS_FOLDER_FULL=!ROOT_DIR!\system\tools"
if not exist "!TOOLS_FOLDER_FULL!\Python" (
echo Creating folder !TOOLS_FOLDER_FULL!\Python...
echo [%DATE% %TIME%] Creating folder !TOOLS_FOLDER_FULL!\Python... >> "%LOG_FILE%"
mkdir "!TOOLS_FOLDER_FULL!\Python"
:: Creer le dossier Python
if not exist "%PYTHON_DIR%" (
mkdir "%PYTHON_DIR%" 2>nul
echo [%DATE% %TIME%] Created folder: %PYTHON_DIR% >> "%LOG_FILE%"
)
set "ZIP_FILE=%ROOT_DIR%\roms\windows\python.zip"
echo Extracting ZIP_FILE : !ZIP_FILE! in /system/tools/Python
echo [%DATE% %TIME%] ZIP_FILE : !ZIP_FILE! >> "%LOG_FILE%"
if exist "!ZIP_FILE!" (
echo [%DATE% %TIME%] Extracting python.zip to !TOOLS_FOLDER_FULL!... >> "%LOG_FILE%"
tar -xf "!ZIP_FILE!" -C "!TOOLS_FOLDER_FULL!\Python" --strip-components=0
echo Extraction finished.
echo [%DATE% %TIME%] Extraction finished. >> "%LOG_FILE%"
del /s /q "!ZIP_FILE!"
echo python.zip file deleted.
echo [%DATE% %TIME%] python.zip file deleted. >> "%LOG_FILE%"
) else (
echo Error: Error python.zip not found please download it from github and put in /roms/windows folder.
echo [%DATE% %TIME%] Error: Error python.zip not found please download it from github and put in /roms/windows folder >> "%LOG_FILE%"
:: Verifier si le ZIP existe, sinon le telecharger
if not exist "%ZIP_FILE%" (
echo %ESC%%YELLOW%^> python.zip not found, downloading from GitHub...%ESC%%RESET%
echo [%DATE% %TIME%] python.zip not found, attempting download >> "%LOG_FILE%"
echo [%DATE% %TIME%] Download URL: %PYTHON_ZIP_URL% >> "%LOG_FILE%"
:: Verifier si curl est disponible
where curl.exe >nul 2>&1
if !ERRORLEVEL! EQU 0 (
echo %ESC%%CYAN%^> Using curl to download...%ESC%%RESET%
echo [%DATE% %TIME%] Using curl.exe for download >> "%LOG_FILE%"
curl.exe -L -# -o "%ZIP_FILE%" "%PYTHON_ZIP_URL%"
set DOWNLOAD_RESULT=!ERRORLEVEL!
) else (
:: Fallback sur PowerShell
echo %ESC%%CYAN%^> Using PowerShell to download...%ESC%%RESET%
echo [%DATE% %TIME%] curl not found, using PowerShell >> "%LOG_FILE%"
powershell -NoProfile -ExecutionPolicy Bypass -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -Uri '%PYTHON_ZIP_URL%' -OutFile '%ZIP_FILE%'"
set DOWNLOAD_RESULT=!ERRORLEVEL!
)
:: Verifier le resultat du telechargement
if !DOWNLOAD_RESULT! NEQ 0 (
echo.
echo %ESC%%RED% ERROR: Download failed!%ESC%%RESET%
echo.
echo Please download python.zip manually from:
echo %ESC%%CYAN%%PYTHON_ZIP_URL%%ESC%%RESET%
echo.
echo And place it in:
echo %ESC%%CYAN%%ROOT_DIR%\roms\windows\%ESC%%RESET%
echo.
echo [%DATE% %TIME%] ERROR: Download failed with code !DOWNLOAD_RESULT! >> "%LOG_FILE%"
goto :error
)
:: Verifier que le fichier a bien ete telecharge et n'est pas vide
if not exist "%ZIP_FILE%" (
echo.
echo %ESC%%RED% ERROR: Download failed - file not created!%ESC%%RESET%
echo [%DATE% %TIME%] ERROR: ZIP file not created after download >> "%LOG_FILE%"
goto :error
)
:: Verifier la taille du fichier (doit etre > 1MB pour etre valide)
for %%A in ("%ZIP_FILE%") do set ZIP_SIZE=%%~zA
if !ZIP_SIZE! LSS 1000000 (
echo.
echo %ESC%%RED% ERROR: Downloaded file appears invalid ^(too small^)!%ESC%%RESET%
echo [%DATE% %TIME%] ERROR: Downloaded file too small: !ZIP_SIZE! bytes >> "%LOG_FILE%"
del /q "%ZIP_FILE%" 2>nul
goto :error
)
echo %ESC%%GREEN%^> Download complete ^(!ZIP_SIZE! bytes^)%ESC%%RESET%
echo [%DATE% %TIME%] Download successful: !ZIP_SIZE! bytes >> "%LOG_FILE%"
)
:: Verifier que tar existe (Windows 10 1803+)
where tar >nul 2>&1
if !ERRORLEVEL! NEQ 0 (
echo.
echo %ESC%%RED% ERROR: tar command not available!%ESC%%RESET%
echo.
echo Please update Windows 10 or extract manually to:
echo %ESC%%CYAN%%PYTHON_DIR%%ESC%%RESET%
echo.
echo [%DATE% %TIME%] ERROR: tar command not found >> "%LOG_FILE%"
goto :error
)
:: Vérifier à nouveau si python.exe existe après extraction
if not exist "!PYTHON_EXE_FULL!" (
echo Error: python.exe not found after extraction at !PYTHON_EXE_FULL!.
echo [%DATE% %TIME%] Error: python.exe not found after extraction at !PYTHON_EXE_FULL! >> "%LOG_FILE%"
:: Extraction avec progression simulee
echo %ESC%%YELLOW%^> Extracting Python...%ESC%%RESET%
echo [%DATE% %TIME%] Extracting python.zip >> "%LOG_FILE%"
<nul set /p "= ["
tar -xf "%ZIP_FILE%" -C "%PYTHON_DIR%" --strip-components=0
set TAR_RESULT=!ERRORLEVEL!
echo %ESC%%GREEN%##########%ESC%%RESET%] Done
if !TAR_RESULT! NEQ 0 (
echo.
echo %ESC%%RED% ERROR: Extraction failed!%ESC%%RESET%
echo [%DATE% %TIME%] ERROR: tar extraction failed with code !TAR_RESULT! >> "%LOG_FILE%"
goto :error
)
echo [%DATE% %TIME%] Extraction completed >> "%LOG_FILE%"
:: Supprimer ZIP
del /q "%ZIP_FILE%" 2>nul
echo %ESC%%GREEN%^> python.zip cleaned up%ESC%%RESET%
echo [%DATE% %TIME%] python.zip deleted >> "%LOG_FILE%"
:: Verifier installation
if not exist "%PYTHON_EXE%" (
echo.
echo %ESC%%RED% ERROR: Python not found after extraction!%ESC%%RESET%
echo [%DATE% %TIME%] ERROR: python.exe not found after extraction >> "%LOG_FILE%"
goto :error
)
)
echo python.exe found.
echo [%DATE% %TIME%] python.exe found. >> "%LOG_FILE%"
:: Vérifier si le script Python existe
echo Checking __main__.py...
echo [%DATE% %TIME%] Checking __main__.py at !MAIN_SCRIPT! >> "%LOG_FILE%"
if not exist "!MAIN_SCRIPT!" (
echo Error: __main__.py not found at !MAIN_SCRIPT!.
echo [%DATE% %TIME%] Error: __main__.py not found at !MAIN_SCRIPT! >> "%LOG_FILE%"
:: Afficher et logger la version Python
for /f "tokens=*" %%v in ('"%PYTHON_EXE%" --version 2^>^&1') do set "PYTHON_VERSION=%%v"
echo %ESC%%GREEN%^> %PYTHON_VERSION% found%ESC%%RESET%
echo [%DATE% %TIME%] %PYTHON_VERSION% detected >> "%LOG_FILE%"
:: =============================================================================
:: Verification script principal
:: =============================================================================
echo %ESC%%YELLOW%[2/3]%ESC%%RESET% Checking RGSX application...
echo [%DATE% %TIME%] Step 2/3: Checking RGSX files >> "%LOG_FILE%"
if not exist "%MAIN_SCRIPT%" (
echo.
echo %ESC%%RED% ERROR: __main__.py not found!%ESC%%RESET%
echo.
echo Expected location:
echo %ESC%%CYAN%%MAIN_SCRIPT%%ESC%%RESET%
echo.
echo [%DATE% %TIME%] ERROR: __main__.py not found at %MAIN_SCRIPT% >> "%LOG_FILE%"
goto :error
)
echo __main__.py found.
echo [%DATE% %TIME%] __main__.py found. >> "%LOG_FILE%"
:: L'étape de mise à jour de la gamelist est désormais appelée depuis __main__.py
echo [%DATE% %TIME%] Skipping external gamelist update (handled in app). >> "%LOG_FILE%"
echo %ESC%%GREEN%^> RGSX files OK%ESC%%RESET%
echo [%DATE% %TIME%] RGSX files verified >> "%LOG_FILE%"
echo Launching __main__.py (attached)...
echo [%DATE% %TIME%] Preparing to launch main. >> "%LOG_FILE%"
:: =============================================================================
:: Lancement
:: =============================================================================
echo %ESC%%YELLOW%[3/3]%ESC%%RESET% Launching RGSX...
echo [%DATE% %TIME%] Step 3/3: Launching application >> "%LOG_FILE%"
:: Assurer le bon dossier de travail pour l'application
:: Changer le repertoire de travail
cd /d "%ROOT_DIR%\roms\ports\RGSX"
echo [%DATE% %TIME%] Working directory: %CD% >> "%LOG_FILE%"
:: Forcer les drivers SDL côté Windows et réduire le bruit console
:: Configuration SDL/Pygame
set PYGAME_HIDE_SUPPORT_PROMPT=1
set SDL_VIDEODRIVER=windows
set SDL_AUDIODRIVER=directsound
echo [%DATE% %TIME%] CWD before launch: %CD% >> "%LOG_FILE%"
set PYTHONWARNINGS=ignore::UserWarning:pygame.pkgdata
:: Lancer l'application dans la même console et attendre sa fin
:: Forcer python.exe pour capturer la sortie
set "PY_MAIN_EXE=!PYTHON_EXE_FULL!"
echo [%DATE% %TIME%] Using interpreter: !PY_MAIN_EXE! >> "%LOG_FILE%"
echo [%DATE% %TIME%] Launching "!MAIN_SCRIPT!" now... >> "%LOG_FILE%"
"!PY_MAIN_EXE!" "!MAIN_SCRIPT!" >> "%LOG_FILE%" 2>&1
set EXITCODE=!ERRORLEVEL!
echo [%DATE% %TIME%] __main__.py exit code: !EXITCODE! >> "%LOG_FILE%"
if "!EXITCODE!"=="0" (
echo Execution finished successfully.
echo [%DATE% %TIME%] Execution of __main__.py finished successfully. >> "%LOG_FILE%"
:: =============================================================================
:: Configuration multi-ecran
:: =============================================================================
:: SDL_VIDEO_FULLSCREEN_HEAD: Selectionne l'ecran pour le mode plein ecran
:: 0 = ecran principal, 1 = ecran secondaire, etc.
:: Ces variables ne sont definies que si --display=N ou --windowed est passe
:: Sinon, le script Python utilisera les parametres de rgsx_settings.json
echo [%DATE% %TIME%] Display configuration: >> "%LOG_FILE%"
if defined DISPLAY_NUM (
set SDL_VIDEO_FULLSCREEN_HEAD=!DISPLAY_NUM!
set RGSX_DISPLAY=!DISPLAY_NUM!
echo [%DATE% %TIME%] SDL_VIDEO_FULLSCREEN_HEAD=!DISPLAY_NUM! ^(from --display arg^) >> "%LOG_FILE%"
echo [%DATE% %TIME%] RGSX_DISPLAY=!DISPLAY_NUM! ^(from --display arg^) >> "%LOG_FILE%"
) else (
echo Error: Failed to execute __main__.py (code !EXITCODE!).
echo [%DATE% %TIME%] Error: Failed to execute __main__.py with error code !EXITCODE!. >> "%LOG_FILE%"
echo [%DATE% %TIME%] Display: using rgsx_settings.json config >> "%LOG_FILE%"
)
if defined WINDOWED_MODE (
set RGSX_WINDOWED=!WINDOWED_MODE!
echo [%DATE% %TIME%] RGSX_WINDOWED=!WINDOWED_MODE! ^(from --windowed arg^) >> "%LOG_FILE%"
) else (
echo [%DATE% %TIME%] Windowed: using rgsx_settings.json config >> "%LOG_FILE%"
)
:: Log environnement
echo [%DATE% %TIME%] Environment variables set: >> "%LOG_FILE%"
echo [%DATE% %TIME%] RGSX_ROOT=%RGSX_ROOT% >> "%LOG_FILE%"
echo [%DATE% %TIME%] SDL_VIDEODRIVER=%SDL_VIDEODRIVER% >> "%LOG_FILE%"
echo [%DATE% %TIME%] SDL_AUDIODRIVER=%SDL_AUDIODRIVER% >> "%LOG_FILE%"
echo.
if defined DISPLAY_NUM (
echo %ESC%%CYAN%Launching on display !DISPLAY_NUM!...%ESC%%RESET%
)
if defined WINDOWED_MODE (
echo %ESC%%CYAN%Windowed mode enabled%ESC%%RESET%
)
echo %ESC%%CYAN%Starting RGSX application...%ESC%%RESET%
echo %ESC%%BOLD%Press Ctrl+C to force quit if needed%ESC%%RESET%
echo.
echo [%DATE% %TIME%] Executing: "%PYTHON_EXE%" "%MAIN_SCRIPT%" >> "%LOG_FILE%"
echo [%DATE% %TIME%] --- Application output start --- >> "%LOG_FILE%"
"%PYTHON_EXE%" "%MAIN_SCRIPT%" >> "%LOG_FILE%" 2>&1
set EXITCODE=!ERRORLEVEL!
echo [%DATE% %TIME%] --- Application output end --- >> "%LOG_FILE%"
echo [%DATE% %TIME%] Exit code: !EXITCODE! >> "%LOG_FILE%"
if "!EXITCODE!"=="0" (
echo.
echo %ESC%%GREEN%RGSX closed successfully.%ESC%%RESET%
echo.
echo [%DATE% %TIME%] Application closed successfully >> "%LOG_FILE%"
) else (
echo.
echo %ESC%%RED%RGSX exited with error code !EXITCODE!%ESC%%RESET%
echo.
echo [%DATE% %TIME%] ERROR: Application exited with code !EXITCODE! >> "%LOG_FILE%"
goto :error
)
:end
echo Task completed.
echo [%DATE% %TIME%] Task completed successfully. >> "%LOG_FILE%"
echo [%DATE% %TIME%] ========================================== >> "%LOG_FILE%"
echo [%DATE% %TIME%] Session ended normally >> "%LOG_FILE%"
echo [%DATE% %TIME%] ========================================== >> "%LOG_FILE%"
timeout /t 2 >nul
exit /b 0
:error
echo An error occurred.
echo [%DATE% %TIME%] An error occurred. >> "%LOG_FILE%"
echo.
echo %ESC%%RED%An error occurred. Check the log file:%ESC%%RESET%
echo %ESC%%CYAN%%LOG_FILE%%ESC%%RESET%
echo.
echo [%DATE% %TIME%] ========================================== >> "%LOG_FILE%"
echo [%DATE% %TIME%] Session ended with errors >> "%LOG_FILE%"
echo [%DATE% %TIME%] ========================================== >> "%LOG_FILE%"
echo.
echo Press any key to close...
pause >nul
exit /b 1