From a51598236f23c89e59ee77eb8e0614358b0e896c Mon Sep 17 00:00:00 2001 From: B1kku <77858854+B1kku@users.noreply.github.com> Date: Thu, 8 May 2025 18:01:35 +0000 Subject: [PATCH] lutris: add module (#6964) Add any number of wine packages Add any number of proton packages Link runners directly from nix instead of using lutris. Specify the steam package lutris should use. Extra packages passed to lutris (mainly since it's often missing essential things, such as umu-launcher) --- modules/lib/maintainers.nix | 6 + modules/modules.nix | 1 + modules/programs/lutris.nix | 215 ++++++++++++++++++ tests/default.nix | 1 + tests/modules/programs/lutris/default.nix | 5 + tests/modules/programs/lutris/empty.nix | 14 ++ .../programs/lutris/runners-configuration.nix | 50 ++++ .../programs/lutris/wine-configuration.nix | 17 ++ 8 files changed, 309 insertions(+) create mode 100644 modules/programs/lutris.nix create mode 100644 tests/modules/programs/lutris/default.nix create mode 100644 tests/modules/programs/lutris/empty.nix create mode 100644 tests/modules/programs/lutris/runners-configuration.nix create mode 100644 tests/modules/programs/lutris/wine-configuration.nix 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 + ''; +}