Compare commits

..

No commits in common. "main" and "3.0.0" have entirely different histories.
main ... 3.0.0

18 changed files with 115 additions and 190 deletions

2
Cargo.lock generated
View file

@ -563,7 +563,7 @@ dependencies = [
[[package]]
name = "envision"
version = "3.0.1"
version = "3.0.0"
dependencies = [
"anyhow",
"ash",

View file

@ -1,6 +1,6 @@
[package]
name = "envision"
version = "3.1.0"
version = "3.0.0"
edition = "2021"
authors = [
"Gabriele Musco <gabmus@disroot.org>",

View file

@ -30,34 +30,6 @@
<url type="bugtracker">@REPO_URL@/issues</url>
<content_rating type="oars-1.0" />
<releases>
<release version="3.1.0" date="2025-04-08">
<description>
<p>What's new</p>
<ul>
<li>don&#x27;t set openvrpaths as read only during profile startup</li>
<li>small design changes to build window ui</li>
<li>add support for vapor openvr compatibility module</li>
<li>remove monado vulkan layers check for nvidia</li>
</ul>
<p>Fixes</p>
<ul>
<li>disable and blacklist wayvr dashboard plugin</li>
<li>monado dependencies: use wayland-protocols-devel on Fedora</li>
</ul>
<p>Other changes</p>
<ul>
<li>clippy</li>
</ul>
</description>
</release>
<release version="3.0.1" date="2025-03-02">
<description>
<p>Fixes</p>
<ul>
<li>libnotify headers path in wivrn depcheck</li>
</ul>
</description>
</release>
<release version="3.0.0" date="2025-03-01">
<description>
<p>Breaking changes</p>

1
dist/arch/PKGBUILD vendored
View file

@ -33,6 +33,7 @@ makedepends=(
)
optdepends=(
'libudev0-shim: steamvr_lh lighthouse driver support'
'monado-vulkan-layers-git: Vulkan layers for NVIDIA users'
)
provides=(envision)
conflicts=(envision)

View file

@ -1,7 +1,7 @@
project(
'envision',
'rust',
version: '3.1.0', # version number row
version: '3.0.0', # version number row
meson_version: '>= 0.59',
license: 'AGPL-3.0-or-later',
)

View file

@ -1,85 +0,0 @@
use crate::{
build_tools::{cmake::Cmake, git::Git},
profile::Profile,
termcolor::TermColor,
ui::job_worker::job::{FuncWorkerData, FuncWorkerOut, WorkerJob},
util::file_utils::{copy_file, rm_rf},
};
use std::{
collections::{HashMap, VecDeque},
fs::create_dir_all,
path::Path,
};
pub fn get_build_vapor_jobs(profile: &Profile, clean_build: bool) -> VecDeque<WorkerJob> {
let mut jobs = VecDeque::<WorkerJob>::new();
jobs.push_back(WorkerJob::new_printer(
"Building VapoR...",
Some(TermColor::Blue),
));
let git = Git {
repo: profile
.ovr_comp
.repo
.as_ref()
.unwrap_or(&"https://github.com/micheal65536/VapoR.git".into())
.clone(),
dir: profile.ovr_comp.path.clone(),
branch: profile
.ovr_comp
.branch
.as_ref()
.unwrap_or(&"master".into())
.clone(),
};
jobs.extend(git.get_pre_build_jobs(profile.pull_on_build));
let build_dir = profile.ovr_comp.path.join("build");
let cmake = Cmake {
env: None,
vars: Some({
let mut cmake_vars: HashMap<String, String> = HashMap::new();
for (k, v) in [
("VAPOR_LOG_SILENT=ON", "ON"),
("CMAKE_BUILD_TYPE", "RelWithDebInfo"),
] {
cmake_vars.insert(k.to_string(), v.to_string());
}
cmake_vars
}),
source_dir: profile.ovr_comp.path.clone(),
build_dir: build_dir.clone(),
};
if !Path::new(&build_dir).is_dir() || clean_build {
rm_rf(&build_dir);
jobs.push_back(cmake.get_prepare_job());
}
jobs.push_back(cmake.get_build_job());
jobs.push_back(WorkerJob::Func(FuncWorkerData {
func: Box::new(move || {
let dest_dir = build_dir.join("bin/linux64");
if let Err(e) = create_dir_all(&dest_dir) {
return FuncWorkerOut {
success: false,
out: vec![format!(
"failed to create dir {}: {e}",
dest_dir.to_string_lossy()
)],
};
}
copy_file(
&build_dir.join("src/vrclient.so"),
&dest_dir.join("vrclient.so"),
);
FuncWorkerOut {
success: true,
out: Vec::default(),
}
}),
}));
jobs
}

View file

@ -4,6 +4,5 @@ pub mod build_mercury;
pub mod build_monado;
pub mod build_opencomposite;
pub mod build_openhmd;
pub mod build_vapor;
pub mod build_wivrn;
pub mod build_xrizer;

View file

@ -37,7 +37,7 @@ fn monado_deps() -> Vec<Dependency> {
packages: HashMap::from([
(LinuxDistro::Arch, "wayland-protocols".into()),
(LinuxDistro::Debian, "wayland-protocols".into()),
(LinuxDistro::Fedora, "wayland-protocols-devel".into()),
(LinuxDistro::Fedora, "wayland-protocols".into()),
(LinuxDistro::Gentoo, "dev-libs/wayland-protocols".into()),
(LinuxDistro::Suse, "wayland-protocols-devel".into()),
]),

View file

@ -253,7 +253,7 @@ fn wivrn_deps() -> Vec<Dependency> {
Dependency {
name: "libnotify-dev".into(),
dep_type: DepType::Include,
filename: "libnotify/notify.h".into(),
filename: "openssl/ssl3.h".into(),
packages: HashMap::from([
(LinuxDistro::Arch, "libnotify".into()),
(LinuxDistro::Alpine, "libnotify-dev".into()),

View file

@ -85,7 +85,6 @@ fn build_steam_openvrpaths() -> OpenVrPaths {
}
pub fn set_current_openvrpaths_to_steam() -> anyhow::Result<()> {
// removing readonly flag just in case, remove this line in the future
set_file_readonly(&get_openvrpaths_vrpath_path(), false)?;
dump_current_openvrpaths(&build_steam_openvrpaths())?;
Ok(())
@ -105,18 +104,19 @@ pub fn build_profile_openvrpaths(profile: &Profile) -> OpenVrPaths {
pub fn set_current_openvrpaths_to_profile(profile: &Profile) -> anyhow::Result<()> {
let dest = get_openvrpaths_vrpath_path();
// removing readonly flag just in case, remove this line in the future
set_file_readonly(&dest, false)?;
backup_steam_openvrpaths();
dump_current_openvrpaths(&build_profile_openvrpaths(profile))?;
set_file_readonly(&dest, true)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::{dump_openvrpaths_to_path, get_openvrpaths_from_path, OpenVrPaths};
use std::path::Path;
use super::{dump_openvrpaths_to_path, get_openvrpaths_from_path, OpenVrPaths};
#[test]
fn can_read_openvrpaths_vrpath_steamvr() {
let ovrp = get_openvrpaths_from_path(Path::new("./test/files/openvrpaths.vrpath")).unwrap();

View file

@ -49,13 +49,13 @@ impl LinuxDistro {
Ok(_) if buf.starts_with("PRETTY_NAME=\"") => {
return buf
.split('=')
.next_back()
.last()
.map(|b| b.trim().trim_matches('"').trim().to_string());
}
Ok(_) if buf.starts_with("NAME=\"") => {
name = buf
.split('=')
.next_back()
.last()
.map(|b| b.trim().trim_matches('"').trim().to_string());
}
_ => {}
@ -79,7 +79,7 @@ impl LinuxDistro {
{
let name = buf
.split('=')
.next_back()
.last()
.unwrap_or_default()
.trim()
.trim_matches('"')

View file

@ -265,7 +265,6 @@ pub enum OvrCompatibilityModuleType {
#[default]
Opencomposite,
Xrizer,
Vapor,
}
impl Display for OvrCompatibilityModuleType {
@ -273,14 +272,13 @@ impl Display for OvrCompatibilityModuleType {
f.write_str(match self {
Self::Opencomposite => "OpenComposite",
Self::Xrizer => "xrizer",
Self::Vapor => "VapoR",
})
}
}
impl OvrCompatibilityModuleType {
pub fn iter() -> Iter<'static, Self> {
[Self::Opencomposite, Self::Xrizer, Self::Vapor].iter()
[Self::Opencomposite, Self::Xrizer].iter()
}
}
@ -291,7 +289,6 @@ impl FromStr for OvrCompatibilityModuleType {
match s.to_lowercase().trim() {
"opencomposite" => Ok(Self::Opencomposite),
"xrizer" => Ok(Self::Xrizer),
"vapor" => Ok(Self::Vapor),
_ => Err(format!("no match for ovr compatibility module `{s}`")),
}
}
@ -302,8 +299,7 @@ impl From<u32> for OvrCompatibilityModuleType {
match value {
0 => Self::Opencomposite,
1 => Self::Xrizer,
2 => Self::Vapor,
_ => panic!("OvrCompatibilityModuleType index out of bounds"),
_ => panic!("OvrCompatibilityModuleType index out of bounds"),
}
}
}
@ -331,9 +327,7 @@ impl ProfileOvrCompatibilityModule {
/// this should correspond to the build output directory
pub fn runtime_dir(&self) -> PathBuf {
match self.mod_type {
OvrCompatibilityModuleType::Opencomposite | OvrCompatibilityModuleType::Vapor => {
self.path.join("build")
}
OvrCompatibilityModuleType::Opencomposite => self.path.join("build"),
OvrCompatibilityModuleType::Xrizer => self.path.join("target/release"),
}
}

View file

@ -33,7 +33,7 @@ pub fn create_about_dialog() -> adw::AboutDialog {
const UNKNOWN: &str = "UNKNOWN";
pub fn populate_debug_info(dialog: &adw::AboutDialog, vkinfo: Option<&VulkanInfo>) {
if !dialog.debug_info().is_empty() {
if dialog.debug_info().len() > 0 {
return;
}
let distro_family = LinuxDistro::get();
@ -70,7 +70,7 @@ pub fn populate_debug_info(dialog: &adw::AboutDialog, vkinfo: Option<&VulkanInfo
.and_then(|s| {
s.split("\n")
.find(|line| line.starts_with("model name"))
.map(|line| line.split(':').next_back().map(|s| s.trim().to_string()))
.map(|line| line.split(':').last().map(|s| s.trim().to_string()))
})
.flatten()
.unwrap_or(UNKNOWN.into())
@ -81,6 +81,12 @@ pub fn populate_debug_info(dialog: &adw::AboutDialog, vkinfo: Option<&VulkanInfo
.map(|i| i.gpu_names.join(", "))
.unwrap_or(UNKNOWN.into())
),
format!(
"Monado Vulkan Layers: {}",
vkinfo
.map(|i| i.has_monado_vulkan_layers.to_string())
.unwrap_or(UNKNOWN.into())
),
format!("Detected XR Devices: {}", {
let devs = PhysicalXRDevice::from_usb();
if devs.is_empty() {

View file

@ -20,8 +20,7 @@ use crate::{
build_basalt::get_build_basalt_jobs, build_libsurvive::get_build_libsurvive_jobs,
build_mercury::get_build_mercury_jobs, build_monado::get_build_monado_jobs,
build_opencomposite::get_build_opencomposite_jobs, build_openhmd::get_build_openhmd_jobs,
build_vapor::get_build_vapor_jobs, build_wivrn::get_build_wivrn_jobs,
build_xrizer::get_build_xrizer_jobs,
build_wivrn::get_build_wivrn_jobs, build_xrizer::get_build_xrizer_jobs,
},
config::{Config, PluginConfig},
constants::APP_NAME,
@ -249,10 +248,6 @@ impl App {
.plugins
.values()
.filter_map(|cp| {
// disable potentially unsafe wayvr_dashboard
if cp.plugin.appid.contains("wayvr_dashboard") {
return None;
}
if cp.enabled && cp.plugin.validate() {
if let Err(e) = cp.plugin.mark_as_executable() {
error!(
@ -538,9 +533,6 @@ impl AsyncComponent for App {
OvrCompatibilityModuleType::Xrizer => {
get_build_xrizer_jobs(&profile, clean_build)
}
OvrCompatibilityModuleType::Vapor => {
get_build_vapor_jobs(&profile, clean_build)
}
});
let missing_deps = profile.missing_dependencies();
if !(self.skip_depcheck || profile.skip_dependency_check || missing_deps.is_empty())
@ -1006,6 +998,7 @@ impl AsyncComponent for App {
config: config.clone(),
selected_profile: selected_profile.clone(),
root_win: root.clone().into(),
vkinfo: vkinfo.clone(),
})
.forward(sender.input_sender(), |message| match message {
MainViewOutMsg::DoStartStopXRService => Msg::DoStartStopXRService,

View file

@ -88,54 +88,43 @@ impl SimpleComponent for BuildWindow {
gtk::Label {
#[track = "model.changed(BuildWindow::build_status())"]
set_markup: match &model.build_status {
BuildStatus::Building => String::default(),
BuildStatus::Done => "Build done, you can close this window".into(),
BuildStatus::Building => "Build in progress...".to_string(),
BuildStatus::Done => "Build done, you can close this window".to_string(),
BuildStatus::Error(code) => {
format!("Build failed: \"{c}\"", c = code)
}
}.as_str(),
#[track = "model.changed(BuildWindow::build_status())"]
set_visible: match &model.build_status {
BuildStatus::Building => false,
BuildStatus::Done | BuildStatus::Error(_) => true,
},
add_css_class: "title-2",
set_wrap: true,
set_wrap_mode: gtk::pango::WrapMode::Word,
set_justify: gtk::Justification::Center,
},
gtk::Button {
#[track = "model.changed(BuildWindow::build_status())"]
set_visible: matches!(&model.build_status, BuildStatus::Building),
add_css_class: "destructive-action",
add_css_class: "circular",
set_icon_name: "window-close-symbolic",
set_tooltip_text: Some("Cancel build"),
connect_clicked[sender] => move |_| {
sender.output(Self::Output::CancelBuild).expect(SENDER_IO_ERR_MSG);
}
},
},
model.term.container.clone(),
},
add_bottom_bar: bottom_bar = &gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
add_bottom_bar: bottom_bar = &gtk::Button {
add_css_class: "pill",
set_halign: gtk::Align::Center,
set_hexpand: true,
set_margin_bottom: 24,
set_spacing: 12,
gtk::Button {
add_css_class: "pill",
set_halign: gtk::Align::Center,
set_label: "Close",
#[track = "model.changed(BuildWindow::can_close())"]
set_visible: model.can_close,
connect_clicked[win] => move |_| {
win.close();
},
set_label: "Close",
set_margin_all: 12,
#[track = "model.changed(BuildWindow::can_close())"]
set_sensitive: model.can_close,
connect_clicked[win] => move |_| {
win.close();
},
// this
gtk::Button {
#[track = "model.changed(BuildWindow::build_status())"]
set_visible: matches!(&model.build_status, BuildStatus::Building),
add_css_class: "destructive-action",
add_css_class: "pill",
set_label: "Cancel build",
connect_clicked[sender] => move |_| {
sender.output(Self::Output::CancelBuild).expect(SENDER_IO_ERR_MSG);
}
},
// ^^^
},
}
}
}
}

View file

@ -25,6 +25,7 @@ use crate::{
file_utils::{get_writer, mount_has_nosuid},
steamvr_utils::chaperone_info_exists,
},
vulkaninfo::VulkanInfo,
wivrn_dbus,
xr_devices::XRDevice,
};
@ -74,6 +75,8 @@ pub struct MainView {
#[tracker::do_not_track]
profile_export_action: gtk::gio::SimpleAction,
xrservice_ready: bool,
#[tracker::do_not_track]
vkinfo: Option<VulkanInfo>,
wivrn_pairing_mode: bool,
wivrn_pin: Option<String>,
wivrn_supports_pairing: bool,
@ -123,6 +126,7 @@ pub struct MainViewInit {
pub config: Config,
pub selected_profile: Profile,
pub root_win: gtk::Window,
pub vkinfo: Option<VulkanInfo>,
}
impl MainView {
@ -457,6 +461,34 @@ impl AsyncComponent for MainView {
set_wrap_mode: gtk::pango::WrapMode::Word,
}
},
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_hexpand: true,
set_vexpand: false,
set_spacing: 12,
add_css_class: "card",
add_css_class: "padded",
set_visible: model
.vkinfo
.as_ref()
.is_some_and(
|i| i.has_nvidia_gpu && !i.has_monado_vulkan_layers
),
warning_heading(),
gtk::Label {
set_label: concat!(
"An Nvidia GPU has been detected, but it ",
"seems you don't have the Monado Vulkan Layers ",
"installed on your system.\n\nInstall the ",
"Monado Vulkan Layers or your XR session will ",
"crash."
),
add_css_class: "warning",
set_xalign: 0.0,
set_wrap: true,
set_wrap_mode: gtk::pango::WrapMode::Word,
}
},
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_hexpand: true,
@ -1071,6 +1103,7 @@ impl AsyncComponent for MainView {
xrservice_ready: false,
profile_delete_action,
profile_export_action,
vkinfo: init.vkinfo,
wivrn_pairing_mode: false,
wivrn_supports_pairing: false,
wivrn_pin: None,

View file

@ -163,10 +163,10 @@ impl Plugin {
/// urls to manifest json files representing plugins.
/// each manifest should be json and the link should always point to the latest version
const MANIFESTS: [&str;2] = [
const MANIFESTS: [&str;3] = [
"https://github.com/galister/wlx-overlay-s/raw/refs/heads/meta/com.github.galister.wlx-overlay-s.json",
"https://github.com/olekolek1000/wayvr-dashboard/raw/refs/heads/meta/dev.oo8.wayvr_dashboard.json",
"https://github.com/StardustXR/telescope/raw/refs/heads/main/envision/org.stardustxr.telescope.json",
// wayvr dashboard potentially unsafe
];
pub async fn refresh_plugins() -> anyhow::Result<Vec<anyhow::Result<Plugin>>> {

View file

@ -5,10 +5,12 @@ use ash::{
#[derive(Debug, Clone)]
pub struct VulkanInfo {
pub has_nvidia_gpu: bool,
pub has_monado_vulkan_layers: bool,
pub gpu_names: Vec<String>,
}
// const NVIDIA_VENDOR_ID: u32 = 0x10de;
const NVIDIA_VENDOR_ID: u32 = 0x10de;
impl VulkanInfo {
/// # Safety
@ -23,19 +25,40 @@ impl VulkanInfo {
None,
)
}?;
let mut has_nvidia_gpu = false;
let mut has_monado_vulkan_layers = false;
let gpu_names = unsafe { instance.enumerate_physical_devices() }?
.into_iter()
.filter_map(|d| {
Some(
unsafe { instance.get_physical_device_properties(d) }
.device_name_as_c_str()
.ok()?
.to_string_lossy()
.to_string(),
)
let props = unsafe { instance.get_physical_device_properties(d) };
if props.vendor_id == NVIDIA_VENDOR_ID {
has_nvidia_gpu = true;
}
if !has_monado_vulkan_layers {
has_monado_vulkan_layers =
unsafe { instance.enumerate_device_layer_properties(d) }
.ok()
.map(|layerprops| {
layerprops.iter().any(|lp| {
lp.layer_name_as_c_str().is_ok_and(|name| {
name.to_string_lossy()
== "VK_LAYER_MND_enable_timeline_semaphore"
})
})
})
== Some(true);
}
props
.device_name_as_c_str()
.ok()
.map(|cs| cs.to_string_lossy().to_string())
})
.collect();
unsafe { instance.destroy_instance(None) };
Ok(Self { gpu_names })
Ok(Self {
gpu_names,
has_nvidia_gpu,
has_monado_vulkan_layers,
})
}
}