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