Merge branch 'flatpak-packaging' into 'main'

Draft: feat: flatpak packaging

See merge request gabmus/envision!90
This commit is contained in:
Charlie Le 2024-11-25 20:08:21 +00:00
commit c99c0eacb3
22 changed files with 273 additions and 72 deletions

96
dist/flatpak/org.gabmus.envision.json vendored Normal file
View file

@ -0,0 +1,96 @@
{
"id": "org.gabmus.envision",
"branch": "47",
"runtime": "org.gnome.Sdk",
"runtime-version": "47",
"sdk": "org.gnome.Sdk",
"sdk-extensions": [
"org.freedesktop.Sdk.Extension.rust-stable",
"org.freedesktop.Sdk.Extension.llvm19"
],
"command": "envision",
"build-options": {
"append-path": "/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm19/bin",
"env": {
"CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER": "clang",
"CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS": "-C link-arg=-fuse-ld=/usr/lib/sdk/rust-stable/bin/mold",
"CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER": "clang",
"CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS": "-C link-arg=-fuse-ld=/usr/lib/sdk/rust-stable/bin/mold"
},
"build-args": [
"--share=network"
]
},
"cleanup": [
"/share/doc",
"/share/man"
],
"finish-args": [
"--share=ipc",
"--share=network",
"--socket=wayland",
"--socket=fallback-x11",
"--socket=pulseaudio",
"--device=all",
"--filesystem=xdg-run/pipewire-0",
"--filesystem=xdg-run/monado_comp_ipc",
"--filesystem=~/.steam",
"--filesystem=~/.var/app/com.valvesoftware.Steam",
"--talk-name=org.freedesktop.Flatpak"
],
"modules": [
{
"name": "OpenXR-SDK",
"buildsystem": "cmake",
"sources": [
{
"type": "git",
"url": "https://github.com/KhronosGroup/OpenXR-SDK-Source.git",
"tag": "release-1.1.42"
}
]
},
{
"name": "vte",
"buildsystem": "meson",
"config-opts": [
"-Dgtk4=true",
"-Dgtk3=false"
],
"sources": [
{
"type": "archive",
"url": "https://gitlab.gnome.org/GNOME/vte/-/archive/0.78.0/vte-0.78.0.tar.gz",
"sha256": "82e19d11780fed4b66400f000829ce5ca113efbbfb7975815f26ed93e4c05f2d"
}
]
},
{
"name": "eigen",
"buildsystem": "cmake",
"builddir": true,
"build-options": {
"config-opts": [
"-DEIGEN_BUILD_CMAKE_PACKAGE=NO"
]
},
"sources": [
{
"type": "git",
"url": "https://gitlab.com/libeigen/eigen.git",
"tag": "3.4.0"
}
]
},
{
"name": "Envision",
"buildsystem": "meson",
"sources": [
{
"type": "dir",
"path": "../../"
}
]
}
]
}

View file

@ -2,6 +2,7 @@ use std::ffi::OsStr;
use std::process::Stdio; use std::process::Stdio;
use std::{collections::HashMap, os::unix::process::ExitStatusExt}; use std::{collections::HashMap, os::unix::process::ExitStatusExt};
use tokio::process::Command; use tokio::process::Command;
use crate::is_flatpak::IS_FLATPAK;
pub struct AsyncProcessOut { pub struct AsyncProcessOut {
pub exit_code: i32, pub exit_code: i32,
@ -13,10 +14,22 @@ pub async fn async_process<S: AsRef<OsStr>, T: AsRef<OsStr>>(
cmd: S, cmd: S,
args: Option<&[T]>, args: Option<&[T]>,
env: Option<HashMap<String, String>>, env: Option<HashMap<String, String>>,
host_spawn: bool,
) -> anyhow::Result<AsyncProcessOut> { ) -> anyhow::Result<AsyncProcessOut> {
let cmd = Command::new(cmd) let mut command: Command;
.args(args.unwrap_or_default()) if *IS_FLATPAK && host_spawn {
.envs(env.unwrap_or_default()) command = Command::new("flatpak-spawn");
command.arg("--host");
for (key, value) in env.unwrap_or_default() {
command.arg(format!("--env={}={}", key, value));
}
command.arg(cmd).args(args.unwrap_or_default());
} else {
command = Command::new(cmd);
command.args(args.unwrap_or_default()).envs(env.unwrap_or_default());
}
let cmd = command
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.stderr(Stdio::piped()) .stderr(Stdio::piped())

View file

@ -26,7 +26,7 @@ impl Cmake {
} }
} }
args.push(self.source_dir.to_string_lossy().to_string()); args.push(self.source_dir.to_string_lossy().to_string());
WorkerJob::new_cmd(self.env.clone(), "cmake".into(), Some(args)) WorkerJob::new_cmd(self.env.clone(), "cmake".into(), Some(args), false)
} }
pub fn get_build_job(&self) -> WorkerJob { pub fn get_build_job(&self) -> WorkerJob {
@ -37,6 +37,7 @@ impl Cmake {
"--build".into(), "--build".into(),
self.build_dir.to_string_lossy().to_string(), self.build_dir.to_string_lossy().to_string(),
]), ]),
false,
) )
} }
@ -48,6 +49,7 @@ impl Cmake {
"--install".into(), "--install".into(),
self.build_dir.to_string_lossy().to_string(), self.build_dir.to_string_lossy().to_string(),
]), ]),
false,
) )
} }
} }

