This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

machines.coaxmetal: Xbox pad pairing + gamepad-ready launcher session

Add strom-pair (in-session NoInputNoOutput agent + polling, bond check) to
pair Xbox Wireless Controllers one at a time, plus the bluez/kernel bits they
need: hardware.xpadneo, disable_ertm=1 (else AuthenticationFailed), and
FastConnectable=true so bonded pads reconnect. Point the launcher session at
evdev via SDL_JOYSTICK_HIDAPI_XBOX=0, poll hotplug with
SDL_JOYSTICK_DISABLE_UDEV=1, and allow input without focus via
SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS=1.

+136 -1
+136 -1
machines/coaxmetal/strom.nix
··· 11 11 # its closure stays tiny; games bring their own nested gamescope/proton. 12 12 strom-session = pkgs.writeShellScript "strom-session" '' 13 13 set -u 14 + # xpadneo owns the Xbox pad's hidraw node, so force SDL (pygame launcher and 15 + # the games) to read the pad through evdev instead of its HIDAPI backend - 16 + # same fix as 2configs/steam.nix, without it SDL finds no controller. 17 + export SDL_JOYSTICK_HIDAPI_XBOX=0 18 + # SDL's udev-monitor hotplug does not deliver in this kiosk session, so a pad 19 + # powered on after the launcher starts is never seen. Force SDL to poll 20 + # /dev/input instead, which reliably picks up pads as they connect/disconnect. 21 + export SDL_JOYSTICK_DISABLE_UDEV=1 22 + # Under cage the launcher window never reports input focus, and SDL suppresses 23 + # joystick (not keyboard) events while unfocused - so keyboard works but the 24 + # pad does not. Allow joystick events regardless of focus. 25 + export SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS=1 14 26 # Relaunch whenever the couch launcher exits (gamepad B / Esc) so the kiosk 15 27 # never drops to a blank tty. Keeping the loop as cage's child means the 16 28 # compositor stays up across relaunches. ··· 19 31 sleep 2 20 32 done 21 33 ''; 34 + 35 + # Bluetooth pairing helper for Xbox Wireless Controllers. Run `strom-pair` 36 + # once per controller (as root / on the console, e.g. 37 + # `ssh root@coaxmetal.r strom-pair`): put the pad in pairing mode (hold the 38 + # Xbox button to power on, then hold the small round sync button on the top 39 + # edge until the light flashes rapidly) and the script blocks until it pairs 40 + # that one fresh pad, then exits. Already-bonded pads are skipped, so just run 41 + # it again for the next controller. Bonded+trusted pads reconnect on their own 42 + # on every later boot. Pass a MAC (`strom-pair AA:BB:...`) to pair just one. 43 + strom-pair = pkgs.writeShellApplication { 44 + name = "strom-pair"; 45 + runtimeInputs = [ pkgs.bluez ]; 46 + text = '' 47 + # Find one not-yet-bonded Xbox pad, echoing its MAC (empty if none). A 48 + # foreground timed scan is reliable; a backgrounded `scan on` exits at once 49 + # without a tty and never actually scans. 50 + find_unpaired() { 51 + local m 52 + bluetoothctl --timeout 12 scan on >/dev/null 2>&1 || true 53 + mapfile -t cand < <(bluetoothctl devices 2>/dev/null | grep -i xbox | awk '{print $2}') 54 + for m in "''${cand[@]:-}"; do 55 + [ -n "$m" ] || continue 56 + bluetoothctl info "$m" 2>/dev/null | grep -q "Bonded: yes" && continue 57 + printf '%s\n' "$m" 58 + return 0 59 + done 60 + return 0 61 + } 62 + 63 + # Pair one pad. Xbox controllers use JustWorks pairing, which needs a 64 + # NoInputNoOutput agent held in the SAME bluetoothctl session as `pair` 65 + # (plus disable_ertm, set system-wide below). `connect` succeeds even when 66 + # pairing fails, so success is judged by "Bonded: yes", not by connect. 67 + pair_one() { 68 + local mac="$1" 69 + { 70 + echo "power on" 71 + echo "agent NoInputNoOutput" 72 + echo "default-agent" 73 + echo "scan on" 74 + sleep 14 75 + echo "pair $mac" 76 + sleep 12 77 + echo "trust $mac" 78 + echo "connect $mac" 79 + sleep 4 80 + echo "quit" 81 + } | bluetoothctl >/dev/null 2>&1 || true 82 + # Bonding can finalize a moment after the session exits; poll a few 83 + # seconds before deciding it failed. 84 + for _ in 1 2 3 4 5; do 85 + bluetoothctl info "$mac" 2>/dev/null | grep -q "Bonded: yes" && return 0 86 + sleep 1 87 + done 88 + return 1 89 + } 90 + 91 + bluetoothctl power on >/dev/null 2>&1 || true 92 + 93 + # Single-pad mode: pair exactly the given MAC and exit. 94 + if [ -n "''${1:-}" ]; then 95 + if pair_one "$1"; then 96 + echo ">> paired, trusted, connected: $1" 97 + exit 0 98 + fi 99 + echo "!! could not pair $1." >&2 100 + exit 1 101 + fi 102 + 103 + # Default: wait for one fresh (unbonded) pad, pair it, then exit. Run once 104 + # per controller; already-bonded pads are ignored so each run grabs the 105 + # next one. 106 + echo ">> put a controller in pairing mode: hold the Xbox button to power" 107 + echo " on, then hold the small round sync button on the top edge until" 108 + echo " the light flashes rapidly. Waiting... (Ctrl-C to abort)" 109 + while true; do 110 + echo ">> scanning..." 111 + mac=$(find_unpaired) 112 + # Show which Xbox pads are visible and whether they are already bonded, 113 + # so it's obvious if the pad is being detected at all. 114 + bluetoothctl devices 2>/dev/null | grep -i xbox | while read -r _ m _; do 115 + if bluetoothctl info "$m" 2>/dev/null | grep -q "Bonded: yes"; then 116 + echo " seen $m (already bonded, skipping)" 117 + else 118 + echo " seen $m (pairable)" 119 + fi 120 + done 121 + if [ -n "$mac" ]; then 122 + printf '>> pairing %s ... ' "$mac" 123 + if pair_one "$mac"; then 124 + echo "OK" 125 + echo ">> paired, trusted, connected: $mac" 126 + exit 0 127 + fi 128 + echo "failed, retrying (keep it in pairing mode)" 129 + fi 130 + sleep 1 131 + done 132 + ''; 133 + }; 22 134 in 23 135 { 24 136 # Dedicated, unprivileged kiosk user. Gamepad input comes through evdev ··· 55 167 # brightnessctl + its udev rules (chgrp backlight to the video group, g+w) so 56 168 # any video-group user (strom, lass) can adjust it without root, e.g. 57 169 # `brightnessctl set 100%` / `brightnessctl set 50%`. 58 - environment.systemPackages = [ pkgs.brightnessctl ]; 170 + environment.systemPackages = [ 171 + pkgs.brightnessctl 172 + strom-pair 173 + ]; 59 174 services.udev.packages = [ pkgs.brightnessctl ]; 60 175 61 176 # Force the backlight to maximum on every boot. Ordered after ··· 67 182 Type = "oneshot"; 68 183 ExecStart = "${pkgs.brightnessctl}/bin/brightnessctl set 100%"; 69 184 }; 185 + }; 186 + 187 + # Xbox Wireless Controllers (Bluetooth). xpadneo for correct rumble/axis 188 + # mapping; the two bluez tweaks + disable_ertm are what make Xbox pads 189 + # actually bond (without disable_ertm they fail with AuthenticationFailed). 190 + # Bluetooth itself (adapter + powerOnBoot) already comes from 191 + # 2configs/pipewire.nix. Pair each pad once with `strom-pair` (see above). 192 + hardware.xpadneo.enable = true; 193 + # Xbox One/Series controllers pair over Bluetooth only with L2CAP ERTM off. 194 + boot.extraModprobeConfig = "options bluetooth disable_ertm=1"; 195 + hardware.bluetooth.settings.General = { 196 + # JustWorksRepairing lets bluez accept fresh pairings without a manual 197 + # remove; Privacy = device pins a static host address so the controller's 198 + # bond key survives RPA rotation. 199 + JustWorksRepairing = "always"; 200 + Privacy = "device"; 201 + # Keep page scan always on so a bonded pad reconnects promptly when powered 202 + # back on (otherwise bonded pads bond but never re-link). Fine on a 203 + # mains-powered kiosk. 204 + FastConnectable = true; 70 205 }; 71 206 }