From 7e09c010db51bd6a13b1035a6b93ba2a5e1b465d Mon Sep 17 00:00:00 2001 From: Aleksander Date: Sun, 27 Oct 2024 21:26:33 +0100 Subject: [PATCH] WayVR: External process support, various tweaks and bugfixes - Support for foreign wayland clients, WayVR process is now separated into managed and external one - Add `run_compositor_at_start` global param - Add `primary` display param - Export WAYLAND_DISPLAY number into XDG_RUNTIME_DIR directory - Bugfix: Redraw event is not triggered after despawning a process - Sanitization in WayVRConfig::post_load() --- src/backend/openvr/mod.rs | 4 +- src/backend/openxr/mod.rs | 4 +- src/backend/wayvr/client.rs | 85 ++++++++++++----- src/backend/wayvr/display.rs | 10 +- src/backend/wayvr/mod.rs | 114 ++++++++++++++++------ src/backend/wayvr/process.rs | 76 +++++++++++++-- src/config_wayvr.rs | 39 +++++++- src/overlays/wayvr.rs | 178 ++++++++++++++++++++++++----------- src/res/wayvr.yaml | 5 + src/state.rs | 5 +- 10 files changed, 397 insertions(+), 123 deletions(-) diff --git a/src/backend/openvr/mod.rs b/src/backend/openvr/mod.rs index 792d09a..2c1b868 100644 --- a/src/backend/openvr/mod.rs +++ b/src/backend/openvr/mod.rs @@ -328,9 +328,7 @@ pub fn openvr_run(running: Arc, show_by_default: bool) -> Result<(), }; #[cfg(feature = "wayvr")] - if let Some(wayvr) = &state.wayvr { - wayvr.borrow_mut().tick_events()?; - } + crate::overlays::wayvr::tick_events::(&mut state, &mut overlays)?; log::trace!("Rendering frame"); diff --git a/src/backend/openxr/mod.rs b/src/backend/openxr/mod.rs index 7d3490b..f2cd591 100644 --- a/src/backend/openxr/mod.rs +++ b/src/backend/openxr/mod.rs @@ -364,9 +364,7 @@ pub fn openxr_run(running: Arc, show_by_default: bool) -> Result<(), } #[cfg(feature = "wayvr")] - if let Some(wayvr) = &app_state.wayvr { - wayvr.borrow_mut().tick_events()?; - } + crate::overlays::wayvr::tick_events::(&mut app_state, &mut overlays)?; for o in overlays.iter_mut() { if !o.state.want_visible { diff --git a/src/backend/wayvr/client.rs b/src/backend/wayvr/client.rs index d727b36..6bb5888 100644 --- a/src/backend/wayvr/client.rs +++ b/src/backend/wayvr/client.rs @@ -1,4 +1,4 @@ -use std::{io::Read, os::unix::net::UnixStream, sync::Arc}; +use std::{io::Read, os::unix::net::UnixStream, path::PathBuf, sync::Arc}; use smithay::{ backend::input::Keycode, @@ -7,9 +7,11 @@ use smithay::{ utils::SerialCounter, }; +use crate::backend::wayvr::{ExternalProcessRequest, WayVRTask}; + use super::{ comp::{self}, - display, process, + display, process, ProcessWayVREnv, }; pub struct WayVRClient { @@ -33,22 +35,29 @@ pub struct WayVRManager { pub clients: Vec, } -fn get_display_auth_from_pid(pid: i32) -> anyhow::Result { +fn get_wayvr_env_from_pid(pid: i32) -> anyhow::Result { let path = format!("/proc/{}/environ", pid); let mut env_data = String::new(); std::fs::File::open(path)?.read_to_string(&mut env_data)?; let lines: Vec<&str> = env_data.split('\0').filter(|s| !s.is_empty()).collect(); + let mut env = ProcessWayVREnv { + display_auth: None, + display_name: None, + }; + for line in lines { if let Some((key, value)) = line.split_once('=') { if key == "WAYVR_DISPLAY_AUTH" { - return Ok(String::from(value)); + env.display_auth = Some(String::from(value)); + } else if key == "WAYVR_DISPLAY_NAME" { + env.display_name = Some(String::from(value)); } } } - anyhow::bail!("Failed to get display auth from PID {}", pid); + Ok(env) } impl WayVRManager { @@ -73,6 +82,10 @@ impl WayVRManager { }) } + pub fn add_client(&mut self, client: WayVRClient) { + self.clients.push(client); + } + fn accept_connection( &mut self, stream: UnixStream, @@ -86,27 +99,46 @@ impl WayVRManager { .unwrap(); let creds = client.get_credentials(&self.display.handle())?; - let auth_key = get_display_auth_from_pid(creds.pid)?; + + let process_env = get_wayvr_env_from_pid(creds.pid)?; // Find suitable auth key from the process list - for process in processes.vec.iter().flatten() { - let process = &process.obj; - - // Find process with matching auth key - if process.auth_key.as_str() == auth_key { - // Check if display handle is valid - if displays.get(&process.display_handle).is_some() { - // Add client - self.clients.push(WayVRClient { - client, - display_handle: process.display_handle, - pid: creds.pid as u32, - }); - return Ok(()); + for p in processes.vec.iter().flatten() { + if let process::Process::Managed(process) = &p.obj { + if let Some(auth_key) = &process_env.display_auth { + // Find process with matching auth key + if process.auth_key.as_str() == auth_key { + // Check if display handle is valid + if displays.get(&process.display_handle).is_some() { + // Add client + self.add_client(WayVRClient { + client, + display_handle: process.display_handle, + pid: creds.pid as u32, + }); + return Ok(()); + } + } } } } - anyhow::bail!("Process auth key is invalid or selected display is non-existent"); + + // This is a new process which we didn't met before. + // Treat external processes exclusively (spawned by the user or external program) + log::warn!( + "External process ID {} connected to this Wayland server", + creds.pid + ); + + self.state + .wayvr_tasks + .send(WayVRTask::NewExternalProcess(ExternalProcessRequest { + env: process_env, + client, + pid: creds.pid as u32, + })); + + Ok(()) } fn accept_connections( @@ -164,6 +196,15 @@ impl WayVRManager { const STARTING_WAYLAND_ADDR_IDX: u32 = 20; +fn export_display_number(display_num: u32) -> anyhow::Result<()> { + let mut path = std::env::var("XDG_RUNTIME_DIR") + .map(PathBuf::from) + .unwrap_or_else(|_| PathBuf::from("/tmp")); + path.push("wayvr.disp"); + std::fs::write(path, format!("{}\n", display_num)).unwrap(); + Ok(()) +} + fn create_wayland_listener() -> anyhow::Result<(super::WaylandEnv, wayland_server::ListeningSocket)> { let mut env = super::WaylandEnv { @@ -194,5 +235,7 @@ fn create_wayland_listener() -> anyhow::Result<(super::WaylandEnv, wayland_serve } }; + let _ = export_display_number(env.display_num); + Ok((env, listener)) } diff --git a/src/backend/wayvr/display.rs b/src/backend/wayvr/display.rs index 0c352c4..986cd0d 100644 --- a/src/backend/wayvr/display.rs +++ b/src/backend/wayvr/display.rs @@ -50,6 +50,7 @@ pub struct Display { pub visible: bool, pub overlay_id: Option, pub wants_redraw: bool, + pub primary: bool, wm: Rc>, pub displayed_windows: Vec, wayland_env: super::WaylandEnv, @@ -81,6 +82,7 @@ impl Display { width: u32, height: u32, name: &str, + primary: bool, ) -> anyhow::Result { let tex_format = ffi::RGBA; let internal_format = ffi::RGBA8; @@ -116,6 +118,7 @@ impl Display { gles_texture, wayland_env, visible: true, + primary, overlay_id: None, tasks: SyncEventQueue::new(), }) @@ -239,7 +242,12 @@ impl Display { pub fn set_visible(&mut self, visible: bool) { log::info!("Display \"{}\" visible: {}", self.name.as_str(), visible); - self.visible = visible; + if self.visible != visible { + self.visible = visible; + if visible { + self.wants_redraw = true; + } + } } pub fn send_mouse_move(&self, manager: &mut WayVRManager, x: u32, y: u32) { diff --git a/src/backend/wayvr/mod.rs b/src/backend/wayvr/mod.rs index fd7c0b9..485343a 100644 --- a/src/backend/wayvr/mod.rs +++ b/src/backend/wayvr/mod.rs @@ -1,4 +1,4 @@ -mod client; +pub mod client; mod comp; pub mod display; pub mod egl_data; @@ -45,9 +45,23 @@ impl WaylandEnv { } } +#[derive(Clone)] +pub struct ProcessWayVREnv { + pub display_auth: Option, + pub display_name: Option, // Externally spawned process by a user script +} + +#[derive(Clone)] +pub struct ExternalProcessRequest { + pub env: ProcessWayVREnv, + pub client: wayland_server::Client, + pub pid: u32, +} + #[derive(Clone)] pub enum WayVRTask { NewToplevel(ClientId, ToplevelSurface), + NewExternalProcess(ExternalProcessRequest), ProcessTerminationRequest(process::ProcessHandle), } @@ -56,7 +70,7 @@ pub struct WayVR { time_start: u64, gles_renderer: GlesRenderer, pub displays: display::DisplayVec, - manager: client::WayVRManager, + pub manager: client::WayVRManager, wm: Rc>, egl_data: Rc, pub processes: process::ProcessVec, @@ -70,8 +84,13 @@ pub enum MouseIndex { Right, } +pub enum TickResult { + NewExternalProcess(ExternalProcessRequest), // Call WayVRManager::add_client after receiving this message +} + impl WayVR { pub fn new() -> anyhow::Result { + log::info!("Initializing WayVR"); let display: wayland_server::Display = wayland_server::Display::new()?; let dh = display.handle(); let compositor = compositor::CompositorState::new::(&dh); @@ -140,7 +159,9 @@ impl WayVR { Ok(()) } - pub fn tick_events(&mut self) -> anyhow::Result<()> { + pub fn tick_events(&mut self) -> anyhow::Result> { + let mut res: Vec = Vec::new(); + // Check for redraw events self.displays.iter_mut(&mut |_, disp| { for disp_window in &disp.displayed_windows { @@ -159,17 +180,18 @@ impl WayVR { SmallVec::new(); self.processes.iter_mut(&mut |handle, process| { if !process.is_running() { - to_remove.push((handle, process.display_handle)); + to_remove.push((handle, process.display_handle())); } }); for (p_handle, disp_handle) in to_remove { self.processes.remove(&p_handle); - if let Some(display) = self.displays.get(&disp_handle) { + if let Some(display) = self.displays.get_mut(&disp_handle) { display .tasks .send(display::DisplayTask::ProcessCleanup(p_handle)); + display.wants_redraw = true; } } @@ -179,6 +201,9 @@ impl WayVR { while let Some(task) = self.tasks.read() { match task { + WayVRTask::NewExternalProcess(req) => { + res.push(TickResult::NewExternalProcess(req)); + } WayVRTask::NewToplevel(client_id, toplevel) => { // Attach newly created toplevel surfaces to displays for client in &self.manager.clients { @@ -197,7 +222,7 @@ impl WayVR { } } else { log::error!( - "Failed to find process by PID {}. It was probably spawned externally.", + "WayVR window creation failed: Unexpected process PID {}. It wasn't registered before.", client.pid ); } @@ -215,7 +240,9 @@ impl WayVR { } self.manager - .tick_wayland(&mut self.displays, &mut self.processes) + .tick_wayland(&mut self.displays, &mut self.processes)?; + + Ok(res) } pub fn tick_finish(&mut self) -> anyhow::Result<()> { @@ -266,8 +293,22 @@ impl WayVR { .map(|display| display.dmabuf_data.clone()) } - pub fn get_display_by_name(&self, name: &str) -> Option { - for (idx, cell) in self.displays.vec.iter().enumerate() { + pub fn get_primary_display(displays: &DisplayVec) -> Option { + for (idx, cell) in displays.vec.iter().enumerate() { + if let Some(cell) = cell { + if cell.obj.primary { + return Some(DisplayVec::get_handle(cell, idx)); + } + } + } + None + } + + pub fn get_display_by_name( + displays: &DisplayVec, + name: &str, + ) -> Option { + for (idx, cell) in displays.vec.iter().enumerate() { if let Some(cell) = cell { if cell.obj.name == name { return Some(DisplayVec::get_handle(cell, idx)); @@ -276,11 +317,13 @@ impl WayVR { } None } + pub fn create_display( &mut self, width: u32, height: u32, name: &str, + primary: bool, ) -> anyhow::Result { let display = display::Display::new( self.wm.clone(), @@ -290,6 +333,7 @@ impl WayVR { width, height, name, + primary, )?; Ok(self.displays.add(display)) } @@ -308,15 +352,15 @@ impl WayVR { ) -> Option { for (idx, cell) in self.processes.vec.iter().enumerate() { if let Some(cell) = &cell { - let process = &cell.obj; - if process.display_handle != display_handle - || process.exec_path != exec_path - || process.args != args - { - continue; + if let process::Process::Managed(process) = &cell.obj { + if process.display_handle != display_handle + || process.exec_path != exec_path + || process.args != args + { + continue; + } + return Some(process::ProcessVec::get_handle(cell, idx)); } - - return Some(process::ProcessVec::get_handle(cell, idx)); } } @@ -328,6 +372,18 @@ impl WayVR { .send(WayVRTask::ProcessTerminationRequest(process_handle)); } + pub fn add_external_process( + &mut self, + display_handle: display::DisplayHandle, + pid: u32, + ) -> process::ProcessHandle { + self.processes + .add(process::Process::External(process::ExternalProcess { + display_handle, + pid, + })) + } + pub fn spawn_process( &mut self, display_handle: display::DisplayHandle, @@ -341,16 +397,18 @@ impl WayVR { .ok_or(anyhow::anyhow!(STR_INVALID_HANDLE_DISP))?; let res = display.spawn_process(exec_path, args, env)?; - Ok(self.processes.add(process::Process { - auth_key: res.auth_key, - child: res.child, - display_handle, - exec_path: String::from(exec_path), - args: args.iter().map(|x| String::from(*x)).collect(), - env: env - .iter() - .map(|(a, b)| (String::from(*a), String::from(*b))) - .collect(), - })) + Ok(self + .processes + .add(process::Process::Managed(process::WayVRProcess { + auth_key: res.auth_key, + child: res.child, + display_handle, + exec_path: String::from(exec_path), + args: args.iter().map(|x| String::from(*x)).collect(), + env: env + .iter() + .map(|(a, b)| (String::from(*a), String::from(*b))) + .collect(), + }))) } } diff --git a/src/backend/wayvr/process.rs b/src/backend/wayvr/process.rs index 5804770..c6c08da 100644 --- a/src/backend/wayvr/process.rs +++ b/src/backend/wayvr/process.rs @@ -2,7 +2,7 @@ use crate::gen_id; use super::display; -pub struct Process { +pub struct WayVRProcess { pub auth_key: String, pub child: std::process::Child, pub display_handle: display::DisplayHandle, @@ -12,7 +12,40 @@ pub struct Process { pub env: Vec<(String, String)>, } -impl Drop for Process { +pub struct ExternalProcess { + pub pid: u32, + pub display_handle: display::DisplayHandle, +} + +pub enum Process { + Managed(WayVRProcess), // Process spawned by WayVR + External(ExternalProcess), // External process not directly controlled by us +} + +impl Process { + pub fn display_handle(&self) -> display::DisplayHandle { + match self { + Process::Managed(p) => p.display_handle, + Process::External(p) => p.display_handle, + } + } + + pub fn is_running(&mut self) -> bool { + match self { + Process::Managed(p) => p.is_running(), + Process::External(p) => p.is_running(), + } + } + + pub fn terminate(&mut self) { + match self { + Process::Managed(p) => p.terminate(), + Process::External(p) => p.terminate(), + } + } +} + +impl Drop for WayVRProcess { fn drop(&mut self) { log::info!( "Sending SIGTERM (graceful exit) to process {}", @@ -22,8 +55,8 @@ impl Drop for Process { } } -impl Process { - pub fn is_running(&mut self) -> bool { +impl WayVRProcess { + fn is_running(&mut self) -> bool { match self.child.try_wait() { Ok(Some(_exit_status)) => false, Ok(None) => true, @@ -35,7 +68,7 @@ impl Process { } } - pub fn terminate(&mut self) { + fn terminate(&mut self) { unsafe { // Gracefully stop process libc::kill(self.child.id() as i32, libc::SIGTERM); @@ -43,13 +76,42 @@ impl Process { } } +impl ExternalProcess { + fn is_running(&self) -> bool { + if self.pid == 0 { + false + } else { + std::fs::metadata(format!("/proc/{}", self.pid)).is_ok() + } + } + + fn terminate(&mut self) { + if self.pid != 0 { + unsafe { + // send SIGINT (^C) + libc::kill(self.pid as i32, libc::SIGINT); + } + } + self.pid = 0; + } +} + gen_id!(ProcessVec, Process, ProcessCell, ProcessHandle); pub fn find_by_pid(processes: &ProcessVec, pid: u32) -> Option { for (idx, cell) in processes.vec.iter().enumerate() { if let Some(cell) = cell { - if cell.obj.child.id() == pid { - return Some(ProcessVec::get_handle(cell, idx)); + match &cell.obj { + Process::Managed(wayvr_process) => { + if wayvr_process.child.id() == pid { + return Some(ProcessVec::get_handle(cell, idx)); + } + } + Process::External(external_process) => { + if external_process.pid == pid { + return Some(ProcessVec::get_handle(cell, idx)); + } + } } } } diff --git a/src/config_wayvr.rs b/src/config_wayvr.rs index eed17fd..9847619 100644 --- a/src/config_wayvr.rs +++ b/src/config_wayvr.rs @@ -2,7 +2,9 @@ compile_error!("WayVR feature is not enabled"); use std::{ + cell::RefCell, collections::{BTreeMap, HashMap}, + rc::Rc, sync::Arc, }; @@ -12,6 +14,7 @@ use crate::{ backend::{ overlay::RelativeTo, task::{TaskContainer, TaskType}, + wayvr, }, config::{load_known_yaml, ConfigType}, overlays::wayvr::WayVRAction, @@ -63,6 +66,7 @@ pub struct WayVRDisplay { pub rotation: Option, pub pos: Option<[f32; 3]>, pub attach_to: Option, + pub primary: Option, } #[derive(Clone, Deserialize, Serialize)] @@ -79,6 +83,7 @@ impl WayVRCatalog { #[derive(Deserialize, Serialize)] pub struct WayVRConfig { pub version: u32, + pub run_compositor_at_start: bool, pub catalogs: HashMap, pub displays: BTreeMap, // sorted alphabetically } @@ -92,7 +97,31 @@ impl WayVRConfig { self.displays.get(name) } - pub fn post_load(&self, tasks: &mut TaskContainer) { + pub fn get_default_display(&self) -> Option<(String, &WayVRDisplay)> { + for (disp_name, disp) in &self.displays { + if disp.primary.unwrap_or(false) { + return Some((disp_name.clone(), disp)); + } + } + None + } + + pub fn post_load( + &self, + tasks: &mut TaskContainer, + ) -> anyhow::Result>>> { + let primary_count = self + .displays + .iter() + .filter(|d| d.1.primary.unwrap_or(false)) + .count(); + + if primary_count > 1 { + anyhow::bail!("Number of primary displays is more than 1") + } else if primary_count == 0 { + log::warn!("No primary display specified"); + } + for (catalog_name, catalog) in &self.catalogs { for app in &catalog.apps { if let Some(b) = app.shown_at_start { @@ -105,6 +134,14 @@ impl WayVRConfig { } } } + + if self.run_compositor_at_start { + // Start Wayland server instantly + Ok(Some(Rc::new(RefCell::new(wayvr::WayVR::new()?)))) + } else { + // Lazy-init WayVR later if the user requested + Ok(None) + } } } diff --git a/src/overlays/wayvr.rs b/src/overlays/wayvr.rs index e5fd5dc..7b25a04 100644 --- a/src/overlays/wayvr.rs +++ b/src/overlays/wayvr.rs @@ -9,7 +9,7 @@ use crate::{ common::OverlayContainer, input::{self, InteractionHandler}, overlay::{ui_transform, OverlayData, OverlayRenderer, OverlayState, SplitOverlayBackend}, - wayvr, + wayvr::{self, display, WayVR}, }, graphics::WlxGraphics, state::{self, AppState, KeyboardFocus}, @@ -119,6 +119,121 @@ impl WayVRRenderer { } } +fn get_or_create_display( + app: &mut AppState, + wayvr: &mut WayVR, + disp_name: &str, +) -> anyhow::Result<(display::DisplayHandle, Option>)> +where + O: Default, +{ + let created_overlay: Option>; + + let disp_handle = if let Some(disp) = WayVR::get_display_by_name(&wayvr.displays, disp_name) { + created_overlay = None; + disp + } else { + let conf_display = app + .session + .wayvr_config + .get_display(disp_name) + .ok_or(anyhow::anyhow!( + "Cannot find display named \"{}\"", + disp_name + ))? + .clone(); + + let disp_handle = wayvr.create_display( + conf_display.width, + conf_display.height, + disp_name, + conf_display.primary.unwrap_or(false), + )?; + + let mut overlay = create_wayvr_display_overlay::( + app, + conf_display.width, + conf_display.height, + disp_handle, + conf_display.scale.unwrap_or(1.0), + )?; + + if let Some(attach_to) = &conf_display.attach_to { + overlay.state.relative_to = attach_to.get_relative_to(); + } + + if let Some(rot) = &conf_display.rotation { + overlay.state.spawn_rotation = glam::Quat::from_axis_angle( + Vec3::from_slice(&rot.axis), + f32::to_radians(rot.angle), + ); + } + + if let Some(pos) = &conf_display.pos { + overlay.state.spawn_point = Vec3A::from_slice(pos); + } + + let display = wayvr.displays.get_mut(&disp_handle).unwrap(); // Never fails + display.overlay_id = Some(overlay.state.id); + + created_overlay = Some(overlay); + + disp_handle + }; + + Ok((disp_handle, created_overlay)) +} + +pub fn tick_events(app: &mut AppState, overlays: &mut OverlayContainer) -> anyhow::Result<()> +where + O: Default, +{ + if let Some(wayvr) = app.wayvr.clone() { + let res = wayvr.borrow_mut().tick_events()?; + + for result in res { + match result { + wayvr::TickResult::NewExternalProcess(req) => { + let config = &app.session.wayvr_config; + + let disp_name = if let Some(display_name) = req.env.display_name { + config + .get_display(display_name.as_str()) + .map(|_| display_name) + } else { + config + .get_default_display() + .map(|(display_name, _)| display_name) + }; + + if let Some(disp_name) = disp_name { + let mut wayvr = wayvr.borrow_mut(); + + log::info!("Registering external process with PID {}", req.pid); + + let (disp_handle, created_overlay) = + get_or_create_display::(app, &mut wayvr, &disp_name)?; + + wayvr.add_external_process(disp_handle, req.pid); + + wayvr.manager.add_client(wayvr::client::WayVRClient { + client: req.client, + display_handle: disp_handle, + pid: req.pid, + }); + + if let Some(created_overlay) = created_overlay { + overlays.add(created_overlay); + } + } + } + } + } + } + + Ok(()) +} + impl WayVRRenderer { fn ensure_dmabuf(&mut self, data: wayvr::egl_data::DMAbufData) -> anyhow::Result<()> { if self.dmabuf_image.is_none() { @@ -285,10 +400,6 @@ fn action_app_click( where O: Default, { - use crate::overlays::wayvr::create_wayvr_display_overlay; - - let mut created_overlay: Option> = None; - let wayvr = app.get_wayvr()?.clone(); let catalog = app @@ -304,54 +415,8 @@ where if let Some(app_entry) = catalog.get_app(app_name) { let mut wayvr = wayvr.borrow_mut(); - let disp_handle = if let Some(disp) = wayvr.get_display_by_name(&app_entry.target_display) { - disp - } else { - let conf_display = app - .session - .wayvr_config - .get_display(&app_entry.target_display) - .ok_or(anyhow::anyhow!( - "Cannot find display named \"{}\"", - app_entry.target_display - ))? - .clone(); - - let display_handle = wayvr.create_display( - conf_display.width, - conf_display.height, - &app_entry.target_display, - )?; - - let mut overlay = create_wayvr_display_overlay::( - app, - conf_display.width, - conf_display.height, - display_handle, - conf_display.scale.unwrap_or(1.0), - )?; - - if let Some(attach_to) = &conf_display.attach_to { - overlay.state.relative_to = attach_to.get_relative_to(); - } - - if let Some(rot) = &conf_display.rotation { - overlay.state.spawn_rotation = glam::Quat::from_axis_angle( - Vec3::from_slice(&rot.axis), - f32::to_radians(rot.angle), - ); - } - - if let Some(pos) = &conf_display.pos { - overlay.state.spawn_point = Vec3A::from_slice(pos); - } - - let display = wayvr.displays.get_mut(&display_handle).unwrap(); // Never fails - display.overlay_id = Some(overlay.state.id); - - created_overlay = Some(overlay); - display_handle - }; + let (disp_handle, created_overlay) = + get_or_create_display::(app, &mut wayvr, &app_entry.target_display)?; // Parse additional args let args_vec: Vec<&str> = if let Some(args) = &app_entry.args { @@ -380,9 +445,10 @@ where // Spawn process wayvr.spawn_process(disp_handle, &app_entry.exec, &args_vec, &env_vec)?; } + Ok(created_overlay) + } else { + Ok(None) } - - Ok(created_overlay) } pub fn action_display_click( @@ -397,7 +463,7 @@ where let wayvr = app.get_wayvr()?; let mut wayvr = wayvr.borrow_mut(); - if let Some(handle) = wayvr.get_display_by_name(display_name) { + if let Some(handle) = WayVR::get_display_by_name(&wayvr.displays, display_name) { if let Some(display) = wayvr.displays.get_mut(&handle) { if let Some(overlay_id) = display.overlay_id { if let Some(overlay) = overlays.mut_by_id(overlay_id) { diff --git a/src/res/wayvr.yaml b/src/res/wayvr.yaml index 292c8a8..ac3d1f9 100644 --- a/src/res/wayvr.yaml +++ b/src/res/wayvr.yaml @@ -5,6 +5,10 @@ version: 1 +# Set to true if you want to make Wyland server instantly available +# (used for remote starting external processes) +run_compositor_at_start: false + displays: Watch: width: 400 @@ -16,6 +20,7 @@ displays: Disp1: width: 640 height: 480 + primary: true # Required if you want to attach external processes (not spawned by WayVR itself) without WAYVR_DISPLAY_NAME set Disp2: width: 1280 height: 720 diff --git a/src/state.rs b/src/state.rs index ee35d21..e789590 100644 --- a/src/state.rs +++ b/src/state.rs @@ -100,7 +100,7 @@ impl AppState { let session = AppSession::load(); #[cfg(feature = "wayvr")] - session.wayvr_config.post_load(&mut tasks); + let wayvr = session.wayvr_config.post_load(&mut tasks)?; Ok(AppState { fc: FontCache::new(session.config.primary_font.clone())?, @@ -116,7 +116,7 @@ impl AppState { keyboard_focus: KeyboardFocus::PhysicalScreen, #[cfg(feature = "wayvr")] - wayvr: None, + wayvr, }) } @@ -126,7 +126,6 @@ impl AppState { if let Some(wvr) = &self.wayvr { Ok(wvr.clone()) } else { - log::info!("Initializing WayVR"); let wayvr = Rc::new(RefCell::new(WayVR::new()?)); self.wayvr = Some(wayvr.clone()); Ok(wayvr)