Compare commits

..

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

49 changed files with 315 additions and 875 deletions

25
Cargo.lock generated
View file

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

View file

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

View file

@ -30,35 +30,6 @@
<url type="bugtracker">@REPO_URL@/issues</url> <url type="bugtracker">@REPO_URL@/issues</url>
<content_rating type="oars-1.0" /> <content_rating type="oars-1.0" />
<releases> <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"> <release version="3.0.1" date="2025-03-02">
<description> <description>
<p>Fixes</p> <p>Fixes</p>

1
dist/arch/PKGBUILD vendored
View file

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

View file

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

View file

@ -2,6 +2,57 @@
set -ev set -ev
PREFIX=$1
CACHE_DIR=$2
if [[ -z $PREFIX ]] || [[ -z $CACHE_DIR ]]; then
echo "Usage: $0 PREFIX CACHE_DIR"
exit 1
fi
ONNX_RELEASES=$(curl -sSL "https://api.github.com/repos/microsoft/onnxruntime/releases")
NUM_RELEASES=$(echo "$ONNX_RELEASES" | jq -r '[ select (.[]!=null) ] | length')
for (( IDX=0; IDX<NUM_RELEASES; IDX++ )); do
ASSETS_LEN=$(echo "$ONNX_RELEASES" | jq -r ".[$IDX].assets_url" | xargs -n 1 curl -sSL | jq -r '[ select (.[]!=null) ] | length')
if [[ $ASSETS_LEN -gt 0 ]]; then
ONNX_VER=$(echo "$ONNX_RELEASES" | jq -r ".[$IDX].tag_name" | tr -d v)
break
fi
done
if [[ -z $ONNX_VER ]]; then
echo "Failed to find a suitable ONNX Runtime release."
exit 1
fi
SYS_ARCH=$(uname -m)
if [[ $SYS_ARCH == x*64 ]]; then
ARCH="x64"
elif [[ $SYS_ARCH == arm64 ]] || [[ $ARCH == aarch64 ]]; then
ARCH="aarch64"
else
echo "CPU architecture '$SYS_ARCH' is not supported"
exit 1
fi
ONNX="onnxruntime-linux-${ARCH}-${ONNX_VER}"
ONNX_URL="https://github.com/microsoft/onnxruntime/releases/download/v${ONNX_VER}/${ONNX}.tgz"
mkdir -p "$CACHE_DIR"
curl -sSL "$ONNX_URL" -o "${CACHE_DIR}/onnxruntime.tgz"
tar xf "${CACHE_DIR}/onnxruntime.tgz" --directory="${CACHE_DIR}"
mkdir -p "${PREFIX}/lib"
mkdir -p "${PREFIX}/include"
cp -r "${CACHE_DIR}/${ONNX}/include/"* "${PREFIX}/include/"
cp -r "${CACHE_DIR}/${ONNX}/lib/"* "${PREFIX}/lib/"
if [[ -z $XDG_DATA_HOME ]]; then if [[ -z $XDG_DATA_HOME ]]; then
DATA_HOME=$HOME/.local/share DATA_HOME=$HOME/.local/share
else else

View file

@ -22,7 +22,7 @@ impl Cmake {
if k.contains(' ') { if k.contains(' ') {
panic!("Cmake vars cannot contain spaces!"); panic!("Cmake vars cannot contain spaces!");
} }
args.push(format!("-D{k}={v}")); args.push(format!("-D{k}={v}", k = k, v = v));
} }
} }
args.push(self.source_dir.to_string_lossy().to_string()); args.push(self.source_dir.to_string_lossy().to_string());

View file

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

View file

