1
0
forked from Mirrors/RGSX

v2.2.0.0 - enhance controller configuration loading, and add support for multiple controller presets by default to avoid configuration and problems

This commit is contained in:
skymike03
2025-09-09 15:14:09 +02:00
parent 4fbab11306
commit 9c7462623c
11 changed files with 670 additions and 31 deletions

View File

@@ -10,6 +10,7 @@ import datetime
import subprocess
import sys
import config
import shutil
from display import (
init_display, draw_loading_screen, draw_error_screen, draw_platform_grid,
@@ -109,31 +110,41 @@ else:
logger.debug(f"Joysticks détectés: {joystick_names}, utilisation du joystick par défaut.")
# Test des boutons du joystick
for name in joystick_names:
if "Xbox" in name or "PlayStation" in name or "Logitech" in name:
if "Xbox" in name:
config.xbox_controller = True
logger.debug(f"Manette Xbox/PlayStation/Logitech détectée: {name}")
print(f"Manette Xbox/PlayStation/Logitech détectée: {name}")
logger.debug(f"Controller detected : {name}")
print(f"Controller detected : {name}")
break
elif "PlayStation" in name:
config.playstation_controller = True
logger.debug(f"Controller detected : {name}")
print(f"Controller detected : {name}")
break
elif "Nintendo" in name:
config.nintendo_controller = True
logger.debug(f"Manette Nintendo détectée: {name}")
print(f"Manette Nintendo détectée: {name}")
logger.debug(f"Controller detected : {name}")
print(f"Controller detected : {name}")
elif "Logitech" in name:
config.logitech_controller = True
logger.debug(f"Controller detected : {name}")
print(f"Controller detected : {name}")
elif "8Bitdo" in name:
config.eightbitdo_controller = True
logger.debug(f"Manette 8Bitdo détectée: {name}")
print(f"Manette 8Bitdo détectée: {name}")
logger.debug(f"Controller detected : {name}")
print(f"Controller detected : {name}")
elif "Steam" in name:
config.steam_controller = True
logger.debug(f"Manette Steam détectée: {name}")
print(f"Manette Steam détectée: {name}")
logger.debug(f"Controller detected : {name}")
print(f"Controller detected : {name}")
elif "TRIMUI Smart Pro" in name:
config.trimui_controller = True
logger.debug(f"TRIMUI Smart Pro détectée: {name}")
print(f"TRIMUI Smart Pro détectée: {name}")
logger.debug(f"Controller detected : {name}")
print(f"Controller detected : {name}")
else:
config.generic_controller = True
logger.debug(f"Manette générique détectée: {name}")
print(f"Manette générique détectée: {name}")
logger.debug(f"Generic controller detected : {name}")
print(f"Generic controller detected : {name}")
# Chargement des paramètres d'accessibilité
config.accessibility_settings = load_accessibility_settings()
# Appliquer la grille d'affichage depuis les paramètres
@@ -233,6 +244,49 @@ config.current_music = current_music # Met à jour la musique en cours dans con
config.history = load_history()
logger.debug(f"Historique de téléchargement : {len(config.history)} entrées")
# Appliquer un préréglage de contrôles si une manette connue est détectée et qu'aucune config n'existe encore
try:
# Copier uniquement si controls.json est absent ou vide
need_preset = (not os.path.exists(config.CONTROLS_CONFIG_PATH)) or (os.path.getsize(config.CONTROLS_CONFIG_PATH) == 0)
if need_preset:
os.makedirs(os.path.dirname(config.CONTROLS_CONFIG_PATH), exist_ok=True)
# Cartographie des flags -> fichiers de préconfig
preset_candidates = []
if getattr(config, 'steam_controller', False):
preset_candidates.append('steam_controller.json')
if getattr(config, 'trimui_controller', False):
preset_candidates.append('trimui_controller.json')
if getattr(config, 'xbox_controller', False):
preset_candidates.append('xbox_controller.json')
if getattr(config, 'playstation_controller', False):
preset_candidates.append('playstation_controller.json')
if getattr(config, 'logitech_controller', False):
preset_candidates.append('logitech_controller.json')
if getattr(config, 'nintendo_controller', False):
preset_candidates.append('nintendo_controller.json')
if getattr(config, 'eightbitdo_controller', False):
preset_candidates.append('8bitdo_controller.json')
if getattr(config, 'generic_controller', False):
preset_candidates.append('generic_controller.json')
# Toujours tenter un générique en dernier recours
if 'generic_controller.json' not in preset_candidates:
preset_candidates.append('generic_controller.json')
for fname in preset_candidates:
src = os.path.join(config.PRECONF_CONTROLS_PATH, fname)
if os.path.exists(src):
try:
shutil.copyfile(src, config.CONTROLS_CONFIG_PATH)
logger.info(f"Préconfiguration des contrôles appliquée depuis {src}")
print(f"Préconfiguration des contrôles appliquée: {fname}")
break
except Exception as e:
logger.error(f"Échec de la copie du préréglage {src} -> {config.CONTROLS_CONFIG_PATH}: {e}")
except Exception as e:
logger.error(f"Erreur lors de l'application d'un préréglage de contrôles: {e}")
# Vérification et chargement de la configuration des contrôles
config.controls_config = load_controls_config()
@@ -241,8 +295,8 @@ if config.controls_config is None:
config.controls_config = {}
logger.debug("Initialisation de config.controls_config avec un dictionnaire vide")
# Vérifier simplement si le fichier controls.json existe
if not os.path.exists(config.CONTROLS_CONFIG_PATH) or not config.controls_config:
# Vérifier si une configuration utilisateur est absente ET qu'aucune config n'a été chargée (préréglage)
if (not os.path.exists(config.CONTROLS_CONFIG_PATH)) and (not config.controls_config):
logger.warning("Fichier controls.json manquant ou vide, configuration manuelle nécessaire")
# Ajouter une configuration minimale de secours pour pouvoir naviguer
config.controls_config = get_emergency_controls()

View File

@@ -0,0 +1,74 @@
{
"confirm": {
"type": "button",
"button": 0,
"display": "A"
},
"cancel": {
"type": "button",
"button": 1,
"display": "B"
},
"up": {
"type": "hat",
"value": [0, 1],
"display": "↑"
},
"down": {
"type": "hat",
"value": [0, -1],
"display": "↓"
},
"left": {
"type": "hat",
"value": [-1, 0],
"display": "←"
},
"right": {
"type": "hat",
"value": [1, 0],
"display": "→"
},
"start": {
"type": "button",
"button": 7,
"display": "Start"
},
"filter": {
"type": "button",
"button": 6,
"display": "Select"
},
"page_up": {
"type": "axis",
"axis": 4,
"direction": 1,
"display": "RT"
},
"page_down": {
"type": "axis",
"axis": 5,
"direction": -1,
"display": "LT"
},
"history": {
"type": "button",
"button": 3,
"display": "Y"
},
"clear_history": {
"type": "button",
"button": 2,
"display": "X"
},
"delete": {
"type": "button",
"button": 4,
"display": "LB"
},
"space": {
"type": "button",
"button": 5,
"display": "RB"
}
}

View File

@@ -0,0 +1,74 @@
{
"confirm": {
"type": "button",
"button": 0,
"display": "A"
},
"cancel": {
"type": "button",
"button": 1,
"display": "B"
},
"up": {
"type": "hat",
"value": [0, 1],
"display": "↑"
},
"down": {
"type": "hat",
"value": [0, -1],
"display": "↓"
},
"left": {
"type": "hat",
"value": [-1, 0],
"display": "←"
},
"right": {
"type": "hat",
"value": [1, 0],
"display": "→"
},
"start": {
"type": "button",
"button": 7,
"display": "Start"
},
"filter": {
"type": "button",
"button": 6,
"display": "Select"
},
"page_up": {
"type": "axis",
"axis": 4,
"direction": 1,
"display": "RT"
},
"page_down": {
"type": "axis",
"axis": 5,
"direction": -1,
"display": "LT"
},
"history": {
"type": "button",
"button": 3,
"display": "Y"
},
"clear_history": {
"type": "button",
"button": 2,
"display": "X"
},
"delete": {
"type": "button",
"button": 4,
"display": "LB"
},
"space": {
"type": "button",
"button": 5,
"display": "RB"
}
}

View File

@@ -0,0 +1,72 @@
{
"confirm": {
"type": "key",
"key": 13,
"display": "Enter"
},
"cancel": {
"type": "key",
"key": 27,
"display": "\u00c9chap"
},
"up": {
"type": "key",
"key": 1073741906,
"display": "\u2191"
},
"down": {
"type": "key",
"key": 1073741905,
"display": "\u2193"
},
"left": {
"type": "key",
"key": 1073741904,
"display": "\u2190"
},
"right": {
"type": "key",
"key": 1073741903,
"display": "\u2192"
},
"start": {
"type": "key",
"key": 1073742054,
"display": "AltGR"
},
"filter": {
"type": "key",
"key": 102,
"display": "F"
},
"page_up": {
"type": "key",
"key": 1073741899,
"display": "Page+"
},
"page_down": {
"type": "key",
"key": 1073741902,
"display": "Page-"
},
"history": {
"type": "key",
"key": 104,
"display": "H"
},
"clear_history": {
"type": "key",
"key": 120,
"display": "X"
},
"delete": {
"type": "key",
"key": 8,
"display": "Backspace"
},
"space": {
"type": "key",
"key": 32,
"display": "Espace"
}
}

View File

@@ -0,0 +1,74 @@
{
"confirm": {
"type": "button",
"button": 1,
"display": "B"
},
"cancel": {
"type": "button",
"button": 0,
"display": "A"
},
"up": {
"type": "hat",
"value": [0, 1],
"display": "↑"
},
"down": {
"type": "hat",
"value": [0, -1],
"display": "↓"
},
"left": {
"type": "hat",
"value": [-1, 0],
"display": "←"
},
"right": {
"type": "hat",
"value": [1, 0],
"display": "→"
},
"start": {
"type": "button",
"button": 7,
"display": "Start"
},
"filter": {
"type": "button",
"button": 6,
"display": "Select"
},
"page_up": {
"type": "axis",
"axis": 4,
"direction": 1,
"display": "ZR"
},
"page_down": {
"type": "axis",
"axis": 5,
"direction": -1,
"display": "ZL"
},
"history": {
"type": "button",
"button": 3,
"display": "Y"
},
"clear_history": {
"type": "button",
"button": 2,
"display": "X"
},
"delete": {
"type": "button",
"button": 4,
"display": "L"
},
"space": {
"type": "button",
"button": 5,
"display": "R"
}
}

View File

@@ -0,0 +1,74 @@
{
"confirm": {
"type": "button",
"button": 3,
"display": "A"
},
"cancel": {
"type": "button",
"button": 4,
"display": "B"
},
"up": {
"type": "button",
"button": 16,
"display": "↑"
},
"down": {
"type": "button",
"button": 17,
"display": "↓"
},
"left": {
"type": "button",
"button": 18,
"display": "←"
},
"right": {
"type": "button",
"button": 19,
"display": "→"
},
"start": {
"type": "button",
"button": 12,
"display": "Start"
},
"filter": {
"type": "button",
"button": 11,
"display": "Select"
},
"page_up": {
"type": "axis",
"axis": 2,
"direction": -1,
"display": "L2"
},
"page_down": {
"type": "axis",
"axis": 5,
"direction": -1,
"display": "R2"
},
"history": {
"type": "button",
"button": 6,
"display": "Y"
},
"clear_history": {
"type": "button",
"button": 5,
"display": "X"
},
"delete": {
"type": "button",
"button": 7,
"display": "L1"
},
"space": {
"type": "button",
"button": 8,
"display": "R1"
}
}

View File

@@ -0,0 +1,84 @@
{
"confirm": {
"type": "button",
"button": 0,
"display": "B"
},
"cancel": {
"type": "button",
"button": 1,
"display": "A"
},
"up": {
"type": "hat",
"value": [
0,
1
],
"display": "↑"
},
"down": {
"type": "hat",
"value": [
0,
-1
],
"display": "↓"
},
"left": {
"type": "hat",
"value": [
-1,
0
],
"display": "←"
},
"right": {
"type": "hat",
"value": [
1,
0
],
"display": "→"
},
"start": {
"type": "button",
"button": 9,
"display": "Start"
},
"filter": {
"type": "button",
"button": 8,
"display": "Select"
},
"page_up": {
"type": "button",
"button": 7,
"display": "R2"
},
"page_down": {
"type": "button",
"button": 6,
"display": "L2"
},
"history": {
"type": "button",
"button": 2,
"display": "X"
},
"clear_history": {
"type": "button",
"button": 3,
"display": "Y"
},
"delete": {
"type": "button",
"button": 5,
"display": "L1"
},
"space": {
"type": "button",
"button": 4,
"display": "R1"
}
}

View File

@@ -0,0 +1,86 @@
{
"confirm": {
"type": "button",
"button": 0,
"display": "A"
},
"cancel": {
"type": "button",
"button": 1,
"display": "B"
},
"up": {
"type": "hat",
"value": [
0,
1
],
"display": "\u2191"
},
"down": {
"type": "hat",
"value": [
0,
-1
],
"display": "\u2193"
},
"left": {
"type": "hat",
"value": [
-1,
0
],
"display": "\u2190"
},
"right": {
"type": "hat",
"value": [
1,
0
],
"display": "\u2192"
},
"start": {
"type": "button",
"button": 7,
"display": "Start"
},
"filter": {
"type": "button",
"button": 6,
"display": "Select"
},
"page_up": {
"type": "axis",
"axis": 4,
"direction": 1,
"display": "RT"
},
"page_down": {
"type": "axis",
"axis": 5,
"direction": -1,
"display": "LT"
},
"history": {
"type": "button",
"button": 3,
"display": "Select"
},
"clear_history": {
"type": "button",
"button": 2,
"display": "X"
},
"delete": {
"type": "button",
"button": 4,
"display": "LB"
},
"space": {
"type": "button",
"button": 5,
"display": "RB"
}
}

View File

@@ -4,7 +4,7 @@ import logging
import platform
# Version actuelle de l'application
app_version = "2.1.0.2"
app_version = "2.2.0.0"
def get_operating_system():
"""Renvoie le nom du système d'exploitation."""
@@ -87,6 +87,7 @@ IMAGES_FOLDER = os.path.join(SAVE_FOLDER, "images")
GAMES_FOLDER = os.path.join(SAVE_FOLDER, "games")
SOURCES_FILE = os.path.join(SAVE_FOLDER, "systems_list.json")
JSON_EXTENSIONS = os.path.join(SAVE_FOLDER, "rom_extensions.json")
PRECONF_CONTROLS_PATH = os.path.join(APP_FOLDER, "assets", "controls")
CONTROLS_CONFIG_PATH = os.path.join(SAVE_FOLDER, "controls.json")
HISTORY_PATH = os.path.join(SAVE_FOLDER, "history.json")
API_KEY_1FICHIER = os.path.join(SAVE_FOLDER, "1fichierAPI.txt")

View File

@@ -47,7 +47,12 @@ def validate_menu_state(state):
def load_controls_config(path=CONTROLS_CONFIG_PATH):
"""Charge la configuration des contrôles depuis un fichier JSON."""
"""Charge la configuration des contrôles.
Priorité:
1) Fichier utilisateur dans SAVE_FOLDER (controls.json)
2) Préréglage correspondant dans PRECONF_CONTROLS_PATH (sans copie)
3) Configuration clavier par défaut
"""
default_config = {
"confirm": {"type": "key", "key": pygame.K_RETURN},
"cancel": {"type": "key", "key": pygame.K_ESCAPE},
@@ -66,24 +71,65 @@ def load_controls_config(path=CONTROLS_CONFIG_PATH):
}
try:
# 1) Fichier utilisateur
if os.path.exists(path):
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
if not isinstance(data, dict):
data = {}
else:
data = {}
changed = False
for k, v in default_config.items():
if k not in data:
data[k] = v
changed = True
if changed:
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)
logging.getLogger(__name__).debug(f"controls.json complété avec les actions manquantes: {path}")
return data
# Compléter les actions manquantes, et sauve seulement si le fichier utilisateur existe
changed = False
for k, v in default_config.items():
if k not in data:
data[k] = v
changed = True
if changed:
try:
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)
logging.getLogger(__name__).debug(f"controls.json complété avec les actions manquantes: {path}")
except Exception as e:
logging.getLogger(__name__).warning(f"Impossible d'écrire les actions manquantes dans {path}: {e}")
return data
# 2) Préréglages sans copie si aucun fichier utilisateur
try:
candidates = []
# Si aucun contrôleur détecté, privilégier le préréglage clavier
if not getattr(config, 'joystick', False) or getattr(config, 'keyboard', False):
candidates.append('keyboard.json')
# Déterminer les préréglages disponibles selon les flags détectés au démarrage
if getattr(config, 'steam_controller', False):
candidates.append('steam_controller.json')
if getattr(config, 'trimui_controller', False):
candidates.append('trimui_controller.json')
if getattr(config, 'xbox_controller', False):
candidates.append('xbox_controller.json')
if getattr(config, 'nintendo_controller', False):
candidates.append('nintendo_controller.json')
if getattr(config, 'eightbitdo_controller', False):
candidates.append('8bitdo_controller.json')
# Fallbacks génériques
if 'generic_controller.json' not in candidates:
candidates.append('generic_controller.json')
if 'xbox_controller.json' not in candidates:
candidates.append('xbox_controller.json')
for fname in candidates:
src = os.path.join(config.PRECONF_CONTROLS_PATH, fname)
if os.path.exists(src):
with open(src, "r", encoding="utf-8") as f:
data = json.load(f)
if isinstance(data, dict) and data:
logging.getLogger(__name__).info(f"Chargement des contrôles préréglés: {fname}")
return data
except Exception as e:
logging.getLogger(__name__).warning(f"Échec du chargement des contrôles préréglés: {e}")
# 3) Fallback clavier par défaut
logging.getLogger(__name__).info("Aucun fichier utilisateur ou préréglage trouvé, utilisation des contrôles par défaut")
return default_config.copy()
except Exception as e:
logging.getLogger(__name__).error(f"Erreur load_controls_config: {e}")
return default_config.copy()

View File

@@ -9,7 +9,7 @@
"loading_test_connection": "Test de la connexion...",
"loading_download_data": "Téléchargement des jeux et images...",
"loading_progress": "Progression : {0}%",
"loading_check_updates": "Vérification des mises à jour... Veuillez patienter...",
"loading_check_updates": "Mise à jour... Veuillez patienter...",
"error_check_updates_failed": "Échec de la vérification des mises à jour.",
"loading_downloading_games_images": "Téléchargement des jeux et images...",
"loading_extracting_data": "Extraction du dossier de données initial...",