diff --git a/services/wolf.sh b/services/wolf.sh index 19d48d2..2db65fb 100644 --- a/services/wolf.sh +++ b/services/wolf.sh @@ -4071,9 +4071,50 @@ except Exception: ls "$_SANG_EMU_DIR" 2>/dev/null exit 1 fi - _SANG_EXE="/home/retro/Applications/$(basename "$_SANG_HOST_FILE")" + _SANG_REAL_EXE="/home/retro/Applications/$(basename "$_SANG_HOST_FILE")" [ -z "$_SANG_NAME" ] && _SANG_NAME=$(basename "$_SANG_HOST_FILE" | sed -E 's/\.(AppImage|sh|x86_64)$//I') + # Route the actual launch through a small fullscreen-forcing wrapper + # instead of pointing Steam's Exe directly at the AppImage. Why: a + # non-Steam game launched from Steam is a SECOND top-level window in + # Wolf's single-app Steam Sway session (RUN_SWAY=true on the 'steam' + # CATALOG entry) — Sway's own kiosk config only auto-fullscreens the + # ONE window it expects (Steam's own), so anything launched from + # inside Steam opens at whatever default size it requests instead, + # which reads as roughly half the screen against a full + # Moonlight-resolution display. Reported live against ES-DE/Dolphin + # added this way. NOT yet confirmed live that swaymsg actually + # reaches Sway from inside this exact container/session — if it + # doesn't, the wrapper's fullscreen loop just fails silently + # (2>/dev/null below) and the app launches exactly as it did before + # this existed, so this is safe to try without risking the launch + # itself. Regenerated on every call (cheap, stateless — nothing to + # lose by overwriting it) so a fix to the wrapper reaches every + # existing shortcut the next time it's (re-)added, not just new ones. + _SANG_WRAP="$_SANG_EMU_DIR/steam-fullscreen-wrap" + cat > "$_SANG_WRAP" << 'WRAPEOF' +#!/bin/bash +# steam-fullscreen-wrap [args...] +# Launches the real target, then repeatedly asks Sway to fullscreen whatever +# currently has input focus for a few seconds after launch — the newly +# launched window is expected to grab focus once it maps, same as any +# ordinary X11/Wayland client. 'fullscreen enable' (not 'toggle') is +# idempotent, so repeating it while the window is already fullscreen is a +# harmless no-op rather than flipping it back off. +REAL_BIN="$1"; shift +"$REAL_BIN" "$@" & +PID=$! +( + for _i in $(seq 1 20); do + sleep 0.5 + swaymsg fullscreen enable 2>/dev/null + done +) & +wait "$PID" +WRAPEOF + chmod +x "$_SANG_WRAP" + _SANG_EXE="/home/retro/Applications/steam-fullscreen-wrap" + STEAM_HOME=$(_steam_home) if [ -z "$STEAM_HOME" ]; then echo "No Steam home found yet under ${WOLF_STATE_DIR:-/etc/wolf}." @@ -4100,10 +4141,10 @@ except Exception: sleep 3 fi - sudo python3 - "$_SANG_VDF" "$_SANG_EXE" "$_SANG_NAME" "/home/retro/Applications" << 'VDFPY' + sudo python3 - "$_SANG_VDF" "$_SANG_EXE" "$_SANG_NAME" "/home/retro/Applications" "$_SANG_REAL_EXE" << 'VDFPY' import sys, struct, os, zlib -path, exe_path, app_name, start_dir = sys.argv[1:5] +path, exe_path, app_name, start_dir, real_exe = sys.argv[1:6] TYPE_MAP, TYPE_STR, TYPE_INT, TYPE_END = 0x00, 0x01, 0x02, 0x08 @@ -4167,13 +4208,30 @@ if shortcuts_entry is None: entries_list = shortcuts_entry[2] +# exe_path now points at the shared steam-fullscreen-wrap script (same Exe +# for every emulator added this way), with the real per-emulator target +# carried in LaunchOptions instead — so matching (for both idempotent +# re-adds and appid uniqueness) has to key on the (exe, LaunchOptions) PAIR, +# not exe alone, or adding a second emulator would silently overwrite the +# first one's shortcut entry. Also cleans up a legacy entry from before this +# wrapper existed, where Exe pointed directly at this same real binary with +# no LaunchOptions at all — re-adding an emulator added under the old +# scheme replaces that stale direct-launch entry instead of leaving a +# duplicate tile behind. quoted_exe = f'"{exe_path}"' -entries_list[:] = [e for e in entries_list if get_field(e[2], 'exe') != quoted_exe] +quoted_launch = f'"{real_exe}"' +entries_list[:] = [ + e for e in entries_list + if not ( + (get_field(e[2], 'exe') == quoted_exe and get_field(e[2], 'LaunchOptions') == quoted_launch) + or get_field(e[2], 'exe') == quoted_launch + ) +] for idx, e in enumerate(entries_list): e[0] = str(idx) new_index = str(len(entries_list)) -crc_input = (exe_path + app_name).encode('utf-8') +crc_input = (exe_path + real_exe + app_name).encode('utf-8') appid = (zlib.crc32(crc_input) | 0x80000000) & 0xFFFFFFFF appid_signed = appid - 0x100000000 if appid >= 0x80000000 else appid @@ -4184,7 +4242,7 @@ new_entry_fields = [ ['StartDir', TYPE_STR, f'"{start_dir}"'], ['icon', TYPE_STR, ''], ['ShortcutPath', TYPE_STR, ''], - ['LaunchOptions', TYPE_STR, ''], + ['LaunchOptions', TYPE_STR, quoted_launch], ['IsHidden', TYPE_INT, 0], ['AllowDesktopConfig', TYPE_INT, 1], ['AllowOverlay', TYPE_INT, 1], @@ -4200,12 +4258,12 @@ entries_list.append([new_index, TYPE_MAP, new_entry_fields]) with open(path, 'wb') as f: f.write(serialize_map(root)) -print(f"Wrote shortcuts.vdf: {app_name} -> {exe_path} (appid {appid_signed})") +print(f"Wrote shortcuts.vdf: {app_name} -> {real_exe} (via {exe_path}, appid {appid_signed})") VDFPY sudo chown 1000:1000 "$_SANG_VDF" echo "" - echo "Added '$_SANG_NAME' -> $_SANG_EXE to Steam's shortcuts.vdf." + echo "Added '$_SANG_NAME' -> $_SANG_REAL_EXE (via the fullscreen wrapper) to Steam's shortcuts.vdf." echo "Open Steam in Moonlight (it will restart since it was stopped above) —" echo "the new tile appears in your Library (may need the Library view, not just Home)." echo "First launch: right-click it -> Properties -> Compatibility, and confirm" @@ -4701,7 +4759,26 @@ CATALOG = { # (see below) — without it, an emulator AppImage (Cemu, etc.) # added as a non-Steam game (./manage.sh steam-add-nonsteam-game) # has no file to actually point Exe at from inside this container. - f'{games}/emulators:/home/retro/Applications:rw'], + f'{games}/emulators:/home/retro/Applications:rw', + # Same roms/saves/bios/retro-home/retroarch mounts as esde/ + # retroarch below — without these, a standalone ES-DE or + # RetroArch AppImage added here as a non-Steam game (same + # mechanism as Cemu above) would see none of the ROMs, cores, + # save states, BIOS files, or ES-DE's own settings/custom + # systems (TI-99, Wii U AntiMicroX command) that the esde/ + # retroarch containers already have — it'd start from a + # completely empty config instead of reusing what's already + # set up. Every path here is the exact same host directory + # those two containers mount, just also visible from Steam. + f'{games}/roms:/ROMs:rw', + f'{games}/saves:/mnt/games/saves:rw', + f'{games}/media:/media:rw', + f'{games}/bios:/home/retro/bioses:rw', + f'{games}/retro-home:/home/retro/.config:rw', + f'{games}/retro-home-data:/home/retro/.local/share:rw', + f'{games}/retroarch:/home/retro/.config/retroarch:rw', + f'{games}/esde-custom-systems:/home/retro/ES-DE/custom_systems:rw', + f'{games}/esde-settings:/home/retro/ES-DE/settings:rw'], env=['PROTON_LOG=1', 'RUN_SWAY=true', 'GOW_REQUIRED_DEVICES=/dev/input/* /dev/dri/* /dev/nvidia*'], cap_add=['SYS_ADMIN', 'SYS_NICE', 'SYS_PTRACE', 'NET_RAW', 'MKNOD', 'NET_ADMIN'], @@ -5295,10 +5372,60 @@ controllers per player slot even when the physical controllers are identical models (the same case \`./manage.sh controllers\` exists to work around for ES-DE). Add Cemu here, then in Steam's own Big Picture Controller Settings you can see and assign each detected controller -individually. This hasn't been confirmed live yet against Wolf's -container-created virtual controllers specifically (real hardware behaves -this way; whether Steam Input sees Wolf's virtual joypads the same way is -still to be tested) — worth trying before assuming it works. +individually. + +**Controller doesn't respond at all once added to Steam?** Confirmed live: +this is Steam Input, not the emulator — by default Steam applies its own +controller configuration to *every* shortcut, Steam or non-Steam alike, +and a freshly-added non-Steam game with no configuration picked yet can +end up with no usable mapping at all rather than passing raw input +through. Fix from Steam's own Big Picture UI: select the shortcut → the +controller icon / **Manage Game** → **Controller Options**, and either +pick a **Gamepad** template (closest to "pass it through as a normal +joystick", needed for ES-DE/RetroArch's own native SDL input handling to +see it at all) or turn **Steam Input** off for that one shortcut entirely +if you don't need the per-device assignment this section is about in the +first place. This is a one-time, per-shortcut setting Steam remembers — +not something this installer can preconfigure from the command line, since +it's stored in Steam's own (separate, Steam Cloud-synced) controller +config rather than \`shortcuts.vdf\`. + +**Opens at roughly half the screen instead of fullscreen?** Also confirmed +live, and now worked around automatically as of the version of this repo +that added the point below — \`steam-add-nonsteam-game\`/ +\`steam-setup-frontends\` route the launch through a small wrapper +(\`emulators/steam-fullscreen-wrap\`) instead of pointing Steam's \`Exe\` +directly at the AppImage. The underlying cause: a non-Steam game launched +from Steam is a *second* top-level window inside Wolf's single-app Steam +Sway session, and Sway's own kiosk config only auto-fullscreens the ONE +window it expects (Steam's own) — anything launched from inside Steam +opens at whatever default size it requests instead. The wrapper launches +the real binary, then repeatedly asks Sway to fullscreen whatever currently +has focus for a few seconds after launch (idempotent — harmless if it's +already fullscreen). **This has not been confirmed live against a real +Wolf Steam session yet** — if \`swaymsg\` isn't reachable from inside that +exact container, the wrapper's fullscreen loop just fails silently and the +window opens at its old default size, same as before this existed; it's +safe to try either way since the underlying launch itself is unaffected. +Re-run \`steam-add-nonsteam-game\`/\`steam-setup-frontends\` for anything +already added before this existed — it replaces the old direct-launch +shortcut with the wrapped one automatically, no duplicate tile left behind. + +**An added emulator's tile appears in Steam but crashes on launch instead +of opening at all** (as opposed to the half-screen case above, where it +does open): that's a different problem — something about the AppImage +itself failing to run inside the Steam container specifically, since the +same binary launching fine through the \`esde\`/\`desktop\` Wolf apps rules out +the AppImage being broken outright. Worth checking before assuming it's +unfixable: +\`\`\`bash +CONTAINER=\$(docker ps --format '{{.Names}}' | grep -i WolfSteam | head -1) +docker exec "\$CONTAINER" /home/retro/Applications/ ; echo "exit: \$?" +\`\`\` +Run directly like this (no wrapper, no Steam involved) the real error +usually prints straight to the terminal — a missing shared library, a +FUSE/AppImage mount failure (\`dlopen(): error loading libfuse.so.2\`), +or similar. That output is what actually narrows down the fix. ## Running ES-DE and/or RetroArch through Steam instead of their own Wolf apps The same Steam Input reasoning above applies beyond just Cemu: ES-DE and