Compare commits

..
4 Commits
Author SHA1 Message Date
Outis ce9a8e8c5b Merge pull request #409 from outis1one/claude/wolf-pair-port-conflict-7nz8qg
Claude/wolf pair port conflict 7nz8qg
2026-08-31 22:45:22 -04:00
Claude bef88e654d Fix two bugs in the AntiMicroX plumbing before it ever runs
1. Glob case mismatch that would have silently no-op'd the whole
   feature: the real GitHub release asset is AntiMicroX-x86_64.AppImage
   (capitalized) — confirmed against the actual release, not assumed.
   Both the helper's own already-downloaded check and this file's own
   post-download lookup used a lowercase-only "antimicrox*.AppImage"
   glob (copied from Azahar's/PCSX2's pattern, which really are
   lowercase). Linux glob matching is case-sensitive, so _AMX_APP would
   have come back empty even after a successful download, hitting the
   "didn't produce a usable AppImage" branch and silently skipping the
   Sway launch hook with the AppImage sitting there unused.

2. Sway match criteria used app_id, which only matches native Wayland
   clients. AntiMicroX is a Qt5/X11 app running under XWayland here —
   same as ES-DE and Steam already are in this exact stock config,
   which is why their own for_window rules use [class="..."], not
   app_id. Switched to class (and title as a fallback, in case the
   exact WM_CLASS AntiMicroX registers differs), with a (?i) case-
   insensitive prefix — confirmed sway uses PCRE2 for criteria matching
   and (?i) is the documented syntax for exactly this, not assumed.

Neither bug was fatal to Wolf itself (both fail quietly rather than
crashing anything), but both would have cost real troubleshooting time
for something that looked like it should just work.
2026-09-01 02:32:45 +00:00
Claude f087984526 Add AntiMicroX plumbing for a universal controller-combo hotkey
Wires up the infrastructure for RetroArch's own hotkeys (global across
every libretro core already) to be matched by an equivalent for
Dolphin/PCSX2/Azahar, none of which support gamepad-bound hotkeys
natively (confirmed open feature requests: PCSX2/pcsx2#1082,
azahar-emu/azahar#722; Dolphin's are hardcoded to keyboard only).

AntiMicroX watches Wolf's virtual gamepad directly — a real uinput
device per Wolf's own "inputtino" docs, not a proprietary channel —
and injects whatever keyboard shortcut the focused emulator actually
expects.

Mechanism: GoW's own stock Sway config (baked into the esde/retroarch
images) has a deliberate first-line extension point,
`include /home/retro/.config/sway/custom-cfg` (confirmed by reading it
directly out of a running container). Since /home/retro/.config is
already the retro-home mount added earlier this session, writing a
file to $GAME_STORAGE_DIR/retro-home/sway/custom-cfg on the host lands
exactly there in both containers — zero changes to GoW's own image or
scripts needed.

Deliberately scoped to plumbing only: download AntiMicroX (reusing the
_wolf_download_emulator_appimage helper added for PCSX2/Dolphin), add
the Sway launch hook (floating window, not hidden, so it's reachable
through the Moonlight stream), and document the actual one-time setup
flow in the generated README. The button mapping itself, including
AntiMicroX's own per-application Auto Profile switching, is built
through its real GUI rather than a hand-authored .gamecontroller.amgp
this repo can't verify blind.

Marked experimental throughout (install prompt defaults to n, DRY-RUN
summary, README) — this is unverified against a live session.
2026-09-01 02:24:29 +00:00
Claude 1ed7fe89ea Add PCSX2 and Dolphin to wolf.sh's auto-download emulators
Factored the existing Azahar-only download block into a reusable
_wolf_download_emulator_appimage() helper (same fetch-latest-release-
and-find-.AppImage-asset logic, parameterized), then reused it for:

- PCSX2 (PS2) — officially publishes Linux AppImages via GitHub
  Releases (PCSX2/pcsx2), same pattern as Azahar.
- Dolphin (GameCube/Wii) — dolphin-emu.org's own Linux distribution
  is Flatpak-only, no official AppImage at all. Uses the well-regarded
  community AppImage build (pkgforge-dev/Dolphin-emu-AppImage)
  instead, with explicit warnings before the prompt and in the
  generated README that this is a third-party build, not an official
  Dolphin release — so the user can decide knowingly rather than this
  silently substituting an unofficial build for what looks like an
  official option.

