Merge branch 'main' into feat/stardust

This commit is contained in:
Gabriele Musco 2024-01-06 10:37:10 +00:00
commit 348dc13614
18 changed files with 197 additions and 59 deletions

View file

@ -22,8 +22,8 @@ cargo:test:
script: script:
- echo 'deb http://deb.debian.org/debian experimental main' > /etc/apt/sources.list.d/experimental.list - echo 'deb http://deb.debian.org/debian experimental main' > /etc/apt/sources.list.d/experimental.list
- apt-get update - apt-get update
- apt-get -t experimental install libgtk-4-dev libadwaita-1-dev libgtksourceview-5-dev libssl-dev -y - apt-get -t experimental install libgtk-4-dev libadwaita-1-dev libgtksourceview-5-dev libssl-dev libjxl-dev -y
- apt-get install meson ninja-build git desktop-file-utils gettext libjxl-dev file libusb-dev libusb-1.0-0-dev curl -y - apt-get install meson ninja-build git desktop-file-utils gettext file libusb-dev libusb-1.0-0-dev curl -y
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o /tmp/rustup.sh - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o /tmp/rustup.sh
- chmod +x /tmp/rustup.sh - chmod +x /tmp/rustup.sh
- /tmp/rustup.sh -y - /tmp/rustup.sh -y
@ -41,8 +41,8 @@ appimage:
script: script:
- echo 'deb http://deb.debian.org/debian experimental main' > /etc/apt/sources.list.d/experimental.list - echo 'deb http://deb.debian.org/debian experimental main' > /etc/apt/sources.list.d/experimental.list
- apt-get update - apt-get update
- apt-get -t experimental install libgtk-4-dev libadwaita-1-dev libgtksourceview-5-dev libssl-dev -y - apt-get -t experimental install libgtk-4-dev libadwaita-1-dev libgtksourceview-5-dev libssl-dev libjxl-dev -y
- apt-get install meson ninja-build git desktop-file-utils gettext libjxl-dev file libusb-dev libusb-1.0-0-dev curl -y - apt-get install meson ninja-build git desktop-file-utils gettext file libusb-dev libusb-1.0-0-dev curl -y
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o /tmp/rustup.sh - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o /tmp/rustup.sh
- chmod +x /tmp/rustup.sh - chmod +x /tmp/rustup.sh
- /tmp/rustup.sh -y - /tmp/rustup.sh -y

2
Cargo.lock generated
View file

@ -1068,7 +1068,7 @@ dependencies = [
[[package]] [[package]]
name = "libmonado-rs" name = "libmonado-rs"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/technobaboo/libmonado-rs#3b3f098cb131843ee90f078e26362fcefe02b822" source = "git+https://github.com/technobaboo/libmonado-rs#d79d5bf11586b8010d8aa016097ebdb07f683c64"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"cmake", "cmake",

View file

@ -33,14 +33,6 @@ cd envision
./dist/appimage/build_appimage.sh ./dist/appimage/build_appimage.sh
``` ```
<!-- no feature flags for now :)
# Feature flags
|Env var|Values|Default|
|---|---|---|
|`ENVISION_FF_USE_LIBMONADO`|`1`: enabled; `0`: disabled|`0`|
-->
# Common issues # Common issues
## NOSUID with systemd-homed ## NOSUID with systemd-homed

View file