View file

@ -13,7 +13,7 @@ impl Git {
fn cmd(&self, args: Vec<String>) -> WorkerJob { fn cmd(&self, args: Vec<String>) -> WorkerJob {
let mut nargs = vec!["-C".into(), self.dir.to_string_lossy().to_string()]; let mut nargs = vec!["-C".into(), self.dir.to_string_lossy().to_string()];
nargs.extend(args); nargs.extend(args);
WorkerJob::new_cmd(None, "git".into(), Some(nargs)) WorkerJob::new_cmd(None, "git".into(), Some(nargs), false)
} }
fn get_repo(&self) -> String { fn get_repo(&self) -> String {
@ -84,6 +84,7 @@ impl Git {
self.dir.to_string_lossy().to_string(), self.dir.to_string_lossy().to_string(),
"--recurse-submodules".into(), "--recurse-submodules".into(),
]), ]),
false,
) )
} }

View file

@ -79,6 +79,7 @@ pub fn get_build_basalt_jobs(profile: &Profile, clean_build: bool) -> VecDeque<W
.to_string_lossy() .to_string_lossy()
.to_string(), .to_string(),
]), ]),
false,
)); ));
jobs.push_back(WorkerJob::new_cmd( jobs.push_back(WorkerJob::new_cmd(
None, None,
@ -100,6 +101,7 @@ pub fn get_build_basalt_jobs(profile: &Profile, clean_build: bool) -> VecDeque<W
.to_string_lossy() .to_string_lossy()
.to_string(), .to_string(),
]), ]),
false,
)); ));
jobs jobs

View file

@ -21,6 +21,7 @@ pub fn get_build_mercury_jobs(profile: &Profile) -> VecDeque<WorkerJob> {
profile.prefix.to_string_lossy().to_string(), profile.prefix.to_string_lossy().to_string(),
get_cache_dir().to_string_lossy().to_string(), get_cache_dir().to_string_lossy().to_string(),
]), ]),
false,
)); ));
jobs jobs

View file

@ -62,6 +62,7 @@ pub fn get_build_openhmd_jobs(profile: &Profile, clean_build: bool) -> VecDeque<
.to_string_lossy() .to_string_lossy()
.to_string(), .to_string(),
]), ]),
false,
)); ));
} }
// build job // build job
@ -69,6 +70,7 @@ pub fn get_build_openhmd_jobs(profile: &Profile, clean_build: bool) -> VecDeque<
None, None,
"ninja".into(), "ninja".into(),
Some(vec!["-C".into(), build_dir.to_string_lossy().to_string()]), Some(vec!["-C".into(), build_dir.to_string_lossy().to_string()]),
false,
)); ));
// install job // install job
jobs.push_back(WorkerJob::new_cmd( jobs.push_back(WorkerJob::new_cmd(
@ -79,6 +81,7 @@ pub fn get_build_openhmd_jobs(profile: &Profile, clean_build: bool) -> VecDeque<
build_dir.to_string_lossy().to_string(), build_dir.to_string_lossy().to_string(),
"install".into(), "install".into(),
]), ]),
false,
)); ));
jobs jobs

View file