Motivated by a live "could not write to/read from Wii system memory"
error — GameCube/Wii isn't handled by a RetroArch libretro core in any
stable way (Dolphin's libretro core is unstable for Wii specifically),
so ES-DE needs a real standalone Dolphin binary to hand those systems
off to, the same way it already does for 3DS via Azahar.

Also updated the DRY-RUN summary and the generated README's emulators
section to describe all three instead of just Azahar.
2026-08-31 23:44:24 +00:00
+199 -38
View File
@@ -205,6 +205,45 @@ fi
register_service wolf gaming "Cloud gaming via Moonlight (Games-on-Whales Wolf)" 47989
# Downloads the latest GitHub-released AppImage for a standalone emulator into
# $EMU_DIR (skipped if a matching file is already there). AppImages dropped
# there are found automatically by ES-DE's app finder (it checks
# ~/Applications inside the container, mounted from emulators/) — this covers
# every system ES-DE hands off to a standalone frontend instead of a
# RetroArch libretro core (3DS, GameCube/Wii, PS2, ...). Same shape as the
# original Azahar-only download this was factored out of, just parameterized
# so PCSX2/Dolphin/anything else reuse the same fetch-latest-release logic
# instead of copy-pasting it per emulator.
_wolf_download_emulator_appimage() {
local _display_name="$1" _owner_repo="$2" _existing_glob="$3" _dir="$4"
if ls "$_dir"/$_existing_glob 2>/dev/null | grep -q .; then
log_info "$_display_name already present in $_dir/"
return 0
fi
local _get=""
echo ""
log_info "$_display_name can be auto-downloaded."
prompt_yn "Download $_display_name AppImage now? (y/n):" "y" _get
[[ "$_get" =~ ^[Yy]$ ]] || return 0
log_info "Fetching latest $_display_name release from GitHub..."
local _url
_url=$(curl -fsSL "https://api.github.com/repos/${_owner_repo}/releases/latest" \
| python3 -c "import sys,json; r=json.load(sys.stdin); \
print(next((a['browser_download_url'] for a in r['assets'] \
if a['name'].endswith('.AppImage')), ''))" 2>/dev/null)
if [[ -z "$_url" ]]; then
log_warning "Could not resolve download URL — get it manually from https://github.com/${_owner_repo}/releases"
return 1
fi
local _file="$_dir/$(basename "$_url")"
curl -fL --progress-bar -o "$_file" "$_url" \
&& chmod +x "$_file" \
&& chown "$ACTUAL_USER:$ACTUAL_USER" "$_file" \
&& log_success "$_display_name downloaded: $_file" \
|| log_warning "Download failed — get it manually from https://github.com/${_owner_repo}/releases"
}
install_wolf() {
require_docker || return 1
@@ -237,6 +276,11 @@ EOF
echo " - Open Moonlight UFW ports: TCP ${WOLF_PORTS_TCP[*]} / UDP ${WOLF_PORTS_UDP[*]}"
echo " - Start Wolf and inject Steam + EmulationStation app profiles"
echo " - Create bios/ + retroarch/{cores,shaders,overlays}/ on the game drive and pre-download RetroArch cores (~1.5 GB)"
echo " - Offer to auto-download standalone emulator AppImages into emulators/:"
echo " Azahar (3DS), PCSX2 (PS2), Dolphin (GameCube/Wii — unofficial community build)"
echo " - Offer to set up AntiMicroX (universal controller-combo hotkey, experimental —"
echo " downloads it and wires a Sway launch hook; the button mapping itself is"
echo " built later through AntiMicroX's own GUI)"
return 0
fi
@@ -771,36 +815,110 @@ UDEV
log_success "Storage layout: $GAME_STORAGE_DIR/{roms/<system>/,steam,saves,media,emulators,...}"
log_info "ES-DE ROM directories pre-created. Drop ROMs in the matching subfolder."
# ── Optional: download Azahar (3DS emulator) ─────────────────────────────
# Azahar is an open-source Citra fork — the recommended legal 3DS emulator
# for ES-DE. AppImages dropped in emulators/ are found automatically by
# ES-DE's app finder (checks ~/Applications inside the container).
local _AZAHAR_DIR="$GAME_STORAGE_DIR/emulators"
if ! ls "$_AZAHAR_DIR"/azahar*.AppImage 2>/dev/null | grep -q .; then
local _get_az=""
echo ""
log_info "3DS emulation: Azahar (open-source Citra fork) can be auto-downloaded."
prompt_yn "Download Azahar 3DS emulator AppImage now? (y/n):" "y" _get_az
if [[ "$_get_az" =~ ^[Yy]$ ]]; then
log_info "Fetching latest Azahar release from GitHub..."
local _AZ_URL
_AZ_URL=$(curl -fsSL "https://api.github.com/repos/azahar-emu/azahar/releases/latest" \
| python3 -c "import sys,json; r=json.load(sys.stdin); \
print(next((a['browser_download_url'] for a in r['assets'] \
if a['name'].endswith('.AppImage')), ''))" 2>/dev/null)
if [[ -n "$_AZ_URL" ]]; then
local _AZ_FILE="$_AZAHAR_DIR/$(basename "$_AZ_URL")"
curl -fL --progress-bar -o "$_AZ_FILE" "$_AZ_URL" \
&& chmod +x "$_AZ_FILE" \
&& chown "$ACTUAL_USER:$ACTUAL_USER" "$_AZ_FILE" \
&& log_success "Azahar downloaded: $_AZ_FILE" \
|| log_warning "Download failed — get it manually from https://github.com/azahar-emu/azahar/releases"
else
log_warning "Could not resolve download URL — get it manually from https://github.com/azahar-emu/azahar/releases"
fi
# ── Optional: download standalone emulator AppImages ─────────────────────
# AppImages dropped in emulators/ are found automatically by ES-DE's app
# finder (checks ~/Applications inside the container). These are the
# systems ES-DE hands off to a standalone frontend rather than a
# RetroArch libretro core (Dolphin's libretro core in particular is
# unstable for Wii — confirmed live: "could not write to/read from Wii
# system memory" errors go away once a real standalone Dolphin build is
# present instead).
local _EMU_DIR="$GAME_STORAGE_DIR/emulators"
_wolf_download_emulator_appimage \
"Azahar (3DS — open-source Citra fork)" "azahar-emu/azahar" "azahar*.AppImage" "$_EMU_DIR"
_wolf_download_emulator_appimage \
"PCSX2 (PS2)" "PCSX2/pcsx2" "pcsx2*.AppImage" "$_EMU_DIR"
# Dolphin itself ships no official Linux AppImage — dolphin-emu.org's own
# Linux distribution is Flatpak-only. pkgforge-dev/Dolphin-emu-AppImage is
# a well-regarded THIRD-PARTY community build, not an official Dolphin
# release — flagged explicitly rather than silently offered as if it were
# one, so skip it and grab Dolphin's own Flatpak/build by hand instead if
# you'd rather not run an unofficial build.
if ! ls "$_EMU_DIR"/*[Dd]olphin*.AppImage 2>/dev/null | grep -q .; then
log_warning "Dolphin (GameCube/Wii) has no official Linux AppImage (dolphin-emu.org ships Flatpak only)."
log_warning "The option below is a well-regarded but THIRD-PARTY community build (pkgforge-dev), not"
log_warning "an official Dolphin release."
fi
_wolf_download_emulator_appimage \
"Dolphin (GameCube/Wii, community AppImage build)" "pkgforge-dev/Dolphin-emu-AppImage" "*[Dd]olphin*.AppImage" "$_EMU_DIR"
# ── Optional: AntiMicroX for a universal controller-combo hotkey ─────────
# RetroArch's own hotkeys (Settings -> Input -> Input Hotkey Binds) are
# global across every libretro core, but Dolphin/PCSX2/Azahar each run as
# standalone apps ES-DE launches directly and none of the three support
# gamepad-bound hotkeys natively (confirmed open feature requests:
# PCSX2/pcsx2#1082, azahar-emu/azahar#722; Dolphin's hotkeys are
# hardcoded to keyboard only). AntiMicroX bridges this by watching the
# real controller device (Wolf's virtual gamepad is a genuine uinput
# device — see games-on-whales/wolf's own docs on "inputtino" — so this
# is a real input device AntiMicroX can open, not a proprietary channel)
# and injecting whatever keyboard shortcut each app actually expects.
#
# GoW's own stock Sway config (/cfg/sway/config, baked into the esde and
# retroarch images — same one both apps' `launcher()` call copies) has a
# deliberate extension point at its very first line:
# include /home/retro/.config/sway/custom-cfg
# Since /home/retro/.config is already our own retro-home mount (see the
# esde/retroarch CATALOG mounts above), just writing a file to
# $GAME_STORAGE_DIR/retro-home/sway/custom-cfg on the host lands exactly
# there in the container, for both apps, with zero changes to GoW's own
# image or scripts needed.
#
# This only wires up the plumbing (download AntiMicroX, launch it
# visibly as a floating window every session). The actual button
# mapping — including AntiMicroX's own per-application "Auto Profile"
# switching (Options -> Settings -> Auto Profile in its GUI) needed to
# send a different key to each emulator from the same physical combo —
# has to be built once through AntiMicroX's own GUI while it's running;
# hand-authoring its .gamecontroller.amgp profile XML blind isn't
# something to guess at. Whatever gets saved there persists automatically
# (same retro-home mount), so it's a one-time setup.
local _AMX_DIR="$GAME_STORAGE_DIR/retro-home/antimicrox"
local _SWAY_CFG_DIR="$GAME_STORAGE_DIR/retro-home/sway"
mkdir -p "$_AMX_DIR" "$_SWAY_CFG_DIR"
echo ""
log_info "AntiMicroX lets you map one controller combo (e.g. Select+Start) to a"
log_info "different real keyboard shortcut per emulator — RetroArch's own hotkeys"
log_info "already cover every RetroArch-driven system; this is for Dolphin/PCSX2/"
log_info "Azahar, none of which support gamepad hotkeys natively. EXPERIMENTAL:"
log_info "the actual button mapping has to be built once through its own GUI."
local _GET_AMX=""
prompt_yn "Set up AntiMicroX for a universal controller hotkey? (y/n):" "n" _GET_AMX
if [[ "$_GET_AMX" =~ ^[Yy]$ ]]; then
# Real release asset is "AntiMicroX-x86_64.AppImage" (capitalized) —
# confirmed against the actual GitHub release, not assumed. A plain
# lowercase glob (like Azahar's/PCSX2's, which really are lowercase)
# would silently never match this one, on both the helper's own
# already-downloaded check and the lookup below.
_wolf_download_emulator_appimage \
"AntiMicroX" "AntiMicroX/antimicrox" "[Aa]nti[Mm]icro[Xx]*.AppImage" "$_AMX_DIR"
local _AMX_APP
_AMX_APP=$(ls "$_AMX_DIR"/[Aa]nti[Mm]icro[Xx]*.AppImage 2>/dev/null | head -1)
if [[ -n "$_AMX_APP" ]]; then
backup_if_exists "$_SWAY_CFG_DIR/custom-cfg"
cat > "$_SWAY_CFG_DIR/custom-cfg" << CFGEOF
# Written by services/wolf.sh — AntiMicroX: universal controller-combo hotkey.
# Launched as a normal floating window (not hidden) so its own GUI is
# reachable through the Moonlight stream for one-time profile setup —
# see ~/docker/wolf/README.md for how to build the actual button mapping.
#
# AntiMicroX is a Qt5/X11 app running under XWayland here, same as ES-DE and
# Steam in this same stock config (see their own [class="..."] rules above) —
# app_id only matches native Wayland clients and would never match this one.
# Matched on both class and title as a fallback in case the exact WM_CLASS
# AntiMicroX registers turns out to differ from what's assumed here.
for_window [class="(?i)antimicrox"] floating enable, resize set 480 360, move position 20 20
for_window [title="(?i)antimicrox"] floating enable, resize set 480 360, move position 20 20
exec /home/retro/.config/antimicrox/$(basename "$_AMX_APP") --appimage-extract-and-run &
CFGEOF
chown -R 1000:1000 "$_AMX_DIR" "$_SWAY_CFG_DIR" 2>/dev/null || true
log_success "AntiMicroX will launch (as a small floating window) alongside ES-DE/RetroArch."
log_info "First session: open its window, map your combo, save — it persists from then on."
else
log_warning "AntiMicroX download didn't produce a usable AppImage — skipping the Sway launch hook."
fi
else
log_info "Azahar already present in $GAME_STORAGE_DIR/emulators/"
fi
# ── App selection ─────────────────────────────────────────────────────────
@@ -3055,7 +3173,7 @@ Two things live on the game drive (\`$GAME_STORAGE_DIR\`):
- \`retroarch/shaders/\` → ~/.config/retroarch/shaders (shaders/CRT filters — persist)
- \`retroarch/overlays/\` → ~/.config/retroarch/overlays (bezels/overlays — persist)
- \`saves/\` → /mnt/games/saves (RetroArch saves & states)
- \`emulators/\` → /mnt/games/emulators + ~/Applications (Azahar 3DS AppImage, etc.)
- \`emulators/\` → /mnt/games/emulators + ~/Applications (Azahar/PCSX2/Dolphin AppImages)
**2. \`wolf-state/\`** — Wolf's entire state folder (\`WOLF_STATE_DIR\` in
\`.env\`). This is the key to keeping games off the OS drive: it holds
@@ -3096,14 +3214,57 @@ ES-DE is usable the moment you launch it; only ROMs and BIOS are yours to add.
- **Exit a game** back to ES-DE: Start+Select opens the RetroArch menu → Quit,
or use the Wolf hotkey START+UP+RB (Ctrl+Alt+Shift+W on a keyboard).
## 3DS emulation
Azahar (open-source Citra fork) AppImage lives in \`emulators/\` → mounted at
\`/mnt/games/emulators\` and \`~/Applications\` (where ES-DE's app finder looks).
Point ES-DE's 3DS system at it via Alternative Emulators if it isn't
auto-detected. Re-download with:
\`\`\`bash
cd $WOLF_DIR && ./manage.sh apps
\`\`\`
## Standalone emulators (3DS / PS2 / GameCube+Wii)
RetroArch's own libretro cores handle these systems poorly or not at all
(Dolphin's libretro core in particular is unstable for Wii), so ES-DE hands
them off to standalone AppImages instead:
- **Azahar** (3DS, open-source Citra fork) — official releases
- **PCSX2** (PS2) — official releases
- **Dolphin** (GameCube/Wii) — Dolphin itself ships no official Linux
AppImage (dolphin-emu.org's own Linux distribution is Flatpak-only); this
uses a well-regarded but **third-party** community AppImage build
(pkgforge-dev) instead. Skip it and install Dolphin's own Flatpak by hand
if you'd rather not run an unofficial build.
All three live in \`emulators/\` → mounted at \`/mnt/games/emulators\` and
\`~/Applications\` (where ES-DE's app finder looks). Point ES-DE's
3DS/PS2/GameCube/Wii systems at one via Alternative Emulators if it isn't
auto-detected. Missing ones get offered again on a fresh install
(\`sudo ./setup.sh wolf\`); to grab just one by hand, download its AppImage
into \`emulators/\`, \`chmod +x\` it.
## AntiMicroX — universal controller-combo hotkey (experimental)
RetroArch's own hotkeys (Settings -> Input -> Input Hotkey Binds) already
cover every RetroArch-driven system, but Dolphin/PCSX2/Azahar don't support
gamepad-bound hotkeys natively — AntiMicroX watches the controller directly
and injects whatever keyboard shortcut each app actually expects, so one
physical combo (e.g. hold Select, press Start) can mean "exit" — or save
state, or anything else — everywhere.
If you said yes to the AntiMicroX prompt during install, it launches
automatically as a small floating window every time ES-DE or RetroArch
starts. **Building the actual mapping is a one-time step, done through its
own GUI, not something this installer can pre-configure:**
1. Start ES-DE (or RetroArch) from Moonlight as normal — AntiMicroX's small
window appears floating in the corner alongside it.
2. In AntiMicroX: set up your controller under its device list, then create
a profile — click a button in its UI, press the real combo on your
gamepad, assign it the keyboard key the target emulator expects for that
action (see each emulator's own Settings -> Input/Hotkeys for its actual
bindings — Dolphin/PCSX2 defaults are commonly F1-F4-range keys).
3. For a **different translation per emulator** from the same physical
combo, use AntiMicroX's own **Options -> Settings -> Auto Profile** —
add a rule per application (matched by window class/title) so e.g.
Select+Start sends Esc when Dolphin has focus but F4 when RetroArch does.
4. Save. It's written under \`~/.config/antimicrox/\` inside the container,
which is the same \`retro-home/\` mount everything else here persists
through — it'll auto-load on every future session with no further setup.
To change the mapping later, just reopen AntiMicroX the same way. To remove
it entirely, delete \`retro-home/sway/custom-cfg\` and \`retro-home/antimicrox/\`
on the host and re-run \`sudo ./setup.sh wolf\`.
## Troubleshooting: app exits immediately ("Permission denied")
If a launcher (Steam, etc.) closes the moment it opens, its Wolf-managed home