mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-13 03:51:10 +00:00
534 lines
19 KiB
Python
Executable File
534 lines
19 KiB
Python
Executable File
#!/usr/bin/python3
|
|
# Player 1 on the session's controller, for every emulator that would otherwise start on the
|
|
# keyboard. The session's pad is an Xbox 360 controller (libfakeinput's identity outside Steam) that
|
|
# SDL maps by itself; what an emulator lacks is a profile that points Player 1 at it. One is
|
|
# written only where the emulator has none yet - a profile the user made is never touched.
|
|
# Run at every desktop start and before every program from the rail.
|
|
import os
|
|
|
|
HOME = os.environ.get("HOME", "/root")
|
|
CONFIG = os.environ.get("XDG_CONFIG_HOME", os.path.join(HOME, ".config"))
|
|
|
|
|
|
def seed(path, text, what, done="Player 1 on the controller"):
|
|
if os.path.exists(path):
|
|
return False
|
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
with open(path + ".tmp", "w", encoding="utf-8") as f:
|
|
f.write(text)
|
|
os.replace(path + ".tmp", path)
|
|
print(f"bannerlator-pad-defaults: {what}: {done} ({path})")
|
|
return True
|
|
|
|
|
|
# RPCS3: its global input configuration (rpcs3::utils::get_input_config_dir() + "Default.yml").
|
|
# Without one it adds the keyboard pad handler (pad_config.cpp, cfg_input::load). SDL handler
|
|
# names from rpcs3/Input/sdl_pad_handler.cpp (button_list, init_config); the device is
|
|
# "<SDL gamepad name> <n>" (sdl_pad_handler::enumerate_devices).
|
|
RPCS3_PLAYER1 = """Player 1 Input:
|
|
Handler: SDL
|
|
Device: Xbox 360 Controller 1
|
|
Config:
|
|
Left Stick Left: LS X-
|
|
Left Stick Down: LS Y-
|
|
Left Stick Right: LS X+
|
|
Left Stick Up: LS Y+
|
|
Right Stick Left: RS X-
|
|
Right Stick Down: RS Y-
|
|
Right Stick Right: RS X+
|
|
Right Stick Up: RS Y+
|
|
Start: Start
|
|
Select: Back
|
|
PS Button: Guide
|
|
Square: West
|
|
Cross: South
|
|
Circle: East
|
|
Triangle: North
|
|
Left: Left
|
|
Down: Down
|
|
Right: Right
|
|
Up: Up
|
|
R1: RB
|
|
R2: RT
|
|
R3: RS
|
|
L1: LB
|
|
L2: LT
|
|
L3: LS
|
|
Left Stick Deadzone: 8000
|
|
Right Stick Deadzone: 8000
|
|
Left Stick Anti-Deadzone: 4259
|
|
Right Stick Anti-Deadzone: 4259
|
|
Left Trigger Threshold: 0
|
|
Right Trigger Threshold: 0
|
|
Color Value B: 20
|
|
LED battery indicator brightness: 10
|
|
Buddy Device: ""
|
|
"""
|
|
|
|
|
|
def rpcs3():
|
|
seed(os.path.join(CONFIG, "rpcs3/input_configs/global/Default.yml"), RPCS3_PLAYER1, "RPCS3")
|
|
rpcs3_shader_mode()
|
|
|
|
|
|
# RPCS3's default shader mode (Async Recompiler with Shader Interpreter) builds 6650 interpreter
|
|
# pipelines at the start of every boot, minutes on an Adreno. The async recompiler compiles only
|
|
# what a game draws, in the background, once. A new install starts with it (a config.yml that
|
|
# holds only this is merged over RPCS3's defaults); an existing one still on the default is moved
|
|
# once, and a marker keeps any later choice the user makes in RPCS3's own settings.
|
|
RPCS3_INTERPRETER = "Async Recompiler with Shader Interpreter"
|
|
RPCS3_RECOMPILER = "Async Recompiler (multi-threaded)" # the GUI says "Async Shader Recompiler"; this is the value (system_config_types.cpp)
|
|
|
|
|
|
def rpcs3_shader_mode():
|
|
d = os.path.join(CONFIG, "rpcs3")
|
|
marker = os.path.join(d, ".droiddeck-shader-mode")
|
|
if os.path.exists(marker):
|
|
return
|
|
cfg = os.path.join(d, "config.yml")
|
|
if not os.path.isfile(cfg):
|
|
if seed(cfg, f"Video:\n Shader Mode: {RPCS3_RECOMPILER}\n", "RPCS3", f"shader mode {RPCS3_RECOMPILER}"):
|
|
open(marker, "w").close()
|
|
return
|
|
with open(cfg, encoding="utf-8", errors="replace") as f:
|
|
text = f.read()
|
|
if f"Shader Mode: {RPCS3_INTERPRETER}" in text:
|
|
text = text.replace(f"Shader Mode: {RPCS3_INTERPRETER}", f"Shader Mode: {RPCS3_RECOMPILER}", 1)
|
|
with open(cfg + ".tmp", "w", encoding="utf-8") as f:
|
|
f.write(text)
|
|
os.replace(cfg + ".tmp", cfg)
|
|
print(f"bannerlator-pad-defaults: RPCS3: shader mode {RPCS3_RECOMPILER} ({cfg})")
|
|
open(marker, "w").close()
|
|
|
|
|
|
# ---- ini helpers (DuckStation and PCSX2 share one ini dialect, keys may repeat) -------------
|
|
|
|
def ini_section(lines, name):
|
|
"""(start, end) line indices of [name]'s body, or None."""
|
|
start = None
|
|
for i, l in enumerate(lines):
|
|
t = l.strip()
|
|
if t.startswith("[") and t.endswith("]"):
|
|
if start is not None:
|
|
return start, i
|
|
if t[1:-1].strip() == name:
|
|
start = i + 1
|
|
return (start, len(lines)) if start is not None else None
|
|
|
|
|
|
def patch_ini_section(path, section, body, what, needle="SDL-", require=None, done="Player 1 on the controller"):
|
|
"""Add body's lines to [section] of an ini the emulator wrote itself, unless the section
|
|
already binds a controller (needle). Missing file: nothing - the emulator writes its own
|
|
defaults on first run and this runs again."""
|
|
if not os.path.isfile(path):
|
|
return
|
|
with open(path, encoding="utf-8", errors="replace") as f:
|
|
text = f.read()
|
|
if require and require not in text:
|
|
return
|
|
lines = text.splitlines()
|
|
span = ini_section(lines, section)
|
|
if span and any(needle in l for l in lines[span[0]:span[1]]):
|
|
return
|
|
new = body.strip("\n").splitlines()
|
|
if span:
|
|
# The controller type the emulator already wrote stays; only bindings are added.
|
|
have = {l.split("=", 1)[0].strip() for l in lines[span[0]:span[1]] if "=" in l}
|
|
new = [l for l in new if not (l.split("=", 1)[0].strip() == "Type" and "Type" in have)]
|
|
end = span[1]
|
|
while end > span[0] and not lines[end - 1].strip():
|
|
end -= 1
|
|
lines[end:end] = new
|
|
else:
|
|
lines += ["", f"[{section}]"] + new
|
|
with open(path + ".tmp", "w", encoding="utf-8") as f:
|
|
f.write("\n".join(lines) + "\n")
|
|
os.replace(path + ".tmp", path)
|
|
print(f"bannerlator-pad-defaults: {what}: {done} ({path})")
|
|
|
|
|
|
# DuckStation: SDL-<n>/<name>, axes signed (src/util/sdl_input_source.cpp); Pad1 keys from
|
|
# src/core/analog_controller.cpp. Its defaults bind the keyboard only (settings.cpp).
|
|
DUCKSTATION_PAD1 = """Type = AnalogController
|
|
Up = SDL-0/DPadUp
|
|
Down = SDL-0/DPadDown
|
|
Left = SDL-0/DPadLeft
|
|
Right = SDL-0/DPadRight
|
|
Cross = SDL-0/A
|
|
Circle = SDL-0/B
|
|
Square = SDL-0/X
|
|
Triangle = SDL-0/Y
|
|
Select = SDL-0/Back
|
|
Start = SDL-0/Start
|
|
Analog = SDL-0/Guide
|
|
L1 = SDL-0/LeftShoulder
|
|
R1 = SDL-0/RightShoulder
|
|
L2 = SDL-0/+LeftTrigger
|
|
R2 = SDL-0/+RightTrigger
|
|
L3 = SDL-0/LeftStick
|
|
R3 = SDL-0/RightStick
|
|
LLeft = SDL-0/-LeftX
|
|
LRight = SDL-0/+LeftX
|
|
LUp = SDL-0/-LeftY
|
|
LDown = SDL-0/+LeftY
|
|
RLeft = SDL-0/-RightX
|
|
RRight = SDL-0/+RightX
|
|
RUp = SDL-0/-RightY
|
|
RDown = SDL-0/+RightY
|
|
LargeMotor = SDL-0/LargeMotor
|
|
SmallMotor = SDL-0/SmallMotor
|
|
"""
|
|
|
|
|
|
def duckstation():
|
|
for d in (os.path.join(CONFIG, "duckstation"), os.path.join(HOME, ".local/share/duckstation")):
|
|
patch_ini_section(os.path.join(d, "settings.ini"), "Pad1", DUCKSTATION_PAD1, "DuckStation")
|
|
|
|
|
|
# PCSX2: FaceSouth/... names (pcsx2/Input/SDLInputSource.cpp), keys from SIO/Pad/PadDualshock2.cpp.
|
|
# An ini without [UI] SettingsVersion is thrown away and rebuilt, so only a complete one is patched.
|
|
PCSX2_PAD1 = """Type = DualShock2
|
|
Up = SDL-0/DPadUp
|
|
Right = SDL-0/DPadRight
|
|
Down = SDL-0/DPadDown
|
|
Left = SDL-0/DPadLeft
|
|
Triangle = SDL-0/FaceNorth
|
|
Circle = SDL-0/FaceEast
|
|
Cross = SDL-0/FaceSouth
|
|
Square = SDL-0/FaceWest
|
|
Select = SDL-0/Back
|
|
Start = SDL-0/Start
|
|
L1 = SDL-0/LeftShoulder
|
|
R1 = SDL-0/RightShoulder
|
|
L2 = SDL-0/+LeftTrigger
|
|
R2 = SDL-0/+RightTrigger
|
|
L3 = SDL-0/LeftStick
|
|
R3 = SDL-0/RightStick
|
|
Analog = SDL-0/Guide
|
|
LUp = SDL-0/-LeftY
|
|
LRight = SDL-0/+LeftX
|
|
LDown = SDL-0/+LeftY
|
|
LLeft = SDL-0/-LeftX
|
|
RUp = SDL-0/-RightY
|
|
RRight = SDL-0/+RightX
|
|
RDown = SDL-0/+RightY
|
|
RLeft = SDL-0/-RightX
|
|
LargeMotor = SDL-0/LargeMotor
|
|
SmallMotor = SDL-0/SmallMotor
|
|
"""
|
|
|
|
|
|
def pcsx2():
|
|
patch_ini_section(os.path.join(CONFIG, "PCSX2/inis/PCSX2.ini"), "Pad1", PCSX2_PAD1, "PCSX2",
|
|
require="SettingsVersion")
|
|
|
|
|
|
|
|
# Dolphin: SDL/<id>/<SDL gamepad name>; control names from InputCommon/.../SDL/SDLGamepad.h (Left Y+
|
|
# is up). A section that exists is taken whole - keys it lacks are unbound - so both are complete.
|
|
DOLPHIN_GC = """[GCPad1]
|
|
Device = SDL/0/Xbox 360 Controller
|
|
Buttons/A = `Button S`
|
|
Buttons/B = `Button W`
|
|
Buttons/X = `Button E`
|
|
Buttons/Y = `Button N`
|
|
Buttons/Z = `Shoulder R`
|
|
Buttons/Start = `Start`
|
|
Main Stick/Up = `Left Y+`
|
|
Main Stick/Down = `Left Y-`
|
|
Main Stick/Left = `Left X-`
|
|
Main Stick/Right = `Left X+`
|
|
C-Stick/Up = `Right Y+`
|
|
C-Stick/Down = `Right Y-`
|
|
C-Stick/Left = `Right X-`
|
|
C-Stick/Right = `Right X+`
|
|
Triggers/L = `Trigger L`
|
|
Triggers/R = `Trigger R`
|
|
Triggers/L-Analog = `Trigger L`
|
|
Triggers/R-Analog = `Trigger R`
|
|
D-Pad/Up = `Pad N`
|
|
D-Pad/Down = `Pad S`
|
|
D-Pad/Left = `Pad W`
|
|
D-Pad/Right = `Pad E`
|
|
Rumble/Motor = `Motor`
|
|
"""
|
|
DOLPHIN_WII = """[Wiimote1]
|
|
Device = SDL/0/Xbox 360 Controller
|
|
Source = 1
|
|
Buttons/A = `Button S`
|
|
Buttons/B = `Trigger R`
|
|
Buttons/1 = `Button W`
|
|
Buttons/2 = `Button N`
|
|
Buttons/- = `Back`
|
|
Buttons/+ = `Start`
|
|
Buttons/Home = `Guide`
|
|
D-Pad/Up = `Pad N`
|
|
D-Pad/Down = `Pad S`
|
|
D-Pad/Left = `Pad W`
|
|
D-Pad/Right = `Pad E`
|
|
IR/Up = `Right Y+`
|
|
IR/Down = `Right Y-`
|
|
IR/Left = `Right X-`
|
|
IR/Right = `Right X+`
|
|
Shake/X = `Button E`
|
|
Shake/Y = `Button E`
|
|
Shake/Z = `Button E`
|
|
Rumble/Motor = `Motor`
|
|
Extension = Nunchuk
|
|
Nunchuk/Buttons/C = `Shoulder L`
|
|
Nunchuk/Buttons/Z = `Trigger L`
|
|
Nunchuk/Stick/Up = `Left Y+`
|
|
Nunchuk/Stick/Down = `Left Y-`
|
|
Nunchuk/Stick/Left = `Left X-`
|
|
Nunchuk/Stick/Right = `Left X+`
|
|
"""
|
|
|
|
|
|
# ARMSX2 - the PS2 emulator (PCSX2 with ARM64 recompilers; upstream PCSX2 interprets on ARM64). Its
|
|
# files are PCSX2's under $XDG_CONFIG_HOME/ARMSX2 (pcsx2/Pcsx2Config.cpp). The user supplies the BIOS
|
|
# and the games; they live in the ROMs folder (/root/ROMs). The guide button opens the pause menu
|
|
# (Hotkeys OpenPauseMenu) - the way out of a game started from the front end, which runs ARMSX2
|
|
# without its window (-batch -bigpicture) - so it is not also the analog toggle here.
|
|
ARMSX2_PAD1 = "\n".join(l for l in PCSX2_PAD1.splitlines() if not l.startswith("Analog ")) + "\n"
|
|
ROMS = "/root/ROMs"
|
|
|
|
|
|
def ps2_folder():
|
|
for name in ("ps2", "PS2"):
|
|
d = os.path.join(ROMS, name)
|
|
if os.path.isdir(d):
|
|
return d
|
|
return os.path.join(ROMS, "ps2")
|
|
|
|
|
|
def ps2_bios_folder():
|
|
base = ps2_folder()
|
|
for name in ("bios", "BIOS", "firmware"):
|
|
d = os.path.join(base, name)
|
|
if os.path.isdir(d):
|
|
return d
|
|
d = os.path.join(base, "bios")
|
|
if os.path.isdir(ROMS):
|
|
try:
|
|
os.makedirs(d, exist_ok=True) # where the user is told to put their BIOS
|
|
except OSError:
|
|
pass
|
|
return d
|
|
|
|
|
|
def armsx2():
|
|
root = os.path.join(CONFIG, "ARMSX2")
|
|
ini = os.path.join(root, "inis/PCSX2.ini")
|
|
old = os.path.join(CONFIG, "PCSX2")
|
|
if not os.path.isfile(ini) and os.path.isfile(os.path.join(old, "inis/PCSX2.ini")):
|
|
# Someone who played on PCSX2 here: the same settings, BIOS choice and memory cards (the
|
|
# saves), copied once. PCSX2's own folder is left as it was.
|
|
import shutil
|
|
for sub in ("inis", "memcards", "bios", "gamesettings", "cheats", "patches", "covers",
|
|
"inputprofiles", "textures"):
|
|
src = os.path.join(old, sub)
|
|
if os.path.isdir(src):
|
|
shutil.copytree(src, os.path.join(root, sub), dirs_exist_ok=True)
|
|
print(f"bannerlator-pad-defaults: ARMSX2: settings and memory cards copied from PCSX2 ({root})")
|
|
elif not os.path.isfile(ini):
|
|
# A new player: ARMSX2's own first-time setup still runs (SetupWizardIncomplete) to pick the
|
|
# BIOS, but looking in the ROMs folder, with the ps2 folder as its game list and the
|
|
# controller already on Player 1.
|
|
bios, games = ps2_bios_folder(), ps2_folder()
|
|
seed(ini, "[UI]\nSettingsVersion = 1\nSetupWizardIncomplete = true\n\n"
|
|
f"[Folders]\nBios = {bios}\n\n"
|
|
f"[GameList]\nRecursivePaths = {games}\n\n"
|
|
# Vulkan outright rather than Auto's pick: the renderer every other path here uses.
|
|
"[EmuCore/GS]\nRenderer = 14\n\n"
|
|
"[Hotkeys]\nOpenPauseMenu = Keyboard/Escape\nOpenPauseMenu = SDL-0/Guide\n\n"
|
|
"[Pad1]\n" + ARMSX2_PAD1, "ARMSX2", "first-time setup, BIOS from " + bios)
|
|
return
|
|
patch_ini_section(ini, "Pad1", ARMSX2_PAD1, "ARMSX2", require="SettingsVersion")
|
|
# The guide button is the pause menu here; PCSX2's profile (ours, copied over) had it as the
|
|
# analog toggle as well.
|
|
with open(ini, encoding="utf-8", errors="replace") as f:
|
|
lines = f.read().splitlines()
|
|
kept = [l for l in lines if l.replace(" ", "") != "Analog=SDL-0/Guide"]
|
|
if len(kept) != len(lines):
|
|
with open(ini + ".tmp", "w", encoding="utf-8") as f:
|
|
f.write("\n".join(kept) + "\n")
|
|
os.replace(ini + ".tmp", ini)
|
|
patch_ini_section(ini, "Hotkeys", "OpenPauseMenu = SDL-0/Guide\n", "ARMSX2", needle="SDL-0/Guide",
|
|
require="SettingsVersion", done="the guide button opens the pause menu")
|
|
patch_ini_section(ini, "GameList", f"RecursivePaths = {ps2_folder()}\n", "ARMSX2", needle=ROMS,
|
|
require="SettingsVersion", done="games listed from the ROMs folder")
|
|
|
|
|
|
def dolphin():
|
|
d = os.path.join(CONFIG, "dolphin-emu")
|
|
seed(os.path.join(d, "GCPadNew.ini"), DOLPHIN_GC, "Dolphin (GameCube)")
|
|
seed(os.path.join(d, "WiimoteNew.ini"), DOLPHIN_WII, "Dolphin (Wii)")
|
|
|
|
|
|
# Cemu: an SDLController profile for the GamePad. The uuid is "<index>_<SDL GUID>" and SDL's GUID
|
|
# carries a CRC of the device name; this is the one SDL reports for the session's pad
|
|
# ("Xbox 360 Controller (0)", 045e:028e - read from RPCS3's SDL log on the device). Mapping ids
|
|
# from VPADController.h, values from Controller.h, Cemu's own SDL default layout plus Home.
|
|
CEMU_MAP = [(1, 1), (2, 0), (3, 3), (4, 2), (5, 9), (6, 10), (7, 42), (8, 43), (9, 6), (10, 4),
|
|
(11, 11), (12, 12), (13, 13), (14, 14), (15, 7), (16, 8), (17, 45), (18, 39), (19, 44),
|
|
(20, 38), (21, 47), (22, 41), (23, 46), (24, 40), (27, 5)]
|
|
CEMU_XML = """<?xml version="1.0" encoding="UTF-8"?>
|
|
<emulated_controller>
|
|
\t<type>Wii U GamePad</type>
|
|
\t<toggle_display>0</toggle_display>
|
|
\t<controller>
|
|
\t\t<api>SDLController</api>
|
|
\t\t<uuid>0_03005e615e0400008e02000010010000</uuid>
|
|
\t\t<display_name>Xbox 360 Controller</display_name>
|
|
\t\t<rumble>0.5</rumble>
|
|
\t\t<mappings>
|
|
""" + "".join(f"\t\t\t<entry>\n\t\t\t\t<mapping>{m}</mapping>\n\t\t\t\t<button>{b}</button>\n\t\t\t</entry>\n" for m, b in CEMU_MAP) + """\t\t</mappings>
|
|
\t</controller>
|
|
</emulated_controller>
|
|
"""
|
|
|
|
|
|
def cemu():
|
|
seed(os.path.join(CONFIG, "Cemu/controllerProfiles/controller0.xml"), CEMU_XML, "Cemu")
|
|
|
|
|
|
# melonDS 1.x: raw SDL joystick indices. The session's pad has the kernel's Xbox 360 layout
|
|
# (buttons A B X Y LB RB Back Start Guide LS RS = 0..10, hat 0 for the d-pad); the d-pad values
|
|
# also take the left stick (EmuInstanceInput.cpp; encoding InputConfig/MapButton.h).
|
|
MELONDS_JOY = """[Instance0.Joystick]
|
|
A = 1
|
|
B = 0
|
|
X = 3
|
|
Y = 2
|
|
L = 4
|
|
R = 5
|
|
Select = 6
|
|
Start = 7
|
|
Up = 17891585
|
|
Down = 16843012
|
|
Left = 1114376
|
|
Right = 65794
|
|
"""
|
|
|
|
|
|
def melonds():
|
|
path = os.path.join(CONFIG, "melonDS/melonDS.toml")
|
|
if not os.path.isfile(path):
|
|
seed(path, "[Instance0]\nJoystickID = 0\n\n" + MELONDS_JOY, "melonDS")
|
|
return
|
|
with open(path, encoding="utf-8", errors="replace") as f:
|
|
text = f.read()
|
|
try:
|
|
import tomllib
|
|
joy = tomllib.loads(text).get("Instance0", {}).get("Joystick", {})
|
|
except Exception:
|
|
return
|
|
if any(isinstance(v, int) and v != -1 for v in joy.values()):
|
|
return # something is bound already
|
|
# Replace melonDS's own all-unbound table with ours (a TOML table can be defined only once).
|
|
out, skipping = [], False
|
|
for line in text.splitlines():
|
|
t = line.strip()
|
|
if t.startswith("["):
|
|
skipping = t == "[Instance0.Joystick]"
|
|
if not skipping:
|
|
out.append(line)
|
|
with open(path + ".tmp", "w", encoding="utf-8") as f:
|
|
f.write("\n".join(out).rstrip("\n") + "\n\n" + MELONDS_JOY)
|
|
os.replace(path + ".tmp", path)
|
|
print(f"bannerlator-pad-defaults: melonDS: Player 1 on the controller ({path})")
|
|
|
|
|
|
# PPSSPP (SDL): its pad defaults leave the shoulders, the right stick and the triggers unbound on
|
|
# SDL builds (SDLJoystick.cpp codes vs KeyMapDefaults.cpp); a [ControlMapping] that exists replaces
|
|
# every default, so this one is complete, keyboard included (device 1 keyboard, 10 pad).
|
|
PPSSPP_CONTROLS = """[ControlMapping]
|
|
Up = 1-19,10-19
|
|
Down = 1-20,10-20
|
|
Left = 1-21,10-21
|
|
Right = 1-22,10-22
|
|
Cross = 1-54,10-189
|
|
Circle = 1-52,10-190
|
|
Square = 1-29,10-191
|
|
Triangle = 1-47,10-188
|
|
Start = 1-62,10-197
|
|
Select = 1-66,10-196
|
|
L = 1-45,10-193
|
|
R = 1-51,10-192
|
|
An.Up = 1-37,10-4003
|
|
An.Down = 1-39,10-4002
|
|
An.Left = 1-38,10-4001
|
|
An.Right = 1-40,10-4000
|
|
RightAn.Up = 10-4007
|
|
RightAn.Down = 10-4006
|
|
RightAn.Left = 10-4005
|
|
RightAn.Right = 10-4004
|
|
Pause = 1-111,10-4
|
|
"""
|
|
|
|
|
|
def ppsspp():
|
|
seed(os.path.join(CONFIG, "ppsspp/PSP/SYSTEM/controls.ini"), PPSSPP_CONTROLS, "PPSSPP")
|
|
|
|
|
|
# RetroArch: the udev joypad driver finds nothing here (no udev database in the runtime); sdl2
|
|
# does, and matches this profile by vendor/product (task_autodetect.c).
|
|
RETROARCH_PROFILE = """input_driver = "sdl2"
|
|
input_device = "Xbox 360 Controller"
|
|
input_vendor_id = "1118"
|
|
input_product_id = "654"
|
|
input_b_btn = "0"
|
|
input_a_btn = "1"
|
|
input_y_btn = "2"
|
|
input_x_btn = "3"
|
|
input_select_btn = "4"
|
|
input_menu_toggle_btn = "5"
|
|
input_start_btn = "6"
|
|
input_l3_btn = "7"
|
|
input_r3_btn = "8"
|
|
input_l_btn = "9"
|
|
input_r_btn = "10"
|
|
input_up_btn = "11"
|
|
input_down_btn = "12"
|
|
input_left_btn = "13"
|
|
input_right_btn = "14"
|
|
input_l2_axis = "+4"
|
|
input_r2_axis = "+5"
|
|
input_l_x_plus_axis = "+0"
|
|
input_l_x_minus_axis = "-0"
|
|
input_l_y_plus_axis = "+1"
|
|
input_l_y_minus_axis = "-1"
|
|
input_r_x_plus_axis = "+2"
|
|
input_r_x_minus_axis = "-2"
|
|
input_r_y_plus_axis = "+3"
|
|
input_r_y_minus_axis = "-3"
|
|
"""
|
|
|
|
|
|
def retroarch():
|
|
d = os.path.join(CONFIG, "retroarch")
|
|
seed(os.path.join(d, "autoconfig/sdl2/Xbox 360 Controller.cfg"), RETROARCH_PROFILE, "RetroArch (profile)")
|
|
cfg = os.path.join(d, "retroarch.cfg")
|
|
if not os.path.isfile(cfg):
|
|
seed(cfg, 'input_joypad_driver = "sdl2"\ninput_autodetect_enable = "true"\n', "RetroArch")
|
|
return
|
|
with open(cfg, encoding="utf-8", errors="replace") as f:
|
|
lines = f.read().splitlines()
|
|
changed = False
|
|
for i, l in enumerate(lines):
|
|
if l.replace(" ", "").startswith('input_joypad_driver="udev"'):
|
|
lines[i] = 'input_joypad_driver = "sdl2"'
|
|
changed = True
|
|
if changed:
|
|
with open(cfg + ".tmp", "w", encoding="utf-8") as f:
|
|
f.write("\n".join(lines) + "\n")
|
|
os.replace(cfg + ".tmp", cfg)
|
|
print(f"bannerlator-pad-defaults: RetroArch: joypad driver udev -> sdl2 ({cfg})")
|
|
|
|
|
|
for step in (rpcs3, duckstation, pcsx2, armsx2, dolphin, cemu, melonds, ppsspp, retroarch):
|
|
try:
|
|
step()
|
|
except Exception as e:
|
|
print(f"bannerlator-pad-defaults: {step.__name__}: {e}")
|