@ -1,8 +1,11 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use crate::{constants::pkg_data_dir, termcolor::TermColor, ui::job_worker::job::WorkerJob}; use crate::{
constants::pkg_data_dir, paths::get_cache_dir, profile::Profile, termcolor::TermColor,
ui::job_worker::job::WorkerJob,
};
pub fn get_build_mercury_jobs() -> VecDeque<WorkerJob> { pub fn get_build_mercury_jobs(profile: &Profile) -> VecDeque<WorkerJob> {
let mut jobs = VecDeque::new(); let mut jobs = VecDeque::new();
jobs.push_back(WorkerJob::new_printer( jobs.push_back(WorkerJob::new_printer(
"Building Mercury...", "Building Mercury...",
@ -14,7 +17,10 @@ pub fn get_build_mercury_jobs() -> VecDeque<WorkerJob> {
.join("scripts/build_mercury.sh") .join("scripts/build_mercury.sh")
.to_string_lossy() .to_string_lossy()
.to_string(), .to_string(),
None, Some(vec![
profile.prefix.to_string_lossy().to_string(),
get_cache_dir().to_string_lossy().to_string(),
]),
)); ));
jobs jobs

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_monado;
pub mod build_opencomposite; pub mod build_opencomposite;
pub mod build_openhmd; pub mod build_openhmd;
pub mod build_vapor;
pub mod build_wivrn; pub mod build_wivrn;
pub mod build_xrizer; pub mod build_xrizer;

View file

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

View file

@ -1,7 +1,7 @@
use super::{ use super::{
boost_deps::boost_deps, boost_deps::boost_deps,
common::{dep_cmake, dep_eigen, dep_gpp, dep_libgl, dep_ninja, dep_opencv}, 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 crate::linux_distro::LinuxDistro;
use std::collections::HashMap; use std::collections::HashMap;
@ -181,5 +181,9 @@ pub fn check_basalt_deps() -> Vec<DependencyCheckResult> {
} }
pub fn get_missing_basalt_deps() -> Vec<Dependency> { 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::{ use super::{
common::{dep_cmake, dep_eigen, dep_gcc, dep_git, dep_gpp, dep_ninja}, common::{dep_cmake, dep_eigen, dep_gcc, dep_git, dep_gpp, dep_ninja},
DepcheckResultGetMissing, Dependency, DependencyCheckResult, Dependency, DependencyCheckResult,
}; };
fn libsurvive_deps() -> Vec<Dependency> { fn libsurvive_deps() -> Vec<Dependency> {
@ -19,5 +19,9 @@ pub fn check_libsurvive_deps() -> Vec<DependencyCheckResult> {
} }
pub fn get_missing_libsurvive_deps() -> Vec<Dependency> { 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,27 +1,10 @@
use super::{ use super::{common::dep_opencv, DepType, Dependency, DependencyCheckResult};
common::dep_opencv, DepType, DepcheckResultGetMissing, Dependency, DependencyCheckResult,
};
use crate::linux_distro::LinuxDistro; use crate::linux_distro::LinuxDistro;
use std::collections::HashMap; use std::collections::HashMap;
fn mercury_deps() -> Vec<Dependency> { fn mercury_deps() -> Vec<Dependency> {
vec![ vec![
dep_opencv(), dep_opencv(),
Dependency {
name: "onnxruntime-dev".into(),
dep_type: DepType::Include,
filename: "onnxruntime/onnxruntime_c_api.h".into(),
packages: HashMap::from([
(LinuxDistro::Arch, "onnxruntime".into()),
(LinuxDistro::Debian, "libonnxruntime-dev".into()),
(LinuxDistro::Fedora, "onnxruntime-devel".into()),
// alpine doesn't seem to have the package
// (LinuxDistro::Alpine, "".into()),
(LinuxDistro::Gentoo, "sci-ml/onnx".into()),
// opensuse doesn't seem to have the package
// (LinuxDistro::Suse, "".into()),
]),
},
Dependency { Dependency {
name: "jq".into(), name: "jq".into(),
dep_type: DepType::Executable, dep_type: DepType::Executable,
@ -56,5 +39,9 @@ pub fn check_mercury_deps() -> Vec<DependencyCheckResult> {
} }
pub fn get_missing_mercury_deps() -> Vec<Dependency> { 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 monado_deps;
pub mod openhmd_deps; pub mod openhmd_deps;
pub mod wivrn_deps; pub mod wivrn_deps;
pub mod xrizer_deps;
use crate::linux_distro::LinuxDistro; use crate::linux_distro::LinuxDistro;
use std::{collections::HashMap, env, fmt::Display, path::Path}; 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> { fn shared_obj_paths() -> Vec<String> {
vec![ vec![
"/lib".into(), "/lib".into(),
@ -136,24 +117,6 @@ fn shared_obj_paths() -> Vec<String> {
"/usr/local/lib64".into(), "/usr/local/lib64".into(),
"/usr/lib/x86_64-linux-gnu".into(), "/usr/lib/x86_64-linux-gnu".into(),
"/usr/lib/aarch64-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/x86_64-linux-gnu".into(),
"/lib/aarch64-linux-gnu".into(), "/lib/aarch64-linux-gnu".into(),
"/app/lib".into(), "/app/lib".into(),
@ -177,8 +140,6 @@ fn include_paths() -> Vec<String> {
"/usr/include/ffmpeg/libpostproc".into(), "/usr/include/ffmpeg/libpostproc".into(),
"/usr/include/ffmpeg/libswresample".into(), "/usr/include/ffmpeg/libswresample".into(),
"/usr/include/ffmpeg/libswscale".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_libgl, dep_libudev, dep_libx11, dep_libxcb, dep_ninja, dep_openxr, dep_vulkan_headers,
dep_vulkan_icd_loader, dep_vulkan_icd_loader,
}, },
DepType, DepcheckResultGetMissing, Dependency, DependencyCheckResult, DepType, Dependency, DependencyCheckResult,
};
use crate::{
depcheck::common::{dep_glslc, dep_libxrandr},
linux_distro::LinuxDistro,
}; };
use crate::{depcheck::common::dep_libxrandr, linux_distro::LinuxDistro};
use std::collections::HashMap; use std::collections::HashMap;
fn monado_deps() -> Vec<Dependency> { fn monado_deps() -> Vec<Dependency> {
@ -40,7 +37,7 @@ fn monado_deps() -> Vec<Dependency> {
packages: HashMap::from([ packages: HashMap::from([
(LinuxDistro::Arch, "wayland-protocols".into()), (LinuxDistro::Arch, "wayland-protocols".into()),
(LinuxDistro::Debian, "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::Gentoo, "dev-libs/wayland-protocols".into()),
(LinuxDistro::Suse, "wayland-protocols-devel".into()), (LinuxDistro::Suse, "wayland-protocols-devel".into()),
]), ]),
@ -63,7 +60,19 @@ fn monado_deps() -> Vec<Dependency> {
dep_ninja(), dep_ninja(),
dep_gcc(), dep_gcc(),
dep_gpp(), 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(), dep_glslang_validator(),
Dependency { Dependency {
name: "sdl2".into(), name: "sdl2".into(),
@ -74,34 +83,10 @@ fn monado_deps() -> Vec<Dependency> {
(LinuxDistro::Debian, "libsdl2-dev".into()), (LinuxDistro::Debian, "libsdl2-dev".into()),
(LinuxDistro::Fedora, "SDL2-devel".into()), (LinuxDistro::Fedora, "SDL2-devel".into()),
(LinuxDistro::Gentoo, "media-libs/libsdl2".into()), (LinuxDistro::Gentoo, "media-libs/libsdl2".into()),
(LinuxDistro::Suse, "sdl2-compat-devel".into()), (LinuxDistro::Suse, "SDL2-devel".into()),
]), ]),
}, },
dep_libudev(), 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 { Dependency {
name: "mesa-common-dev".into(), name: "mesa-common-dev".into(),
dep_type: DepType::Include, dep_type: DepType::Include,
@ -122,5 +107,9 @@ pub fn check_monado_deps() -> Vec<DependencyCheckResult> {
} }
pub fn get_missing_monado_deps() -> Vec<Dependency> { 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::{ use super::{
common::{dep_gcc, dep_git, dep_gpp, dep_ninja}, common::{dep_gcc, dep_git, dep_gpp, dep_ninja},
DepcheckResultGetMissing, Dependency, DependencyCheckResult, Dependency, DependencyCheckResult,
}; };
use crate::linux_distro::LinuxDistro; use crate::linux_distro::LinuxDistro;
use std::collections::HashMap; use std::collections::HashMap;
@ -31,5 +31,9 @@ pub fn check_openhmd_deps() -> Vec<DependencyCheckResult> {
} }
pub fn get_missing_openhmd_deps() -> Vec<Dependency> { 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_libudev, dep_libx11, dep_libxcb, dep_ninja, dep_openxr, dep_vulkan_headers,
dep_vulkan_icd_loader, dep_vulkan_icd_loader,
}, },
DepType, DepcheckResultGetMissing, Dependency, DependencyCheckResult, DepType, Dependency, DependencyCheckResult,
}; };
use crate::{ use crate::{
depcheck::common::{dep_libgl, dep_libxrandr}, depcheck::common::{dep_libgl, dep_libxrandr},
@ -270,5 +270,9 @@ pub fn check_wivrn_deps() -> Vec<DependencyCheckResult> {
} }
pub fn get_missing_wivrn_deps() -> Vec<Dependency> { 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

@ -13,7 +13,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, format!("{APP_ID}/1.0").parse().unwrap()); headers.insert(USER_AGENT, format!("{}/1.0", APP_ID).parse().unwrap());
headers headers
} }

View file

@ -65,7 +65,7 @@ fn env_var_descriptions() -> Vec<(&'static str, &'static str)> {
fn env_var_descriptions_as_paragraph() -> String { fn env_var_descriptions_as_paragraph() -> String {
ENV_VAR_DESCRIPTIONS ENV_VAR_DESCRIPTIONS
.iter() .iter()
.map(|(k, v)| format!("<span size=\"large\" weight=\"bold\">{k}</span>\n{v}")) .map(|(k, v)| format!("<span size=\"large\" weight=\"bold\">{}</span>\n{}", k, v))
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("\n\n") .join("\n\n")
} }

View file

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

View file

@ -143,7 +143,7 @@ pub struct WivrnConfig {
pub encoders: Vec<WivrnConfEncoder>, pub encoders: Vec<WivrnConfEncoder>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub application: Option<WivrnConfigApplication>, pub application: Option<WivrnConfigApplication>,
#[serde(default, rename = "tcp-only")] #[serde(default)]
pub tcp_only: bool, pub tcp_only: bool,
/// contains unknown fields /// contains unknown fields
#[serde(flatten)] #[serde(flatten)]

View file

@ -116,7 +116,7 @@ fn list_gpus() -> Vec<GpuSysDrm> {
for i in 0..5 { for i in 0..5 {
// arbitrary range, find a better way // arbitrary range, find a better way
let card_dir = PathBuf::from(format!("/sys/class/drm/card{i}")); let card_dir = PathBuf::from(format!("/sys/class/drm/card{}", i));
let vendor_file = card_dir.join("device/vendor"); let vendor_file = card_dir.join("device/vendor");
if let Some(mut reader) = get_reader(&vendor_file) { if let Some(mut reader) = get_reader(&vendor_file) {
let mut buf = String::new(); let mut buf = String::new();

View file

@ -49,13 +49,13 @@ impl LinuxDistro {
Ok(_) if buf.starts_with("PRETTY_NAME=\"") => { Ok(_) if buf.starts_with("PRETTY_NAME=\"") => {
return buf return buf
.split('=') .split('=')
.next_back() .last()
.map(|b| b.trim().trim_matches('"').trim().to_string()); .map(|b| b.trim().trim_matches('"').trim().to_string());
} }
Ok(_) if buf.starts_with("NAME=\"") => { Ok(_) if buf.starts_with("NAME=\"") => {
name = buf name = buf
.split('=') .split('=')
.next_back() .last()
.map(|b| b.trim().trim_matches('"').trim().to_string()); .map(|b| b.trim().trim_matches('"').trim().to_string());
} }
_ => {} _ => {}
@ -79,7 +79,7 @@ impl LinuxDistro {
{ {
let name = buf let name = buf
.split('=') .split('=')
.next_back() .last()
.unwrap_or_default() .unwrap_or_default()
.trim() .trim()
.trim_matches('"') .trim_matches('"')
@ -115,7 +115,6 @@ impl LinuxDistro {
|| s.contains("steamos") || s.contains("steamos")
|| s.contains("steam os") || s.contains("steam os")
|| s.contains("endeavour") || s.contains("endeavour")
|| s.contains("cachyos")
|| s.contains("garuda") || s.contains("garuda")
{ {
return Some(Self::Arch); return Some(Self::Arch);
@ -151,33 +150,7 @@ impl LinuxDistro {
Self::Alpine => format!("sudo apk add {}", packages.join(" ")), Self::Alpine => format!("sudo apk add {}", packages.join(" ")),
Self::Debian => format!("sudo apt install {}", packages.join(" ")), Self::Debian => format!("sudo apt install {}", packages.join(" ")),
Self::Gentoo => format!("sudo emerge -av {}", packages.join(" ")), Self::Gentoo => format!("sudo emerge -av {}", packages.join(" ")),
Self::Suse => { Self::Suse => format!("sudo zypper install {}", packages.join(" ")),
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::Fedora => { Self::Fedora => {
let mut install_rpmfusion_cmd: Option<String> = None; let mut install_rpmfusion_cmd: Option<String> = None;
let mut swap_ffmpeg_cmd: Option<String> = None; let mut swap_ffmpeg_cmd: Option<String> = None;
@ -217,10 +190,10 @@ impl LinuxDistro {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::LinuxDistro;
use crate::depcheck::common::{dep_openxr, dep_pkexec, dep_vulkan_icd_loader};
use std::path::Path; use std::path::Path;
use super::LinuxDistro;
#[test] #[test]
fn can_detect_arch_linux_from_etc_os_release() { fn can_detect_arch_linux_from_etc_os_release() {
assert_eq!( assert_eq!(
@ -230,34 +203,4 @@ mod tests {
Some(LinuxDistro::Arch) 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,16 +11,9 @@ use relm4::{
gtk::{self, gdk, gio, glib, prelude::*}, gtk::{self, gdk, gio, glib, prelude::*},
MessageBroker, RelmApp, MessageBroker, RelmApp,
}; };
use std::{ use std::env;
env, use steam_linux_runtime_injector::restore_runtime_entrypoint;
fs::{read_dir, remove_file}, use tracing::warn;
os::unix::fs::MetadataExt,
path::{Path, PathBuf},
};
use steam_linux_runtime_injector::{
restore_sniper_runtime_entrypoint, restore_soldier_runtime_entrypoint,
};
use tracing::{error, warn};
use tracing_subscriber::{ use tracing_subscriber::{
filter::LevelFilter, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer, filter::LevelFilter, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer,
}; };
@ -70,50 +63,7 @@ fn restore_steam_xr_files() {
} }
} }
} }
restore_sniper_runtime_entrypoint(); restore_runtime_entrypoint();
restore_soldier_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<()> { fn main() -> Result<()> {
@ -121,8 +71,6 @@ fn main() -> Result<()> {
panic!("{APP_NAME} cannot run as root"); panic!("{APP_NAME} cannot run as root");
} }
restore_steam_xr_files(); 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 rolling_log_writer = tracing_appender::rolling::daily(get_logs_dir(), "log");
let (non_blocking_appender, _appender_guard) = let (non_blocking_appender, _appender_guard) =
@ -142,10 +90,6 @@ fn main() -> Result<()> {
) )
.init(); .init();
if let Err(e) = old_logs_removal_res {
error!("Failed to remove old log files: {e}");
}
// Prepare i18n // Prepare i18n
gettextrs::setlocale(LocaleCategory::LcAll, ""); gettextrs::setlocale(LocaleCategory::LcAll, "");
gettextrs::bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR).expect("Unable to bind the text domain"); gettextrs::bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR).expect("Unable to bind the text domain");
@ -160,7 +104,7 @@ fn main() -> Result<()> {
} }
let provider = gtk::CssProvider::new(); let provider = gtk::CssProvider::new();
provider.load_from_resource(&format!("{RESOURCES_BASE_PATH}/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

@ -17,7 +17,7 @@ pub fn is_openxr_ready() -> bool {
let Ok(xr_instance) = entry.create_instance( let Ok(xr_instance) = entry.create_instance(
&xr::ApplicationInfo { &xr::ApplicationInfo {
application_name: &format!("{CMD_NAME}-openxr-prober"), application_name: &format!("{}-openxr-prober", CMD_NAME),
application_version: 0, application_version: 0,
engine_name: CMD_NAME, engine_name: CMD_NAME,
engine_version: 0, engine_version: 0,

View file

@ -2,8 +2,7 @@ use crate::{
depcheck::{ depcheck::{
basalt_deps::get_missing_basalt_deps, libsurvive_deps::get_missing_libsurvive_deps, basalt_deps::get_missing_basalt_deps, libsurvive_deps::get_missing_libsurvive_deps,
mercury_deps::get_missing_mercury_deps, monado_deps::get_missing_monado_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, openhmd_deps::get_missing_openhmd_deps, wivrn_deps::get_missing_wivrn_deps, Dependency,
xrizer_deps::get_missing_xrizer_deps, Dependency,
}, },
file_builders::active_runtime_json::ActiveRuntime, file_builders::active_runtime_json::ActiveRuntime,
paths::{get_data_dir, BWRAP_SYSTEM_PREFIX, SYSTEM_PREFIX}, paths::{get_data_dir, BWRAP_SYSTEM_PREFIX, SYSTEM_PREFIX},
@ -15,7 +14,7 @@ use serde::{Deserialize, Serialize};
use std::{ use std::{
collections::HashMap, collections::HashMap,
fmt::Display, fmt::Display,
fs::{remove_dir_all, File}, fs::File,
io::BufReader, io::BufReader,
path::{Path, PathBuf}, path::{Path, PathBuf},
slice::Iter, slice::Iter,
@ -266,7 +265,6 @@ pub enum OvrCompatibilityModuleType {
#[default] #[default]
Opencomposite, Opencomposite,
Xrizer, Xrizer,
Vapor,
} }
impl Display for OvrCompatibilityModuleType { impl Display for OvrCompatibilityModuleType {
@ -274,23 +272,13 @@ impl Display for OvrCompatibilityModuleType {
f.write_str(match self { f.write_str(match self {
Self::Opencomposite => "OpenComposite", Self::Opencomposite => "OpenComposite",
Self::Xrizer => "xrizer", Self::Xrizer => "xrizer",
Self::Vapor => "VapoR",
}) })
} }
} }
impl OvrCompatibilityModuleType { impl OvrCompatibilityModuleType {
pub fn iter() -> Iter<'static, Self> { pub fn iter() -> Iter<'static, Self> {
[Self::Opencomposite, Self::Xrizer, Self::Vapor].iter() [Self::Opencomposite, Self::Xrizer].iter()
}
pub fn get_missing_deps(&self) -> Vec<Dependency> {
match self {
OvrCompatibilityModuleType::Xrizer => get_missing_xrizer_deps(),
OvrCompatibilityModuleType::Opencomposite | OvrCompatibilityModuleType::Vapor => {
Vec::default()
}
}
} }
} }
@ -301,7 +289,6 @@ impl FromStr for OvrCompatibilityModuleType {
match s.to_lowercase().trim() { match s.to_lowercase().trim() {
"opencomposite" => Ok(Self::Opencomposite), "opencomposite" => Ok(Self::Opencomposite),
"xrizer" => Ok(Self::Xrizer), "xrizer" => Ok(Self::Xrizer),
"vapor" => Ok(Self::Vapor),
_ => Err(format!("no match for ovr compatibility module `{s}`")), _ => Err(format!("no match for ovr compatibility module `{s}`")),
} }
} }
@ -312,8 +299,7 @@ impl From<u32> for OvrCompatibilityModuleType {
match value { match value {
0 => Self::Opencomposite, 0 => Self::Opencomposite,
1 => Self::Xrizer, 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 { pub fn runtime_dir(&self) -> PathBuf {
match self.mod_type { match self.mod_type {
OvrCompatibilityModuleType::Opencomposite => self.path.join("build"), OvrCompatibilityModuleType::Opencomposite => self.path.join("build"),
OvrCompatibilityModuleType::Vapor => self.path.join("build/install_pfx/lib/VapoR"),
OvrCompatibilityModuleType::Xrizer => self.path.join("target/release"), OvrCompatibilityModuleType::Xrizer => self.path.join("target/release"),
} }
} }
@ -459,29 +444,6 @@ impl Profile {
get_data_dir().join("prefixes").join(uuid) 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 { pub fn xr_runtime_json_env_var(&self) -> String {
format!( format!(
"XR_RUNTIME_JSON=\"{prefix}/share/openxr/1/openxr_{runtime}.json\"", "XR_RUNTIME_JSON=\"{prefix}/share/openxr/1/openxr_{runtime}.json\"",
@ -746,7 +708,7 @@ impl Profile {
if self.features.mercury_enabled { if self.features.mercury_enabled {
missing_deps.extend(get_missing_mercury_deps()); 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.sort_unstable();
missing_deps.dedup(); // dedup only works if sorted, hence the above missing_deps.dedup(); // dedup only works if sorted, hence the above

View file

@ -21,7 +21,7 @@ pub fn lighthouse_profile() -> Profile {
environment.insert("LD_LIBRARY_PATH".into(), prepare_ld_library_path(&prefix)); environment.insert("LD_LIBRARY_PATH".into(), prepare_ld_library_path(&prefix));
Profile { Profile {
uuid: "lighthouse-default".into(), uuid: "lighthouse-default".into(),
name: format!("Lighthouse Driver - {APP_NAME} Default"), name: format!("Lighthouse Driver - {name} Default", name = APP_NAME),
xrservice_path: data_monado_path(), xrservice_path: data_monado_path(),
xrservice_type: XRServiceType::Monado, xrservice_type: XRServiceType::Monado,
ovr_comp: ProfileOvrCompatibilityModule { ovr_comp: ProfileOvrCompatibilityModule {

View file

@ -21,7 +21,7 @@ pub fn openhmd_profile() -> Profile {
environment.insert("LD_LIBRARY_PATH".into(), prepare_ld_library_path(&prefix)); environment.insert("LD_LIBRARY_PATH".into(), prepare_ld_library_path(&prefix));
Profile { Profile {
uuid: "openhmd-default".into(), uuid: "openhmd-default".into(),
name: format!("OpenHMD - {APP_NAME} Default"), name: format!("OpenHMD - {name} Default", name = APP_NAME),
xrservice_path: data_monado_path(), xrservice_path: data_monado_path(),
xrservice_type: XRServiceType::Monado, xrservice_type: XRServiceType::Monado,
ovr_comp: ProfileOvrCompatibilityModule { ovr_comp: ProfileOvrCompatibilityModule {

View file

@ -22,7 +22,7 @@ pub fn simulated_profile() -> Profile {
); );
Profile { Profile {
uuid: "simulated-default".into(), uuid: "simulated-default".into(),
name: format!("Simulated Driver - {APP_NAME} Default"), name: format!("Simulated Driver - {name} Default", name = APP_NAME),
xrservice_path: data_monado_path(), xrservice_path: data_monado_path(),
xrservice_type: XRServiceType::Monado, xrservice_type: XRServiceType::Monado,
ovr_comp: ProfileOvrCompatibilityModule { ovr_comp: ProfileOvrCompatibilityModule {

View file

@ -23,7 +23,7 @@ pub fn survive_profile() -> Profile {
environment.insert("LD_LIBRARY_PATH".into(), prepare_ld_library_path(&prefix)); environment.insert("LD_LIBRARY_PATH".into(), prepare_ld_library_path(&prefix));
Profile { Profile {
uuid: "survive-default".into(), uuid: "survive-default".into(),
name: format!("Survive - {APP_NAME} Default"), name: format!("Survive - {name} Default", name = APP_NAME),
xrservice_path: data_monado_path(), xrservice_path: data_monado_path(),
xrservice_type: XRServiceType::Monado, xrservice_type: XRServiceType::Monado,
ovr_comp: ProfileOvrCompatibilityModule { ovr_comp: ProfileOvrCompatibilityModule {

View file

@ -19,7 +19,7 @@ pub fn wivrn_profile() -> Profile {
environment.insert("U_PACING_APP_USE_MIN_FRAME_PERIOD".into(), "1".into()); environment.insert("U_PACING_APP_USE_MIN_FRAME_PERIOD".into(), "1".into());
Profile { Profile {
uuid: "wivrn-default".into(), uuid: "wivrn-default".into(),
name: format!("WiVRn - {APP_NAME} Default"), name: format!("WiVRn - {name} Default", name = APP_NAME),
xrservice_path: data_wivrn_path(), xrservice_path: data_wivrn_path(),
xrservice_type: XRServiceType::Wivrn, xrservice_type: XRServiceType::Wivrn,
ovr_comp: ProfileOvrCompatibilityModule { ovr_comp: ProfileOvrCompatibilityModule {

View file

@ -21,7 +21,7 @@ pub fn wmr_profile() -> Profile {
environment.insert("LD_LIBRARY_PATH".into(), prepare_ld_library_path(&prefix)); environment.insert("LD_LIBRARY_PATH".into(), prepare_ld_library_path(&prefix));
Profile { Profile {
uuid: "wmr-default".into(), uuid: "wmr-default".into(),
name: format!("WMR - {APP_NAME} Default"), name: format!("WMR - {name} Default", name = APP_NAME),
xrservice_path: data_monado_path(), xrservice_path: data_monado_path(),
xrservice_type: XRServiceType::Monado, xrservice_type: XRServiceType::Monado,
ovr_comp: ProfileOvrCompatibilityModule { ovr_comp: ProfileOvrCompatibilityModule {

View file

@ -2,7 +2,7 @@ use crate::{
paths::get_backup_dir, paths::get_backup_dir,
profile::Profile, profile::Profile,
util::{ util::{
file_utils::{copy_file, get_writer, mark_as_executable}, file_utils::{copy_file, get_writer},
steam_library_folder::SteamLibraryFolder, steam_library_folder::SteamLibraryFolder,
}, },
}; };
@ -15,85 +15,45 @@ use std::{
}; };
use tracing::error; use tracing::error;
pub const SNIPER_RUNTIME_STEAM_APPID: u32 = 1628350; pub const PRESSURE_VESSEL_STEAM_APPID: u32 = 1628350;
pub const SOLDIER_RUNTIME_STEAM_APPID: u32 = 1391110;
fn get_sniper_runtime_entrypoint_path() -> Option<PathBuf> { fn get_runtime_entrypoint_path() -> Option<PathBuf> {
match SteamLibraryFolder::get_folders() { match SteamLibraryFolder::get_folders() {
Ok(libraryfolders) => libraryfolders Ok(libraryfolders) => libraryfolders
.iter() .iter()
.find(|(_, folder)| folder.apps.contains_key(&SNIPER_RUNTIME_STEAM_APPID)) .find(|(_, folder)| folder.apps.contains_key(&PRESSURE_VESSEL_STEAM_APPID))
.map(|(_, folder)| { .map(|(_, folder)| {
PathBuf::from(&folder.path) PathBuf::from(&folder.path)
.join("steamapps/common/SteamLinuxRuntime_sniper/_v2-entry-point") .join("steamapps/common/SteamLinuxRuntime_sniper/_v2-entry-point")
}), }),
Err(e) => { Err(e) => {
error!("unable to get sniper runtime entrypoint path: {e}"); error!("unable to get runtime entrypoint path: {e}");
None
}
}
}
fn get_soldier_runtime_entrypoint_path() -> Option<PathBuf> {
match SteamLibraryFolder::get_folders() {
Ok(libraryfolders) => libraryfolders
.iter()
.find(|(_, folder)| folder.apps.contains_key(&SOLDIER_RUNTIME_STEAM_APPID))
.map(|(_, folder)| {
PathBuf::from(&folder.path)
.join("steamapps/common/SteamLinuxRuntime_soldier/_v2-entry-point")
}),
Err(e) => {
error!("unable to get soldier runtime entrypoint path: {e}");
None None
} }
} }
} }
lazy_static! { lazy_static! {
static ref STEAM_SNIPER_RUNTIME_ENTRYPOINT_PATH: Option<PathBuf> = static ref STEAM_RUNTIME_ENTRYPOINT_PATH: Option<PathBuf> = get_runtime_entrypoint_path();
get_sniper_runtime_entrypoint_path();
static ref STEAM_SOLDIER_RUNTIME_ENTRYPOINT_PATH: Option<PathBuf> =
get_soldier_runtime_entrypoint_path();
} }
fn get_backup_sniper_runtime_entrypoint_location() -> PathBuf { fn get_backup_runtime_entrypoint_location() -> PathBuf {
get_backup_dir().join("_v2-entry-point.bak") get_backup_dir().join("_v2-entry-point.bak")
} }
fn get_backup_soldier_runtime_entrypoint_location() -> PathBuf { fn backup_runtime_entrypoint(path: &Path) {
get_backup_dir().join("_v2-entry-point.soldier.bak") copy_file(path, &get_backup_runtime_entrypoint_location());
} }
fn backup_sniper_runtime_entrypoint(path: &Path) { pub fn restore_runtime_entrypoint() {
copy_file(path, &get_backup_sniper_runtime_entrypoint_location()); if let Some(path) = STEAM_RUNTIME_ENTRYPOINT_PATH.as_ref() {
} let backup = get_backup_runtime_entrypoint_location();
fn backup_soldier_runtime_entrypoint(path: &Path) {
copy_file(path, &get_backup_soldier_runtime_entrypoint_location());
}
pub fn restore_sniper_runtime_entrypoint() {
if let Some(path) = STEAM_SNIPER_RUNTIME_ENTRYPOINT_PATH.as_ref() {
let backup = get_backup_sniper_runtime_entrypoint_location();
if Path::new(&backup).is_file() { if Path::new(&backup).is_file() {
copy_file(&backup, path); copy_file(&backup, path);
let _ = mark_as_executable(path);
} }
} }
} }
pub fn restore_soldier_runtime_entrypoint() {
if let Some(path) = STEAM_SOLDIER_RUNTIME_ENTRYPOINT_PATH.as_ref() {
let backup = get_backup_soldier_runtime_entrypoint_location();
if Path::new(&backup).is_file() {
copy_file(&backup, path);
let _ = mark_as_executable(path);
}
}
}
/// this implementation is identical for both sniper and soldier runtimes
fn append_to_runtime_entrypoint(data: &str, path: &Path) -> anyhow::Result<()> { fn append_to_runtime_entrypoint(data: &str, path: &Path) -> anyhow::Result<()> {
let existing = read_to_string(path)?; let existing = read_to_string(path)?;
let new = existing.replace( let new = existing.replace(
@ -105,12 +65,10 @@ fn append_to_runtime_entrypoint(data: &str, path: &Path) -> anyhow::Result<()> {
Ok(()) Ok(())
} }
pub fn set_sniper_runtime_entrypoint_launch_opts_from_profile( pub fn set_runtime_entrypoint_launch_opts_from_profile(profile: &Profile) -> anyhow::Result<()> {
profile: &Profile, restore_runtime_entrypoint();
) -> anyhow::Result<()> { if let Some(dest) = STEAM_RUNTIME_ENTRYPOINT_PATH.as_ref() {
restore_sniper_runtime_entrypoint(); backup_runtime_entrypoint(dest);
if let Some(dest) = STEAM_SNIPER_RUNTIME_ENTRYPOINT_PATH.as_ref() {
backup_sniper_runtime_entrypoint(dest);
append_to_runtime_entrypoint( append_to_runtime_entrypoint(
&profile &profile
.get_env_vars() .get_env_vars()
@ -120,31 +78,8 @@ pub fn set_sniper_runtime_entrypoint_launch_opts_from_profile(
.join("\n"), .join("\n"),
dest, dest,
)?; )?;
mark_as_executable(dest)?;
return Ok(()); return Ok(());
} }
bail!("Could not find valid sniper runtime entrypoint"); bail!("Could not find valid runtime entrypoint");
}
pub fn set_soldier_runtime_entrypoint_launch_opts_from_profile(
profile: &Profile,
) -> anyhow::Result<()> {
restore_soldier_runtime_entrypoint();
if let Some(dest) = STEAM_SOLDIER_RUNTIME_ENTRYPOINT_PATH.as_ref() {
backup_soldier_runtime_entrypoint(dest);
append_to_runtime_entrypoint(
&profile
.get_env_vars()
.iter()
.map(|ev| "export ".to_string() + ev)
.collect::<Vec<String>>()
.join("\n"),
dest,
)?;
mark_as_executable(dest)?;
return Ok(());
}
bail!("Could not find valid soldier runtime entrypoint");
} }

View file

@ -33,7 +33,7 @@ pub fn create_about_dialog() -> adw::AboutDialog {
const UNKNOWN: &str = "UNKNOWN"; const UNKNOWN: &str = "UNKNOWN";
pub fn populate_debug_info(dialog: &adw::AboutDialog, vkinfo: Option<&VulkanInfo>) { pub fn populate_debug_info(dialog: &adw::AboutDialog, vkinfo: Option<&VulkanInfo>) {
if !dialog.debug_info().is_empty() { if dialog.debug_info().len() > 0 {
return; return;
} }
let distro_family = LinuxDistro::get(); let distro_family = LinuxDistro::get();
@ -70,7 +70,7 @@ pub fn populate_debug_info(dialog: &adw::AboutDialog, vkinfo: Option<&VulkanInfo
.and_then(|s| { .and_then(|s| {
s.split("\n") s.split("\n")
.find(|line| line.starts_with("model name")) .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() .flatten()
.unwrap_or(UNKNOWN.into()) .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(", ")) .map(|i| i.gpu_names.join(", "))
.unwrap_or(UNKNOWN.into()) .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: {}", { format!("Detected XR Devices: {}", {
let devs = PhysicalXRDevice::from_usb(); let devs = PhysicalXRDevice::from_usb();
if devs.is_empty() { 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_basalt::get_build_basalt_jobs, build_libsurvive::get_build_libsurvive_jobs,
build_mercury::get_build_mercury_jobs, build_monado::get_build_monado_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_opencomposite::get_build_opencomposite_jobs, build_openhmd::get_build_openhmd_jobs,
build_vapor::get_build_vapor_jobs, build_wivrn::get_build_wivrn_jobs, build_wivrn::get_build_wivrn_jobs, build_xrizer::get_build_xrizer_jobs,
build_xrizer::get_build_xrizer_jobs,
}, },
config::{Config, PluginConfig}, config::{Config, PluginConfig},
constants::APP_NAME, constants::APP_NAME,
@ -41,11 +40,8 @@ use crate::{
profile::{OvrCompatibilityModuleType, Profile, XRServiceType}, profile::{OvrCompatibilityModuleType, Profile, XRServiceType},
stateless_action, stateless_action,
steam_linux_runtime_injector::{ steam_linux_runtime_injector::{
restore_sniper_runtime_entrypoint, restore_soldier_runtime_entrypoint, restore_runtime_entrypoint, set_runtime_entrypoint_launch_opts_from_profile,
set_sniper_runtime_entrypoint_launch_opts_from_profile,
set_soldier_runtime_entrypoint_launch_opts_from_profile,
}, },
termcolor::TermColor,
util::file_utils::{ util::file_utils::{
setcap_cap_sys_nice_eip, setcap_cap_sys_nice_eip_cmd, verify_cap_sys_nice_eip, setcap_cap_sys_nice_eip, setcap_cap_sys_nice_eip_cmd, verify_cap_sys_nice_eip,
}, },
@ -54,7 +50,6 @@ use crate::{
xr_devices::XRDevice, xr_devices::XRDevice,
}; };
use adw::{prelude::*, ResponseAppearance}; use adw::{prelude::*, ResponseAppearance};
use delicious_adwaita::{theme::Theme, ThemeEngine};
use gtk::glib::{self, clone}; use gtk::glib::{self, clone};
use notify_rust::NotificationHandle; use notify_rust::NotificationHandle;
use relm4::{ use relm4::{
@ -100,8 +95,6 @@ pub struct App {
inhibit_fail_notif: Option<NotificationHandle>, inhibit_fail_notif: Option<NotificationHandle>,
pluginstore: Option<AsyncController<PluginStore>>, pluginstore: Option<AsyncController<PluginStore>>,
theme_engine: ThemeEngine,
} }
#[derive(Debug)] #[derive(Debug)]
@ -119,8 +112,7 @@ pub enum Msg {
StartWithDebug, StartWithDebug,
RestartXRService, RestartXRService,
ProfileSelected(Profile), ProfileSelected(Profile),
/// bool param: delete files DeleteProfile,
DeleteProfile(bool),
SaveProfile(Profile), SaveProfile(Profile),
RunSetCap, RunSetCap,
OpenLibsurviveSetup, OpenLibsurviveSetup,
@ -136,8 +128,6 @@ pub enum Msg {
WivrnCheckPairMode, WivrnCheckPairMode,
OpenPluginStore, OpenPluginStore,
UpdateConfigPlugins(HashMap<String, PluginConfig>), UpdateConfigPlugins(HashMap<String, PluginConfig>),
ShowThemeManager,
SaveThemeConfig,
NoOp, NoOp,
} }
@ -236,17 +226,13 @@ impl App {
); );
worker.start(); worker.start();
self.xrservice_worker = Some(worker); self.xrservice_worker = Some(worker);
let set_sniper_launch_opts_res =
set_sniper_runtime_entrypoint_launch_opts_from_profile(&prof);
let set_soldier_launch_opts_res =
set_soldier_runtime_entrypoint_launch_opts_from_profile(&prof);
self.main_view self.main_view
.sender() .sender()
.emit(MainViewMsg::XRServiceActiveChanged( .emit(MainViewMsg::XRServiceActiveChanged(
true, true,
Some(self.get_selected_profile()), Some(self.get_selected_profile()),
// show launch opts only if setting the runtime entrypoint fails // show launch opts only if setting the runtime entrypoint fails
set_sniper_launch_opts_res.is_err() || set_soldier_launch_opts_res.is_err(), set_runtime_entrypoint_launch_opts_from_profile(&prof).is_err(),
)); ));
self.debug_view self.debug_view
.sender() .sender()
@ -310,8 +296,7 @@ impl App {
} }
pub fn restore_openxr_openvr_files(&self) { pub fn restore_openxr_openvr_files(&self) {
restore_sniper_runtime_entrypoint(); restore_runtime_entrypoint();
restore_soldier_runtime_entrypoint();
if let Err(e) = remove_current_active_runtime() { if let Err(e) = remove_current_active_runtime() {
alert( alert(
"Could not remove profile active runtime", "Could not remove profile active runtime",
@ -380,7 +365,7 @@ impl AsyncComponent for App {
set_content: Some(&adw::NavigationPage::new(model.debug_view.widget(), "Debug View")), set_content: Some(&adw::NavigationPage::new(model.debug_view.widget(), "Debug View")),
set_show_content: false, set_show_content: false,
set_collapsed: !model.config.debug_view_enabled, set_collapsed: !model.config.debug_view_enabled,
}, }
}, },
connect_close_request[sender] => move |win| { connect_close_request[sender] => move |win| {
sender.input(Msg::SaveWinSize(win.width(), win.height())); sender.input(Msg::SaveWinSize(win.width(), win.height()));
@ -404,27 +389,6 @@ impl AsyncComponent for App {
) { ) {
match message { match message {
Msg::NoOp => {} 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) => { Msg::OnServiceLog(rows) => {
if !rows.is_empty() { if !rows.is_empty() {
self.debug_view self.debug_view
@ -555,7 +519,7 @@ impl AsyncComponent for App {
jobs.extend(get_build_basalt_jobs(&profile, clean_build)); jobs.extend(get_build_basalt_jobs(&profile, clean_build));
} }
if profile.features.mercury_enabled { if profile.features.mercury_enabled {
jobs.extend(get_build_mercury_jobs()); jobs.extend(get_build_mercury_jobs(&profile));
} }
jobs.extend(match profile.xrservice_type { jobs.extend(match profile.xrservice_type {
XRServiceType::Monado => get_build_monado_jobs(&profile, clean_build), XRServiceType::Monado => get_build_monado_jobs(&profile, clean_build),
@ -569,9 +533,6 @@ impl AsyncComponent for App {
OvrCompatibilityModuleType::Xrizer => { OvrCompatibilityModuleType::Xrizer => {
get_build_xrizer_jobs(&profile, clean_build) get_build_xrizer_jobs(&profile, clean_build)
} }
OvrCompatibilityModuleType::Vapor => {
get_build_vapor_jobs(&profile, clean_build)
}
}); });
let missing_deps = profile.missing_dependencies(); let missing_deps = profile.missing_dependencies();
if !(self.skip_depcheck || profile.skip_dependency_check || missing_deps.is_empty()) if !(self.skip_depcheck || profile.skip_dependency_check || missing_deps.is_empty())
@ -661,10 +622,6 @@ impl AsyncComponent for App {
if dep_pkexec().check() { if dep_pkexec().check() {
self.setcap_confirm_dialog.present(Some(&self.app_win)); self.setcap_confirm_dialog.present(Some(&self.app_win));
} else { } else {
self.build_window
.sender()
.emit(BuildWindowMsg::UpdateContent(vec![TermColor::Red
.colorize("pkexec not found, cannot set capabilities\n")]));
alert_w_widget( alert_w_widget(
"pkexec not found", "pkexec not found",
Some(&format!( Some(&format!(
@ -687,7 +644,7 @@ impl AsyncComponent for App {
self.build_window self.build_window
.sender() .sender()
.emit(BuildWindowMsg::UpdateBuildStatus(BuildStatus::Error( .emit(BuildWindowMsg::UpdateBuildStatus(BuildStatus::Error(
format!("Exit status {errcode}"), format!("Exit status {}", errcode),
))); )));
} }
}; };
@ -697,16 +654,9 @@ impl AsyncComponent for App {
w.stop(); w.stop();
} }
} }
Msg::DeleteProfile(delete_files) => { Msg::DeleteProfile => {
let todel = self.get_selected_profile(); let todel = self.get_selected_profile();
if todel.editable { 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.user_profiles.retain(|p| p.uuid != todel.uuid);
self.config.save(); self.config.save();
self.profiles = self.config.profiles(); self.profiles = self.config.profiles();
@ -746,7 +696,6 @@ impl AsyncComponent for App {
} }
Msg::RunSetCap => { Msg::RunSetCap => {
if !dep_pkexec().check() { if !dep_pkexec().check() {
// there's a precheck ahead of this, this should likely never happen
error!("pkexec not found, skipping setcap"); error!("pkexec not found, skipping setcap");
} else { } else {
let profile = self.get_selected_profile(); let profile = self.get_selected_profile();
@ -764,26 +713,8 @@ impl AsyncComponent for App {
if let Err(e) = setcap_cap_sys_nice_eip(&profile).await { if let Err(e) = setcap_cap_sys_nice_eip(&profile).await {
setcap_failed_dialog(); setcap_failed_dialog();
error!("failed running setcap: {e}"); 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 { } else if !verify_cap_sys_nice_eip(&profile).await {
setcap_failed_dialog(); 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")
]));
} }
} }
} }
@ -1031,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 // this bypasses the macro because I need the underlying gio action
// to enable/disable it in update() // to enable/disable it in update()
let configure_wivrn_action = { let configure_wivrn_action = {
@ -1078,12 +998,13 @@ impl AsyncComponent for App {
config: config.clone(), config: config.clone(),
selected_profile: selected_profile.clone(), selected_profile: selected_profile.clone(),
root_win: root.clone().into(), root_win: root.clone().into(),
vkinfo: vkinfo.clone(),
}) })
.forward(sender.input_sender(), |message| match message { .forward(sender.input_sender(), |message| match message {
MainViewOutMsg::DoStartStopXRService => Msg::DoStartStopXRService, MainViewOutMsg::DoStartStopXRService => Msg::DoStartStopXRService,
MainViewOutMsg::RestartXRService => Msg::RestartXRService, MainViewOutMsg::RestartXRService => Msg::RestartXRService,
MainViewOutMsg::ProfileSelected(uuid) => Msg::ProfileSelected(uuid), 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::SaveProfile(p) => Msg::SaveProfile(p),
MainViewOutMsg::OpenLibsurviveSetup => Msg::OpenLibsurviveSetup, MainViewOutMsg::OpenLibsurviveSetup => Msg::OpenLibsurviveSetup,
MainViewOutMsg::BuildProfile(clean) => Msg::BuildProfile(clean), MainViewOutMsg::BuildProfile(clean) => Msg::BuildProfile(clean),
@ -1110,17 +1031,6 @@ impl AsyncComponent for App {
.detach(), .detach(),
split_view: None, split_view: None,
setcap_confirm_dialog, 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, config,
profiles, profiles,
xrservice_worker: None, xrservice_worker: None,
@ -1209,7 +1119,6 @@ new_stateless_action!(pub QuitAction, AppActionGroup, "quit");
new_stateful_action!(pub DebugViewToggleAction, AppActionGroup, "debugviewtoggle", (), bool); new_stateful_action!(pub DebugViewToggleAction, AppActionGroup, "debugviewtoggle", (), bool);
new_stateless_action!(pub ConfigureWivrnAction, AppActionGroup, "configurewivrn"); new_stateless_action!(pub ConfigureWivrnAction, AppActionGroup, "configurewivrn");
new_stateless_action!(pub PluginStoreAction, AppActionGroup, "store"); 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 DebugOpenDataAction, AppActionGroup, "debugopendata");
new_stateless_action!(pub DebugOpenPrefixAction, AppActionGroup, "debugopenprefix"); 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 super::{term_widget::TermWidget, SENDER_IO_ERR_MSG};
use adw::prelude::*; use adw::prelude::*;
use relm4::prelude::*; use relm4::prelude::*;
@ -90,54 +88,43 @@ impl SimpleComponent for BuildWindow {
gtk::Label { gtk::Label {
#[track = "model.changed(BuildWindow::build_status())"] #[track = "model.changed(BuildWindow::build_status())"]
set_markup: match &model.build_status { set_markup: match &model.build_status {
BuildStatus::Building => String::default(), BuildStatus::Building => "Build in progress...".to_string(),
BuildStatus::Done => "Build done, you can close this window".into(), BuildStatus::Done => "Build done, you can close this window".to_string(),
BuildStatus::Error(code) => { BuildStatus::Error(code) => {
format!("Build failed: \"{code}\"") format!("Build failed: \"{c}\"", c = code)
} }
}.as_str(), }.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", add_css_class: "title-2",
set_wrap: true, set_wrap: true,
set_wrap_mode: gtk::pango::WrapMode::Word, set_wrap_mode: gtk::pango::WrapMode::Word,
set_justify: gtk::Justification::Center, 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(), model.term.container.clone(),
}, },
add_bottom_bar: bottom_bar = &gtk::Box { add_bottom_bar: bottom_bar = &gtk::Button {
set_orientation: gtk::Orientation::Horizontal, add_css_class: "pill",
set_halign: gtk::Align::Center, set_halign: gtk::Align::Center,
set_hexpand: true, set_label: "Close",
set_margin_bottom: 24, set_margin_all: 12,
set_spacing: 12, #[track = "model.changed(BuildWindow::can_close())"]
gtk::Button { set_sensitive: model.can_close,
add_css_class: "pill", connect_clicked[win] => move |_| {
set_halign: gtk::Align::Center,
set_label: "Close", win.close();
#[track = "model.changed(BuildWindow::can_close())"]
set_visible: 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("success");
label.remove_css_class("error"); label.remove_css_class("error");
match status { match status {
BuildStatus::Done => { BuildStatus::Done => label.add_css_class("success"),
label.add_css_class("success"); BuildStatus::Error(_) => label.add_css_class("error"),
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")
]));
}
_ => {} _ => {}
} }
if status != BuildStatus::Building { if status != BuildStatus::Building {

View file

@ -21,11 +21,11 @@ use crate::{
paths::{get_data_dir, get_home_dir}, paths::{get_data_dir, get_home_dir},
profile::{LighthouseDriver, Profile, XRServiceType}, profile::{LighthouseDriver, Profile, XRServiceType},
stateless_action, stateless_action,
ui::app::ThemeManagerAction,
util::{ util::{
file_utils::{get_writer, mount_has_nosuid}, file_utils::{get_writer, mount_has_nosuid},
steamvr_utils::chaperone_info_exists, steamvr_utils::chaperone_info_exists,
}, },
vulkaninfo::VulkanInfo,
wivrn_dbus, wivrn_dbus,
xr_devices::XRDevice, xr_devices::XRDevice,
}; };
@ -75,6 +75,8 @@ pub struct MainView {
#[tracker::do_not_track] #[tracker::do_not_track]
profile_export_action: gtk::gio::SimpleAction, profile_export_action: gtk::gio::SimpleAction,
xrservice_ready: bool, xrservice_ready: bool,
#[tracker::do_not_track]
vkinfo: Option<VulkanInfo>,
wivrn_pairing_mode: bool, wivrn_pairing_mode: bool,
wivrn_pin: Option<String>, wivrn_pin: Option<String>,
wivrn_supports_pairing: bool, wivrn_supports_pairing: bool,
@ -113,8 +115,7 @@ pub enum MainViewOutMsg {
DoStartStopXRService, DoStartStopXRService,
RestartXRService, RestartXRService,
ProfileSelected(Profile), ProfileSelected(Profile),
/// bool param: delete files DeleteProfile,
DeleteProfile(bool),
SaveProfile(Profile), SaveProfile(Profile),
OpenLibsurviveSetup, OpenLibsurviveSetup,
/// params: clean /// params: clean
@ -125,6 +126,7 @@ pub struct MainViewInit {
pub config: Config, pub config: Config,
pub selected_profile: Profile, pub selected_profile: Profile,
pub root_win: gtk::Window, pub root_win: gtk::Window,
pub vkinfo: Option<VulkanInfo>,
} }
impl MainView { impl MainView {
@ -160,7 +162,6 @@ impl AsyncComponent for MainView {
"Configure _WiVRn" => ConfigureWivrnAction, "Configure _WiVRn" => ConfigureWivrnAction,
}, },
section! { section! {
"Change _Theme" => ThemeManagerAction,
"_About" => AboutAction, "_About" => AboutAction,
}, },
}, },
@ -460,6 +461,34 @@ impl AsyncComponent for MainView {
set_wrap_mode: gtk::pango::WrapMode::Word, 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 { gtk::Box {
set_orientation: gtk::Orientation::Vertical, set_orientation: gtk::Orientation::Vertical,
set_hexpand: true, set_hexpand: true,
@ -934,13 +963,6 @@ impl AsyncComponent for MainView {
let profile_delete_confirm_dialog = adw::AlertDialog::builder() let profile_delete_confirm_dialog = adw::AlertDialog::builder()
.heading("Are you sure you want to delete this profile?") .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(); .build();
profile_delete_confirm_dialog.add_response("no", "_No"); profile_delete_confirm_dialog.add_response("no", "_No");
profile_delete_confirm_dialog.add_response("yes", "_Yes"); profile_delete_confirm_dialog.add_response("yes", "_Yes");
@ -952,19 +974,10 @@ impl AsyncComponent for MainView {
clone!( clone!(
#[strong] #[strong]
sender, sender,
move |dialog, res| { move |_, 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);
}
if res == "yes" { if res == "yes" {
sender sender
.output(Self::Output::DeleteProfile(delete_files)) .output(Self::Output::DeleteProfile)
.expect("Sender output failed"); .expect("Sender output failed");
} }
} }
@ -1090,6 +1103,7 @@ impl AsyncComponent for MainView {
xrservice_ready: false, xrservice_ready: false,
profile_delete_action, profile_delete_action,
profile_export_action, profile_export_action,
vkinfo: init.vkinfo,
wivrn_pairing_mode: false, wivrn_pairing_mode: false,
wivrn_supports_pairing: false, wivrn_supports_pairing: false,
wivrn_pin: None, wivrn_pin: None,

View file

@ -31,7 +31,6 @@ pub enum AddCustomPluginWinMsg {
Present, Present,
Close, Close,
OnNameChange(String), OnNameChange(String),
OnArgsChange(String),
OnExecPathChange(Option<String>), OnExecPathChange(Option<String>),
Add, Add,
} }
@ -96,11 +95,7 @@ impl SimpleComponent for AddCustomPluginWin {
"", "",
clone!( clone!(
#[strong] sender, #[strong] sender,
move |row| sender.input( move |row| sender.input(Self::Input::OnNameChange(row.text().to_string()))
Self::Input::OnNameChange(
row.text().to_string()
)
)
) )
), ),
add: &file_row( add: &file_row(
@ -110,23 +105,9 @@ impl SimpleComponent for AddCustomPluginWin {
Some(model.parent.clone()), Some(model.parent.clone()),
clone!( clone!(
#[strong] sender, #[strong] sender,
move |path_s| sender.input( move |path_s| sender.input(Self::Input::OnExecPathChange(path_s))
Self::Input::OnExecPathChange(path_s)
)
) )
), )
add: &entry_row(
"Plugin Arguments",
"",
clone!(
#[strong] sender,
move |row| sender.input(
Self::Input::OnArgsChange(
row.text().to_string()
)
)
)
),
}, },
}, },
}, },
@ -158,17 +139,6 @@ impl SimpleComponent for AddCustomPluginWin {
self.plugin.name = name; self.plugin.name = name;
self.set_can_add(self.plugin.validate()); self.set_can_add(self.plugin.validate());
} }
Self::Input::OnArgsChange(args) => {
let args = args.trim().to_string();
self.plugin.args = if args.is_empty() {
None
} else {
// it's fine to have them joined
// since they will ultimately be
// passed as a joined string
Some(vec![args])
}
}
Self::Input::OnExecPathChange(ep) => { Self::Input::OnExecPathChange(ep) => {
self.plugin.exec_path = ep.map(PathBuf::from); self.plugin.exec_path = ep.map(PathBuf::from);
self.set_can_add(self.plugin.validate()); self.set_can_add(self.plugin.validate());

View file

@ -165,8 +165,8 @@ impl Plugin {
/// each manifest should be json and the link should always point to the latest version /// each manifest should be json and the link should always point to the latest version
const MANIFESTS: [&str;3] = [ 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/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/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>>> { pub async fn refresh_plugins() -> anyhow::Result<Vec<anyhow::Result<Plugin>>> {

View file

@ -213,7 +213,7 @@ pub fn file_row<F: Fn(Option<String>) + 'static + Clone>(
let filedialog = gtk::FileDialog::builder() let filedialog = gtk::FileDialog::builder()
.modal(true) .modal(true)
.title(format!("Select {title}")) .title(format!("Select {}", title))
.build(); .build();
row.connect_activated(clone!( row.connect_activated(clone!(
@ -255,7 +255,7 @@ pub fn path_row<F: Fn(Option<String>) + 'static + Clone>(
let (row, path_label) = filedialog_row_base(title, description, value, cb.clone()); let (row, path_label) = filedialog_row_base(title, description, value, cb.clone());
let filedialog = gtk::FileDialog::builder() let filedialog = gtk::FileDialog::builder()
.modal(true) .modal(true)
.title(format!("Select Path for {title}")) .title(format!("Select Path for {}", title))
.build(); .build();
row.connect_activated(clone!( row.connect_activated(clone!(

View file

@ -45,7 +45,8 @@ impl SimpleComponent for SteamLaunchOptionsBox {
add_css_class: "dim-label", add_css_class: "dim-label",
set_hexpand: true, set_hexpand: true,
set_label: format!( set_label: format!(
"Set this string in the launch options of Steam games, so that they can pick up the {APP_NAME} runtime correctly") "Set this string in the launch options of Steam games, so that they can pick up the {app} runtime correctly",
app = APP_NAME)
.as_str(), .as_str(),
set_xalign: 0.0, set_xalign: 0.0,
set_wrap: true, set_wrap: true,

View file

@ -112,7 +112,7 @@ impl AsyncComponent for WivrnWiredStartBox {
Self::Input::UpdateSelectedProfile(p) => self.set_selected_profile(p), Self::Input::UpdateSelectedProfile(p) => self.set_selected_profile(p),
Self::Input::StartWivrnClient => { Self::Input::StartWivrnClient => {
if !dep_adb().check() { if !dep_adb().check() {
alert("ADB is not installed", Some(&format!("Please install ADB on your computer to start the WiVRn client from {APP_NAME}.")), Some(&self.root_win)); alert("ADB is not installed", Some(&format!("Please install ADB on your computer to start the WiVRn client from {}.", APP_NAME)), Some(&self.root_win));
return; return;
} }
self.set_start_client_status(StartClientStatus::InProgress); self.set_start_client_status(StartClientStatus::InProgress);

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 anyhow::bail;
use nix::{ use nix::{
errno::Errno, errno::Errno,
@ -79,29 +79,9 @@ pub fn set_file_readonly(path: &Path, readonly: bool) -> anyhow::Result<()> {
Ok(fs::set_permissions(path, perms)?) 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> { pub fn setcap_cap_sys_nice_eip_cmd(profile: &Profile) -> Vec<String> {
vec![ vec![
setcap_executable().unwrap_or("setcap".into()), "setcap".into(),
"CAP_SYS_NICE=eip".into(), "CAP_SYS_NICE=eip".into(),
profile profile
.prefix .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 { pub async fn verify_cap_sys_nice_eip(profile: &Profile) -> bool {
let xrservice_binary = profile.xrservice_binary().to_string_lossy().to_string(); let xrservice_binary = profile.xrservice_binary().to_string_lossy().to_string();
if let Some(getcap_exec) = getcap_executable() { match async_process("getcap", Some(&[&xrservice_binary]), None).await {
match async_process(&getcap_exec, Some(&[&xrservice_binary]), None).await { Err(e) => {
Err(e) => { error!("failed to run `getcap {xrservice_binary}`: {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 false
} } else {
Ok(out) => { out.stdout.to_lowercase().contains("cap_sys_nice=eip")
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 {
error!("getcap executable does not exist");
false
} }
} }

View file

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

View file

@ -12,5 +12,5 @@
} }
], ],
"application": ["foobar", "baz"], "application": ["foobar", "baz"],
"tcp-only": true "tcp_only": true
} }