1 { config, lib, pkgs, ... }:
6 cfg = config.programs.steam;
7 gamescopeCfg = config.programs.gamescope;
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 ${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 = mkEnableOption (lib.mdDoc "steam");
32 defaultText = literalExpression "pkgs.steam";
33 example = literalExpression ''
34 pkgs.steam-small.override {
40 extraLibraries = p: with p; [
45 apply = steam: steam.override (prev: {
46 extraLibraries = pkgs: let
47 prevLibs = if prev ? extraLibraries then prev.extraLibraries pkgs else [ ];
48 additionalLibs = with config.hardware.opengl;
49 if pkgs.stdenv.hostPlatform.is64bit
50 then [ package ] ++ extraPackages
51 else [ package32 ] ++ extraPackages32;
52 in prevLibs ++ additionalLibs;
53 } // optionalAttrs (cfg.gamescopeSession.enable && gamescopeCfg.capSysNice)
55 buildFHSEnv = pkgs.buildFHSEnv.override {
56 # use the setuid wrapped bubblewrap
57 bubblewrap = "${config.security.wrapperDir}/..";
60 description = lib.mdDoc ''
61 The Steam package to use. Additional libraries are added from the system
62 configuration to ensure graphics work properly.
64 Use this option to customise the Steam package rather than adding your
65 custom Steam to {option}`environment.systemPackages` yourself.
69 remotePlay.openFirewall = mkOption {
72 description = lib.mdDoc ''
73 Open ports in the firewall for Steam Remote Play.
77 dedicatedServer.openFirewall = mkOption {
80 description = lib.mdDoc ''
81 Open ports in the firewall for Source Dedicated Server.
85 gamescopeSession = mkOption {
86 description = mdDoc "Run a GameScope driven Steam session from your display-manager";
88 type = types.submodule {
90 enable = mkEnableOption (mdDoc "GameScope Session");
92 type = types.listOf types.str;
94 description = mdDoc ''
95 Arguments to be passed to GameScope for the session.
100 type = types.attrsOf types.str;
102 description = mdDoc ''
103 Environmental variables to be passed to GameScope for the session.
111 config = mkIf cfg.enable {
112 hardware.opengl = { # this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932
115 driSupport32Bit = true;
118 security.wrappers = mkIf (cfg.gamescopeSession.enable && gamescopeCfg.capSysNice) {
119 # needed or steam fails
123 source = "${pkgs.bubblewrap}/bin/bwrap";
128 programs.gamescope.enable = mkDefault cfg.gamescopeSession.enable;
129 services.xserver.displayManager.sessionPackages = mkIf cfg.gamescopeSession.enable [ gamescopeSessionFile ];
131 # optionally enable 32bit pulseaudio support if pulseaudio is enabled
132 hardware.pulseaudio.support32Bit = config.hardware.pulseaudio.enable;
134 hardware.steam-hardware.enable = true;
136 environment.systemPackages = [
139 ] ++ lib.optional cfg.gamescopeSession.enable steam-gamescope;
141 networking.firewall = lib.mkMerge [
142 (mkIf cfg.remotePlay.openFirewall {
143 allowedTCPPorts = [ 27036 ];
144 allowedUDPPortRanges = [ { from = 27031; to = 27036; } ];
147 (mkIf cfg.dedicatedServer.openFirewall {
148 allowedTCPPorts = [ 27015 ]; # SRCDS Rcon port
149 allowedUDPPorts = [ 27015 ]; # Gameplay traffic
154 meta.maintainers = with maintainers; [ mkg20001 ];