+7
-2
README.md
+7
-2
README.md
···
4
4
# AtomicXR #
5
5
XR for Universal Blue and Fedora Atomic Desktops.
6
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.
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.
8
12
9
13
This repo contains the AtomicXR core libraries and CLI. Other AtomicXR repos can be found below:
10
14
- [AtomicXR Homebrew Tap](https://tangled.org/@matrixfurry.com/homebrew-atomicxr): XR packaging for Fedora Atomic.
···
22
26
From this repo: `./install.sh`
23
27
24
28
## Usage ##
25
-
> IMPORTANT: To build profiles in Envision, right click it in your desktop's menu and select "Launch in Build Mode"
29
+
> [!IMPORTANT]
30
+
> To build profiles in Envision, right click it in your desktop's menu and select "Launch in Build Mode"
26
31
27
32
CLI:
28
33
- Install Envision: `axr envision install`
+1
-1
data/envision-plugins/manifest/wayvr-dashboard.json
+1
-1
data/envision-plugins/manifest/wayvr-dashboard.json
···
9
9
],
10
10
"description": "Launch your favorite desktop applications and games seamlessly within your VR environment (wlx-overlay-s addon)",
11
11
"short_description": "Game and application launcher for VR",
12
-
"exec_path": "/home/linuxbrew/.linuxbrew/bin/wlx-overlay-s",
12
+
"exec_path": "/home/linuxbrew/.linuxbrew/bin/wayvr-dashboard",
13
13
"env_vars": { "GDK_BACKEND": "wayland" },
14
14
"dependencies": ["com.matrixfurry.atomicxr.wlx-overlay-s"],
15
15
"plugin_type": "WayVrDashboard"
+2
-2
docs/contributing.md
+2
-2
docs/contributing.md
···
33
33
34
34
Commit signing is optional but highly recommended. Once you make your first signed commit, all future commits must be signed.
35
35
36
-
Please see the [security policy](./SECURITY.md) for more information.
36
+
Please see the [security policy](./security.md) for more information.
37
37
38
38
## Submitting changes
39
39
<!--TODO: Provide more documentation on how pull requests work on Tangled-->
···
62
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
63
64
64
# Security
65
-
Please see the [security policy](./SECURITY.md)
65
+
Please see the [security policy](./security.md)
66
66
67
67
# Maintainers
68
68
+1
-1
docs/security.md
+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).
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
2
3
3
# Reporting
4
4
Please report security issues via [SimpleX](https://smp18.simplex.im/a#Wc2x2IBqqwzgmeGkN0XtKrGMLBxFuxmoYgkTGgGgR0M) or [Email](mailto:shiloh@shilohfen.com).
+38
install.nu
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+
}