1 { config, lib, pkgs, ... }:
4 cfg = config.programs.steam;
5 gamescopeCfg = config.programs.gamescope;
7 extraCompatPaths = lib.makeSearchPathOutput "steamcompattool" "" cfg.extraCompatPackages;
10 exports = builtins.attrValues (builtins.mapAttrs (n: v: "export ${n}=${v}") cfg.gamescopeSession.env);
12 pkgs.writeShellScriptBin "steam-gamescope" ''
13 ${builtins.concatStringsSep "\n" exports}
14 gamescope --steam ${builtins.toString cfg.gamescopeSession.args} -- steam -tenfoot -pipewire-dmabuf
17 gamescopeSessionFile =
18 (pkgs.writeTextDir "share/wayland-sessions/steam.desktop" ''
21 Comment=A digital distribution platform
22 Exec=${steam-gamescope}/bin/steam-gamescope
24 '').overrideAttrs (_: { passthru.providedSessions = [ "steam" ]; });
26 options.programs.steam = {
27 enable = lib.mkEnableOption "steam";
29 package = lib.mkOption {
30 type = lib.types.package;
32 defaultText = lib.literalExpression "pkgs.steam";
33 example = lib.literalExpression ''
34 pkgs.steam-small.override {
40 extraLibraries = p: with p; [
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)
61 buildFHSEnv = pkgs.buildFHSEnv.override {
62 # use the setuid wrapped bubblewrap
63 bubblewrap = "${config.security.wrapperDir}/..";
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.
75 extraPackages = lib.mkOption {
76 type = lib.types.listOf lib.types.package;
78 example = lib.literalExpression ''
84 Additional packages to add to the Steam environment.
88 extraCompatPackages = lib.mkOption {
89 type = lib.types.listOf lib.types.package;
91 example = lib.literalExpression ''
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.
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 ]";
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.
118 remotePlay.openFirewall = lib.mkOption {
119 type = lib.types.bool;
122 Open ports in the firewall for Steam Remote Play.
126 dedicatedServer.openFirewall = lib.mkOption {
127 type = lib.types.bool;
130 Open ports in the firewall for Source Dedicated Server.
134 localNetworkGameTransfers.openFirewall = lib.mkOption {
135 type = lib.types.bool;
138 Open ports in the firewall for Steam Local Network Game Transfers.
142 gamescopeSession = lib.mkOption {
143 description = "Run a GameScope driven Steam session from your display-manager";
145 type = lib.types.submodule {
147 enable = lib.mkEnableOption "GameScope Session";
148 args = lib.mkOption {
149 type = lib.types.listOf lib.types.str;
152 Arguments to be passed to GameScope for the session.
157 type = lib.types.attrsOf lib.types.str;
160 Environmental variables to be passed to GameScope for the session.
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)
173 enable = lib.mkEnableOption "protontricks, a simple wrapper for running Winetricks commands for Proton-enabled games";
174 package = lib.mkPackageOption pkgs "protontricks" { };
178 config = lib.mkIf cfg.enable {
179 hardware.graphics = { # this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932
184 security.wrappers = lib.mkIf (cfg.gamescopeSession.enable && gamescopeCfg.capSysNice) {
185 # needed or steam fails
189 source = "${pkgs.bubblewrap}/bin/bwrap";
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 = [
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
216 (lib.mkIf cfg.remotePlay.openFirewall {
217 allowedTCPPorts = [ 27036 ];
218 allowedUDPPortRanges = [ { from = 27031; to = 27035; } ];
221 (lib.mkIf cfg.dedicatedServer.openFirewall {
222 allowedTCPPorts = [ 27015 ]; # SRCDS Rcon port
223 allowedUDPPorts = [ 27015 ]; # Gameplay traffic
226 (lib.mkIf cfg.localNetworkGameTransfers.openFirewall {
227 allowedTCPPorts = [ 27040 ]; # Data transfers
232 meta.maintainers = lib.teams.steam.members;