diff --git a/nixos/modules/services/video/wivrn.nix b/nixos/modules/services/video/wivrn.nix index f571832053ad6..da6edd02f5b2f 100644 --- a/nixos/modules/services/video/wivrn.nix +++ b/nixos/modules/services/video/wivrn.nix @@ -24,36 +24,66 @@ let # For the application option to work with systemd PATH, we find the store binary path of # the package, concat all of the following strings, and then update the application attribute. - # Application can either be a package or a list that has a package as the first element. - applicationExists = builtins.hasAttr "application" cfg.config.json; + + # Since the json config attribute type "configFormat.type" doesn't allow specifying types for + # individual attributes, we have to type check manually. + + # The application option must be either a package or a list with package as the first element. + + # Checking if an application is provided + applicationAttrExists = builtins.hasAttr "application" cfg.config.json; applicationListNotEmpty = ( if builtins.isList cfg.config.json.application then (builtins.length cfg.config.json.application) != 0 else true ); - applicationCheck = applicationExists && applicationListNotEmpty; + applicationCheck = applicationAttrExists && applicationListNotEmpty; - applicationBinary = ( + # Manage packages and their exe paths + applicationAttr = ( if builtins.isList cfg.config.json.application then builtins.head cfg.config.json.application else cfg.config.json.application ); - applicationStrings = builtins.tail cfg.config.json.application; - - applicationPath = mkIf applicationCheck applicationBinary; + applicationPackage = mkIf applicationCheck applicationAttr; + applicationPackageExe = getExe applicationAttr; + serverPackageExe = getExe cfg.package; + # Managing strings + applicationStrings = builtins.tail cfg.config.json.application; applicationConcat = ( if builtins.isList cfg.config.json.application then - builtins.concatStringsSep " " ([ (getExe applicationBinary) ] ++ applicationStrings) + builtins.concatStringsSep " " ([ applicationPackageExe ] ++ applicationStrings) else - (getExe applicationBinary) + applicationPackageExe ); + + # Manage config file applicationUpdate = recursiveUpdate cfg.config.json ( optionalAttrs applicationCheck { application = applicationConcat; } ); configFile = configFormat.generate "config.json" applicationUpdate; + enabledConfig = optionalString cfg.config.enable "-f ${configFile}"; + + # Manage server executables and flags + serverExec = builtins.concatStringsSep " " ( + [ + serverPackageExe + "--systemd" + enabledConfig + ] + ++ cfg.extraServerFlags + ); + applicationExec = builtins.concatStringsSep " " ( + [ + serverPackageExe + "--application" + enabledConfig + ] + ++ cfg.extraApplicationFlags + ); in { options = { @@ -84,6 +114,19 @@ in }; }; + extraServerFlags = mkOption { + type = types.listOf types.str; + description = "Flags to add to the wivrn service."; + default = [ ]; + example = ''[ "--no-publish-service" ]''; + }; + + extraApplicationFlags = mkOption { + type = types.listOf types.str; + description = "Flags to add to the wivrn-application service. This is NOT the WiVRn startup application."; + default = [ ]; + }; + extraPackages = mkOption { type = types.listOf types.package; description = "Packages to add to the wivrn-application service $PATH."; @@ -96,17 +139,17 @@ in json = mkOption { type = configFormat.type; description = '' - Configuration for WiVRn. The attributes are serialized to JSON in config.json. + Configuration for WiVRn. The attributes are serialized to JSON in config.json. If a config or certain attributes are not provided, the server will default to stock values. Note that the application option must be either a package or a list with package as the first element. - See https://github.com/Meumeu/WiVRn/blob/master/docs/configuration.md + See https://github.com/WiVRn/WiVRn/blob/master/docs/configuration.md ''; default = { }; example = literalExpression '' { - scale = 0.8; + scale = 0.5; bitrate = 100000000; encoders = [ { @@ -119,7 +162,6 @@ in } ]; application = [ pkgs.wlx-overlay-s ]; - tcp_only = true; } ''; }; @@ -130,14 +172,14 @@ in config = mkIf cfg.enable { assertions = [ { - assertion = !applicationCheck || isDerivation applicationBinary; + assertion = !applicationCheck || isDerivation applicationAttr; message = "The application in WiVRn configuration is not a package. Please ensure that the application is a package or that a package is the first element in the list."; } ]; systemd.user = { services = { - # The WiVRn server runs in a hardened service and starts the applications in a different service + # The WiVRn server runs in a hardened service and starts the application in a different service wivrn = { description = "WiVRn XR runtime service"; environment = { @@ -148,9 +190,7 @@ in IPC_EXIT_ON_DISCONNECT = "off"; } // cfg.monadoEnvironment; serviceConfig = { - ExecStart = ( - (getExe cfg.package) + " --systemd" + optionalString cfg.config.enable " -f ${configFile}" - ); + ExecStart = serverExec; # Hardening options CapabilityBoundingSet = [ "CAP_SYS_NICE" ]; AmbientCapabilities = [ "CAP_SYS_NICE" ]; @@ -169,34 +209,24 @@ in RestrictSUIDSGID = true; }; wantedBy = mkIf cfg.autoStart [ "default.target" ]; - restartTriggers = [ - cfg.package - configFile - ]; + restartTriggers = [ cfg.package ]; }; wivrn-application = mkIf applicationCheck { description = "WiVRn application service"; requires = [ "wivrn.service" ]; serviceConfig = { - ExecStart = ( - (getExe cfg.package) + " --application" + optionalString cfg.config.enable " -f ${configFile}" - ); + ExecStart = applicationExec; Restart = "on-failure"; RestartSec = 0; PrivateTmp = true; }; - # We need to add the application to PATH so WiVRn can find it - path = [ applicationPath ] ++ cfg.extraPackages; + path = [ applicationPackage ] ++ cfg.extraPackages; }; }; }; services = { - # WiVRn can be used with some wired headsets so we include xr-hardware - udev.packages = with pkgs; [ - android-udev-rules - xr-hardware - ]; + udev.packages = with pkgs; [ android-udev-rules ]; avahi = { enable = true; publish = { @@ -214,7 +244,7 @@ in environment = { systemPackages = [ cfg.package - applicationPath + applicationPackage ]; pathsToLink = [ "/share/openxr" ]; etc."xdg/openxr/1/active_runtime.json" = mkIf cfg.defaultRuntime { diff --git a/pkgs/by-name/wi/wivrn/force-enable-steamvr_lh.patch b/pkgs/by-name/wi/wivrn/force-enable-steamvr_lh.patch new file mode 100644 index 0000000000000..7ea67c738506c --- /dev/null +++ b/pkgs/by-name/wi/wivrn/force-enable-steamvr_lh.patch @@ -0,0 +1,13 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 6c7956b4c..633fb6f72 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -205,6 +205,7 @@ option(BUILD_TESTING "Enable building of the test suite?" ON) + if(EXISTS "$ENV{HOME}/.steam/root") + set(XRT_HAVE_STEAM YES) + endif() ++set(XRT_HAVE_STEAM YES) + + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(XRT_HAVE_INTERNAL_HID ON) + diff --git a/pkgs/by-name/wi/wivrn/package.nix b/pkgs/by-name/wi/wivrn/package.nix index f795ec72b348f..452d14a3082a7 100644 --- a/pkgs/by-name/wi/wivrn/package.nix +++ b/pkgs/by-name/wi/wivrn/package.nix @@ -1,4 +1,5 @@ { + # Commented packages are not currently in nixpkgs. They don't appear to cause a problem when not present. config, lib, stdenv, @@ -7,49 +8,81 @@ applyPatches, autoAddDriverRunpath, avahi, + bluez, boost, + cjson, cli11, cmake, cudaPackages ? { }, cudaSupport ? config.cudaSupport, + dbus, + # depthai + doxygen, eigen, + elfutils, ffmpeg, freetype, git, + glib, glm, glslang, + gst_all_1, harfbuzz, - libdrm, + hidapi, + # leapsdk + # leapv2 libGL, - libva, - libpulseaudio, libX11, libXrandr, + libbsd, + libdrm, + libdwg, + libjpeg, + libmd, + libnotify, + libpulseaudio, + librealsense, + librsvg, + libsurvive, + libunwind, + libusb1, + libuvc, + libva, + makeDesktopItem, nix-update-script, nlohmann_json, onnxruntime, + opencv4, + openhmd, + openvr, openxr-loader, + orc, + # percetto pipewire, pkg-config, python3, + qt6, + SDL2, shaderc, spdlog, systemd, udev, vulkan-headers, vulkan-loader, - vulkan-tools, + wayland, + wayland-protocols, + wayland-scanner, x264, }: stdenv.mkDerivation (finalAttrs: { pname = "wivrn"; - version = "0.19"; + version = "0.22"; src = fetchFromGitHub { owner = "wivrn"; repo = "wivrn"; rev = "v${finalAttrs.version}"; - hash = "sha256-DYV+JUWjjhLZLq+4Hv7jxOyxDqQut/mU1X0ZFMoNkDI="; + hash = "sha256-i/CG+zD64cwnu0z1BRkRn7Wm67KszE+wZ5geeAvrvMY="; }; monado = applyPatches { @@ -57,18 +90,17 @@ stdenv.mkDerivation (finalAttrs: { domain = "gitlab.freedesktop.org"; owner = "monado"; repo = "monado"; - rev = "bcbe19ddd795f182df42051e5495e9727db36c1c"; - hash = "sha256-sh5slHROcuC3Dgenu1+hm8U5lUOW48JUbiluYvc3NiQ="; + rev = "aa2b0f9f1d638becd6bb9ca3c357ac2561a36b07"; + hash = "sha256-yfHtkMvX/gyVG0UgpSB6KjSDdCym6Reb9LRb3OortaI="; }; patches = [ - "${finalAttrs.src}/patches/monado/0001-c-multi-disable-dropping-of-old-frames.patch" - "${finalAttrs.src}/patches/monado/0002-ipc-server-Always-listen-to-stdin.patch" - "${finalAttrs.src}/patches/monado/0003-c-multi-Don-t-log-frame-time-diff.patch" - "${finalAttrs.src}/patches/monado/0005-distortion-images.patch" - "${finalAttrs.src}/patches/monado/0008-Use-mipmaps-for-distortion-shader.patch" - "${finalAttrs.src}/patches/monado/0009-convert-to-YCbCr-in-monado.patch" + ./force-enable-steamvr_lh.patch ]; + + postPatch = '' + ${finalAttrs.src}/patches/apply.sh ${finalAttrs.src}/patches/monado/* + ''; }; strictDeps = true; @@ -85,65 +117,126 @@ stdenv.mkDerivation (finalAttrs: { fi ''; - nativeBuildInputs = [ - cmake - git - glslang - pkg-config - python3 - ] ++ lib.optionals cudaSupport [ autoAddDriverRunpath ]; + nativeBuildInputs = + [ + cmake + doxygen + git + glib + glslang + librsvg + pkg-config + python3 + qt6.wrapQtAppsHook + ] + ++ lib.optionals cudaSupport [ + autoAddDriverRunpath + ]; - buildInputs = [ - avahi - boost - cli11 - eigen - ffmpeg - freetype - glm - harfbuzz - libdrm - libGL - libva - libX11 - libXrandr - libpulseaudio - nlohmann_json - onnxruntime - openxr-loader - pipewire - shaderc - spdlog - systemd - udev - vulkan-headers - vulkan-loader - vulkan-tools - x264 - ] ++ lib.optionals cudaSupport [ cudaPackages.cudatoolkit ]; + buildInputs = + [ + avahi + boost + bluez + cjson + cli11 + dbus + eigen + elfutils + ffmpeg + freetype + glib + glm + gst_all_1.gst-plugins-base + gst_all_1.gstreamer + harfbuzz + hidapi + libbsd + libdrm + libdwg + libGL + libjpeg + libmd + libnotify + librealsense + libsurvive + libunwind + libusb1 + libuvc + libva + libX11 + libXrandr + libpulseaudio + nlohmann_json + opencv4 + openhmd + openvr + openxr-loader + onnxruntime + orc + pipewire + qt6.qtbase + qt6.qttools + SDL2 + shaderc + spdlog + systemd + udev + vulkan-headers + vulkan-loader + wayland + wayland-protocols + wayland-scanner + x264 + ] + ++ lib.optionals cudaSupport [ + cudaPackages.cudatoolkit + ]; cmakeFlags = [ + (lib.cmakeBool "WIVRN_USE_NVENC" cudaSupport) (lib.cmakeBool "WIVRN_USE_VAAPI" true) + (lib.cmakeBool "WIVRN_USE_VULKAN" true) (lib.cmakeBool "WIVRN_USE_X264" true) - (lib.cmakeBool "WIVRN_USE_NVENC" cudaSupport) - (lib.cmakeBool "WIVRN_USE_SYSTEMD" true) (lib.cmakeBool "WIVRN_USE_PIPEWIRE" true) (lib.cmakeBool "WIVRN_USE_PULSEAUDIO" true) + (lib.cmakeBool "WIVRN_FEATURE_STEAMVR_LIGHTHOUSE" true) (lib.cmakeBool "WIVRN_BUILD_CLIENT" false) - (lib.cmakeBool "WIVRN_OPENXR_INSTALL_ABSOLUTE_RUNTIME_PATH" true) + (lib.cmakeBool "WIVRN_BUILD_DASHBOARD" true) + (lib.cmakeBool "WIVRN_CHECK_CAPSYSNICE" false) (lib.cmakeBool "FETCHCONTENT_FULLY_DISCONNECTED" true) + (lib.cmakeFeature "WIVRN_OPENXR_MANIFEST_TYPE" "absolute") + (lib.cmakeFeature "GIT_DESC" "${finalAttrs.version}") (lib.cmakeFeature "FETCHCONTENT_SOURCE_DIR_MONADO" "${finalAttrs.monado}") + (lib.cmakeFeature "CUDA_TOOLKIT_ROOT_DIR" "${cudaPackages.cudatoolkit}") + ]; + + desktopItems = [ + (makeDesktopItem { + name = "WiVRn Server"; + desktopName = "WiVRn Server"; + genericName = "WiVRn Server"; + comment = "Play your PC VR games on a standalone headset"; + icon = "io.github.wivrn.wivrn"; + exec = "wivrn-dashboard"; + type = "Application"; + categories = [ + "Network" + "Game" + ]; + }) ]; passthru.updateScript = nix-update-script { }; meta = with lib; { description = "An OpenXR streaming application to a standalone headset"; - homepage = "https://github.com/Meumeu/WiVRn/"; - changelog = "https://github.com/Meumeu/WiVRn/releases/"; + homepage = "https://github.com/WiVRn/WiVRn/"; + changelog = "https://github.com/WiVRn/WiVRn/releases/"; license = licenses.gpl3Only; maintainers = with maintainers; [ passivelemon ]; platforms = platforms.linux; mainProgram = "wivrn-server"; + sourceProvenance = with sourceTypes; [ fromSource ]; }; })