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",
]
[[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.1"
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.1"
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,35 +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>

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.1', # version number row
meson_version: '>= 0.59',
license: 'AGPL-3.0-or-later',
)

View file

@ -2,6 +2,57 @@
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
DATA_HOME=$HOME/.local/share
else

View file

@ -22,7 +22,7 @@ impl Cmake {
if k.contains(' ') {
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());

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,24 +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()),
("CMAKE_POLICY_VERSION_MINIMUM", "3.5".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,8 +1,11 @@
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();
jobs.push_back(WorkerJob::new_printer(
"Building Mercury...",
@ -14,7 +17,10 @@ pub fn get_build_mercury_jobs() -> VecDeque<WorkerJob> {
.join("scripts/build_mercury.sh")
.to_string_lossy()
.to_string(),
None,
Some(vec![
profile.prefix.to_string_lossy().to_string(),
get_cache_dir().to_string_lossy().to_string(),
]),
));
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_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,27 +1,10 @@
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;
fn mercury_deps() -> Vec<Dependency> {
vec![
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 {
name: "jq".into(),
dep_type: DepType::Executable,
@ -56,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},
@ -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

@ -13,7 +13,7 @@ const CHUNK_SIZE: usize = 1024;
fn headers() -> HeaderMap {
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
}

View file

@ -65,7 +65,7 @@ fn env_var_descriptions() -> Vec<(&'static str, &'static str)> {
fn env_var_descriptions_as_paragraph() -> String {
ENV_VAR_DESCRIPTIONS
.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>>()
.join("\n\n")
}

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

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

View file

@ -116,7 +116,7 @@ fn list_gpus() -> Vec<GpuSysDrm> {
for i in 0..5 {
// 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");
if let Some(mut reader) = get_reader(&vendor_file) {
let mut buf = String::new();

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,16 +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 steam_linux_runtime_injector::{
restore_sniper_runtime_entrypoint, restore_soldier_runtime_entrypoint,
};
use tracing::{error, warn};
use std::env;
use steam_linux_runtime_injector::restore_runtime_entrypoint;
use tracing::warn;
use tracing_subscriber::{
filter::LevelFilter, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer,
};
@ -70,50 +63,7 @@ fn restore_steam_xr_files() {
}
}
}
restore_sniper_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))
restore_runtime_entrypoint();
}
fn main() -> Result<()> {
@ -121,8 +71,6 @@ fn main() -> Result<()> {
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) =
@ -142,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");
@ -160,7 +104,7 @@ fn main() -> Result<()> {
}
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() {
gtk::style_context_add_provider_for_display(
&display,

View file

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

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,7 +299,6 @@ impl From<u32> for OvrCompatibilityModuleType {
match value {
0 => Self::Opencomposite,
1 => Self::Xrizer,
2 => Self::Vapor,
_ => 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

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

View file

@ -22,7 +22,7 @@ pub fn simulated_profile() -> Profile {
);
Profile {
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_type: XRServiceType::Monado,
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));
Profile {
uuid: "survive-default".into(),
name: format!("Survive - {APP_NAME} Default"),
name: format!("Survive - {name} Default", name = APP_NAME),
xrservice_path: data_monado_path(),
xrservice_type: XRServiceType::Monado,
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());
Profile {
uuid: "wivrn-default".into(),
name: format!("WiVRn - {APP_NAME} Default"),
name: format!("WiVRn - {name} Default", name = APP_NAME),
xrservice_path: data_wivrn_path(),
xrservice_type: XRServiceType::Wivrn,
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));
Profile {
uuid: "wmr-default".into(),
name: format!("WMR - {APP_NAME} Default"),
name: format!("WMR - {name} Default", name = APP_NAME),
xrservice_path: data_monado_path(),
xrservice_type: XRServiceType::Monado,
ovr_comp: ProfileOvrCompatibilityModule {

View file

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

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,
@ -41,11 +40,8 @@ use crate::{
profile::{OvrCompatibilityModuleType, Profile, XRServiceType},
stateless_action,
steam_linux_runtime_injector::{
restore_sniper_runtime_entrypoint, restore_soldier_runtime_entrypoint,
set_sniper_runtime_entrypoint_launch_opts_from_profile,
set_soldier_runtime_entrypoint_launch_opts_from_profile,
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,
},
@ -54,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::{
@ -100,8 +95,6 @@ pub struct App {
inhibit_fail_notif: Option<NotificationHandle>,
pluginstore: Option<AsyncController<PluginStore>>,
theme_engine: ThemeEngine,
}
#[derive(Debug)]
@ -119,8 +112,7 @@ pub enum Msg {
StartWithDebug,
RestartXRService,
ProfileSelected(Profile),
/// bool param: delete files
DeleteProfile(bool),
DeleteProfile,
SaveProfile(Profile),
RunSetCap,
OpenLibsurviveSetup,
@ -136,8 +128,6 @@ pub enum Msg {
WivrnCheckPairMode,
OpenPluginStore,
UpdateConfigPlugins(HashMap<String, PluginConfig>),
ShowThemeManager,
SaveThemeConfig,
NoOp,
}
@ -236,17 +226,13 @@ impl App {
);
worker.start();
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
.sender()
.emit(MainViewMsg::XRServiceActiveChanged(
true,
Some(self.get_selected_profile()),
// 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
.sender()
@ -310,8 +296,7 @@ impl App {
}
pub fn restore_openxr_openvr_files(&self) {
restore_sniper_runtime_entrypoint();
restore_soldier_runtime_entrypoint();
restore_runtime_entrypoint();
if let Err(e) = remove_current_active_runtime() {
alert(
"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_show_content: false,
set_collapsed: !model.config.debug_view_enabled,
},
}
},
connect_close_request[sender] => move |win| {
sender.input(Msg::SaveWinSize(win.width(), win.height()));
@ -404,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
@ -555,7 +519,7 @@ impl AsyncComponent for App {
jobs.extend(get_build_basalt_jobs(&profile, clean_build));
}
if profile.features.mercury_enabled {
jobs.extend(get_build_mercury_jobs());
jobs.extend(get_build_mercury_jobs(&profile));
}
jobs.extend(match profile.xrservice_type {
XRServiceType::Monado => get_build_monado_jobs(&profile, clean_build),
@ -569,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())
@ -661,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!(
@ -687,7 +644,7 @@ impl AsyncComponent for App {
self.build_window
.sender()
.emit(BuildWindowMsg::UpdateBuildStatus(BuildStatus::Error(
format!("Exit status {errcode}"),
format!("Exit status {}", errcode),
)));
}
};
@ -697,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();
@ -746,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();
@ -764,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")
]));
}
}
}
@ -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
// to enable/disable it in update()
let configure_wivrn_action = {
@ -1078,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),
@ -1110,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,
@ -1209,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: \"{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,
},
},
model.term.container.clone(),
},
add_bottom_bar: bottom_bar = &gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
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();
},
},
// 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",
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::Button {
add_css_class: "pill",
set_halign: gtk::Align::Center,
set_label: "Close",
set_margin_all: 12,
#[track = "model.changed(BuildWindow::can_close())"]
set_sensitive: model.can_close,
connect_clicked[win] => move |_| {
win.close();
},
}
}
}
}
@ -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

@ -31,7 +31,6 @@ pub enum AddCustomPluginWinMsg {
Present,
Close,
OnNameChange(String),
OnArgsChange(String),
OnExecPathChange(Option<String>),
Add,
}
@ -96,11 +95,7 @@ impl SimpleComponent for AddCustomPluginWin {
"",
clone!(
#[strong] sender,
move |row| sender.input(
Self::Input::OnNameChange(
row.text().to_string()
)
)
move |row| sender.input(Self::Input::OnNameChange(row.text().to_string()))
)
),
add: &file_row(
@ -110,23 +105,9 @@ impl SimpleComponent for AddCustomPluginWin {
Some(model.parent.clone()),
clone!(
#[strong] sender,
move |path_s| sender.input(
Self::Input::OnExecPathChange(path_s)
move |path_s| sender.input(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.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.plugin.exec_path = ep.map(PathBuf::from);
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
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

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

View file

@ -45,7 +45,8 @@ impl SimpleComponent for SteamLaunchOptionsBox {
add_css_class: "dim-label",
set_hexpand: true,
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(),
set_xalign: 0.0,
set_wrap: true,

View file

@ -112,7 +112,7 @@ impl AsyncComponent for WivrnWiredStartBox {
Self::Input::UpdateSelectedProfile(p) => self.set_selected_profile(p),
Self::Input::StartWivrnClient => {
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;
}
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 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,8 +93,7 @@ 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 {
match async_process("getcap", Some(&[&xrservice_binary]), None).await {
Err(e) => {
error!("failed to run `getcap {xrservice_binary}`: {e:?}");
false
@ -133,10 +112,6 @@ pub async fn verify_cap_sys_nice_eip(profile: &Profile) -> bool {
}
}
}
} else {
error!("getcap executable does not exist");
false
}
}
pub async fn setcap_cap_sys_nice_eip(profile: &Profile) -> anyhow::Result<()> {

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) }
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()?
.to_string_lossy()
.to_string(),
)
.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,
})
}
}

View file

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