@ -61,6 +61,7 @@ meson.add_dist_script(
global_conf = configuration_data() global_conf = configuration_data()
global_conf.set('APP_ID', application_id) global_conf.set('APP_ID', application_id)
global_conf.set('RESOURCES_BASE_PATH', '/' + base_id.replace('.', '/'))
global_conf.set('PKGDATADIR', pkgdatadir) global_conf.set('PKGDATADIR', pkgdatadir)
global_conf.set('PROFILE', profile) global_conf.set('PROFILE', profile)
global_conf.set('VERSION', version + version_suffix) global_conf.set('VERSION', version + version_suffix)

View file

@ -30,6 +30,7 @@ pub fn get_build_libsurvive_jobs(profile: &Profile, clean_build: bool) -> VecDeq
let mut cmake_vars: HashMap<String, String> = HashMap::new(); let mut cmake_vars: HashMap<String, String> = HashMap::new();
cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "Release".into()); cmake_vars.insert("CMAKE_BUILD_TYPE".into(), "Release".into());
cmake_vars.insert("ENABLE_api_example".into(), "OFF".into()); cmake_vars.insert("ENABLE_api_example".into(), "OFF".into());
cmake_vars.insert("USE_HIDAPI".into(), "ON".into());
cmake_vars.insert("CMAKE_SKIP_INSTALL_RPATH".into(), "YES".into()); cmake_vars.insert("CMAKE_SKIP_INSTALL_RPATH".into(), "YES".into());
cmake_vars.insert("CMAKE_INSTALL_PREFIX".into(), profile.prefix.clone()); cmake_vars.insert("CMAKE_INSTALL_PREFIX".into(), profile.prefix.clone());
cmake_vars.insert( cmake_vars.insert(

View file

@ -2,6 +2,7 @@ use crate::paths::get_exec_prefix;
pub const APP_NAME: &str = "@PRETTY_NAME@"; pub const APP_NAME: &str = "@PRETTY_NAME@";
pub const APP_ID: &str = "@APP_ID@"; pub const APP_ID: &str = "@APP_ID@";
pub const RESOURCES_BASE_PATH: &str = "@RESOURCES_BASE_PATH@";
pub const PKG_DATA_DIR: &str = "@PKGDATADIR@"; pub const PKG_DATA_DIR: &str = "@PKGDATADIR@";
pub const RESOURCES: &str = concat!("@PKGDATADIR@", "/resources.gresource"); pub const RESOURCES: &str = concat!("@PKGDATADIR@", "/resources.gresource");
pub const CMD_NAME: &str = "@CMD_NAME@"; pub const CMD_NAME: &str = "@CMD_NAME@";

View file

@ -1,4 +1,4 @@
use crate::file_utils::get_writer; use crate::{constants::APP_ID, file_utils::get_writer};
use reqwest::{ use reqwest::{
header::{HeaderMap, USER_AGENT}, header::{HeaderMap, USER_AGENT},
Method, Method,
@ -11,7 +11,7 @@ const CHUNK_SIZE: usize = 1024;
fn headers() -> HeaderMap { fn headers() -> HeaderMap {
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
headers.insert(USER_AGENT, "org.gabmus.envision/1.0".parse().unwrap()); headers.insert(USER_AGENT, format!("{}/1.0", APP_ID).parse().unwrap());
headers headers
} }

View file

@ -22,7 +22,7 @@ pub static ENV_VAR_DESCRIPTIONS: Map<&str, &str> = phf_map! {
pub fn env_var_descriptions_as_paragraph() -> String { pub fn env_var_descriptions_as_paragraph() -> String {
ENV_VAR_DESCRIPTIONS ENV_VAR_DESCRIPTIONS
.into_iter() .into_iter()
.map(|(k, v)| format!("{}: {}", k, v)) .map(|(k, v)| format!(" \u{2022} <b>{}</b>: {}", k, v))
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("\n\n") .join("\n\n")
} }

View file

@ -1,13 +1,10 @@
use std::env;
use anyhow::Result; use anyhow::Result;
use constants::{resources, APP_ID, APP_NAME, GETTEXT_PACKAGE, LOCALE_DIR}; use constants::{resources, APP_ID, APP_NAME, GETTEXT_PACKAGE, LOCALE_DIR, RESOURCES_BASE_PATH};
use file_builders::{ use file_builders::{
active_runtime_json::{get_current_active_runtime, set_current_active_runtime_to_steam}, active_runtime_json::{get_current_active_runtime, set_current_active_runtime_to_steam},
openvrpaths_vrpath::{get_current_openvrpaths, set_current_openvrpaths_to_steam}, openvrpaths_vrpath::{get_current_openvrpaths, set_current_openvrpaths_to_steam},
}; };
use gettextrs::LocaleCategory; use gettextrs::LocaleCategory;
use gtk::prelude::ObjectExt;
use relm4::{ use relm4::{
adw, adw,
gtk::{self, gdk, gio, glib}, gtk::{self, gdk, gio, glib},
@ -83,7 +80,7 @@ fn main() -> Result<()> {
} }
let provider = gtk::CssProvider::new(); let provider = gtk::CssProvider::new();
provider.load_from_resource("/org/gabmus/envision/style.css"); provider.load_from_resource(&format!("{}/style.css", RESOURCES_BASE_PATH));
if let Some(display) = gdk::Display::default() { if let Some(display) = gdk::Display::default() {
gtk::style_context_add_provider_for_display( gtk::style_context_add_provider_for_display(
&display, &display,

View file

@ -236,26 +236,45 @@ impl Display for Profile {
impl Default for Profile { impl Default for Profile {
fn default() -> Self { fn default() -> Self {
let uuid = Uuid::new_v4().to_string();
let profile_dir = format!("{}/{}", get_data_dir(), uuid);
Self { Self {
uuid: Uuid::new_v4().to_string(),
name: "Default profile name".into(), name: "Default profile name".into(),
xrservice_path: data_monado_path(), xrservice_path: format!("{}/xrservice", profile_dir),
xrservice_type: XRServiceType::Monado, xrservice_type: XRServiceType::Monado,
xrservice_repo: None, xrservice_repo: None,
xrservice_cmake_flags: HashMap::<String, String>::default(), xrservice_cmake_flags: HashMap::<String, String>::default(),
opencomposite_path: data_opencomposite_path(), features: ProfileFeatures {
features: ProfileFeatures::default(), libsurvive: ProfileFeature {
enabled: false,
path: Some(format!("{}/libsurvive", profile_dir)),
repo: None,
feature_type: ProfileFeatureType::Libsurvive,
},
basalt: ProfileFeature {
enabled: false,
path: Some(format!("{}/basalt", profile_dir)),
repo: None,
feature_type: ProfileFeatureType::Basalt,
},
openhmd: ProfileFeature {
enabled: false,
path: Some(format!("{}/openhmd", profile_dir)),
repo: None,
feature_type: ProfileFeatureType::OpenHmd,
},
mercury_enabled: false,
},
environment: HashMap::new(), environment: HashMap::new(),
prefix: format!( prefix: format!("{}/prefixes/{}", get_data_dir(), uuid),
"{data}/prefixes/default_profile_prefix",
data = get_data_dir()
),
can_be_built: true, can_be_built: true,
pull_on_build: true, pull_on_build: true,
opencomposite_repo: None, opencomposite_repo: None,
opencomposite_path: format!("{}/opencomposite", profile_dir),
editable: true, editable: true,
lighthouse_driver: LighthouseDriver::default(), lighthouse_driver: LighthouseDriver::default(),
xrservice_launch_options: String::default(), xrservice_launch_options: String::default(),
uuid,
} }
} }
} }
@ -307,10 +326,30 @@ impl Profile {
} }
pub fn create_duplicate(&self) -> Self { pub fn create_duplicate(&self) -> Self {
let mut dup = self.clone(); if !self.can_be_built {
dup.uuid = Uuid::new_v4().to_string(); let mut dup = self.clone();
dup.editable = true; dup.uuid = Uuid::new_v4().to_string();
dup.name = format!("Duplicate of {}", dup.name); dup.name = format!("Duplicate of {}", dup.name);
dup.editable = true;
return dup;
}
let mut dup = Self::default();
dup.name = format!("Duplicate of {}", self.name);
dup.xrservice_type = self.xrservice_type.clone();
dup.xrservice_repo = self.xrservice_repo.clone();
dup.xrservice_cmake_flags = self.xrservice_cmake_flags.clone();
dup.features.libsurvive.enabled = self.features.libsurvive.enabled;
dup.features.libsurvive.repo = self.features.libsurvive.repo.clone();
dup.features.basalt.enabled = self.features.basalt.enabled;
dup.features.basalt.repo = self.features.basalt.repo.clone();
dup.features.openhmd.enabled = self.features.openhmd.enabled;
dup.features.openhmd.repo = self.features.openhmd.repo.clone();
dup.features.mercury_enabled = self.features.mercury_enabled;
dup.environment = self.environment.clone();
dup.pull_on_build = self.pull_on_build;
dup.opencomposite_repo = self.opencomposite_repo.clone();
dup.lighthouse_driver = self.lighthouse_driver;
dup.xrservice_launch_options = self.xrservice_launch_options.clone();
dup dup
} }
@ -347,10 +386,14 @@ impl Profile {
} }
pub fn xrservice_binary(&self) -> String { pub fn xrservice_binary(&self) -> String {
match self.xrservice_type { format!(
XRServiceType::Monado => format!("{pfx}/bin/monado-service", pfx = self.prefix), "{}/bin/{}",
XRServiceType::Wivrn => format!("{pfx}/bin/wivrn-server", pfx = self.prefix), self.prefix,
} match self.xrservice_type {
XRServiceType::Monado => "monado-service",
XRServiceType::Wivrn => "wivrn-server",
}
)
} }
pub fn can_start(&self) -> bool { pub fn can_start(&self) -> bool {

View file

@ -61,7 +61,7 @@ impl SimpleComponent for DebugView {
menu! { menu! {
debug_menu: { debug_menu: {
section! { section! {
"Open Envision _Data Folder" => DebugOpenDataAction, "Open _Data Folder" => DebugOpenDataAction,
"Open _Prefix Folder" => DebugOpenPrefixAction, "Open _Prefix Folder" => DebugOpenPrefixAction,
}, },
} }

View file

@ -1,11 +0,0 @@
use lazy_static::lazy_static;
use std::env;
fn get_ff_libmonado_device_enumeration_enabled() -> bool {
env::var("ENVISION_FF_USE_LIBMONADO").unwrap_or_default() == "1"
}
lazy_static! {
pub static ref FF_LIBMONADO_DEVICE_ENUMERATION_ENABLED: bool =
get_ff_libmonado_device_enumeration_enabled();
}

View file

@ -228,7 +228,7 @@ impl SimpleComponent for MainView {
set_label: concat!( set_label: concat!(
"Your current prefix is inside a partition ", "Your current prefix is inside a partition ",
"mounted with the nosuid option. This will prevent ", "mounted with the nosuid option. This will prevent ",
"the Envision runtime from acquiring certain privileges ", "the XR runtime from acquiring certain privileges ",
"and will cause noticeable stutter when running XR ", "and will cause noticeable stutter when running XR ",
"applications." "applications."
), ),

View file

@ -6,7 +6,6 @@ pub mod debug_view;
pub mod devices_box; pub mod devices_box;
pub mod factories; pub mod factories;
pub mod fbt_config_editor; pub mod fbt_config_editor;
pub mod feature_flags;
pub mod install_wivrn_box; pub mod install_wivrn_box;
pub mod job_worker; pub mod job_worker;
pub mod libsurvive_setup_window; pub mod libsurvive_setup_window;

View file

@ -179,6 +179,7 @@ pub fn path_row<F: Fn(Option<String>) + 'static + Clone>(
None => "(None)", None => "(None)",
Some(p) => p.as_str(), Some(p) => p.as_str(),
}) })
.wrap(true)
.build(); .build();
row.add_suffix(path_label); row.add_suffix(path_label);

View file

@ -146,9 +146,9 @@ impl SimpleComponent for ProfileEditor {
"Lighthouse Driver", "Lighthouse Driver",
Some(concat!( Some(concat!(
"Driver for lighhouse tracked XR devices (ie: Valve Index, HTC Vive...). Only applicable for Monado.\n\n", "Driver for lighhouse tracked XR devices (ie: Valve Index, HTC Vive...). Only applicable for Monado.\n\n",
"Vive: 3DOF tracking\n\n", " \u{2022} Vive: 3DOF tracking\n",
"Survive: 6DOF reverse engineered lighthouse tracking provided by Libsurvive\n\n", " \u{2022} Survive: 6DOF reverse engineered lighthouse tracking provided by Libsurvive\n",
"SteamVR: 6DOF lighthouse tracking using the proprietary SteamVR driver", " \u{2022} SteamVR: 6DOF lighthouse tracking using the proprietary SteamVR driver",
)), )),
model.profile.borrow().lighthouse_driver.to_string().as_str(), model.profile.borrow().lighthouse_driver.to_string().as_str(),
LighthouseDriver::iter() LighthouseDriver::iter()

View file

@ -69,11 +69,11 @@ impl SimpleComponent for SteamVrCalibrationBox {
set_hexpand: true, set_hexpand: true,
set_label: concat!( set_label: concat!(
"Run a quick SteamVR calibration.\n\n", "Run a quick SteamVR calibration.\n\n",
"\u{2022} Plug in your HMD and place it on the floor, ", " \u{2022} Plug in your HMD and place it on the floor, ",
"approximately in the middle of your play area\n", "approximately in the middle of your play area\n",
"\u{2022} Make sure your controllers and other VR devices ", " \u{2022} Make sure your controllers and other VR devices ",
"are turned off\n", "are turned off\n",
"\u{2022} Click the Calibrate button and wait for the ", " \u{2022} Click the Calibrate button and wait for the ",
"process to finish\n\n", "process to finish\n\n",
"Note that the orientation of your HMD during this process ", "Note that the orientation of your HMD during this process ",
"will dictate the forward direction of your play area.", "will dictate the forward direction of your play area.",

View file

@ -11,6 +11,21 @@ pub enum XRDeviceRole {
Eyes, Eyes,
HandTrackingLeft, HandTrackingLeft,
HandTrackingRight, HandTrackingRight,
HandheldObject,
LeftFoot,
RightFoot,
LeftShoulder,
RightShoulder,
LeftElbow,
RightElbow,
LeftKnee,
RightKnee,
Waist,
Chest,
Camera,
Keyboard,
GenericTracker, GenericTracker,
/** /**
* Devices with no role * Devices with no role
@ -34,6 +49,21 @@ impl Display for XRDeviceRole {
Self::Eyes => "Eye Tracking", Self::Eyes => "Eye Tracking",
Self::HandTrackingLeft => "Hand tracking left", Self::HandTrackingLeft => "Hand tracking left",
Self::HandTrackingRight => "Hand tracking right", Self::HandTrackingRight => "Hand tracking right",
Self::HandheldObject => "Handheld object",
Self::LeftFoot => "Left foot",
Self::RightFoot => "Right foot",
Self::LeftShoulder => "Left shoulder",
Self::RightShoulder => "Right shoulder",
Self::LeftElbow => "Left elbow",
Self::RightElbow => "Right elbow",
Self::LeftKnee => "Left knee",
Self::RightKnee => "Right knee",
Self::Waist => "Waist",
Self::Chest => "Chest",
Self::Camera => "Camera",
Self::Keyboard => "Keyboard",
Self::GenericTracker => "Generic tracker", Self::GenericTracker => "Generic tracker",
Self::Other => "", Self::Other => "",
}) })
@ -62,6 +92,19 @@ impl XRDeviceRole {
Self::Eyes, Self::Eyes,
Self::HandTrackingLeft, Self::HandTrackingLeft,
Self::HandTrackingRight, Self::HandTrackingRight,
Self::HandheldObject,
Self::LeftFoot,
Self::RightFoot,
Self::LeftShoulder,
Self::RightShoulder,
Self::LeftElbow,
Self::RightElbow,
Self::LeftKnee,
Self::RightKnee,
Self::Waist,
Self::Chest,
Self::Camera,
Self::Keyboard,
Self::GenericTracker, Self::GenericTracker,
] ]
.iter() .iter()
@ -76,6 +119,21 @@ impl XRDeviceRole {
Self::Eyes => "eyes", Self::Eyes => "eyes",
Self::HandTrackingLeft => "hand-tracking-left", Self::HandTrackingLeft => "hand-tracking-left",
Self::HandTrackingRight => "hand-tracking-right", Self::HandTrackingRight => "hand-tracking-right",
Self::HandheldObject => "handheld-object",
Self::LeftFoot => "left-foot",
Self::RightFoot => "right-foot",
Self::LeftShoulder => "left-shoulder",
Self::RightShoulder => "right-shoulder",
Self::LeftElbow => "left-elbow",
Self::RightElbow => "right-elbow",
Self::LeftKnee => "left-knee",
Self::RightKnee => "right-knee",
Self::Waist => "waist",
Self::Chest => "chest",
Self::Camera => "camera",
Self::Keyboard => "keyboard",
Self::GenericTracker => "generic-tracker", // not actually in monado Self::GenericTracker => "generic-tracker", // not actually in monado
Self::Other => "other", // not actually in monado Self::Other => "other", // not actually in monado
} }
@ -90,8 +148,23 @@ impl XRDeviceRole {
Self::Eyes => 4, Self::Eyes => 4,
Self::HandTrackingLeft => 5, Self::HandTrackingLeft => 5,
Self::HandTrackingRight => 6, Self::HandTrackingRight => 6,
Self::GenericTracker => 7,
Self::Other => 8, Self::HandheldObject => 7,
Self::LeftFoot => 8,
Self::RightFoot => 9,
Self::LeftShoulder => 10,
Self::RightShoulder => 11,
Self::LeftElbow => 12,
Self::RightElbow => 13,
Self::LeftKnee => 14,
Self::RightKnee => 15,
Self::Waist => 16,
Self::Chest => 17,
Self::Camera => 18,
Self::Keyboard => 19,
Self::GenericTracker => 20,
Self::Other => 21,
} }
} }
@ -104,6 +177,19 @@ impl XRDeviceRole {
"eyes" => Some(Self::Eyes), "eyes" => Some(Self::Eyes),
"hand-tracking-left" => Some(Self::HandTrackingLeft), "hand-tracking-left" => Some(Self::HandTrackingLeft),
"hand-tracking-right" => Some(Self::HandTrackingRight), "hand-tracking-right" => Some(Self::HandTrackingRight),
"handheld-object" => Some(Self::HandheldObject),
"left-foot" => Some(Self::LeftFoot),
"right-foot" => Some(Self::RightFoot),
"left-shoulder" => Some(Self::LeftShoulder),
"right-shoulder" => Some(Self::RightShoulder),
"left-elbow" => Some(Self::LeftElbow),
"right-elbow" => Some(Self::RightElbow),
"left-knee" => Some(Self::LeftKnee),
"right-knee" => Some(Self::RightKnee),
"waist" => Some(Self::Waist),
"chest" => Some(Self::Chest),
"camera" => Some(Self::Camera),
"keyboard" => Some(Self::Keyboard),
_ => None, _ => None,
} }
} }
@ -117,6 +203,21 @@ impl XRDeviceRole {
"Eye Tracking" => Self::Eyes, "Eye Tracking" => Self::Eyes,
"Hand tracking left" => Self::HandTrackingLeft, "Hand tracking left" => Self::HandTrackingLeft,
"Hand tracking right" => Self::HandTrackingRight, "Hand tracking right" => Self::HandTrackingRight,
"Handheld object" => Self::HandheldObject,
"Left foot" => Self::LeftFoot,
"Right foot" => Self::RightFoot,
"Left shoulder" => Self::LeftShoulder,
"Right shoulder" => Self::RightShoulder,
"Left elbow" => Self::LeftElbow,
"Right elbow" => Self::RightElbow,
"Left knee" => Self::LeftKnee,
"Right knee" => Self::RightKnee,
"Waist" => Self::Waist,
"Chest" => Self::Chest,
"Camera" => Self::Camera,
"Keyboard" => Self::Keyboard,
"Generic tracker" => Self::GenericTracker, "Generic tracker" => Self::GenericTracker,
_ => Self::GenericTracker, _ => Self::GenericTracker,
} }
@ -179,6 +280,19 @@ impl XRDevice {
XRDeviceRole::Right, XRDeviceRole::Right,
XRDeviceRole::HandTrackingLeft, XRDeviceRole::HandTrackingLeft,
XRDeviceRole::HandTrackingRight, XRDeviceRole::HandTrackingRight,
XRDeviceRole::HandheldObject,
XRDeviceRole::LeftFoot,
XRDeviceRole::RightFoot,
XRDeviceRole::LeftShoulder,
XRDeviceRole::RightShoulder,
XRDeviceRole::LeftElbow,
XRDeviceRole::RightElbow,
XRDeviceRole::LeftKnee,
XRDeviceRole::RightKnee,
XRDeviceRole::Waist,
XRDeviceRole::Chest,
XRDeviceRole::Camera,
XRDeviceRole::Keyboard,
XRDeviceRole::Gamepad, XRDeviceRole::Gamepad,
XRDeviceRole::Eyes, XRDeviceRole::Eyes,
] ]