{ config, lib, pkgs, ... }: let inherit (lib) mkOption mkEnableOption mkIf types optional optionalAttrs optionalString nameValuePair mapAttrs' filter filterAttrs filterAttrsRecursive attrNames concatStringsSep toLower recursiveUpdate listToAttrs getExe ; cfg = config.programs.lutris; settingsFormat = pkgs.formats.yaml { }; formatWineName = (package: toLower package.name); in { options.programs.lutris = { enable = mkEnableOption "lutris."; package = lib.mkPackageOption pkgs "lutris" { }; steamPackage = mkOption { default = null; example = "pkgs.steam or osConfig.programs.steam.package"; description = '' This must be the same you use for your system, or two instances will conflict, for example, if you configure steam through the nixos module, a good value is "osConfig.programs.steam.package" ''; type = types.nullOr types.package; }; extraPackages = mkOption { default = [ ]; example = "with pkgs; [mangohud winetricks gamescope gamemode umu-launcher]"; description = '' List of packages to pass as extraPkgs to lutris. Please note runners are not detected properly this way, use a proper option for those. ''; type = types.listOf types.package; }; defaultWinePackage = mkOption { default = null; example = "pkgs.proton-ge-bin"; description = '' The wine/proton package to set as the default for lutris. It must still be set under proton/winePackages. ''; type = types.nullOr types.package; }; protonPackages = mkOption { default = [ ]; example = "[ pkgs.proton-ge-bin ]"; description = '' List of proton packages to be added for lutris to use with umu-launcher. ''; type = types.listOf types.package; }; winePackages = mkOption { default = [ ]; example = "[ pkgs.wineWow64Packages.full ]"; description = '' List of wine packages to be added for lutris to use. ''; type = types.listOf types.package; }; runners = mkOption { default = { }; example = '' runners = { cemu.package = pkgs.cemu; pcsx2.config = { system.disable_screen_saver = true; runner.runner_executable = "$\{pkgs.pcsx2}/bin/pcsx2-qt"; }; }; ''; description = '' Attribute set of Lutris runners along with their configurations. Each runner must be named exactly as lutris expects on `lutris --list-runners`. Note that runners added here won't be configurable through Lutris using the GUI. ''; type = types.attrsOf ( types.submodule { options = { package = mkOption { default = null; example = "pkgs.cemu"; description = '' The package to use for this runner, nix will try to find the executable for this package. A more specific path can be set by using settings.runner.runner_executable instead. Uncompatible with certain runners, such as wine. ''; type = types.nullOr types.package; }; settings = mkOption { default = { }; description = '' Settings passed directly to lutris for this runner's config at XDG_CONFIG/lutris/runners. ''; type = types.submodule { options = { runner = mkOption { default = { }; description = '' Runner specific options. For references, you must look for the file of said runner on lutris' source code. ''; type = types.submodule { freeformType = settingsFormat.type; options = { runner_executable = mkOption { type = types.either types.str types.path; default = ""; description = '' Specific option to point to a runner executable directly, don't set runner.package if you set this. Uncompatible with certain runners such as wine. ''; }; }; }; }; system = mkOption { default = { }; description = '' Lutris system options for this runner. Reference for system options: https://github.com/lutris/lutris/blob/master/lutris/sysoptions.py#L78 ''; type = types.submodule { freeformType = settingsFormat.type; }; }; }; }; }; }; } ); }; }; meta.maintainers = [ lib.hm.maintainers.bikku ]; config = mkIf cfg.enable { assertions = [ (lib.hm.assertions.assertPlatform "programs.lutris" pkgs lib.platforms.linux) (lib.hm.assertions.assertPlatform "programs.lutris" pkgs lib.platforms.x86_64) ]; warnings = let redundantRunners = attrNames ( filterAttrs ( _: runner_config: runner_config.package != null && runner_config.settings.runner.runner_executable != "" ) cfg.runners ); in filter (e: e != "") [ (optionalString (redundantRunners != [ ]) '' Under programs.lutris.runners, the following lutris runners had both a .package and .settings.runner.runner_executable options set: - ${concatStringsSep ", " redundantRunners} Note that runner_executable overrides package, setting both is pointless. '') (optionalString ((cfg.runners.wine.package or null) != null) '' Setting programs.lutris.runners.wine.package does nothing. Use the respective options, proton/winePackages and defaultWinePackage. '') (optionalString ((cfg ? runners.wine.settings.runner.version) && (cfg.defaultWinePackage != null)) '' Found conflicting options under programs.lutris, both runners.wine.settings.version and defaultWinePackage were set. '' ) ]; home.packages = let lutris-overrides = { # This only adds pkgs.steam to the extraPkgs, I see no reason to ever enable it. steamSupport = false; extraPkgs = (prev: cfg.extraPackages ++ optional (cfg.steamPackage != null) cfg.steamPackage); }; in [ (cfg.package.override lutris-overrides) ]; xdg.configFile = let buildRunnerConfig = ( runner_name: runner_config: # Remove the unset values so they don't end up on the final config. filterAttrsRecursive (name: value: value != { } && value != null && value != "") { "${runner_name}" = runner_config.settings.runner # If set translate .package to runner_executable // (optionalAttrs (runner_config.package != null) { runner_executable = getExe runner_config.package; }); inherit (runner_config.settings) system; } ); # Extra default config for wine if defaultWinePackage was set wine_extra = optionalAttrs (cfg.defaultWinePackage != null) { wine = { package = null; settings = { runner.version = formatWineName cfg.defaultWinePackage; system = { }; }; }; }; in mapAttrs' ( runner_name: runner_config: nameValuePair "lutris/runners/${runner_name}.yml" { source = settingsFormat.generate "${runner_name}.yml" (buildRunnerConfig runner_name runner_config); } ) (recursiveUpdate wine_extra cfg.runners); xdg.dataFile = let buildWineLink = type: packages: map ( # lutris seems to not detect wine/proton if the name has some caps package: (nameValuePair "lutris/runners/${type}/${formatWineName package}" { source = package; }) ) packages; steamcompattools = map (proton: proton.steamcompattool) cfg.protonPackages; in listToAttrs (buildWineLink "wine" cfg.winePackages ++ buildWineLink "proton" steamcompattools); }; }