XR for Universal Blue and Fedora Atomic Desktops

Compare changes

Choose any two refs to compare.

+7 -2
README.md
··· 4 # AtomicXR # 5 XR for Universal Blue and Fedora Atomic Desktops. 6 7 - > NOTE: The `main` branch is in feature freeze while while AtomicXR is rewritten. Only critical bug fixes will be accepted. See the `zig` branch for more information. 8 9 This repo contains the AtomicXR core libraries and CLI. Other AtomicXR repos can be found below: 10 - [AtomicXR Homebrew Tap](https://tangled.org/@matrixfurry.com/homebrew-atomicxr): XR packaging for Fedora Atomic. ··· 22 From this repo: `./install.sh` 23 24 ## Usage ## 25 - > IMPORTANT: To build profiles in Envision, right click it in your desktop's menu and select "Launch in Build Mode" 26 27 CLI: 28 - Install Envision: `axr envision install`
··· 4 # AtomicXR # 5 XR for Universal Blue and Fedora Atomic Desktops. 6 7 + > [!IMPORTANT] 8 + > AtomicXR is looking for a new maintainer. Development will be slow, and testing may be limited. 9 + > If you notice any problems, please [open an issue](https://tangled.org/@matrixfurry.com/atomic-xr/issues/new)! 10 + > 11 + > See [this blog post](https://blog.matrixfurry.com/3m3q3ok4nus2o) for more information. 12 13 This repo contains the AtomicXR core libraries and CLI. Other AtomicXR repos can be found below: 14 - [AtomicXR Homebrew Tap](https://tangled.org/@matrixfurry.com/homebrew-atomicxr): XR packaging for Fedora Atomic. ··· 26 From this repo: `./install.sh` 27 28 ## Usage ## 29 + > [!IMPORTANT] 30 + > To build profiles in Envision, right click it in your desktop's menu and select "Launch in Build Mode" 31 32 CLI: 33 - Install Envision: `axr envision install`
+1 -1
data/envision-plugins/manifest/wayvr-dashboard.json
··· 9 ], 10 "description": "Launch your favorite desktop applications and games seamlessly within your VR environment (wlx-overlay-s addon)", 11 "short_description": "Game and application launcher for VR", 12 - "exec_path": "/home/linuxbrew/.linuxbrew/bin/wlx-overlay-s", 13 "env_vars": { "GDK_BACKEND": "wayland" }, 14 "dependencies": ["com.matrixfurry.atomicxr.wlx-overlay-s"], 15 "plugin_type": "WayVrDashboard"
··· 9 ], 10 "description": "Launch your favorite desktop applications and games seamlessly within your VR environment (wlx-overlay-s addon)", 11 "short_description": "Game and application launcher for VR", 12 + "exec_path": "/home/linuxbrew/.linuxbrew/bin/wayvr-dashboard", 13 "env_vars": { "GDK_BACKEND": "wayland" }, 14 "dependencies": ["com.matrixfurry.atomicxr.wlx-overlay-s"], 15 "plugin_type": "WayVrDashboard"
+2 -2
docs/contributing.md
··· 33 34 Commit signing is optional but highly recommended. Once you make your first signed commit, all future commits must be signed. 35 36 - Please see the [security policy](./SECURITY.md) for more information. 37 38 ## Submitting changes 39 <!--TODO: Provide more documentation on how pull requests work on Tangled--> ··· 62 If you are unsure, or you had help from automated AI-generation in part of your contribution, please tell us when contributing. It does not mean the contribution will be rejected outright. We will evaluate on a case-by-case basis. We value honesty, and take it into consideration. 63 64 # Security 65 - Please see the [security policy](./SECURITY.md) 66 67 # Maintainers 68
··· 33 34 Commit signing is optional but highly recommended. Once you make your first signed commit, all future commits must be signed. 35 36 + Please see the [security policy](./security.md) for more information. 37 38 ## Submitting changes 39 <!--TODO: Provide more documentation on how pull requests work on Tangled--> ··· 62 If you are unsure, or you had help from automated AI-generation in part of your contribution, please tell us when contributing. It does not mean the contribution will be rejected outright. We will evaluate on a case-by-case basis. We value honesty, and take it into consideration. 63 64 # Security 65 + Please see the [security policy](./security.md) 66 67 # Maintainers 68
+1 -1
docs/security.md
··· 1 - For the most up-to-date security policy, please see [matrixfurry.com/common](https://tangled.org/@matrixfurry.com/common/blob/main/SECURITY.md). 2 3 # Reporting 4 Please report security issues via [SimpleX](https://smp18.simplex.im/a#Wc2x2IBqqwzgmeGkN0XtKrGMLBxFuxmoYgkTGgGgR0M) or [Email](mailto:shiloh@shilohfen.com).
··· 1 + For the most up-to-date security policy, please see [matrixfurry.com/common](https://tangled.org/@matrixfurry.com/common/blob/main/docs/security.md). 2 3 # Reporting 4 Please report security issues via [SimpleX](https://smp18.simplex.im/a#Wc2x2IBqqwzgmeGkN0XtKrGMLBxFuxmoYgkTGgGgR0M) or [Email](mailto:shiloh@shilohfen.com).
+38
install.nu
···
··· 1 + #!/usr/bin/env nu 2 + # SPDX-License-Identifier: AGPL-3.0-only 3 + # Copyright (c) 2025 Shiloh Fen <shiloh@shilohfen.com> 4 + 5 + use std log 6 + 7 + const self: path = path self . 8 + 9 + def main [ 10 + prefix: string = "~/.local" 11 + --install-library (-l) 12 + --force (-f) # Overwrite existing files 13 + ] { 14 + let prefix = $prefix | path expand 15 + log debug $"Install prefix: ($prefix)" 16 + let lib_dir = $prefix | path join "share" "atomic-xr" "lib" 17 + let axr = open ($self | path join 'src' 'axr.nu.in') --raw 18 + | str replace -a "/@AXR_LIB@/" $"use ($lib_dir)/atomic-xr" 19 + let bin = $prefix | path join "bin" "axr" 20 + 21 + if $force { 22 + rm -rf ($lib_dir | path join "atomic-xr") 23 + rm -f $bin 24 + } 25 + 26 + mkdir $lib_dir 27 + cp -r ($self | path join 'src' 'atomic-xr') $lib_dir 28 + if $install_library { 29 + if $force {rm -f ($NU_LIB_DIRS | first | path join "atomic-xr")} 30 + ln -s ($lib_dir | path join "atomic-xr") ($NU_LIB_DIRS | first) 31 + } 32 + 33 + mkdir ($bin | path dirname) 34 + $axr | save $bin 35 + chmod +x $bin 36 + 37 + log info "AtomicXR CLI has been installed, use `axr -l` for a list of commands." 38 + }
+12
install.sh
···
··· 1 + #!/usr/bin/bash 2 + # SPDX-License-Identifier: AGPL-3.0-only 3 + # Copyright (c) 2025 Shiloh Fen <shiloh@shilohfen.com> 4 + 5 + if ! nu -v $>/dev/null; then 6 + brew install nushell \ 7 + || echo -e "\e[35mFailed to install Nushell using Homebrew. Please install Nushell or Homebrew, then re-run this script.\e[0m" 8 + fi 9 + 10 + if nu -v $>/dev/null; then 11 + nu ./install.nu 12 + fi
+119
src/atomic-xr/_debug.nu
···
··· 1 + # SPDX-License-Identifier: AGPL-3.0-only 2 + # Copyright (c) 2025 Shiloh Fen <shiloh@shilohfen.com> 3 + 4 + use std log 5 + 6 + export def main [ 7 + output?: path 8 + --base64 # Output base64 instead of creating a file 9 + --full (-f) # Include extra debug information 10 + ] { 11 + let output = $output | default (mktemp -t --suffix .msgpackz) | path expand 12 + 13 + log info "Gathering debug information" 14 + 15 + let flatpak_installed = flatpak list --columns application | lines 16 + let sys = if $full { 17 + { 18 + host: (sys host | reject hostname) 19 + cpu: (sys cpu) 20 + mem: (sys mem) 21 + temp: (sys temp) 22 + disks: (sys disks) 23 + } 24 + } else { 25 + { 26 + host: (sys host | reject hostname) 27 + } 28 + } 29 + 30 + { 31 + debug_version: "1.0.0" 32 + sys: $sys 33 + brew: { 34 + installed: (which brew | is-not-empty) 35 + doctor: (try {brew doctor o+e>| $in}) 36 + atomicxr_installed: (try { 37 + brew list --full-name 38 + | find -n matrixfurry.com/atomicxr/ 39 + | each {|| brew info --json $in | from json} 40 + | flatten 41 + }) 42 + } 43 + distrobox: { 44 + installed: (which distrobox | is-not-empty) 45 + } 46 + envision: { 47 + container: (try { 48 + distrobox ls --no-color 49 + | parse -r '(?P<id>.*)\s\|\s(?P<name>.*)\s\|\s(?P<status>.*)\s\|\s(?P<image>.*)' 50 + | str trim 51 + | skip 1 52 + | where name == envision 53 + }) 54 + paths_exist: { 55 + bin: ("~/.local/bin/envision" | path exists) 56 + devel_desktop: ("~/.local/share/applications/org.gabmus.envision.Devel.desktop" | path exists) 57 + desktop: ("~/.local/share/applications/org.gabmus.envision.desktop" | path exists) 58 + localshare_bin: ("~/.local/share/bin/envision" | path exists) 59 + config: ("~/.config/envision/envision.json" | path exists) 60 + } 61 + config: (try {open ~/.config/envision/envision.json}) 62 + } 63 + paths_exist: { 64 + local: { 65 + _self: ("~/.local" | path exists) 66 + share: { 67 + _self: ("~/.local/share" | path exists) 68 + applications: ("~/.local/share/applications" | path exists) 69 + } 70 + bin: ("~/.local/bin" | path exists) 71 + } 72 + } 73 + steam: { 74 + paths: { 75 + dotsteam: (try {ls -lD ~/.steam}) 76 + localshare: (try {ls -lD ~/.local/share/Steam}) 77 + flatpak: ("~/.var/app/com.valvesoftware.Steam" | path exists) 78 + } 79 + } 80 + rpm-ostree: { 81 + requested: (rpm-ostree status --json | from json | get deployments | first | get requested-packages) 82 + } 83 + flatpak: { 84 + installed: { 85 + steam: ($flatpak_installed has "com.valvesoftware.Steam") 86 + gaiasky: ($flatpak_installed has "space.gaiasky.GaiaSky") 87 + } 88 + } 89 + } 90 + | to msgpackz 91 + | if $base64 { 92 + print (ansi cyan) 93 + $in | encode base64 | print 94 + print (ansi reset) 95 + } else { 96 + save -f $output 97 + log info $"Successfully created debug file. It has been saved to ($output)" 98 + } 99 + 100 + if $base64 { 101 + log info $"If you'd like to check what info that contains, use `axr _debug decode --base64 {base64}`" 102 + } else { 103 + log info $"If you'd like to check what info it contains, use `axr _debug decode ($output)`" 104 + } 105 + } 106 + 107 + export def decode [ 108 + input # Base64 or file to decode 109 + --base64 110 + --explore (-e) # Explore output 111 + ] { 112 + if $base64 { 113 + $input | decode base64 114 + } else { 115 + open $input 116 + } 117 + | from msgpackz 118 + | if $explore {explore} else {$in} 119 + }
+35
src/atomic-xr/_migrate.nu
···
··· 1 + # SPDX-License-Identifier: AGPL-3.0-only 2 + # Copyright (c) 2025 Shiloh Fen <shiloh@shilohfen.com> 3 + 4 + use std log 5 + use envision.nu 6 + 7 + # Migrate from version 1.x.x to 2.x.x 8 + export def "v1" [] { 9 + log info "Starting migration: 1.x.x -> 2.x.x" 10 + 11 + log info "Updating Envision config" 12 + do { 13 + const envision_config_path = "~/.config/envision/envision.json" | path expand 14 + 15 + let envision_config = try {open $envision_config_path} catch { 16 + log info "Envision config not found, no config migration nessesary" 17 + return 18 + } 19 + let plugins = $envision_config.plugins 20 + | reject -o ...("cc.dwagon.atomic-xr.{wlx-overlay-s,motoc,oscavmgr.project-babble,oscavmgr.wivrn}" | str expand) 21 + 22 + $envision_config 23 + | merge {plugins: $plugins} 24 + | save -f $envision_config_path 25 + log info "Envision plugins unregistered successfully" 26 + } 27 + 28 + if ("~/.local/bin/envision" | path exists) { 29 + envision upgrade 30 + } else { 31 + log info "Envision is not installed, no upgrade nessesary" 32 + } 33 + 34 + log info "Migration completed: 1.x.x -> 2.x.x" 35 + }
+12
src/atomic-xr/bin/oscavmgr-launch.sh
···
··· 1 + #!/usr/bin/env bash 2 + # SPDX-License-Identifier: AGPL-3.0-only 3 + # Copyright (c) 2025 Shiloh Fen <shiloh@shilohfen.com> 4 + # 5 + # Portions of this file are derived from OscAvMgr, which is licensed under the MIT License 6 + # Copyright (c) 2023 galister 7 + 8 + trap 'jobs -p | xargs kill' EXIT 9 + 10 + VrcAdvert OscAvMgr 9402 9002 --tracking & 11 + 12 + oscavmgr "${@}"
+134
src/atomic-xr/envision.nu
···
··· 1 + # SPDX-License-Identifier: AGPL-3.0-only 2 + # Copyright (c) 2025 Shiloh Fen <shiloh@shilohfen.com> 3 + 4 + const repo = "~/.local/share/atomic-xr/envision" | path expand 5 + const envision_config_path = "~/.config/envision/envision.json" | path expand 6 + 7 + const self = path self . 8 + 9 + use std log 10 + use runtime.nu 11 + 12 + export def install [ 13 + --no-monado (-m) # Don't install runtime deps for Monado 14 + --no-slam (-s) # Don't install runtime deps for SLAM tracking with Basalt 15 + --dev (-d) # Use main branch rather than latest release 16 + ] { 17 + runtime install xr (if not $no_monado {"monado"}) (if not $no_slam {"slam"}) 18 + 19 + distrobox create envision --no-entry -Y -i registry.fedoraproject.org/fedora-toolbox:(sys host | get os_version) 20 + distrobox enter envision -- sudo dnf install -y nu 21 + upgrade --dev=($dev) 22 + log info "Successfully installed Envision" 23 + } 24 + 25 + export def uninstall [] { 26 + const files = [ 27 + ~/.local/bin/envision 28 + ~/.local/share/applications/org.gabmus.envision.Devel.desktop 29 + ~/.local/share/applications/org.gabmus.envision.desktop 30 + ~/.local/share/bin/envision 31 + ~/.local/share/icons/hicolor/scalable/apps/org.gabmus.envision* 32 + ~/.local/share/metainfo/org.gabmus.envision.appdata.xml 33 + ] 34 + 35 + try {distrobox rm -f envision} 36 + log info "Removing files" 37 + rm -f ...$files 38 + log info "Successfully uninstalled Envision" 39 + } 40 + 41 + export def upgrade [ 42 + --dev (-d) # Use main branch rather than latest release 43 + ] { 44 + distrobox upgrade envision 45 + 46 + mkdir ($repo | path dirname) 47 + if ($repo | path exists) { 48 + cd $repo 49 + git reset --hard # Reset patches 50 + git switch main 51 + git pull 52 + } else { 53 + git clone "https://gitlab.com/gabmus/envision.git" $repo 54 + cd $repo 55 + } 56 + 57 + if not $dev { 58 + git switch --detach (git tag -l --sort=v:refname | lines | last) 59 + } 60 + 61 + let patches = $self | path join patches envision 62 + 63 + ls $patches 64 + | get name 65 + | each {|patch| 66 + git apply $patch 67 + } 68 + 69 + { 70 + if (rpm -q rpmfusion-free-release | complete | get exit_code) != 0 { 71 + sudo dnf install -y https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-(rpm -E %fedora).noarch.rpm 72 + sudo dnf swap -y ffmpeg-free ffmpeg --allowerasing 73 + } 74 + 75 + sudo dnf builddep -y envision 76 + 77 + # TODO: Only install dependencies required for the requested profiles 78 + [ 79 + # WiVRn 80 + envision-wivrn 81 + avahi-devel 82 + avahi-glib-devel 83 + cli11-devel 84 + glslang-devel 85 + glslc 86 + gstreamer1-plugins-base-devel 87 + gstreamer1-devel 88 + ffmpeg-devel 89 + libnotify-devel 90 + libva-devel 91 + json-devel 92 + x264-devel 93 + pipewire-devel 94 + cmake 95 + gcc-c++ 96 + clang19-devel 97 + openxr-devel 98 + android-tools 99 + librsvg2-devel 100 + libarchive-devel 101 + 102 + # Monado 103 + envision-monado 104 + cmake 105 + gcc-c++ 106 + glslang-devel 107 + glslc 108 + libbsd-devel 109 + libusb1 110 + libusb1-devel 111 + SDL2-devel 112 + wayland-protocols-devel 113 + openxr-devel 114 + 115 + # WMR 116 + fmt-devel 117 + fmt-devel 118 + git-lfs 119 + glew-devel 120 + glew-devel 121 + gtest-devel 122 + jq 123 + lz4-devel 124 + tbb-devel 125 + ] | sudo dnf install -y ...$in 126 + 127 + meson setup build -Dprefix=($env.HOME)/.local --reconfigure 128 + ninja -C build 129 + ninja -C build install 130 + } 131 + | distrobox enter envision -- nu -c $"do (view source $in)" 132 + 133 + log info "Successfully upgraded Envision" 134 + }
+91
src/atomic-xr/flatpak.nu
···
··· 1 + # SPDX-License-Identifier: AGPL-3.0-only 2 + # Copyright (c) 2025 Shiloh Fen <shiloh@shilohfen.com> 3 + 4 + use std log 5 + 6 + const xr_permissions = { 7 + filesystem: [ 8 + "xdg-data/envision:ro" 9 + "xdg-run/monado_comp_ipc" 10 + "/var/lib/flatpak/app/io.github.wivrn.wivrn:ro" 11 + "~/.var/app/io.github.wivrn.wivrn:ro" 12 + "xdg-run/wivrn" 13 + "xdg-config/openxr:ro" 14 + "xdg-config/openvr:ro" 15 + ] 16 + } 17 + 18 + def "permissions allow" [appid: string] { 19 + flatpak override --user ...( 20 + $"--filesystem={($xr_permissions.filesystem | str join ',')}" | str expand 21 + ) $appid 22 + } 23 + 24 + def "permissions revoke" [appid: string] { 25 + flatpak override --user ...( 26 + $"--nofilesystem={($xr_permissions.filesystem | str replace -r ':.*' '' | str join ',')}" | str expand 27 + ) $appid 28 + } 29 + 30 + export module steam { 31 + # Enable XR support for Steam Flatpak 32 + export def enable-xr [] { 33 + permissions allow com.valvesoftware.Steam 34 + 35 + if not ("~/.steam" | path exists) { 36 + ln -s ~/.var/app/com.valvesoftware.Steam/.steam ~/ 37 + log debug "Symlinked Steam Flatpak's .steam directory to ~/.steam." 38 + } else if (ls -lD ~/.steam).0.target != ("~/.var/app/com.valvesoftware.Steam/.steam/" | path expand) { 39 + error make { 40 + msg: "Could not link Steam Flatpak's .steam directory to ~/.steam." 41 + help: "Back up or remove your existing ~/.steam directory, then run this command again." 42 + } 43 + } 44 + 45 + log info "Successfully set up Steam Flatpak XR." 46 + } 47 + 48 + # Disable XR support for Steam Flatpak 49 + export def disable-xr [] { 50 + permissions revoke com.valvesoftware.Steam 51 + 52 + if not ("~/.steam" | path exists) { 53 + log debug "~/.steam does not exist." 54 + } else if (ls -lD ~/.steam).0.target == ("~/.var/app/com.valvesoftware.Steam/.steam/" | path expand) { 55 + rm ~/.steam 56 + if ("~/.steam.bak-axr" | path exists) { 57 + mv ~/.steam.bak-axr ~/.steam 58 + } 59 + } 60 + 61 + log info "Successfully reset Steam Flatpak XR." 62 + } 63 + } 64 + 65 + export module gaiasky { 66 + # Enable VR mode for Gaia Sky 67 + export def enable-vr [] { 68 + permissions allow space.gaiasky.GaiaSky 69 + [ 70 + (open /var/lib/flatpak/app/space.gaiasky.GaiaSky/current/active/export/share/applications/space.gaiasky.GaiaSky.desktop 71 + | str replace "Exec=/usr/bin/flatpak run --branch=stable --arch=x86_64 --command=gaiasky space.gaiasky.GaiaSky" "Exec=/usr/bin/flatpak run --branch=stable --arch=x86_64 --command=gaiasky space.gaiasky.GaiaSky -vr") 72 + "Actions=DesktopMode;" 73 + "" 74 + "[Desktop Action DesktopMode]" 75 + "Exec=/usr/bin/flatpak run --branch=stable --arch=x86_64 --command=gaiasky space.gaiasky.GaiaSky" 76 + "Name=Launch in Desktop Mode" 77 + ] 78 + | str join "\n" 79 + | save -f ~/.local/share/applications/space.gaiasky.GaiaSky.desktop 80 + 81 + log info "Successfully enabled Gaia Sky's VR mode." 82 + } 83 + 84 + # Disable VR mode for Gaia Sky 85 + export def disable-vr [] { 86 + permissions revoke space.gaiasky.GaiaSky 87 + rm -f ~/.local/share/applications/space.gaiasky.GaiaSky.desktop 88 + 89 + log info "Successfully disabled Gaia Sky's VR mode." 90 + } 91 + }
+12
src/atomic-xr/homebrew.nu
···
··· 1 + # SPDX-License-Identifier: AGPL-3.0-only 2 + # Copyright (c) 2025 Shiloh Fen <shiloh@shilohfen.com> 3 + 4 + # Tap AtomicXR Homebrew 5 + export def tap [] { 6 + brew tap matrixfurry.com/atomicxr https://tangled.sh/@matrixfurry.com/homebrew-atomicxr 7 + } 8 + 9 + # Untap AtomicXR Homebrew 10 + export def untap [] { 11 + brew untap shiloh/atomicxr 12 + }
+12
src/atomic-xr/mod.nu
···
··· 1 + # SPDX-License-Identifier: AGPL-3.0-only 2 + # Copyright (c) 2025 Shiloh Fen <shiloh@shilohfen.com> 3 + 4 + export use _debug.nu 5 + export use _migrate.nu 6 + export use envision.nu 7 + export use flatpak.nu 8 + export use homebrew.nu 9 + export use oscavmgr.nu 10 + export use runtime.nu 11 + export use steamvr-lh.nu 12 + export use wayvr.nu
+18
src/atomic-xr/oscavmgr.nu
···
··· 1 + # SPDX-License-Identifier: AGPL-3.0-only 2 + # Copyright (c) 2025 Shiloh Fen <shiloh@shilohfen.com> 3 + 4 + const self = path self . 5 + 6 + # Start OSC Avatar Manager and advertise it using VrcAdvert 7 + export def start [ 8 + module: string # babble, openxr (WiVRn), alvr 9 + ] { 10 + if (which oscavmgr | length) == 0 or (which VrcAdvert | length) == 0 { 11 + error make -u { 12 + msg: "OscAvMgr and VrcAdvert are not installed" 13 + help: "Use `brew install oscavmgr vrc-advert` to install them" 14 + } 15 + } 16 + 17 + exec ($self | path join 'bin' 'oscavmgr-launch.sh') $module 18 + }
+25
src/atomic-xr/patches/envision/0001-Add-build-mode-to-desktop-entry.patch
···
··· 1 + From d2359c7ad267214a1bc63775c5d95a5a09cd19cb Mon Sep 17 00:00:00 2001 2 + From: "@matrixfurry.com" <did:plc:zmjoeu3stwcn44647rhxa44o> 3 + Date: Fri, 5 Sep 2025 13:38:33 -0500 4 + Subject: [PATCH 1/4] Add build mode to desktop entry 5 + 6 + --- 7 + data/org.gabmus.envision.desktop.in.in | 5 +++++ 8 + 1 file changed, 5 insertions(+) 9 + 10 + diff --git a/data/org.gabmus.envision.desktop.in.in b/data/org.gabmus.envision.desktop.in.in 11 + index 31ac287..394ff19 100644 12 + --- a/data/org.gabmus.envision.desktop.in.in 13 + +++ b/data/org.gabmus.envision.desktop.in.in 14 + @@ -12,3 +12,8 @@ Keywords=vr;virtual;reality;monado; 15 + Icon=@APP_ID@ 16 + StartupNotify=true 17 + X-GNOME-UsesNotifications=true 18 + +Actions=BuildMode; 19 + + 20 + +[Desktop Action BuildMode] 21 + +Exec=distrobox enter envision -- @CMD_NAME@ 22 + +Name=Launch in Build Mode 23 + -- 24 + 2.51.0 25 +
+35
src/atomic-xr/patches/envision/0002-Replace-plugin-manifests.patch
···
··· 1 + From 7e56cd4c602069392be88e2ecd87dff6bf993f60 Mon Sep 17 00:00:00 2001 2 + From: "@matrixfurry.com" <did:plc:zmjoeu3stwcn44647rhxa44o> 3 + Date: Fri, 5 Sep 2025 13:45:50 -0500 4 + Subject: [PATCH 2/4] Replace plugin manifests 5 + 6 + --- 7 + src/ui/plugins/mod.rs | 12 ++++++++---- 8 + 1 file changed, 8 insertions(+), 4 deletions(-) 9 + 10 + diff --git a/src/ui/plugins/mod.rs b/src/ui/plugins/mod.rs 11 + index cb18440..c4902fe 100644 12 + --- a/src/ui/plugins/mod.rs 13 + +++ b/src/ui/plugins/mod.rs 14 + @@ -163,10 +163,14 @@ impl Plugin { 15 + 16 + /// urls to manifest json files representing plugins. 17 + /// each manifest should be json and the link should always point to the latest version 18 + -const MANIFESTS: [&str;3] = [ 19 + - "https://github.com/galister/wlx-overlay-s/raw/refs/heads/meta/com.github.galister.wlx-overlay-s.json", 20 + - "https://github.com/StardustXR/telescope/raw/refs/heads/main/envision/org.stardustxr.telescope.json", 21 + - "https://github.com/olekolek1000/wayvr-dashboard/raw/refs/heads/meta/dev.oo8.wayvr_dashboard.json", 22 + +const MANIFESTS: [&str;7] = [ 23 + + "https://tangled.org/matrixfurry.com/atomic-xr/raw/main/data/envision-plugins/manifest/wlx-overlay-s.json", 24 + + "https://tangled.org/matrixfurry.com/atomic-xr/raw/main/data/envision-plugins/manifest/wayvr-dashboard.json", 25 + + "https://tangled.org/matrixfurry.com/atomic-xr/raw/main/data/envision-plugins/manifest/lovr-playspace.json", 26 + + "https://tangled.org/matrixfurry.com/atomic-xr/raw/main/data/envision-plugins/manifest/motoc.json", 27 + + "https://tangled.org/matrixfurry.com/atomic-xr/raw/main/data/envision-plugins/manifest/oscavmgr.openxr.json", 28 + + "https://tangled.org/matrixfurry.com/atomic-xr/raw/main/data/envision-plugins/manifest/oscavmgr.babble.json", 29 + + "https://tangled.org/matrixfurry.com/atomic-xr/raw/main/data/envision-plugins/manifest/index-camera-passthrough.json", 30 + ]; 31 + 32 + pub async fn refresh_plugins() -> anyhow::Result<Vec<anyhow::Result<Plugin>>> { 33 + -- 34 + 2.51.0 35 +
+31
src/atomic-xr/patches/envision/0003-Modify-plugin-download-error-message.patch
···
··· 1 + From e8f9266d41e4e9275b5031c63a017255749fefb1 Mon Sep 17 00:00:00 2001 2 + From: "@matrixfurry.com" <did:plc:zmjoeu3stwcn44647rhxa44o> 3 + Date: Fri, 5 Sep 2025 23:07:27 -0500 4 + Subject: [PATCH 3/4] Modify plugin download error message 5 + 6 + --- 7 + src/ui/plugins/store.rs | 8 ++------ 8 + 1 file changed, 2 insertions(+), 6 deletions(-) 9 + 10 + diff --git a/src/ui/plugins/store.rs b/src/ui/plugins/store.rs 11 + index 0faf99a..17d1534 100644 12 + --- a/src/ui/plugins/store.rs 13 + +++ b/src/ui/plugins/store.rs 14 + @@ -357,12 +357,8 @@ impl AsyncComponent for PluginStore { 15 + None => { 16 + alert( 17 + "Download failed", 18 + - Some(&format!( 19 + - "Downloading {} {} failed:\n\nNo executable url provided for this plugin, this is likely a bug!", 20 + - plugin.name, 21 + - plugin.version.as_ref().unwrap_or(&"(no version)".to_string())) 22 + - ), 23 + - Some(&self.win.as_ref().unwrap().clone().upcast::<gtk::Window>()) 24 + + Some("Please use Homebrew to install plugins."), 25 + + Some(&self.win.as_ref().unwrap().clone().upcast::<gtk::Window>()), 26 + ); 27 + } 28 + }; 29 + -- 30 + 2.51.0 31 +
+27
src/atomic-xr/patches/envision/0004-Add-plugin-to-config-when-enabled.patch
···
··· 1 + From 39e8a8787ef0b0bb69c8754a880bb6bc4b2fe707 Mon Sep 17 00:00:00 2001 2 + From: "@matrixfurry.com" <did:plc:zmjoeu3stwcn44647rhxa44o> 3 + Date: Sat, 6 Sep 2025 01:13:52 -0500 4 + Subject: [PATCH 4/4] Add plugin to config when enabled 5 + 6 + --- 7 + src/ui/plugins/store.rs | 4 ++++ 8 + 1 file changed, 4 insertions(+) 9 + 10 + diff --git a/src/ui/plugins/store.rs b/src/ui/plugins/store.rs 11 + index 17d1534..c8f989d 100644 12 + --- a/src/ui/plugins/store.rs 13 + +++ b/src/ui/plugins/store.rs 14 + @@ -413,6 +413,10 @@ impl AsyncComponent for PluginStore { 15 + self.set_locked(false); 16 + } 17 + Self::Input::SetEnabled(signal_sender, plugin, enabled) => { 18 + + // HACK: I'm sure there's a much better way to do this, but I'm not a Rust 19 + + // HACK: ...developer, and this seems to work fine. 20 + + self.add_plugin_to_config(&sender, plugin.clone()); 21 + + 22 + if let Some(cp) = self.config_plugins.get_mut(&plugin.appid) { 23 + if let Err(e) = plugin.set_enabled(enabled) { 24 + error!( 25 + -- 26 + 2.51.0 27 +
+124
src/atomic-xr/runtime.nu
···
··· 1 + # SPDX-License-Identifier: AGPL-3.0-only 2 + # Copyright (c) 2025 Shiloh Fen <shiloh@shilohfen.com> 3 + 4 + use std log 5 + 6 + const runtime_deps = { 7 + xr: [openxr-libs openvr-api] 8 + monado: [ 9 + libuvc 10 + openhmd 11 + opencv 12 + onnxruntime 13 + librealsense 14 + opencv-video 15 + ] 16 + slam: [boost] 17 + } 18 + 19 + # Install runtime dependencies 20 + export def install [ 21 + ...groups: string # Package groups to install 22 + ] { 23 + ensure-installed (get-packages $groups) 24 + } 25 + 26 + # Uninstall runtime dependencies 27 + export def uninstall [ 28 + ...groups: string # Package groups to install 29 + ] { 30 + ensure-not-installed (get-packages $groups) 31 + } 32 + 33 + def get-packages [ 34 + groups: list<string> 35 + ] { 36 + if ($groups | is-empty) { 37 + $runtime_deps 38 + | transpose group packages 39 + } else { 40 + validate $groups 41 + 42 + $runtime_deps 43 + | transpose group packages 44 + | where group in $groups 45 + } 46 + | get packages 47 + | flatten 48 + } 49 + 50 + def validate [ 51 + groups: list<string> 52 + ] { 53 + let avaliable_groups = $runtime_deps 54 + | transpose group packages 55 + | get group 56 + 57 + $groups 58 + | each {|group| 59 + if $group not-in $avaliable_groups { 60 + error make { 61 + msg: "Unknown package group" 62 + label: { 63 + span: (metadata $group).span 64 + text: $"Unknown package group: ($group)" 65 + } 66 + help: $"Avaliable groups: ($avaliable_groups | str join ', ')" 67 + } 68 + } 69 + } 70 + } 71 + 72 + def ensure-installed [ 73 + packages: list<string> 74 + ] { 75 + let missing_packages = $packages 76 + | each {|package| 77 + if (rpm -q $package | complete | get exit_code) != 0 { 78 + $package 79 + } 80 + } 81 + 82 + if ($missing_packages | is-not-empty) { 83 + if (sys host).name == "Fedora Linux" { 84 + pkexec dnf install ...$missing_packages 85 + } else { 86 + rpm-ostree install ...$missing_packages 87 + 88 + job spawn { 89 + match (notify-send -u critical "Package(s) installed" "Please reboot or use `rpm-ostree ex apply-live` to apply changes." -A reboot="Reboot" -A live="Apply Changes Live") { 90 + "reboot" => (systemctl reboot) 91 + "live" => (pkexec rpm-ostree ex apply-live) 92 + } 93 + } 94 + null 95 + } 96 + } 97 + } 98 + 99 + def ensure-not-installed [ 100 + packages: list<string> 101 + ] { 102 + let installed_packages = $packages 103 + | each {|package| 104 + if (rpm -q $package | complete | get exit_code) == 0 { 105 + $package 106 + } 107 + } 108 + 109 + if ($installed_packages | is-not-empty) { 110 + if (sys host).name == "Fedora Linux" { 111 + pkexec dnf remove ...$installed_packages 112 + } else { 113 + rpm-ostree remove ...$installed_packages 114 + 115 + job spawn { 116 + match (notify-send -u critical "Package(s) removed" "Please reboot or use `rpm-ostree ex apply-live` to apply changes." -A reboot="Reboot" -A live="Apply Changes Live") { 117 + "reboot" => (systemctl reboot) 118 + "live" => (pkexec rpm-ostree ex apply-live --allow-replacement) 119 + } 120 + } 121 + null 122 + } 123 + } 124 + }
+92
src/atomic-xr/steamvr-lh.nu
···
··· 1 + # SPDX-License-Identifier: AGPL-3.0-only 2 + # Copyright (c) 2025 Shiloh Fen <shiloh@shilohfen.com> 3 + 4 + use std log 5 + 6 + def "ask yn" [prompt: string] { 7 + loop { 8 + match (input -n 1 $"($prompt) [y/n]: " | str downcase) { 9 + "y" => (return true) 10 + "n" => (return false) 11 + _ => (log error "Invalid option, please choose `y` or `n`.") 12 + } 13 + } 14 + } 15 + 16 + # SteamVR Quick Calibration 17 + export def calibrate [] { 18 + # TODO: Support using native steam installation 19 + log warning "This is intended for Flatpak Steam, if you're using native Steam, please use Envision instead." 20 + log warning "If you'd like to use this feature with native Steam, please open an issue on Tangled." 21 + 22 + if (ask yn "Do you own a lighthouse-tracked HMD? (e.g. Index, Vive Pro)") { 23 + input -sn 1 "Please connect your headset and place it on the floor, then press any key to start the calibration." 24 + 25 + log info "Starting SteamVR Lighthouse calibration" 26 + 27 + let vrcmd_bin: path = $"($env.HOME)/.steam/root/steamapps/common/SteamVR/bin/linux64/vrcmd" 28 + let environment = $"LD_LIBRARY_PATH=('~/.var/app/com.valvesoftware.Steam/.steam/root/steamapps/common/SteamVR/bin/linux64' | path expand)" 29 + 30 + let server = job spawn {flatpak run --command=$"($vrcmd_bin)" --env=$"($environment)" com.valvesoftware.Steam --pollposes} 31 + sleep 2sec # TODO: detect when the server is ready 32 + try { 33 + flatpak run --command=$"($vrcmd_bin)" --env=$"($environment)" com.valvesoftware.Steam --resetroomsetup 34 + } catch {|err| 35 + job kill $server 36 + error make $err 37 + } 38 + 39 + job kill $server 40 + log info "Calibration finished" 41 + } else { 42 + log info "Creating default Lighthouse Database" 43 + 44 + if not ("~/.steam/root/config/lighthouse/" | path exists) { 45 + error make { 46 + msg: "Could not find lighthouse configuration directory." 47 + help: "Make sure you have SteamVR installed, and .steam points to the correct location. You may need to run `axr flatpak steam enable-xr` if you're using the Steam Flatpak." 48 + } 49 + } 50 + 51 + { 52 + base_stations: [] 53 + known_objects: [] 54 + known_universes: [] 55 + revision: 1 56 + } 57 + | save -f ~/.steam/root/config/lighthouse/lighthousedb.json 58 + 59 + log info "Created default Lighthouse Database" 60 + } 61 + } 62 + 63 + # Run lighthouse console natively or via flatpak depending on which is installed 64 + def lh-console [...args]: any -> any { 65 + if (which lighthouse_console | length) > 0 { 66 + log debug "Running lighthouse_console from PATH" 67 + run-external lighthouse_console 68 + } else if not ("~/.steam" | path exists) and ( 69 + "~/.var/app/com.valvesoftware.Steam/.steam/root/steamapps/common/SteamVR/tools/lighthouse/bin/linux64/lighthouse_console" 70 + | path exists 71 + ) or (ls -lD ~/.steam).0.target == ("~/.var/app/com.valvesoftware.Steam/.steam/" | path expand) { 72 + log debug "Running lighthouse_console from steam flatpak" 73 + flatpak run --command=$"($env.HOME)/.var/app/com.valvesoftware.Steam/.steam/root/steamapps/common/SteamVR/tools/lighthouse/bin/linux64/lighthouse_console" com.valvesoftware.Steam ...$args 74 + } else if ("~/.steam" | path exists) { 75 + log debug "Running lighthouse_console from steam native" 76 + run-external $"($env.HOME)/.steam/root/steamapps/common/SteamVR/tools/lighthouse/bin/linux64/lighthouse_console" ...$args 77 + } else { 78 + error make { 79 + msg: "Could not find lighthouse_console" 80 + help: "Please make sure SteamVR is installed" 81 + } 82 + } 83 + } 84 + 85 + # Open SteamVR's lighthouse_console 86 + # 87 + # Useful for pairing, dumping device configs, etc. 88 + export def console [ 89 + ...args # Arguments to pass to lighthouse_console 90 + ] { 91 + lh-console ...$args 92 + }
+20
src/atomic-xr/wayvr.nu
···
··· 1 + # SPDX-License-Identifier: AGPL-3.0-only 2 + # Copyright (c) 2025 Shiloh Fen <shiloh@shilohfen.com> 3 + 4 + # Enable WayVR Dashboard 5 + export def "dashboard enable" [] { 6 + mkdir ~/.config/wlxoverlay/wayvr.conf.d 7 + 8 + {dashboard: { 9 + exec: /home/linuxbrew/.linuxbrew/bin/wayvr-dashboard 10 + args: '' 11 + env: [] 12 + }} 13 + | to yaml 14 + | save ~/.config/wlxoverlay/wayvr.conf.d/dashboard.yaml 15 + } 16 + 17 + # Disable WayVR Dashboard 18 + export def "dashboard disable" [] { 19 + rm ~/.config/wlxoverlay/wayvr.conf.d/dashboard.yaml 20 + }
+64
src/axr.nu.in
···
··· 1 + #!/usr/bin/env nu 2 + # SPDX-License-Identifier: AGPL-3.0-only 3 + # Copyright (c) 2025 Shiloh Fen <shiloh@shilohfen.com> 4 + 5 + use std log 6 + /@AXR_LIB@/ 7 + 8 + def main --wrapped [ 9 + # TODO: --choose (-c) # Interactively choose a function 10 + --help (-h) 11 + --list (-l) # List avaliable commands 12 + --interactive (-i) # Enter interactive shell with the module loaded 13 + ...function 14 + ] { 15 + let function = $function | str join ' ' 16 + if $interactive { 17 + exec nu -e "/@AXR_LIB@/; print $'(ansi yellow)AtomicXR module loaded.(ansi reset)'" 18 + } else if $help or $list or ($function | is-empty) { 19 + print-functions 20 + } else { 21 + # Make sure the function is valid to avoid unhelpful error messages 22 + # This regex checks if $function starts with anything in get-functions 23 + if $function like $"^\(?:(get-functions | str join '|')\)" { 24 + nu -c $"/@AXR_LIB@/; atomic-xr ($function)" 25 + } else { 26 + log error $"Invalid function: `($function)`" 27 + print-functions 28 + } 29 + } 30 + } 31 + 32 + def get-functions [] { 33 + scope modules 34 + | where name == atomic-xr 35 + | get 0.commands.name 36 + } 37 + 38 + def print-functions [] { 39 + get-functions 40 + | where not ($it starts-with "_") 41 + | each {|function| 42 + let help = help atomic-xr $function | lines 43 + 44 + let short_description = $help 45 + | first 46 + | if $in == $"(ansi green)Usage(ansi reset):" { 47 + null 48 + } else { 49 + $in 50 + } 51 + 52 + # let usage = $help 53 + # | get ( 54 + # ($help | enumerate | where item == $"(ansi green)Usage(ansi reset):").0.index + 1 55 + # ) 56 + # | str trim 57 + 58 + { 59 + command: $"(ansi cyan)($function)(ansi reset)" 60 + description: $"(ansi lp)($short_description)(ansi reset)" 61 + } 62 + } 63 + | table --index false --theme rounded 64 + }