@ -125,6 +125,8 @@ fn include_paths() -> Vec<String> {
vec![ vec![
"/usr/include".into(), "/usr/include".into(),
"/usr/local/include".into(), "/usr/local/include".into(),
// flatpak applications use /app
"/app/include".into(),
// fedora puts avcodec here // fedora puts avcodec here
"/usr/include/ffmpeg".into(), "/usr/include/ffmpeg".into(),
"/usr/include/x86_64-linux-gnu".into(), "/usr/include/x86_64-linux-gnu".into(),

View file

@ -1,8 +1,5 @@
use crate::{ use crate::{
paths::{get_backup_dir, SYSTEM_PREFIX}, is_flatpak::IS_FLATPAK, paths::{get_backup_dir, get_home_dir, SYSTEM_PREFIX}, profile::Profile, util::file_utils::{copy_file, deserialize_file, get_writer, set_file_readonly}, xdg::XDG
profile::Profile,
util::file_utils::{copy_file, deserialize_file, get_writer, set_file_readonly},
xdg::XDG,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
@ -138,17 +135,28 @@ fn relativize_active_runtime_lib_path(ar: &ActiveRuntime, path: &Path) -> Active
} }
pub fn set_current_active_runtime_to_profile(profile: &Profile) -> anyhow::Result<()> { pub fn set_current_active_runtime_to_profile(profile: &Profile) -> anyhow::Result<()> {
let dest = get_active_runtime_json_path(); let mut dests = vec![
set_file_readonly(&dest, false)?; get_active_runtime_json_path(),
get_home_dir().join(".var/app/com.valvesoftware.Steam/config/openxr/1/active_runtime.json"),
];
if *IS_FLATPAK {
dests.push(get_home_dir().join(".config/openxr/1/active_runtime.json"));
}
backup_steam_active_runtime(); backup_steam_active_runtime();
let pfx = profile.clone().prefix; let pfx: PathBuf = profile.clone().prefix;
for dest in dests {
if !dest.is_file() {
continue;
}
let mut ar = build_profile_active_runtime(profile)?; let mut ar = build_profile_active_runtime(profile)?;
// hack: relativize libopenxr_monado.so path for system installs // hack: relativize libopenxr_monado.so path for system installs
if pfx == PathBuf::from(SYSTEM_PREFIX) { if pfx == PathBuf::from(SYSTEM_PREFIX) {
ar = relativize_active_runtime_lib_path(&ar, &dest); ar = relativize_active_runtime_lib_path(&ar, &dest);
} }
dump_current_active_runtime(&ar)?; set_file_readonly(&dest, false)?;
dump_active_runtime_to_path(&ar, &dest)?;
set_file_readonly(&dest, true)?; set_file_readonly(&dest, true)?;
}
Ok(()) Ok(())
} }

View file

@ -1,10 +1,7 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use crate::{ use crate::{
paths::get_backup_dir, is_flatpak::IS_FLATPAK, paths::{get_home_dir, get_backup_dir}, profile::Profile, util::file_utils::{copy_file, deserialize_file, get_writer, set_file_readonly}, xdg::XDG
profile::Profile,
util::file_utils::{copy_file, deserialize_file, get_writer, set_file_readonly},
xdg::XDG,
}; };
use serde::{ser::Error, Deserialize, Serialize}; use serde::{ser::Error, Deserialize, Serialize};
@ -23,9 +20,19 @@ pub fn get_openvr_conf_dir() -> PathBuf {
} }
fn get_openvrpaths_vrpath_path() -> PathBuf { fn get_openvrpaths_vrpath_path() -> PathBuf {
if *IS_FLATPAK {
return get_home_dir().join(".config/openvr/openvrpaths.vrpath")
}
get_openvr_conf_dir().join("openvrpaths.vrpath") get_openvr_conf_dir().join("openvrpaths.vrpath")
} }
fn get_openvrpaths_vrpaths() -> Vec<PathBuf> {
vec![
get_openvrpaths_vrpath_path(),
get_home_dir().join(".var/app/com.valvesoftware.Steam/config/openvr/openvrpaths.vrpath"),
]
}
pub fn is_steam(ovr_paths: &OpenVrPaths) -> bool { pub fn is_steam(ovr_paths: &OpenVrPaths) -> bool {
ovr_paths.runtime.iter().any(|rt| { ovr_paths.runtime.iter().any(|rt| {
rt.to_string_lossy() rt.to_string_lossy()
@ -104,11 +111,15 @@ 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();
set_file_readonly(&dest, false)?;
backup_steam_openvrpaths(); backup_steam_openvrpaths();
dump_current_openvrpaths(&build_profile_openvrpaths(profile))?; for dest in get_openvrpaths_vrpaths() {
if !dest.is_file() {
continue;
}
set_file_readonly(&dest, false)?;
dump_openvrpaths_to_path(&build_profile_openvrpaths(profile), &dest)?;
set_file_readonly(&dest, true)?; set_file_readonly(&dest, true)?;
}
Ok(()) Ok(())
} }

6
src/is_flatpak.rs Normal file
View file

@ -0,0 +1,6 @@
use lazy_static::lazy_static;
use std::path::Path;
lazy_static! {
pub static ref IS_FLATPAK: bool = Path::new("/.flatpak-info").is_file();
}

View file

@ -30,6 +30,7 @@ pub mod env_var_descriptions;
pub mod file_builders; pub mod file_builders;
pub mod gpu_profile; pub mod gpu_profile;
pub mod is_appimage; pub mod is_appimage;
pub mod is_flatpak;
pub mod linux_distro; pub mod linux_distro;
pub mod log_parser; pub mod log_parser;
pub mod openxr_prober; pub mod openxr_prober;

View file

@ -20,34 +20,21 @@ use crate::{
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_wivrn::get_build_wivrn_jobs, build_wivrn::get_build_wivrn_jobs,
}, }, config::Config, constants::APP_NAME, depcheck::{
config::Config,
constants::APP_NAME,
depcheck::{
basalt_deps::get_missing_basalt_deps, common::dep_pkexec, basalt_deps::get_missing_basalt_deps, common::dep_pkexec,
libsurvive_deps::get_missing_libsurvive_deps, mercury_deps::get_missing_mercury_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, monado_deps::get_missing_monado_deps, openhmd_deps::get_missing_openhmd_deps,
wivrn_deps::get_missing_wivrn_deps, wivrn_deps::get_missing_wivrn_deps,
}, }, file_builders::{
file_builders::{
active_runtime_json::{ active_runtime_json::{
set_current_active_runtime_to_profile, set_current_active_runtime_to_steam, set_current_active_runtime_to_profile, set_current_active_runtime_to_steam,
}, },
openvrpaths_vrpath::{ openvrpaths_vrpath::{
set_current_openvrpaths_to_profile, set_current_openvrpaths_to_steam, set_current_openvrpaths_to_profile, set_current_openvrpaths_to_steam,
}, },
}, }, is_flatpak::IS_FLATPAK, linux_distro::LinuxDistro, openxr_prober::is_openxr_ready, paths::get_data_dir, profile::{Profile, XRServiceType}, stateless_action, steam_linux_runtime_injector::{
linux_distro::LinuxDistro,
openxr_prober::is_openxr_ready,
paths::get_data_dir,
profile::{Profile, XRServiceType},
stateless_action,
steam_linux_runtime_injector::{
restore_runtime_entrypoint, set_runtime_entrypoint_launch_opts_from_profile, restore_runtime_entrypoint, set_runtime_entrypoint_launch_opts_from_profile,
}, }, util::{cmd_utils::CommandUnsandbox, file_utils::{setcap_cap_sys_nice_eip, setcap_cap_sys_nice_eip_cmd}}, vulkaninfo::VulkanInfo, xr_devices::XRDevice
util::file_utils::{setcap_cap_sys_nice_eip, setcap_cap_sys_nice_eip_cmd},
vulkaninfo::VulkanInfo,
xr_devices::XRDevice,
}; };
use adw::{prelude::*, ResponseAppearance}; use adw::{prelude::*, ResponseAppearance};
use gtk::glib::{self, clone}; use gtk::glib::{self, clone};
@ -56,7 +43,7 @@ use relm4::{
new_action_group, new_stateful_action, new_stateless_action, new_action_group, new_stateful_action, new_stateless_action,
prelude::*, prelude::*,
}; };
use std::{collections::VecDeque, fs::remove_file, time::Duration}; use std::{collections::VecDeque, fs::remove_file, process::Command, time::Duration};
pub struct App { pub struct App {
application: adw::Application, application: adw::Application,
@ -175,9 +162,16 @@ impl App {
}; };
self.debug_view.sender().emit(DebugViewMsg::ClearLog); self.debug_view.sender().emit(DebugViewMsg::ClearLog);
self.xr_devices = vec![]; self.xr_devices = vec![];
if *IS_FLATPAK {
Command::new_unsandboxed("rm", None)
.arg(prof.xrservice_type.ipc_file_path().to_str().unwrap())
.output()
.expect("Failed to remove xrservice IPC file");
} else {
remove_file(prof.xrservice_type.ipc_file_path()) remove_file(prof.xrservice_type.ipc_file_path())
.is_err() .is_err()
.then(|| println!("Failed to remove xrservice IPC file")); .then(|| println!("Failed to remove xrservice IPC file"));
}
let worker = JobWorker::xrservice_worker_wrap_from_profile( let worker = JobWorker::xrservice_worker_wrap_from_profile(
&prof, &prof,
sender.input_sender(), sender.input_sender(),
@ -222,6 +216,7 @@ impl App {
Some(prof.environment.clone()), Some(prof.environment.clone()),
"sh".into(), "sh".into(),
Some(vec!["-c".into(), autostart_cmd.clone()]), Some(vec!["-c".into(), autostart_cmd.clone()]),
true
)); ));
let autostart_worker = JobWorker::new(jobs, sender.input_sender(), |msg| match msg { let autostart_worker = JobWorker::new(jobs, sender.input_sender(), |msg| match msg {
JobWorkerOut::Log(rows) => Msg::OnServiceLog(rows), JobWorkerOut::Log(rows) => Msg::OnServiceLog(rows),
@ -533,7 +528,7 @@ impl AsyncComponent for App {
.sender() .sender()
.emit(BuildWindowMsg::UpdateBuildStatus(BuildStatus::Done)); .emit(BuildWindowMsg::UpdateBuildStatus(BuildStatus::Done));
let profile = self.get_selected_profile(); let profile = self.get_selected_profile();
if dep_pkexec().check() { if dep_pkexec().check() || *IS_FLATPAK {
self.setcap_confirm_dialog.present(Some(&self.app_win)); self.setcap_confirm_dialog.present(Some(&self.app_win));
} else { } else {
alert_w_widget( alert_w_widget(
@ -606,11 +601,11 @@ impl AsyncComponent for App {
.emit(DebugViewMsg::UpdateSelectedProfile(prof.clone())); .emit(DebugViewMsg::UpdateSelectedProfile(prof.clone()));
} }
Msg::RunSetCap => { Msg::RunSetCap => {
if !dep_pkexec().check() { if dep_pkexec().check() || *IS_FLATPAK {
println!("pkexec not found, skipping setcap");
} else {
let profile = self.get_selected_profile(); let profile = self.get_selected_profile();
setcap_cap_sys_nice_eip(&profile).await; setcap_cap_sys_nice_eip(&profile).await;
} else {
println!("pkexec not found, skipping setcap");
} }
} }
Msg::ProfileSelected(prof) => { Msg::ProfileSelected(prof) => {
@ -698,7 +693,7 @@ impl AsyncComponent for App {
let worker = JobWorker::new_with_timer( let worker = JobWorker::new_with_timer(
Duration::from_millis(500), Duration::from_millis(500),
WorkerJob::new_func(Box::new(move || { WorkerJob::new_func(Box::new(move || {
let ready = is_openxr_ready(); let ready = !*IS_FLATPAK && is_openxr_ready();
FuncWorkerOut { FuncWorkerOut {
success: ready, success: ready,
..Default::default() ..Default::default()

View file

@ -218,6 +218,7 @@ impl AsyncComponent for InstallWivrnBox {
"adb", "adb",
Some(&["install", "-r", "-g", apk_path.to_string_lossy().to_string().as_str()]), Some(&["install", "-r", "-g", apk_path.to_string_lossy().to_string().as_str()]),
None, None,
false,
) )
.await .await
{ {

View file

@ -3,19 +3,12 @@ use super::{
state::JobWorkerState, state::JobWorkerState,
}; };
use crate::{ use crate::{
profile::{LighthouseDriver, Profile}, profile::{LighthouseDriver, Profile}, ui::SENDER_IO_ERR_MSG, util::cmd_utils::CommandUnsandbox
ui::SENDER_IO_ERR_MSG,
}; };
use nix::unistd::Pid; use nix::unistd::Pid;
use relm4::{prelude::*, Worker}; use relm4::{prelude::*, Worker};
use std::{ use std::{
collections::VecDeque, collections::VecDeque, io::{BufRead, BufReader}, mem, os::unix::process::ExitStatusExt, process::{Command, Stdio}, sync::{Arc, Mutex}, thread
io::{BufRead, BufReader},
mem,
os::unix::process::ExitStatusExt,
process::{Command, Stdio},
sync::{Arc, Mutex},
thread,
}; };
macro_rules! logger_thread { macro_rules! logger_thread {
@ -92,9 +85,15 @@ impl Worker for InternalJobWorker {
match &mut job { match &mut job {
WorkerJob::Cmd(data) => { WorkerJob::Cmd(data) => {
let data = data.clone(); let data = data.clone();
if let Ok(mut cmd) = Command::new(data.command) let mut cmd: Command;
if data.host_spawn {
cmd = Command::new_unsandboxed(&data.command, Some(data.environment))
} else {
cmd = Command::new(&data.command);
cmd.envs(data.environment);
}
if let Ok(mut cmd) = cmd
.args(data.args) .args(data.args)
.envs(data.environment)
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stderr(Stdio::piped()) .stderr(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
@ -221,9 +220,10 @@ impl InternalJobWorker {
), ),
}; };
let data = CmdWorkerData { let data = CmdWorkerData {
environment: env, environment: env.clone(),
command, command: command.clone(),
args, args: args.clone(),
host_spawn: true,
}; };
let mut jobs = VecDeque::new(); let mut jobs = VecDeque::new();
jobs.push_back(WorkerJob::Cmd(data)); jobs.push_back(WorkerJob::Cmd(data));

View file

@ -7,6 +7,7 @@ pub struct CmdWorkerData {
pub environment: HashMap<String, String>, pub environment: HashMap<String, String>,
pub command: String, pub command: String,
pub args: Vec<String>, pub args: Vec<String>,
pub host_spawn: bool,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -38,11 +39,13 @@ impl WorkerJob {
env: Option<HashMap<String, String>>, env: Option<HashMap<String, String>>,
cmd: String, cmd: String,
args: Option<Vec<String>>, args: Option<Vec<String>>,
host_spawn: bool
) -> Self { ) -> Self {
Self::Cmd(CmdWorkerData { Self::Cmd(CmdWorkerData {
environment: env.unwrap_or_default(), environment: env.unwrap_or_default(),
command: cmd, command: cmd,
args: args.unwrap_or_default(), args: args.unwrap_or_default(),
host_spawn,
}) })
} }

View file

@ -17,6 +17,7 @@ use crate::{
config::Config, config::Config,
depcheck::common::dep_pkexec, depcheck::common::dep_pkexec,
gpu_profile::{get_amd_gpu_power_profile, GpuPowerProfile}, gpu_profile::{get_amd_gpu_power_profile, GpuPowerProfile},
is_flatpak::IS_FLATPAK,
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,
@ -270,7 +271,7 @@ impl SimpleComponent for MainView {
add_css_class: "card", add_css_class: "card",
add_css_class: "padded", add_css_class: "padded",
#[track = "model.changed(Self::selected_profile())"] #[track = "model.changed(Self::selected_profile())"]
set_visible: match mount_has_nosuid(&model.selected_profile.prefix) { set_visible: !*IS_FLATPAK && match mount_has_nosuid(&model.selected_profile.prefix) {
Ok(b) => b, Ok(b) => b,
Err(_) => { Err(_) => {
eprintln!( eprintln!(
@ -302,7 +303,34 @@ impl SimpleComponent for MainView {
add_css_class: "card", add_css_class: "card",
add_css_class: "padded", add_css_class: "padded",
#[track = "model.changed(Self::selected_profile())"] #[track = "model.changed(Self::selected_profile())"]
set_visible: !dep_pkexec().check(), set_visible: *IS_FLATPAK,
warning_heading(),
gtk::Label {
set_label: concat!(
"Envision is currently running as a Flatpak.\n",
"If Steam is running as a Flatpak, it will need to be granted certain ",
"permissions to run VR applications. Run the following command on your host ",
"terminal to grant the Steam Flatpak access to Envision's Monado socket:\n\n",
"<tt>flatpak --user override --filesystem=xdg-run/monado_comp_ipc com.valvesoftware.Steam</tt>\n\n",
"Run the following to also grant the Steam Flatpak access to Envision's data:\n\n",
"<tt>flatpak --user override --filesystem=~/.var/app/org.gabmus.envision com.valvesoftware.Steam</tt>\n\n",
),
set_use_markup: true,
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,
set_vexpand: false,
set_spacing: 12,
add_css_class: "card",
add_css_class: "padded",
#[track = "model.changed(Self::selected_profile())"]
set_visible: !*IS_FLATPAK && !dep_pkexec().check(),
warning_heading(), warning_heading(),
gtk::Label { gtk::Label {
set_label: &format!( set_label: &format!(

View file

@ -159,6 +159,7 @@ impl SimpleComponent for SteamVrCalibrationBox {
Some(env.clone()), Some(env.clone()),
vrcmd.clone(), vrcmd.clone(),
Some(vec!["--pollposes".into()]), Some(vec!["--pollposes".into()]),
false,
)); ));
JobWorker::new(jobs, sender.input_sender(), |msg| match msg { JobWorker::new(jobs, sender.input_sender(), |msg| match msg {
JobWorkerOut::Log(_) => Self::Input::NoOp, JobWorkerOut::Log(_) => Self::Input::NoOp,
@ -178,6 +179,7 @@ impl SimpleComponent for SteamVrCalibrationBox {
Some(env), Some(env),
vrcmd, vrcmd,
Some(vec!["--resetroomsetup".into()]), Some(vec!["--resetroomsetup".into()]),
false,
)); ));
JobWorker::new(jobs, sender.input_sender(), |msg| match msg { JobWorker::new(jobs, sender.input_sender(), |msg| match msg {
JobWorkerOut::Log(_) => Self::Input::NoOp, JobWorkerOut::Log(_) => Self::Input::NoOp,

View file

@ -136,7 +136,8 @@ impl AsyncComponent for WivrnWiredStartBox {
"adb shell am start -a android.intent.action.VIEW -d \"wivrn+tcp://127.0.0.1\" $PACKAGE" "adb shell am start -a android.intent.action.VIEW -d \"wivrn+tcp://127.0.0.1\" $PACKAGE"
) )
]), ]),
None None,
false,
).await { ).await {
Ok(out) if out.exit_code == 0 => Ok(out) if out.exit_code == 0 =>
StartClientStatus::Success StartClientStatus::Success

24
src/util/cmd_utils.rs Normal file
View file

@ -0,0 +1,24 @@
use std::{collections::HashMap, process::Command};
use crate::is_flatpak::IS_FLATPAK;
pub trait CommandUnsandbox {
fn new_unsandboxed(program: &str, envs: Option<HashMap<String, String>>) -> Self;
}
impl CommandUnsandbox for Command {
fn new_unsandboxed(program: &str, envs: Option<HashMap<String, String>>) -> Self {
if *IS_FLATPAK {
let mut cmd = Command::new("flatpak-spawn");
cmd.arg("--host");
for (key, value) in envs.unwrap_or_default() {
cmd.arg(format!("--env={}={}", key, value));
}
cmd.arg(program);
return cmd;
}
let mut cmd = Command::new(program);
cmd.envs(envs.unwrap_or_default());
cmd
}
}

View file

@ -81,7 +81,7 @@ pub fn setcap_cap_sys_nice_eip_cmd(profile: &Profile) -> Vec<String> {
} }
pub async fn setcap_cap_sys_nice_eip(profile: &Profile) { pub async fn setcap_cap_sys_nice_eip(profile: &Profile) {
if let Err(e) = async_process("pkexec", Some(&setcap_cap_sys_nice_eip_cmd(profile)), None).await if let Err(e) = async_process("pkexec", Some(&setcap_cap_sys_nice_eip_cmd(profile)), None, true).await
{ {
eprintln!("Error: failed running setcap: {e}"); eprintln!("Error: failed running setcap: {e}");
} }

View file

@ -1,3 +1,4 @@
pub mod file_utils; pub mod file_utils;
pub mod hash; pub mod hash;
pub mod steamvr_utils; pub mod steamvr_utils;
pub mod cmd_utils;