manage.sh controllers: poll Wolf's logs, label slots ordinally; document Cemu profile-editing recovery
Two follow-ups from live testing that just confirmed the whole controllers_override chain works end to end: 1. manage.sh controllers now polls `docker compose logs wolf` for "Creating <TYPE> joypad for controller <N>" lines (most recent per slot wins) and shows what it last saw before asking anything, using it as the suggested default at each prompt. Every prompt is now labeled "1st controller"/"2nd controller"/etc - always meaning Wolf's own 0-indexed "controller 0"/"controller 1" - instead of asking for a raw slot number, closing the exact client-vs-controller 0-vs-1 confusion surfaced live earlier. Documents plainly that this can show what TYPE a slot last used but can't show or control WHICH physical controller becomes which slot - that's decided by Moonlight client-side, upstream of Wolf's API entirely. 2. Documents a full recovery procedure in the Wii U section for when Cemu's Input Settings Save button is cut off-screen (confirmed live: happens even maximized, at higher resolutions, and closing the window doesn't save) - an Alt-drag/Alt+F7 window-manager fix first, then editing Cemu's own controllerProfiles/controllerN.xml directly on the host (plain XML, not inside the container) as a full GUI bypass. Documents the key insight that made this actually work live: <mapping>/<button> pairs are universal SDL_CONTROLLER_BUTTON_* semantics (confirmed earlier against real captured profiles from different controller brands), so a working mapping can be copied verbatim into a different device's empty <mappings/> block without redoing the live button-capture in Cemu's GUI at all. Verified the redesigned manage.sh controllers flow end to end against a mocked docker compose logs matching the user's real log output (both the log-seen and no-logs-seen paths), plus the ordinal-suffix logic in isolation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VLX1yYKJExGSXmgUhxKQG6
This commit is contained in:
+126
-2
@@ -3433,6 +3433,59 @@ print(next(iter(ids)) if len(ids) == 1 else '')
|
||||
CLIENT_ID="${CLIENT_IDS[$((PICK-1))]}"
|
||||
fi
|
||||
|
||||
# Wolf's own logs record "Creating <TYPE> joypad for controller <N>"
|
||||
# every time it creates a virtual pad (confirmed live against real
|
||||
# log output) — polling this gives real, human-readable context per
|
||||
# slot instead of asking the user to guess or watch logs by hand
|
||||
# themselves. Wolf's own wording is 0-indexed ("controller 0",
|
||||
# "controller 1"); every prompt in this tool is deliberately
|
||||
# 1-indexed instead ("1st controller", "2nd controller"...) —
|
||||
# confirmed live that mixing the two ("exactly one CLIENT is
|
||||
# active" read as "exactly one CONTROLLER" right above a
|
||||
# 0-indexed-sounding prompt) is a real, easy misread. "1st
|
||||
# controller" here always means Wolf's "controller 0", "2nd" means
|
||||
# "controller 1", and so on — stated explicitly at every prompt so
|
||||
# the two numbering schemes never have to be reconciled in your head.
|
||||
#
|
||||
# NOTE: this only tells you what TYPE Wolf most recently created
|
||||
# for a given slot NUMBER — it can't tell you which PHYSICAL
|
||||
# controller that was, and it can't let you reassign a physical
|
||||
# controller to a different slot number. Which controller becomes
|
||||
# slot 0 vs. slot 1 is decided entirely by Moonlight (the client),
|
||||
# upstream of Wolf, based on connection order — not something this
|
||||
# override (or Wolf's API at all) can control.
|
||||
_ordsuffix() {
|
||||
case "$1" in
|
||||
1) echo "st" ;;
|
||||
2) echo "nd" ;;
|
||||
3) echo "rd" ;;
|
||||
*) echo "th" ;;
|
||||
esac
|
||||
}
|
||||
echo ""
|
||||
echo "Checking Wolf's logs for controllers it has already created a pad for..."
|
||||
declare -A LAST_SEEN_TYPE
|
||||
while IFS=$'\t' read -r cnum ctype; do
|
||||
[ -n "$cnum" ] && LAST_SEEN_TYPE["$cnum"]="$ctype"
|
||||
done < <(docker compose logs wolf 2>/dev/null | python3 -c "
|
||||
import re, sys
|
||||
for line in sys.stdin:
|
||||
m = re.search(r'Creating (\w+) joypad for controller (\d+)', line)
|
||||
if m:
|
||||
print(f'{m.group(2)}\t{m.group(1).upper()}')
|
||||
")
|
||||
if [ "${#LAST_SEEN_TYPE[@]}" -gt 0 ]; then
|
||||
echo "Last type Wolf created, per controller (most recent reconnect wins):"
|
||||
for cnum in $(printf '%s\n' "${!LAST_SEEN_TYPE[@]}" | sort -n); do
|
||||
_n=$((cnum + 1))
|
||||
echo " Your ${_n}$(_ordsuffix "$_n") controller (Wolf calls it \"controller $cnum\"): last seen as ${LAST_SEEN_TYPE[$cnum]}"
|
||||
done
|
||||
else
|
||||
echo "No controllers found in Wolf's logs yet — connect them and start a"
|
||||
echo "stream first if you want this list populated (not required, you can"
|
||||
echo "still answer blind below)."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Now, separately: how many physical GAMEPADS/CONTROLLERS do you have"
|
||||
echo "connected to that device right now (this tool can't detect that —"
|
||||
@@ -3446,8 +3499,16 @@ print(next(iter(ids)) if len(ids) == 1 else '')
|
||||
|
||||
OVERRIDE_TYPES=()
|
||||
for i in $(seq 1 "$NSLOTS"); do
|
||||
read -r -p " Slot $i type (AUTO/XBOX/PS/NINTENDO) [AUTO]: " T
|
||||
T="${T:-AUTO}"
|
||||
cnum=$((i - 1))
|
||||
SEEN="${LAST_SEEN_TYPE[$cnum]:-}"
|
||||
DEFAULT="${SEEN:-AUTO}"
|
||||
SUF="$(_ordsuffix "$i")"
|
||||
if [ -n "$SEEN" ]; then
|
||||
read -r -p " Your ${i}${SUF} controller (controller $cnum, last seen as $SEEN) — force to which type? (AUTO/XBOX/PS/NINTENDO) [$DEFAULT]: " T
|
||||
else
|
||||
read -r -p " Your ${i}${SUF} controller (controller $cnum, not seen yet) — force to which type? (AUTO/XBOX/PS/NINTENDO) [AUTO]: " T
|
||||
fi
|
||||
T="${T:-$DEFAULT}"
|
||||
T="$(echo "$T" | tr '[:lower:]' '[:upper:]')"
|
||||
case "$T" in
|
||||
AUTO|XBOX|PS|NINTENDO) ;;
|
||||
@@ -4114,6 +4175,31 @@ once (Wolf doesn't dedupe repeated pairings of the same device) — every
|
||||
duplicate of an active ID is tagged ACTIVE together, so it doesn't matter
|
||||
which one you'd have picked by hand.
|
||||
|
||||
**It labels controllers "1st"/"2nd"/"3rd"/"4th" — always meaning Wolf's own
|
||||
"controller 0"/"controller 1"/"controller 2"/"controller 3".** Wolf's own
|
||||
log wording is 0-indexed; this tool is deliberately 1-indexed throughout
|
||||
so the two numbering schemes never have to be reconciled in your head
|
||||
(confirmed live that mixing them — reading "exactly one client is
|
||||
active" as "exactly one controller" right above a 0-indexed-sounding
|
||||
prompt — is a real, easy misread).
|
||||
|
||||
Before asking anything, it polls \`docker compose logs wolf\` for every
|
||||
\`Creating <TYPE> joypad for controller <N>\` line Wolf has ever logged
|
||||
(most recent per slot wins) and shows you what it last saw, e.g.:
|
||||
\`\`\`
|
||||
Your 1st controller (Wolf calls it "controller 0"): last seen as NINTENDO
|
||||
Your 2nd controller (Wolf calls it "controller 1"): last seen as XBOX
|
||||
\`\`\`
|
||||
— then uses that as the suggested default at each prompt, so if nothing's
|
||||
changed you can just hit Enter through them. **This can't tell you which
|
||||
physical controller became which slot, and it can't let you reassign
|
||||
one** — which controller becomes slot 0 vs. slot 1 is decided entirely by
|
||||
Moonlight (the client), based on connection order, upstream of Wolf and
|
||||
this tool alike. If you need to know that for certain, connect your
|
||||
controllers one at a time while following the log live
|
||||
(\`docker compose logs -f wolf | grep -i controller\`) — you'll see
|
||||
exactly which slot number each one becomes as it connects.
|
||||
|
||||
Then it asks how many controllers to configure and what type to force
|
||||
each one to. There are only **3 concrete types** (confirmed against Wolf's own
|
||||
virtual-pad source, \`inputtino\`) — not one per real controller brand:
|
||||
@@ -4397,6 +4483,44 @@ time Cemu launches from ES-DE too.
|
||||
characters?** That's not a Cemu-specific bug — see
|
||||
"Multiple controllers" above (\`./manage.sh controllers\`).
|
||||
|
||||
**If Input Settings' Save button is cut off the bottom of the screen**
|
||||
(confirmed live: happens even maximized, at higher resolutions, and
|
||||
closing the window doesn't save — Cemu's Input Settings dialog is a
|
||||
fixed size, not resizable, and can genuinely be taller than the session's
|
||||
viewport) — try moving the dialog first: hold **Alt** and **left-click-drag
|
||||
anywhere inside it** (not just the titlebar) to reposition it upward, or
|
||||
use xfwm4's own **Alt+F7** (grab-to-move, drop with click or Enter) if
|
||||
that doesn't work. Either is a plain X11/XFCE window-manager trick, not
|
||||
Cemu-specific, and usually the fastest fix.
|
||||
|
||||
**If that still doesn't work, or you'd rather skip the GUI entirely: edit
|
||||
the controller profile file directly.** Cemu's per-slot controller config
|
||||
isn't inside the Docker container at all — it's plain XML on the game
|
||||
drive (Desktop and ES-DE both mount \`~/.config\` from the same place):
|
||||
\`\`\`bash
|
||||
GAME_DIR=\$(grep '^GAME_STORAGE_DIR=' $WOLF_DIR/.env | cut -d= -f2-)
|
||||
ls "\$GAME_DIR/retro-home/Cemu/controllerProfiles/"
|
||||
\`\`\`
|
||||
File naming is 0-indexed the same way Wolf's own controller numbering is
|
||||
(yet another place this comes up) — \`controller0.xml\` is Cemu's
|
||||
**Controller 1**, \`controller1.xml\` is **Controller 2**, and so on.
|
||||
Each holds one or more \`<controller>\` blocks (one per SDL device that's
|
||||
ever been assigned to that slot, identified by its own \`<uuid>\`/
|
||||
\`<display_name>\`) with a \`<mappings>\` list of \`<mapping>\`/\`<button>\`
|
||||
pairs. **Confirmed against real captured profiles from different
|
||||
controller brands: these values are universal SDL_CONTROLLER_BUTTON_*
|
||||
semantics, not raw per-device button indices** — so a complete, working
|
||||
\`<mappings>\` block can be copied verbatim from one \`<controller>\` entry
|
||||
into a different device's empty one in the same file (e.g. a
|
||||
newly-selected device whose Save got cut off, leaving it with
|
||||
\`<mappings />\` and nothing bound) without touching Cemu's GUI at all. If
|
||||
a slot ends up with more than one \`<controller>\` block and only one
|
||||
device is actually in use, deleting the stale block(s) removes any
|
||||
ambiguity about which one Cemu picks. Back up the file first
|
||||
(\`cp controllerN.xml controllerN.xml.bak\`) before hand-editing; no
|
||||
\`sudo\` needed, these files are written world-writable by Cemu itself.
|
||||
Close Cemu completely and relaunch for a hand-edited profile to take effect.
|
||||
|
||||
ROMs are mounted at \`/ROMs\` inside Desktop too, so Cemu's own File → Load
|
||||
can browse straight to \`/ROMs/wiiu/\` without needing ES-DE at all.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user