diff --git a/modules/lib/maintainers.nix b/modules/lib/maintainers.nix index 0c1196b9c..37cb8578c 100644 --- a/modules/lib/maintainers.nix +++ b/modules/lib/maintainers.nix @@ -792,4 +792,10 @@ github = "miku4k"; githubId = 89653242; }; + bikku = { + name = "Bikku"; + email = "bikku+dev@slmail.me"; + github = "b1kku"; + githubId = 77858854; + }; } diff --git a/modules/modules.nix b/modules/modules.nix index 2d62416df..67e0e0d27 100644 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -175,6 +175,7 @@ let ./programs/lieer.nix ./programs/looking-glass-client.nix ./programs/lsd.nix + ./programs/lutris.nix ./programs/man.nix ./programs/mangohud.nix ./programs/matplotlib.nix diff --git a/modules/programs/lutris.nix b/modules/programs/lutris.nix new file mode 100644 index 000000000..1ddf8d9ad --- /dev/null +++ b/modules/programs/lutris.nix @@ -0,0 +1,215 @@ +{ + config, + lib, + pkgs, + ... +}: +let + inherit (lib) + mkOption + mkEnableOption + mkIf + types + optional + optionalAttrs + nameValuePair + mapAttrs' + filterAttrs + attrNames + concatStringsSep + toLower + listToAttrs + getExe + ; + cfg = config.programs.lutris; + settingsFormat = pkgs.formats.yaml { }; +in +{ + options.programs.lutris = { + enable = mkEnableOption "lutris."; + package = mkOption { + default = pkgs.lutris; + description = '' + The lutris package to use. + ''; + type = types.package; + }; + 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; + }; + 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.executable_path = "$\{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.executable_path instead. + ''; + 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 = { + executable_path = 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. + ''; + }; + }; + }; + }; + 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) + ]; + warnings = + let + redundantRunners = attrNames ( + filterAttrs ( + _: runner_config: + runner_config.package != null && runner_config.settings.runner.executable_path != "" + ) cfg.runners + ); + in + mkIf (redundantRunners != [ ]) [ + '' + Under programs.lutris.runners, the following lutris runners had both a + .package and .settings.runner.executable_path options set: + - ${concatStringsSep ", " redundantRunners} + Note that executable_path overrides package, setting both is pointless. + '' + ]; + 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: + { + "${runner_name}" = + (optionalAttrs (runner_config.settings.runner != { }) runner_config.settings.runner) + // (optionalAttrs + (runner_config.package != null && runner_config.settings.runner.executable_path == "") + { + executable_path = getExe runner_config.package; + } + ); + } + // optionalAttrs (runner_config.settings.system != { }) { + system = runner_config.settings.system; + } + ); + in + mapAttrs' ( + runner_name: runner_config: + nameValuePair "lutris/runners/${runner_name}.yml" { + source = settingsFormat.generate "${runner_name}.yml" (buildRunnerConfig runner_name runner_config); + } + ) 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}/${toLower package.name}" { + source = package; + }) + ) packages; + steamcompattools = map (proton: proton.steamcompattool) cfg.protonPackages; + in + listToAttrs (buildWineLink "wine" cfg.winePackages ++ buildWineLink "proton" steamcompattools); + }; +} diff --git a/tests/default.nix b/tests/default.nix index 269043b36..96f89a48e 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -378,6 +378,7 @@ import nmtSrc { ./modules/programs/kodi ./modules/programs/kickoff ./modules/programs/looking-glass-client + ./modules/programs/lutris ./modules/programs/mangohud ./modules/programs/mpvpaper ./modules/programs/ncmpcpp-linux diff --git a/tests/modules/programs/lutris/default.nix b/tests/modules/programs/lutris/default.nix new file mode 100644 index 000000000..27865e2b0 --- /dev/null +++ b/tests/modules/programs/lutris/default.nix @@ -0,0 +1,5 @@ +{ + lutris-runners = ./runners-configuration.nix; + # lutris-wine = ./wine-configuration.nix; + lutris-empty = ./empty.nix; +} diff --git a/tests/modules/programs/lutris/empty.nix b/tests/modules/programs/lutris/empty.nix new file mode 100644 index 000000000..8e2409e22 --- /dev/null +++ b/tests/modules/programs/lutris/empty.nix @@ -0,0 +1,14 @@ +{ pkgs, lib, ... }: +{ + programs.lutris.enable = true; + nmt.script = + let + wineRunnersDir = "home-files/.local/share/lutris/runners"; + runnersDir = "home-files/.config/lutris/runners"; + in + '' + assertPathNotExists ${wineRunnersDir}/proton + assertPathNotExists ${wineRunnersDir}/wine + assertPathNotExists ${runnersDir} + ''; +} diff --git a/tests/modules/programs/lutris/runners-configuration.nix b/tests/modules/programs/lutris/runners-configuration.nix new file mode 100644 index 000000000..5d9d3845a --- /dev/null +++ b/tests/modules/programs/lutris/runners-configuration.nix @@ -0,0 +1,50 @@ +{ pkgs, lib, ... }: +{ + programs.lutris = { + enable = true; + runners = { + cemu.package = pkgs.cemu; + pcsx2.settings = { + system.disable_screen_saver = true; + runner.executable_path = "${pkgs.pcsx2}/bin/pcsx2-qt"; + }; + rpcs3 = { + package = pkgs.rpcs3; + settings = { + system.disable_screen_saver = true; + runner.nogui = true; + }; + }; + }; + }; + + nmt.script = + let + runnersDir = "home-files/.config/lutris/runners"; + expectedCemu = builtins.toFile "cemu.yml" '' + cemu: + executable_path: '${lib.getExe pkgs.cemu}' + ''; + expectedPcsx2 = builtins.toFile "pcsx2.yml" '' + pcsx2: + executable_path: '${pkgs.pcsx2}/bin/pcsx2-qt' + system: + disable_screen_saver: true + ''; + expectedRpcs3 = builtins.toFile "rpcs3.yml" '' + rpcs3: + executable_path: '${lib.getExe pkgs.rpcs3}' + nogui: true + system: + disable_screen_saver: true + ''; + in + '' + assertFileExists ${runnersDir}/cemu.yml + assertFileContent ${runnersDir}/cemu.yml ${expectedCemu} + assertFileExists ${runnersDir}/pcsx2.yml + assertFileContent ${runnersDir}/pcsx2.yml ${expectedPcsx2} + assertFileExists ${runnersDir}/rpcs3.yml + assertFileContent ${runnersDir}/rpcs3.yml ${expectedRpcs3} + ''; +} diff --git a/tests/modules/programs/lutris/wine-configuration.nix b/tests/modules/programs/lutris/wine-configuration.nix new file mode 100644 index 000000000..ad53b7011 --- /dev/null +++ b/tests/modules/programs/lutris/wine-configuration.nix @@ -0,0 +1,17 @@ +{ pkgs, lib, ... }: +{ + programs.lutris = { + enable = true; + protonPackages = with pkgs; [ proton-ge-bin ]; + winePackages = with pkgs; [ wineWow64Packages.full ]; + }; + + nmt.script = + let + runnersDir = "home-files/.local/share/lutris/runners"; + in + '' + assertFileExists ${runnersDir}/proton/${lib.toLower pkgs.proton-ge-bin.steamcompattool.name}/proton + assertFileExists ${runnersDir}/wine/${lib.toLower pkgs.wineWow64Packages.name}/bin/wine + ''; +}