Compare commits

..

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

29 changed files with 209 additions and 714 deletions

25
Cargo.lock generated
View file

@ -445,16 +445,6 @@ dependencies = [
"typenum",
]
[[package]]
name = "delicious-adwaita"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e53548c789a95211e0ce6d26c213067002b9b4360f8de69046d84de78ad9da3f"
dependencies = [
"gtk4",
"libadwaita",
]
[[package]]
name = "deranged"
version = "0.3.11"
@ -573,11 +563,10 @@ dependencies = [
[[package]]
name = "envision"
version = "3.1.1"
version = "3.0.0"
dependencies = [
"anyhow",
"ash",
"delicious-adwaita",
"gettext-rs",
"git2",
"gtk4",
@ -1084,9 +1073,9 @@ dependencies = [
[[package]]
name = "gtk4"
version = "0.9.6"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af1c491051f030994fd0cde6f3c44f3f5640210308cff1298c7673c47408091d"
checksum = "9376d14d7e33486c54823a42bef296e882b9f25cb4c52b52f4d1d57bbadb5b6d"
dependencies = [
"cairo-rs",
"field-offset",
@ -1117,9 +1106,9 @@ dependencies = [
[[package]]
name = "gtk4-sys"
version = "0.9.6"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41e03b01e54d77c310e1d98647d73f996d04b2f29b9121fe493ea525a7ec03d6"
checksum = "e653b0a9001ba9be1ffddb9373bfe9a111f688222f5aeee2841481300d91b55a"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
@ -1534,9 +1523,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libadwaita"
version = "0.7.2"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500135d29c16aabf67baafd3e7741d48e8b8978ca98bac39e589165c8dc78191"
checksum = "8611ee9fb85e7606c362b513afcaf5b59853f79e4d98caaaf581d99465014247"
dependencies = [
"gdk4",
"gio",

View file

@ -1,6 +1,6 @@
[package]
name = "envision"
version = "3.1.1"
version = "3.0.0"
edition = "2021"
authors = [
"Gabriele Musco <gabmus@disroot.org>",
@ -44,4 +44,3 @@ tracing-subscriber = { version = "0.3.19", features = ["env-filter", "json"] }
tracing = "0.1.41"
tracing-appender = "0.2.3"
serde_yaml = "0.9.34"
delicious-adwaita = { version = "0.3.0", features = ["all_themes"] }

View file

@ -30,43 +30,6 @@
<url type="bugtracker">@REPO_URL@/issues</url>
<content_rating type="oars-1.0" />
<releases>
<release version="3.1.1" date="2025-04-22">
<description>
<p>Fixes</p>
<ul>
<li>add libusb and libusb-dev deps</li>
<li>Revert &quot;disable and blacklist wayvr dashboard plugin&quot;</li>
</ul>
</description>
</release>
<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.1', # version number row
version: '3.0.0', # version number row
meson_version: '>= 0.59',
license: 'AGPL-3.0-or-later',
)

View file

@ -5,10 +5,7 @@ use crate::{
ui::job_worker::job::WorkerJob,
util::file_utils::rm_rf,
};
use std::{
collections::{HashMap, VecDeque},
num::NonZero,
};
use std::collections::{HashMap, VecDeque};
pub fn get_build_basalt_jobs(profile: &Profile, clean_build: bool) -> VecDeque<WorkerJob> {
let mut jobs = VecDeque::<WorkerJob>::new();
@ -43,23 +40,11 @@ pub fn get_build_basalt_jobs(profile: &Profile, clean_build: bool) -> VecDeque<W
env: Some({
let mut cmake_env: HashMap<String, String> = HashMap::new();
for (k, v) in [
// The basalt build uses a lot of RAM, so we have to limit the number of
// build processes to not starve the system of memory
// Limit to 6 build processes at most
(
"CMAKE_BUILD_PARALLEL_LEVEL",
std::cmp::min(
6,
std::thread::available_parallelism()
.map(NonZero::get)
.unwrap_or(2),
)
.to_string(),
),
("CMAKE_BUILD_TYPE", "RelWithDebInfo".into()),
("BUILD_TESTS", "off".into()),
("CMAKE_BUILD_PARALLEL_LEVEL", "2"),
("CMAKE_BUILD_TYPE", "RelWithDebInfo"),
("BUILD_TESTS", "off"),
] {
cmake_env.insert(k.to_string(), v);
cmake_env.insert(k.to_string(), v.to_string());
}
cmake_env
}),

View file

@ -1,68 +0,0 @@
use crate::{
build_tools::{cmake::Cmake, git::Git},
profile::Profile,
termcolor::TermColor,
ui::job_worker::job::WorkerJob,
util::file_utils::rm_rf,
};
use std::{
collections::{HashMap, VecDeque},
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 install_dir = build_dir.join("install_pfx");
let cmake = Cmake {
env: None,
vars: Some({
let mut cmake_vars: HashMap<String, String> = HashMap::new();
for (k, v) in [
("VAPOR_LOG_SILENT", "ON"),
("USE_SYSTEM_OPENXR", "OFF"),
("CMAKE_BUILD_TYPE", "RelWithDebInfo"),
] {
cmake_vars.insert(k.to_string(), v.to_string());
}
cmake_vars.insert(
"CMAKE_INSTALL_PREFIX".into(),
install_dir.to_string_lossy().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(cmake.get_install_job());
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

@ -45,10 +45,6 @@ const fn default_win_size() -> [i32; 2] {
DEFAULT_WIN_SIZE
}
fn default_theme_name() -> String {
"Follow system".into()
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Config {
pub selected_profile_uuid: String,
@ -58,8 +54,6 @@ pub struct Config {
pub win_size: [i32; 2],
#[serde(default)]
pub plugins: HashMap<String, PluginConfig>,
#[serde(default = "default_theme_name")]
pub theme_name: String,
}
impl Default for Config {
@ -71,7 +65,6 @@ impl Default for Config {
user_profiles: Vec::default(),
win_size: DEFAULT_WIN_SIZE,
plugins: HashMap::default(),
theme_name: default_theme_name(),
}
}
}

View file

@ -1,7 +1,7 @@
use super::{
boost_deps::boost_deps,
common::{dep_cmake, dep_eigen, dep_gpp, dep_libgl, dep_ninja, dep_opencv},
DepType, DepcheckResultGetMissing, Dependency, DependencyCheckResult,
DepType, Dependency, DependencyCheckResult,
};
use crate::linux_distro::LinuxDistro;
use std::collections::HashMap;
@ -181,5 +181,9 @@ pub fn check_basalt_deps() -> Vec<DependencyCheckResult> {
}
pub fn get_missing_basalt_deps() -> Vec<Dependency> {
check_basalt_deps().filter_missing_deps()
check_basalt_deps()
.iter()
.filter(|res| !res.found)
.map(|res| res.dependency.clone())
.collect()
}

View file

@ -303,35 +303,3 @@ pub fn dep_adb() -> Dependency {
]),
}
}
pub fn dep_getcap_setcap() -> Dependency {
Dependency {
name: "libcap".into(),
dep_type: DepType::Executable,
filename: "setcap".into(),
packages: HashMap::from([
(LinuxDistro::Arch, "libcap".into()),
(LinuxDistro::Debian, "libcap2-bin".into()),
(LinuxDistro::Fedora, "libcap".into()),
(LinuxDistro::Alpine, "libcap".into()),
(LinuxDistro::Gentoo, "sys-libs/libcap".into()),
(LinuxDistro::Suse, "libcap-progs".into()),
]),
}
}
pub fn dep_glslc() -> Dependency {
Dependency {
name: "glslc".into(),
dep_type: DepType::Executable,
filename: "glslc".into(),
packages: HashMap::from([
(LinuxDistro::Arch, "shaderc".into()),
(LinuxDistro::Debian, "glslc".into()),
(LinuxDistro::Fedora, "glslc".into()),
(LinuxDistro::Alpine, "shaderc".into()),
(LinuxDistro::Gentoo, "media-libs/shaderc".into()),
(LinuxDistro::Suse, "shaderc".into()),
]),
}
}

View file

@ -1,6 +1,6 @@
use super::{
common::{dep_cmake, dep_eigen, dep_gcc, dep_git, dep_gpp, dep_ninja},
DepcheckResultGetMissing, Dependency, DependencyCheckResult,
Dependency, DependencyCheckResult,
};
fn libsurvive_deps() -> Vec<Dependency> {
@ -19,5 +19,9 @@ pub fn check_libsurvive_deps() -> Vec<DependencyCheckResult> {
}
pub fn get_missing_libsurvive_deps() -> Vec<Dependency> {
check_libsurvive_deps().filter_missing_deps()
check_libsurvive_deps()
.iter()
.filter(|res| !res.found)
.map(|res| res.dependency.clone())
.collect()
}

View file

@ -1,6 +1,4 @@
use super::{
common::dep_opencv, DepType, DepcheckResultGetMissing, Dependency, DependencyCheckResult,
};
use super::{common::dep_opencv, DepType, Dependency, DependencyCheckResult};
use crate::linux_distro::LinuxDistro;
use std::collections::HashMap;
@ -41,5 +39,9 @@ pub fn check_mercury_deps() -> Vec<DependencyCheckResult> {
}
pub fn get_missing_mercury_deps() -> Vec<Dependency> {
check_mercury_deps().filter_missing_deps()
check_mercury_deps()
.iter()
.filter(|res| !res.found)
.map(|res| res.dependency.clone())
.collect()
}

View file

@ -6,7 +6,6 @@ pub mod mercury_deps;
pub mod monado_deps;
pub mod openhmd_deps;
pub mod wivrn_deps;
pub mod xrizer_deps;
use crate::linux_distro::LinuxDistro;
use std::{collections::HashMap, env, fmt::Display, path::Path};
@ -109,24 +108,6 @@ impl Display for DependencyCheckResult {
}
}
pub trait DepcheckResultGetMissing {
fn filter_missing_deps(self) -> Vec<Dependency>;
}
impl DepcheckResultGetMissing for Vec<DependencyCheckResult> {
fn filter_missing_deps(self) -> Vec<Dependency> {
self.into_iter()
.filter_map(|res| {
if !res.found {
Some(res.dependency)
} else {
None
}
})
.collect()
}
}
fn shared_obj_paths() -> Vec<String> {
vec![
"/lib".into(),
@ -136,24 +117,6 @@ fn shared_obj_paths() -> Vec<String> {
"/usr/local/lib64".into(),
"/usr/lib/x86_64-linux-gnu".into(),
"/usr/lib/aarch64-linux-gnu".into(),
// Debian puts libclang in /usr/lib/llvm-[llvm major version]/lib.
"/usr/lib/llvm-15/lib".into(),
"/usr/lib/llvm-16/lib".into(),
"/usr/lib/llvm-19/lib".into(),
// Fedora puts libclang in /usr/lib64/llvm[llvm major version]/lib as well as /usr/lib64.
"/usr/lib64/llvm15/lib".into(),
"/usr/lib64/llvm16/lib".into(),
"/usr/lib64/llvm17/lib".into(),
"/usr/lib64/llvm18/lib".into(),
"/usr/lib64/llvm19/lib".into(),
"/usr/lib64/llvm20/lib".into(),
// Gentoo puts libclang in /usr/lib/llvm/[llvm major version]/lib64.
"/usr/lib/llvm/15/lib64".into(),
"/usr/lib/llvm/16/lib64".into(),
"/usr/lib/llvm/17/lib64".into(),
"/usr/lib/llvm/18/lib64".into(),
"/usr/lib/llvm/19/lib64".into(),
"/usr/lib/llvm/20/lib64".into(),
"/lib/x86_64-linux-gnu".into(),
"/lib/aarch64-linux-gnu".into(),
"/app/lib".into(),
@ -177,8 +140,6 @@ fn include_paths() -> Vec<String> {
"/usr/include/ffmpeg/libpostproc".into(),
"/usr/include/ffmpeg/libswresample".into(),
"/usr/include/ffmpeg/libswscale".into(),
// opensuse puts wayland-client.h here
"/usr/include/wayland".into(),
]
}

View file

@ -4,12 +4,9 @@ use super::{
dep_libgl, dep_libudev, dep_libx11, dep_libxcb, dep_ninja, dep_openxr, dep_vulkan_headers,
dep_vulkan_icd_loader,
},
DepType, DepcheckResultGetMissing, Dependency, DependencyCheckResult,
};
use crate::{
depcheck::common::{dep_glslc, dep_libxrandr},
linux_distro::LinuxDistro,
DepType, Dependency, DependencyCheckResult,
};
use crate::{depcheck::common::dep_libxrandr, linux_distro::LinuxDistro};
use std::collections::HashMap;
fn monado_deps() -> Vec<Dependency> {
@ -40,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()),
]),
@ -63,7 +60,19 @@ fn monado_deps() -> Vec<Dependency> {
dep_ninja(),
dep_gcc(),
dep_gpp(),
dep_glslc(),
Dependency {
name: "glslc".into(),
dep_type: DepType::Executable,
filename: "glslc".into(),
packages: HashMap::from([
(LinuxDistro::Arch, "shaderc".into()),
(LinuxDistro::Debian, "glslc".into()),
(LinuxDistro::Fedora, "glslc".into()),
(LinuxDistro::Alpine, "shaderc".into()),
(LinuxDistro::Gentoo, "media-libs/shaderc".into()),
(LinuxDistro::Suse, "shaderc".into()),
]),
},
dep_glslang_validator(),
Dependency {
name: "sdl2".into(),
@ -74,34 +83,10 @@ fn monado_deps() -> Vec<Dependency> {
(LinuxDistro::Debian, "libsdl2-dev".into()),
(LinuxDistro::Fedora, "SDL2-devel".into()),
(LinuxDistro::Gentoo, "media-libs/libsdl2".into()),
(LinuxDistro::Suse, "sdl2-compat-devel".into()),
(LinuxDistro::Suse, "SDL2-devel".into()),
]),
},
dep_libudev(),
Dependency {
name: "libusb".into(),
dep_type: DepType::SharedObject,
filename: "libusb-1.0.so".into(),
packages: HashMap::from([
(LinuxDistro::Arch, "libusb".into()),
(LinuxDistro::Debian, "libusb-1.0-0".into()),
(LinuxDistro::Fedora, "libusb1".into()),
(LinuxDistro::Gentoo, "dev-libs/libusb".into()),
(LinuxDistro::Suse, "libusb-1_0-0".into()),
]),
},
Dependency {
name: "libusb-dev".into(),
dep_type: DepType::Include,
filename: "libusb-1.0/libusb.h".into(),
packages: HashMap::from([
(LinuxDistro::Arch, "libusb".into()),
(LinuxDistro::Debian, "libusb-1.0-0-dev".into()),
(LinuxDistro::Fedora, "libusb1-devel".into()),
(LinuxDistro::Gentoo, "dev-libs/libusb".into()),
(LinuxDistro::Suse, "libusb-1_0-devel".into()),
]),
},
Dependency {
name: "mesa-common-dev".into(),
dep_type: DepType::Include,
@ -122,5 +107,9 @@ pub fn check_monado_deps() -> Vec<DependencyCheckResult> {
}
pub fn get_missing_monado_deps() -> Vec<Dependency> {
check_monado_deps().filter_missing_deps()
check_monado_deps()
.iter()
.filter(|res| !res.found)
.map(|res| res.dependency.clone())
.collect()
}

View file

@ -1,6 +1,6 @@
use super::{
common::{dep_gcc, dep_git, dep_gpp, dep_ninja},
DepcheckResultGetMissing, Dependency, DependencyCheckResult,
Dependency, DependencyCheckResult,
};
use crate::linux_distro::LinuxDistro;
use std::collections::HashMap;
@ -31,5 +31,9 @@ pub fn check_openhmd_deps() -> Vec<DependencyCheckResult> {
}
pub fn get_missing_openhmd_deps() -> Vec<Dependency> {
check_openhmd_deps().filter_missing_deps()
check_openhmd_deps()
.iter()
.filter(|res| !res.found)
.map(|res| res.dependency.clone())
.collect()
}

View file

@ -4,7 +4,7 @@ use super::{
dep_libudev, dep_libx11, dep_libxcb, dep_ninja, dep_openxr, dep_vulkan_headers,
dep_vulkan_icd_loader,
},
DepType, DepcheckResultGetMissing, Dependency, DependencyCheckResult,
DepType, Dependency, DependencyCheckResult,
};
use crate::{
depcheck::common::{dep_libgl, dep_libxrandr},
@ -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()),
@ -270,5 +270,9 @@ pub fn check_wivrn_deps() -> Vec<DependencyCheckResult> {
}
pub fn get_missing_wivrn_deps() -> Vec<Dependency> {
check_wivrn_deps().filter_missing_deps()
check_wivrn_deps()
.iter()
.filter(|res| !res.found)
.map(|res| res.dependency.clone())
.collect()
}

View file

@ -1,65 +0,0 @@
use super::{DepType, DepcheckResultGetMissing, Dependency, DependencyCheckResult};
use crate::{depcheck::common::dep_glslc, linux_distro::LinuxDistro};
use std::collections::HashMap;
fn xrizer_deps() -> Vec<Dependency> {
vec![
dep_glslc(),
Dependency {
name: "cargo".into(),
dep_type: DepType::Executable,
filename: "cargo".into(),
packages: HashMap::from([
(LinuxDistro::Arch, "rust".into()),
(LinuxDistro::Debian, "cargo".into()),
(LinuxDistro::Fedora, "cargo".into()),
(LinuxDistro::Alpine, "cargo".into()),
(LinuxDistro::Suse, "cargo".into()),
]),
},
Dependency {
name: "libxcb-glx".into(),
dep_type: DepType::Include,
filename: "xcb/glx.h".into(),
packages: HashMap::from([
(LinuxDistro::Arch, "libxcb".into()),
(LinuxDistro::Debian, "libxcb-glx0-dev".into()),
(LinuxDistro::Fedora, "libxcb-devel".into()),
(LinuxDistro::Gentoo, "x11-libs/libxcb".into()),
(LinuxDistro::Suse, "libxcb-devel".into()),
]),
},
Dependency {
name: "libclang".into(),
dep_type: DepType::SharedObject,
filename: "libclang.so".into(),
packages: HashMap::from([
(LinuxDistro::Arch, "clang".into()),
(LinuxDistro::Debian, "libclang-19-dev".into()),
(LinuxDistro::Fedora, "clang19-devel".into()),
(LinuxDistro::Gentoo, "llvm-core/clang-runtime".into()),
(LinuxDistro::Suse, "clang19-devel".into()),
]),
},
Dependency {
name: "wayland-dev".into(),
dep_type: DepType::Include,
filename: "wayland-client.h".into(),
packages: HashMap::from([
(LinuxDistro::Arch, "wayland".into()),
(LinuxDistro::Debian, "libwayland-dev".into()),
(LinuxDistro::Fedora, "wayland-devel".into()),
(LinuxDistro::Gentoo, "dev-libs/wayland".into()),
(LinuxDistro::Suse, "wayland-devel".into()),
]),
},
]
}
pub fn check_xrizer_deps() -> Vec<DependencyCheckResult> {
Dependency::check_many(xrizer_deps())
}
pub fn get_missing_xrizer_deps() -> Vec<Dependency> {
check_xrizer_deps().filter_missing_deps()
}

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('"')
@ -115,7 +115,6 @@ impl LinuxDistro {
|| s.contains("steamos")
|| s.contains("steam os")
|| s.contains("endeavour")
|| s.contains("cachyos")
|| s.contains("garuda")
{
return Some(Self::Arch);
@ -151,33 +150,7 @@ impl LinuxDistro {
Self::Alpine => format!("sudo apk add {}", packages.join(" ")),
Self::Debian => format!("sudo apt install {}", packages.join(" ")),
Self::Gentoo => format!("sudo emerge -av {}", packages.join(" ")),
Self::Suse => {
let mut opi_pkgs = Vec::new();
let mut zypper_pkgs = Vec::new();
for pkg in packages {
if ["OpenXR-SDK-devel"].contains(&pkg.as_str()) {
opi_pkgs.push(pkg.clone());
} else {
zypper_pkgs.push(pkg.clone());
}
}
[
if opi_pkgs.is_empty() {
None
} else {
Some(format!("opi {}", opi_pkgs.join(" ")))
},
if zypper_pkgs.is_empty() {
None
} else {
Some(format!("sudo zypper install {}", zypper_pkgs.join(" ")))
},
]
.iter()
.filter_map(|c| c.clone())
.collect::<Vec<String>>()
.join(" && ")
}
Self::Suse => format!("sudo zypper install {}", packages.join(" ")),
Self::Fedora => {
let mut install_rpmfusion_cmd: Option<String> = None;
let mut swap_ffmpeg_cmd: Option<String> = None;
@ -217,10 +190,10 @@ impl LinuxDistro {
#[cfg(test)]
mod tests {
use super::LinuxDistro;
use crate::depcheck::common::{dep_openxr, dep_pkexec, dep_vulkan_icd_loader};
use std::path::Path;
use super::LinuxDistro;
#[test]
fn can_detect_arch_linux_from_etc_os_release() {
assert_eq!(
@ -230,34 +203,4 @@ mod tests {
Some(LinuxDistro::Arch)
)
}
#[test]
fn can_account_for_opensuse_opi_packages() {
assert_eq!(
LinuxDistro::Suse
.install_command(
&[dep_openxr(), dep_vulkan_icd_loader()]
.iter()
.map(|dep| dep.package_name_for_distro(Some(&LinuxDistro::Suse)))
.collect::<Vec<String>>()
)
.as_str(),
"opi OpenXR-SDK-devel && sudo zypper install vulkan-devel"
)
}
#[test]
fn opensuse_opi_does_not_interfere_if_not_needed() {
assert_eq!(
LinuxDistro::Suse
.install_command(
&[dep_pkexec(), dep_vulkan_icd_loader()]
.iter()
.map(|dep| dep.package_name_for_distro(Some(&LinuxDistro::Suse)))
.collect::<Vec<String>>()
)
.as_str(),
"sudo zypper install polkit vulkan-devel"
)
}
}

View file

@ -11,14 +11,9 @@ use relm4::{
gtk::{self, gdk, gio, glib, prelude::*},
MessageBroker, RelmApp,
};
use std::{
env,
fs::{read_dir, remove_file},
os::unix::fs::MetadataExt,
path::{Path, PathBuf},
};
use std::env;
use steam_linux_runtime_injector::restore_runtime_entrypoint;
use tracing::{error, warn};
use tracing::warn;
use tracing_subscriber::{
filter::LevelFilter, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer,
};
@ -71,55 +66,11 @@ fn restore_steam_xr_files() {
restore_runtime_entrypoint();
}
const LOGS_MAX_SIZE_BYTES: u64 = 1000000000; // 1GB
fn remove_old_logs(dir: &Path, log_files: Option<Vec<PathBuf>>) -> anyhow::Result<()> {
let log_files: Vec<PathBuf> = log_files
.map::<anyhow::Result<Vec<PathBuf>>, _>(Ok)
.unwrap_or_else(|| {
let mut files: Vec<PathBuf> = read_dir(dir)?
.filter_map(|de| {
let p = de.ok()?.path();
if p.is_file() && !p.is_symlink() {
Some(p)
} else {
None
}
})
.collect();
files.sort_unstable();
Ok(files)
})?;
let total_size = log_files
.iter()
.filter_map(|p| Some(p.metadata().ok()?.size()))
.reduce(u64::saturating_add)
.unwrap_or(0);
// if size is under threshold, finish
if total_size < LOGS_MAX_SIZE_BYTES {
return Ok(());
}
// keep a minimum of 3 logs
if log_files.len() <= 3 {
return Ok(());
}
remove_file(log_files.first().ok_or_else(||
anyhow::Error::msg(
"Could not get first item in log files list, but they should be more than 3! This is a bug!"
)
)?)?;
remove_old_logs(dir, Some(log_files))
}
fn main() -> Result<()> {
if env::var("USER").unwrap_or_else(|_| env::var("USERNAME").unwrap_or_default()) == "root" {
panic!("{APP_NAME} cannot run as root");
}
restore_steam_xr_files();
// deferring error logging for this since tracing isn't initialized yet
let old_logs_removal_res = remove_old_logs(&get_logs_dir(), None);
let rolling_log_writer = tracing_appender::rolling::daily(get_logs_dir(), "log");
let (non_blocking_appender, _appender_guard) =
@ -139,10 +90,6 @@ fn main() -> Result<()> {
)
.init();
if let Err(e) = old_logs_removal_res {
error!("Failed to remove old log files: {e}");
}
// Prepare i18n
gettextrs::setlocale(LocaleCategory::LcAll, "");
gettextrs::bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR).expect("Unable to bind the text domain");

View file

@ -2,8 +2,7 @@ use crate::{
depcheck::{
basalt_deps::get_missing_basalt_deps, libsurvive_deps::get_missing_libsurvive_deps,
mercury_deps::get_missing_mercury_deps, monado_deps::get_missing_monado_deps,
openhmd_deps::get_missing_openhmd_deps, wivrn_deps::get_missing_wivrn_deps,
xrizer_deps::get_missing_xrizer_deps, Dependency,
openhmd_deps::get_missing_openhmd_deps, wivrn_deps::get_missing_wivrn_deps, Dependency,
},
file_builders::active_runtime_json::ActiveRuntime,
paths::{get_data_dir, BWRAP_SYSTEM_PREFIX, SYSTEM_PREFIX},
@ -15,7 +14,7 @@ use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
fmt::Display,
fs::{remove_dir_all, File},
fs::File,
io::BufReader,
path::{Path, PathBuf},
slice::Iter,
@ -266,7 +265,6 @@ pub enum OvrCompatibilityModuleType {
#[default]
Opencomposite,
Xrizer,
Vapor,
}
impl Display for OvrCompatibilityModuleType {
@ -274,23 +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()
}
pub fn get_missing_deps(&self) -> Vec<Dependency> {
match self {
OvrCompatibilityModuleType::Xrizer => get_missing_xrizer_deps(),
OvrCompatibilityModuleType::Opencomposite | OvrCompatibilityModuleType::Vapor => {
Vec::default()
}
}
[Self::Opencomposite, Self::Xrizer].iter()
}
}
@ -301,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}`")),
}
}
@ -312,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"),
}
}
}
@ -342,7 +328,6 @@ impl ProfileOvrCompatibilityModule {
pub fn runtime_dir(&self) -> PathBuf {
match self.mod_type {
OvrCompatibilityModuleType::Opencomposite => self.path.join("build"),
OvrCompatibilityModuleType::Vapor => self.path.join("build/install_pfx/lib/VapoR"),
OvrCompatibilityModuleType::Xrizer => self.path.join("target/release"),
}
}
@ -459,29 +444,6 @@ impl Profile {
get_data_dir().join("prefixes").join(uuid)
}
/// deletes files and folders associated to this profile (mostly repo clones)
pub fn delete_files(&self) -> Vec<std::io::Result<()>> {
[
Some(&self.xrservice_path),
Some(&self.ovr_comp.path),
self.features.libsurvive.path.as_ref(),
self.features.basalt.path.as_ref(),
self.features.openhmd.path.as_ref(),
]
.iter()
.map(|dir| match dir {
Some(dir) => {
if dir.try_exists().unwrap_or_default() {
remove_dir_all(dir)
} else {
Ok(())
}
}
None => Ok(()),
})
.collect()
}
pub fn xr_runtime_json_env_var(&self) -> String {
format!(
"XR_RUNTIME_JSON=\"{prefix}/share/openxr/1/openxr_{runtime}.json\"",
@ -746,7 +708,7 @@ impl Profile {
if self.features.mercury_enabled {
missing_deps.extend(get_missing_mercury_deps());
}
missing_deps.extend(self.ovr_comp.mod_type.get_missing_deps());
// no listed deps for opencomp
}
missing_deps.sort_unstable();
missing_deps.dedup(); // dedup only works if sorted, hence the above

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,
@ -43,7 +42,6 @@ use crate::{
steam_linux_runtime_injector::{
restore_runtime_entrypoint, set_runtime_entrypoint_launch_opts_from_profile,
},
termcolor::TermColor,
util::file_utils::{
setcap_cap_sys_nice_eip, setcap_cap_sys_nice_eip_cmd, verify_cap_sys_nice_eip,
},
@ -52,7 +50,6 @@ use crate::{
xr_devices::XRDevice,
};
use adw::{prelude::*, ResponseAppearance};
use delicious_adwaita::{theme::Theme, ThemeEngine};
use gtk::glib::{self, clone};
use notify_rust::NotificationHandle;
use relm4::{
@ -98,8 +95,6 @@ pub struct App {
inhibit_fail_notif: Option<NotificationHandle>,
pluginstore: Option<AsyncController<PluginStore>>,
theme_engine: ThemeEngine,
}
#[derive(Debug)]
@ -117,8 +112,7 @@ pub enum Msg {
StartWithDebug,
RestartXRService,
ProfileSelected(Profile),
/// bool param: delete files
DeleteProfile(bool),
DeleteProfile,
SaveProfile(Profile),
RunSetCap,
OpenLibsurviveSetup,
@ -134,8 +128,6 @@ pub enum Msg {
WivrnCheckPairMode,
OpenPluginStore,
UpdateConfigPlugins(HashMap<String, PluginConfig>),
ShowThemeManager,
SaveThemeConfig,
NoOp,
}
@ -373,7 +365,7 @@ impl AsyncComponent for App {
set_content: Some(&adw::NavigationPage::new(model.debug_view.widget(), "Debug View")),
set_show_content: false,
set_collapsed: !model.config.debug_view_enabled,
},
}
},
connect_close_request[sender] => move |win| {
sender.input(Msg::SaveWinSize(win.width(), win.height()));
@ -397,27 +389,6 @@ impl AsyncComponent for App {
) {
match message {
Msg::NoOp => {}
Msg::ShowThemeManager => {
let dialog = self
.theme_engine
.theme_chooser_dialog(Theme::default_themes().as_ref());
dialog.set_content_height(2000);
dialog.present(Some(&self.app_win));
dialog.connect_closed(clone!(
#[strong]
sender,
move |_| {
sender.input(Msg::SaveThemeConfig);
}
));
}
Msg::SaveThemeConfig => {
let name = self.theme_engine.current_theme_name();
if self.config.theme_name != name {
self.config.theme_name = name;
self.config.save();
}
}
Msg::OnServiceLog(rows) => {
if !rows.is_empty() {
self.debug_view
@ -562,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())
@ -654,10 +622,6 @@ impl AsyncComponent for App {
if dep_pkexec().check() {
self.setcap_confirm_dialog.present(Some(&self.app_win));
} else {
self.build_window
.sender()
.emit(BuildWindowMsg::UpdateContent(vec![TermColor::Red
.colorize("pkexec not found, cannot set capabilities\n")]));
alert_w_widget(
"pkexec not found",
Some(&format!(
@ -690,16 +654,9 @@ impl AsyncComponent for App {
w.stop();
}
}
Msg::DeleteProfile(delete_files) => {
Msg::DeleteProfile => {
let todel = self.get_selected_profile();
if todel.editable {
if delete_files {
for res in todel.delete_files() {
if let Err(e) = res {
error!("Error deleting profile directory: {e}");
}
}
}
self.config.user_profiles.retain(|p| p.uuid != todel.uuid);
self.config.save();
self.profiles = self.config.profiles();
@ -739,7 +696,6 @@ impl AsyncComponent for App {
}
Msg::RunSetCap => {
if !dep_pkexec().check() {
// there's a precheck ahead of this, this should likely never happen
error!("pkexec not found, skipping setcap");
} else {
let profile = self.get_selected_profile();
@ -757,26 +713,8 @@ impl AsyncComponent for App {
if let Err(e) = setcap_cap_sys_nice_eip(&profile).await {
setcap_failed_dialog();
error!("failed running setcap: {e}");
self.build_window
.sender()
.emit(BuildWindowMsg::UpdateContent(vec![
TermColor::Red.colorize("Setting capabilities failed\n")
]));
} else if !verify_cap_sys_nice_eip(&profile).await {
setcap_failed_dialog();
error!("setcap succeeded but capabilities were reset");
self.build_window
.sender()
.emit(BuildWindowMsg::UpdateContent(vec![TermColor::Red
.colorize(
"Setting capabilities succeeded, but capabilities have been reset\n",
)]));
} else {
self.build_window
.sender()
.emit(BuildWindowMsg::UpdateContent(vec![
TermColor::Green.colorize("Capabilities set correctly\n")
]));
}
}
}
@ -1024,17 +962,6 @@ impl AsyncComponent for App {
}
)
);
stateless_action!(
actions,
ThemeManagerAction,
clone!(
#[strong]
sender,
move |_| {
sender.input(Msg::ShowThemeManager);
}
)
);
// this bypasses the macro because I need the underlying gio action
// to enable/disable it in update()
let configure_wivrn_action = {
@ -1071,12 +998,13 @@ 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,
MainViewOutMsg::RestartXRService => Msg::RestartXRService,
MainViewOutMsg::ProfileSelected(uuid) => Msg::ProfileSelected(uuid),
MainViewOutMsg::DeleteProfile(delete_files) => Msg::DeleteProfile(delete_files),
MainViewOutMsg::DeleteProfile => Msg::DeleteProfile,
MainViewOutMsg::SaveProfile(p) => Msg::SaveProfile(p),
MainViewOutMsg::OpenLibsurviveSetup => Msg::OpenLibsurviveSetup,
MainViewOutMsg::BuildProfile(clean) => Msg::BuildProfile(clean),
@ -1103,17 +1031,6 @@ impl AsyncComponent for App {
.detach(),
split_view: None,
setcap_confirm_dialog,
theme_engine: ThemeEngine::new_with_theme(&{
if config.theme_name == "Follow system" {
Theme::default()
} else {
Theme::default_themes()
.into_iter()
.find(|t| t.name == config.theme_name)
.unwrap_or_default()
}
})
.unwrap(),
config,
profiles,
xrservice_worker: None,
@ -1202,7 +1119,6 @@ new_stateless_action!(pub QuitAction, AppActionGroup, "quit");
new_stateful_action!(pub DebugViewToggleAction, AppActionGroup, "debugviewtoggle", (), bool);
new_stateless_action!(pub ConfigureWivrnAction, AppActionGroup, "configurewivrn");
new_stateless_action!(pub PluginStoreAction, AppActionGroup, "store");
new_stateless_action!(pub ThemeManagerAction, AppActionGroup, "thememanager");
new_stateless_action!(pub DebugOpenDataAction, AppActionGroup, "debugopendata");
new_stateless_action!(pub DebugOpenPrefixAction, AppActionGroup, "debugopenprefix");

View file

@ -1,5 +1,3 @@
use crate::termcolor::TermColor;
use super::{term_widget::TermWidget, SENDER_IO_ERR_MSG};
use adw::prelude::*;
use relm4::prelude::*;
@ -90,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);
}
},
// ^^^
},
}
}
}
}
@ -166,18 +153,8 @@ impl SimpleComponent for BuildWindow {
label.remove_css_class("success");
label.remove_css_class("error");
match status {
BuildStatus::Done => {
label.add_css_class("success");
sender.input(BuildWindowMsg::UpdateContent(vec![
TermColor::Blue.colorize("Build completed!\n")
]));
}
BuildStatus::Error(_) => {
label.add_css_class("error");
sender.input(BuildWindowMsg::UpdateContent(vec![
TermColor::Blue.colorize("Build failed!\n")
]));
}
BuildStatus::Done => label.add_css_class("success"),
BuildStatus::Error(_) => label.add_css_class("error"),
_ => {}
}
if status != BuildStatus::Building {

View file

@ -21,11 +21,11 @@ use crate::{
paths::{get_data_dir, get_home_dir},
profile::{LighthouseDriver, Profile, XRServiceType},
stateless_action,
ui::app::ThemeManagerAction,
util::{
file_utils::{get_writer, mount_has_nosuid},
steamvr_utils::chaperone_info_exists,
},
vulkaninfo::VulkanInfo,
wivrn_dbus,
xr_devices::XRDevice,
};
@ -75,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,
@ -113,8 +115,7 @@ pub enum MainViewOutMsg {
DoStartStopXRService,
RestartXRService,
ProfileSelected(Profile),
/// bool param: delete files
DeleteProfile(bool),
DeleteProfile,
SaveProfile(Profile),
OpenLibsurviveSetup,
/// params: clean
@ -125,6 +126,7 @@ pub struct MainViewInit {
pub config: Config,
pub selected_profile: Profile,
pub root_win: gtk::Window,
pub vkinfo: Option<VulkanInfo>,
}
impl MainView {
@ -160,7 +162,6 @@ impl AsyncComponent for MainView {
"Configure _WiVRn" => ConfigureWivrnAction,
},
section! {
"Change _Theme" => ThemeManagerAction,
"_About" => AboutAction,
},
},
@ -460,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,
@ -934,13 +963,6 @@ impl AsyncComponent for MainView {
let profile_delete_confirm_dialog = adw::AlertDialog::builder()
.heading("Are you sure you want to delete this profile?")
.extra_child(
&gtk::CheckButton::builder()
.label("Delete all files and folders associated with profile")
.halign(gtk::Align::Center)
.hexpand(true)
.build(),
)
.build();
profile_delete_confirm_dialog.add_response("no", "_No");
profile_delete_confirm_dialog.add_response("yes", "_Yes");
@ -952,19 +974,10 @@ impl AsyncComponent for MainView {
clone!(
#[strong]
sender,
move |dialog, res| {
let delete_files_checkbox = dialog
.extra_child()
.and_then(|child| child.downcast::<gtk::CheckButton>().ok());
let delete_files = delete_files_checkbox
.as_ref()
.is_some_and(|c| c.is_active());
if let Some(check) = delete_files_checkbox {
check.set_active(false);
}
move |_, res| {
if res == "yes" {
sender
.output(Self::Output::DeleteProfile(delete_files))
.output(Self::Output::DeleteProfile)
.expect("Sender output failed");
}
}
@ -1090,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

@ -165,8 +165,8 @@ impl Plugin {
/// each manifest should be json and the link should always point to the latest version
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/StardustXR/telescope/raw/refs/heads/main/envision/org.stardustxr.telescope.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",
];
pub async fn refresh_plugins() -> anyhow::Result<Vec<anyhow::Result<Plugin>>> {

View file

@ -1,4 +1,4 @@
use crate::{async_process::async_process, depcheck::common::dep_getcap_setcap, profile::Profile};
use crate::{async_process::async_process, profile::Profile};
use anyhow::bail;
use nix::{
errno::Errno,
@ -79,29 +79,9 @@ pub fn set_file_readonly(path: &Path, readonly: bool) -> anyhow::Result<()> {
Ok(fs::set_permissions(path, perms)?)
}
pub fn setcap_executable() -> Option<String> {
if dep_getcap_setcap().check() {
Some("setcap".into())
} else if Path::new("/sbin/setcap").try_exists().unwrap_or_default() {
Some("/sbin/setcap".into())
} else {
None
}
}
pub fn getcap_executable() -> Option<String> {
if dep_getcap_setcap().check() {
Some("getcap".into())
} else if Path::new("/sbin/getcap").try_exists().unwrap_or_default() {
Some("/sbin/getcap".into())
} else {
None
}
}
pub fn setcap_cap_sys_nice_eip_cmd(profile: &Profile) -> Vec<String> {
vec![
setcap_executable().unwrap_or("setcap".into()),
"setcap".into(),
"CAP_SYS_NICE=eip".into(),
profile
.prefix
@ -113,29 +93,24 @@ pub fn setcap_cap_sys_nice_eip_cmd(profile: &Profile) -> Vec<String> {
pub async fn verify_cap_sys_nice_eip(profile: &Profile) -> bool {
let xrservice_binary = profile.xrservice_binary().to_string_lossy().to_string();
if let Some(getcap_exec) = getcap_executable() {
match async_process(&getcap_exec, Some(&[&xrservice_binary]), None).await {
Err(e) => {
error!("failed to run `getcap {xrservice_binary}`: {e:?}");
match async_process("getcap", Some(&[&xrservice_binary]), None).await {
Err(e) => {
error!("failed to run `getcap {xrservice_binary}`: {e:?}");
false
}
Ok(out) => {
debug!("getcap {xrservice_binary} stdout: {}", out.stdout);
debug!("getcap {xrservice_binary} stderr: {}", out.stderr);
if out.exit_code != 0 {
error!(
"command `getcap {xrservice_binary}` failed with status code {}",
out.exit_code
);
false
}
Ok(out) => {
debug!("getcap {xrservice_binary} stdout: {}", out.stdout);
debug!("getcap {xrservice_binary} stderr: {}", out.stderr);
if out.exit_code != 0 {
error!(
"command `getcap {xrservice_binary}` failed with status code {}",
out.exit_code
);
false
} else {
out.stdout.to_lowercase().contains("cap_sys_nice=eip")
}
} else {
out.stdout.to_lowercase().contains("cap_sys_nice=eip")
}
}
} else {
error!("getcap executable does not exist");
false
}
}

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,
})
}
}