This repository has no description
0

Configure Feed

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

1{ 2 config, 3 pkgs, 4 ... 5}: 6let 7 # Run the launcher + games from the on-disk strom checkout at 8 # /home/strom/strom when it exists, else the public github flake (see the 9 # STROM_FLAKE selection in strom-session below). 10 # 11 # Compositor: sway (wlroots) via greetd autologin - NOT cage, and NOT an outer 12 # gamescope session. Each game still brings its own gamescope, nested ONCE 13 # inside sway. 14 # 15 # Why sway and not cage: a game's nested gamescope prefers its WAYLAND backend, 16 # which needs wlroots protocols that cage (a minimal kiosk compositor) does not 17 # expose. Under cage gamescope's wayland backend fails ("Couldn't create 18 # Wayland objects") and silently falls back to its SDL backend, whose Vulkan 19 # swapchain remake loops and aborts on this RADV RENOIR iGPU 20 # (rendervulkan.cpp vulkan_remake_swapchain) - that is what crashed Baba Is You 21 # a couple seconds in. sway exposes those protocols, so gamescope inits its 22 # wayland backend and never touches the crashing SDL path. (An outer gamescope 23 # session instead double-nests gamescope-in-gamescope, which is worse.) 24 # Keeping per-game gamescope preserves render resolution/scaling, the in-game 25 # exit chord, and bare-tty portability - hence no STROM_NO_GAMESCOPE. 26 strom-session = pkgs.writeShellScript "strom-session" '' 27 set -u 28 # xpadneo owns the Xbox pad's hidraw node, so force SDL (pygame launcher and 29 # the games) to read the pad through evdev instead of its HIDAPI backend - 30 # same fix as 2configs/steam.nix, without it SDL finds no controller. 31 export SDL_JOYSTICK_HIDAPI_XBOX=0 32 # SDL's udev-monitor hotplug does not deliver in this kiosk session, so a pad 33 # powered on after the launcher starts is never seen. Force SDL to poll 34 # /dev/input instead, which reliably picks up pads as they connect/disconnect. 35 export SDL_JOYSTICK_DISABLE_UDEV=1 36 # In the kiosk the launcher window may not report input focus, and SDL 37 # suppresses joystick (not keyboard) events while unfocused - so allow 38 # joystick events regardless of focus. 39 export SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS=1 40 # Resolve the strom flake once: prefer the on-disk checkout at 41 # /home/strom/strom (edit + test there before pushing to github), and fall 42 # back to the public flake when it is absent. The launcher runs from this 43 # ref, and pkgs/launcher resolves each game's ref from STROM_FLAKE too (it 44 # sets the default with := so this export wins). 45 if [ -d /home/strom/strom ]; then 46 STROM_FLAKE=/home/strom/strom 47 else 48 STROM_FLAKE=github:kraftwerk-gaming/strom 49 fi 50 export STROM_FLAKE 51 # strom-run (the per-game supervisor) needs a delegated cgroup v2 so it can 52 # mkdir a child cgroup and cgroup.kill the game tree atomically. The greetd 53 # PAM session drops us into a root-owned logind session scope 54 # (user-1011.slice/session-cN.scope) that is NOT delegated, so strom-run dies 55 # with "could not create cgroup (Permission denied)" and the game never 56 # launches. Re-parent into a delegated transient scope under the user manager 57 # (user@UID.service, which logind DOES delegate). Wait for the user bus first: 58 # on boot the PAM session opens slightly before the user manager's bus socket. 59 rd="''${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" 60 for _ in $(seq 1 50); do 61 [ -S "$rd/bus" ] && break 62 sleep 0.2 63 done 64 # Relaunch whenever the couch launcher exits (gamepad B / Esc) so the kiosk 65 # never drops to a blank screen. The loop runs inside the delegated scope so 66 # every game inherits a writable cgroup; sway stays up (the launcher is just 67 # one of its clients) across relaunches. 68 # 69 # setpriv --ambient-caps=-all: the login session may carry an ambient 70 # capability (e.g. cap_wake_alarm) that every child inherits. bwrap refuses 71 # to start when it sees capabilities in its permitted set without being 72 # setuid ("Unexpected capabilities but not setuid, old file caps config?"), 73 # which instant-crashes every sandboxed game. Drop the ambient set for the 74 # whole launcher tree so bwrap (and thus each game) starts clean. 75 exec ${pkgs.systemd}/bin/systemd-run --user --scope -p Delegate=yes \ 76 --quiet --collect -- \ 77 ${pkgs.util-linux}/bin/setpriv --ambient-caps=-all \ 78 ${pkgs.bash}/bin/bash -c 'while true; do 79 ${config.nix.package}/bin/nix run "$STROM_FLAKE#scripts.launcher" 80 sleep 2 81 done' 82 ''; 83 84 # Bare wlroots kiosk: no window chrome, every toplevel fullscreen, no idle 85 # blanking. Its whole reason to exist over cage is that it exposes the wlroots 86 # protocols each game's nested gamescope needs for its wayland backend (see 87 # the top-of-file note). 88 sway-kiosk = pkgs.writeText "sway-kiosk.conf" '' 89 default_border none 90 default_floating_border none 91 gaps inner 0 92 gaps outer 0 93 # Fullscreen every toplevel: the launcher grid and each game's gamescope. 94 for_window [app_id=".*"] fullscreen enable 95 for_window [class=".*"] fullscreen enable 96 # No swaybar / no swayidle -> no bar, no DPMS blank (mains-powered kiosk). 97 exec ${strom-session} 98 ''; 99in 100{ 101 # Dedicated, unprivileged kiosk user. Gamepad input comes through evdev 102 # (input group), audio through the system-wide pipewire socket (pipewire 103 # group), and the games need video/audio like any local player. 104 users.users.strom = { 105 isNormalUser = true; 106 extraGroups = [ 107 "video" 108 "audio" 109 "input" 110 "pipewire" 111 ]; 112 }; 113 114 # Kiosk autostart: greetd autologins the strom user on tty1 into sway, which 115 # runs the couch launcher as its sole fullscreen client. default_session 116 # re-runs the same session if sway ever exits. 117 services.greetd = { 118 enable = true; 119 settings = 120 let 121 session = { 122 command = "${pkgs.sway}/bin/sway --config ${sway-kiosk}"; 123 user = "strom"; 124 }; 125 in 126 { 127 initial_session = session; 128 default_session = session; 129 }; 130 }; 131 132 # The panel comes up at ~1% backlight, unreadable for a couch setup. Ship 133 # brightnessctl + its udev rules (chgrp backlight to the video group, g+w) so 134 # any video-group user (strom, lass) can adjust it without root, e.g. 135 # `brightnessctl set 100%` / `brightnessctl set 50%`. 136 environment.systemPackages = [ 137 pkgs.brightnessctl 138 ]; 139 services.udev.packages = [ pkgs.brightnessctl ]; 140 141 # Force the backlight to maximum on every boot. Ordered after 142 # systemd-backlight, which would otherwise restore the last (dim) saved level. 143 systemd.services.max-brightness = { 144 wantedBy = [ "multi-user.target" ]; 145 after = [ "systemd-backlight@backlight:amdgpu_bl1.service" ]; 146 serviceConfig = { 147 Type = "oneshot"; 148 ExecStart = "${pkgs.brightnessctl}/bin/brightnessctl set 100%"; 149 }; 150 }; 151 152 # Xbox Wireless Controllers (Bluetooth). xpadneo for correct rumble/axis 153 # mapping; the two bluez tweaks + disable_ertm are what make Xbox pads 154 # actually bond (without disable_ertm they fail with AuthenticationFailed). 155 # Bluetooth itself (adapter + powerOnBoot) already comes from 156 # 2configs/pipewire.nix. Pair pads with bluetuith (also from pipewire.nix). 157 hardware.xpadneo.enable = true; 158 # Xbox One/Series controllers pair over Bluetooth only with L2CAP ERTM off. 159 boot.extraModprobeConfig = "options bluetooth disable_ertm=1"; 160 hardware.bluetooth.settings.General = { 161 # JustWorksRepairing lets bluez accept fresh pairings without a manual 162 # remove; Privacy = device pins a static host address so the controller's 163 # bond key survives RPA rotation. 164 JustWorksRepairing = "always"; 165 Privacy = "device"; 166 # Keep page scan always on so a bonded pad reconnects promptly when powered 167 # back on (otherwise bonded pads bond but never re-link). Fine on a 168 # mains-powered kiosk. 169 FastConnectable = true; 170 # In this busy-RF LAN environment the cache fills with hundreds of stray 171 # BLE devices; evict non-bonded ones ~30s after they leave range so they 172 # do not pile up and saturate the adapter. 173 TemporaryTimeout = 30; 174 }; 175 176 # Persistent JustWorks pairing agent. Xbox pads pair via JustWorks, and on 177 # reconnect a pad whose bond went stale re-initiates pairing. bluez needs a 178 # registered agent to auto-confirm that; bluetuith only provides one while you 179 # are actively pairing in it, so an autonomous re-pair hits "No agent 180 # available for request type 2" and the link drops. Keep a NoInputNoOutput 181 # agent registered at all times so pairing/re-pairing just works. 182 systemd.services.bt-agent = { 183 wantedBy = [ "multi-user.target" ]; 184 after = [ "bluetooth.service" ]; 185 requires = [ "bluetooth.service" ]; 186 serviceConfig = { 187 ExecStart = "${pkgs.bluez-tools}/bin/bt-agent -c NoInputNoOutput"; 188 Restart = "always"; 189 RestartSec = 2; 190 }; 191 }; 192}