at master 9.1 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8let 9 cfg = config.programs.steam; 10 gamescopeCfg = config.programs.gamescope; 11 12 extraCompatPaths = lib.makeSearchPathOutput "steamcompattool" "" cfg.extraCompatPackages; 13 14 steam-gamescope = 15 let 16 exports = builtins.attrValues ( 17 builtins.mapAttrs (n: v: "export ${n}=${v}") cfg.gamescopeSession.env 18 ); 19 in 20 pkgs.writeShellScriptBin "steam-gamescope" '' 21 ${builtins.concatStringsSep "\n" exports} 22 gamescope --steam ${builtins.toString cfg.gamescopeSession.args} -- steam ${builtins.toString cfg.gamescopeSession.steamArgs} 23 ''; 24 25 gamescopeSessionFile = 26 (pkgs.writeTextDir "share/wayland-sessions/steam.desktop" '' 27 [Desktop Entry] 28 Name=Steam 29 Comment=A digital distribution platform 30 Exec=${steam-gamescope}/bin/steam-gamescope 31 Type=Application 32 '').overrideAttrs 33 (_: { 34 passthru.providedSessions = [ "steam" ]; 35 }); 36in 37{ 38 options.programs.steam = { 39 enable = lib.mkEnableOption "steam"; 40 41 package = lib.mkOption { 42 type = lib.types.package; 43 default = pkgs.steam; 44 defaultText = lib.literalExpression "pkgs.steam"; 45 example = lib.literalExpression '' 46 pkgs.steam.override { 47 extraEnv = { 48 MANGOHUD = true; 49 OBS_VKCAPTURE = true; 50 RADV_TEX_ANISO = 16; 51 }; 52 extraLibraries = p: with p; [ 53 atk 54 ]; 55 } 56 ''; 57 apply = 58 steam: 59 steam.override ( 60 prev: 61 { 62 extraEnv = 63 (lib.optionalAttrs (cfg.extraCompatPackages != [ ]) { 64 STEAM_EXTRA_COMPAT_TOOLS_PATHS = extraCompatPaths; 65 }) 66 // (lib.optionalAttrs cfg.extest.enable { 67 LD_PRELOAD = "${pkgs.pkgsi686Linux.extest}/lib/libextest.so"; 68 }) 69 // (prev.extraEnv or { }); 70 extraLibraries = 71 pkgs: 72 let 73 prevLibs = if prev ? extraLibraries then prev.extraLibraries pkgs else [ ]; 74 additionalLibs = 75 with config.hardware.graphics; 76 if pkgs.stdenv.hostPlatform.is64bit then 77 [ package ] ++ extraPackages 78 else 79 [ package32 ] ++ extraPackages32; 80 in 81 prevLibs ++ additionalLibs; 82 extraPkgs = p: (cfg.extraPackages ++ lib.optionals (prev ? extraPkgs) (prev.extraPkgs p)); 83 } 84 // lib.optionalAttrs (cfg.gamescopeSession.enable && gamescopeCfg.capSysNice) { 85 buildFHSEnv = pkgs.buildFHSEnv.override { 86 # use the setuid wrapped bubblewrap 87 bubblewrap = "${config.security.wrapperDir}/.."; 88 }; 89 } 90 ); 91 description = '' 92 The Steam package to use. Additional libraries are added from the system 93 configuration to ensure graphics work properly. 94 95 Use this option to customise the Steam package rather than adding your 96 custom Steam to {option}`environment.systemPackages` yourself. 97 ''; 98 }; 99 100 extraPackages = lib.mkOption { 101 type = lib.types.listOf lib.types.package; 102 default = [ ]; 103 example = lib.literalExpression '' 104 with pkgs; [ 105 gamescope 106 ] 107 ''; 108 description = '' 109 Additional packages to add to the Steam environment. 110 ''; 111 }; 112 113 extraCompatPackages = lib.mkOption { 114 type = lib.types.listOf lib.types.package; 115 default = [ ]; 116 example = lib.literalExpression '' 117 with pkgs; [ 118 proton-ge-bin 119 ] 120 ''; 121 description = '' 122 Extra packages to be used as compatibility tools for Steam on Linux. Packages will be included 123 in the `STEAM_EXTRA_COMPAT_TOOLS_PATHS` environmental variable. For more information see 124 https://github.com/ValveSoftware/steam-for-linux/issues/6310. 125 126 These packages must be Steam compatibility tools that have a `steamcompattool` output. 127 ''; 128 }; 129 130 fontPackages = lib.mkOption { 131 type = lib.types.listOf lib.types.package; 132 # `fonts.packages` is a list of paths now, filter out which are not packages 133 default = builtins.filter lib.types.package.check config.fonts.packages; 134 defaultText = lib.literalExpression "builtins.filter lib.types.package.check config.fonts.packages"; 135 example = lib.literalExpression "with pkgs; [ source-han-sans ]"; 136 description = '' 137 Font packages to use in Steam. 138 139 Defaults to system fonts, but could be overridden to use other fonts useful for users who would like to customize CJK fonts used in Steam. According to the [upstream issue](https://github.com/ValveSoftware/steam-for-linux/issues/10422#issuecomment-1944396010), Steam only follows the per-user fontconfig configuration. 140 ''; 141 }; 142 143 remotePlay.openFirewall = lib.mkOption { 144 type = lib.types.bool; 145 default = false; 146 description = '' 147 Open ports in the firewall for Steam Remote Play. 148 ''; 149 }; 150 151 dedicatedServer.openFirewall = lib.mkOption { 152 type = lib.types.bool; 153 default = false; 154 description = '' 155 Open ports in the firewall for Source Dedicated Server. 156 ''; 157 }; 158 159 localNetworkGameTransfers.openFirewall = lib.mkOption { 160 type = lib.types.bool; 161 default = false; 162 description = '' 163 Open ports in the firewall for Steam Local Network Game Transfers. 164 ''; 165 }; 166 167 gamescopeSession = lib.mkOption { 168 description = "Run a GameScope driven Steam session from your display-manager"; 169 default = { }; 170 type = lib.types.submodule { 171 options = { 172 enable = lib.mkEnableOption "GameScope Session"; 173 args = lib.mkOption { 174 type = lib.types.listOf lib.types.str; 175 default = [ ]; 176 description = '' 177 Arguments to be passed to GameScope for the session. 178 ''; 179 }; 180 181 env = lib.mkOption { 182 type = lib.types.attrsOf lib.types.str; 183 default = { }; 184 description = '' 185 Environmental variables to be passed to GameScope for the session. 186 ''; 187 }; 188 189 steamArgs = lib.mkOption { 190 type = lib.types.listOf lib.types.str; 191 default = [ 192 "-tenfoot" 193 "-pipewire-dmabuf" 194 ]; 195 description = '' 196 Arguments to be passed to Steam for the session. 197 ''; 198 }; 199 }; 200 }; 201 }; 202 203 extest.enable = lib.mkEnableOption '' 204 Load the extest library into Steam, to translate X11 input events to 205 uinput events (e.g. for using Steam Input on Wayland) 206 ''; 207 208 protontricks = { 209 enable = lib.mkEnableOption "protontricks, a simple wrapper for running Winetricks commands for Proton-enabled games"; 210 package = lib.mkPackageOption pkgs "protontricks" { }; 211 }; 212 }; 213 214 config = lib.mkIf cfg.enable { 215 hardware.graphics = { 216 # this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932 217 enable = true; 218 enable32Bit = true; 219 }; 220 221 security.wrappers = lib.mkIf (cfg.gamescopeSession.enable && gamescopeCfg.capSysNice) { 222 # needed or steam fails 223 bwrap = { 224 owner = "root"; 225 group = "root"; 226 source = "${pkgs.bubblewrap}/bin/bwrap"; 227 setuid = true; 228 }; 229 }; 230 231 programs.steam.extraPackages = cfg.fontPackages; 232 233 programs.gamescope.enable = lib.mkDefault cfg.gamescopeSession.enable; 234 services.displayManager.sessionPackages = lib.mkIf cfg.gamescopeSession.enable [ 235 gamescopeSessionFile 236 ]; 237 238 # enable 32bit pulseaudio/pipewire support if needed 239 services.pulseaudio.support32Bit = config.services.pulseaudio.enable; 240 services.pipewire.alsa.support32Bit = config.services.pipewire.alsa.enable; 241 242 hardware.steam-hardware.enable = true; 243 244 environment.systemPackages = [ 245 cfg.package 246 cfg.package.run 247 ] 248 ++ lib.optional cfg.gamescopeSession.enable steam-gamescope 249 ++ lib.optional cfg.protontricks.enable ( 250 cfg.protontricks.package.override { inherit extraCompatPaths; } 251 ); 252 253 networking.firewall = lib.mkMerge [ 254 (lib.mkIf (cfg.remotePlay.openFirewall || cfg.localNetworkGameTransfers.openFirewall) { 255 allowedUDPPorts = [ 27036 ]; # Peer discovery 256 }) 257 258 (lib.mkIf cfg.remotePlay.openFirewall { 259 allowedTCPPorts = [ 27036 ]; 260 allowedUDPPortRanges = [ 261 { 262 from = 27031; 263 to = 27035; 264 } 265 ]; 266 }) 267 268 (lib.mkIf cfg.dedicatedServer.openFirewall { 269 allowedTCPPorts = [ 27015 ]; # SRCDS Rcon port 270 allowedUDPPorts = [ 27015 ]; # Gameplay traffic 271 }) 272 273 (lib.mkIf cfg.localNetworkGameTransfers.openFirewall { 274 allowedTCPPorts = [ 27040 ]; # Data transfers 275 }) 276 ]; 277 }; 278 279 meta.maintainers = lib.teams.steam.members; 280}