diff --git a/Cargo.toml b/Cargo.toml index 40ec14d..e144805 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,8 @@ members = [ "wlx-overlay-s", "wlx-capture", "dash-frontend", - "wayvr-ipc", "wayvrctl", + "wayvr-ipc", + "wayvrctl", ] resolver = "3" diff --git a/wgui/src/layout.rs b/wgui/src/layout.rs index 5a7174b..5320790 100644 --- a/wgui/src/layout.rs +++ b/wgui/src/layout.rs @@ -382,7 +382,7 @@ impl Layout { let mut iter = |idx: usize| -> anyhow::Result { let child_id = self.state.tree.get_child_id(parent_node_id, idx); - self.push_event_widget(child_id, event, event_result, alterables, user_data, false)?; + self.push_event_widget(child_id, event, event_result, alterables, user_data)?; Ok(!event_result.can_propagate()) }; @@ -403,7 +403,6 @@ impl Layout { event_result: &mut EventResult, alterables: &mut EventAlterables, user_data: &mut (&'a mut U1, &'a mut U2), - is_root_node: bool, ) -> anyhow::Result<()> { let l = self.state.tree.layout(node_id)?; let Some(widget_id) = self.state.tree.get_node_context(node_id).copied() else { @@ -495,7 +494,6 @@ impl Layout { &mut event_result, &mut alterables, &mut (user1, user2), - true, )?; self.process_alterables(alterables)?; Ok(event_result) diff --git a/wlx-overlay-s/Cargo.toml b/wlx-overlay-s/Cargo.toml index 99551c3..3b2e9f7 100644 --- a/wlx-overlay-s/Cargo.toml +++ b/wlx-overlay-s/Cargo.toml @@ -81,9 +81,11 @@ tracing = "0.1.43" vulkano = { workspace = true } vulkano-shaders = { workspace = true } wgui = { path = "../wgui" } +bytes = { version = "1.11.0" } +wayvr-ipc = { path = "../wayvr-ipc", default-features = false } ################################ -#WayVR-only deps +# Wayland Server deps ################################ khronos-egl = { version = "6.0.0", features = ["static"], optional = true } smithay = { version = "0.7.0", default-features = false, features = [ @@ -96,8 +98,6 @@ smithay = { version = "0.7.0", default-features = false, features = [ uuid = { version = "1.19.0", features = ["v4", "fast-rng"], optional = true } wayland-client = { workspace = true, optional = true } wayland-egl = { version = "0.32.8", optional = true } -bytes = { version = "1.11.0", optional = true } -wayvr-ipc = { path = "../wayvr-ipc", default-features = false, optional = true } rust-embed = { workspace = true } signal-hook = "0.3.18" ################################ @@ -105,6 +105,7 @@ signal-hook = "0.3.18" [build-dependencies] regex = { version = "1.12.2" } +# TODO: rename "wayvr" feature to "wayland-server" [features] default = ["openvr", "openxr", "osc", "x11", "wayland", "wayvr"] openvr = ["dep:ovr_overlay", "dep:json"] @@ -121,7 +122,5 @@ wayvr = [ "dep:uuid", "dep:wayland-client", "dep:wayland-egl", - "dep:bytes", - "dep:wayvr-ipc", ] as-raw-xcb-connection = [] diff --git a/wlx-overlay-s/src/backend/openvr/mod.rs b/wlx-overlay-s/src/backend/openvr/mod.rs index f6b2aea..3ee98b6 100644 --- a/wlx-overlay-s/src/backend/openvr/mod.rs +++ b/wlx-overlay-s/src/backend/openvr/mod.rs @@ -303,11 +303,10 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr let _ = sender.send_params(&overlays, &app.input_state.devices); } - #[cfg(feature = "wayvr")] if let Err(e) = - crate::overlays::wayvr::tick_events::(&mut app, &mut overlays) + crate::ipc::events::tick_events::(&mut app, &mut overlays) { - log::error!("WayVR tick_events failed: {e:?}"); + log::error!("WayVR IPC tick_events failed: {e:?}"); } log::trace!("Rendering frame"); @@ -336,9 +335,7 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr .for_each(|o| o.after_render(universe.clone(), &mut overlay_mgr, &app.gfx)); #[cfg(feature = "wayvr")] - if let Some(wayvr) = &app.wayvr { - wayvr.borrow_mut().data.tick_finish()?; - } + app.wayvr.borrow_mut().data.tick_finish()?; // chaperone } // main_loop diff --git a/wlx-overlay-s/src/backend/openxr/mod.rs b/wlx-overlay-s/src/backend/openxr/mod.rs index 27fc268..ac829aa 100644 --- a/wlx-overlay-s/src/backend/openxr/mod.rs +++ b/wlx-overlay-s/src/backend/openxr/mod.rs @@ -373,9 +373,9 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr #[cfg(feature = "wayvr")] if let Err(e) = - crate::overlays::wayvr::tick_events::(&mut app, &mut overlays) + crate::ipc::events::tick_events::(&mut app, &mut overlays) { - log::error!("WayVR tick_events failed: {e:?}"); + log::error!("WayVR IPC tick_events failed: {e:?}"); } // Begin rendering @@ -459,9 +459,7 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr // End layer composition #[cfg(feature = "wayvr")] - if let Some(wayvr) = &app.wayvr { - wayvr.borrow_mut().data.tick_finish()?; - } + app.wayvr.borrow_mut().data.tick_finish()?; // Begin layer submit layers.sort_by(|a, b| b.0.total_cmp(&a.0)); diff --git a/wlx-overlay-s/src/backend/wayvr/comp.rs b/wlx-overlay-s/src/backend/wayvr/comp.rs index 1e07bab..b945a51 100644 --- a/wlx-overlay-s/src/backend/wayvr/comp.rs +++ b/wlx-overlay-s/src/backend/wayvr/comp.rs @@ -37,8 +37,9 @@ use wayland_server::Client; use wayland_server::backend::{ClientData, ClientId, DisconnectReason}; use wayland_server::protocol::wl_surface::WlSurface; +use crate::ipc::event_queue::SyncEventQueue; + use super::WayVRTask; -use super::event_queue::SyncEventQueue; pub struct Application { pub gles_renderer: GlesRenderer, diff --git a/wlx-overlay-s/src/backend/wayvr/display.rs b/wlx-overlay-s/src/backend/wayvr/display.rs index f5c8909..9a4d763 100644 --- a/wlx-overlay-s/src/backend/wayvr/display.rs +++ b/wlx-overlay-s/src/backend/wayvr/display.rs @@ -17,12 +17,13 @@ use smithay::{ use wayvr_ipc::packet_server; use crate::{ - backend::wayvr::time::get_millis, gen_id, subsystem::hid::WheelDelta, windowing::OverlayID, + backend::wayvr::time::get_millis, gen_id, ipc::event_queue::SyncEventQueue, + subsystem::hid::WheelDelta, windowing::OverlayID, }; use super::{ BlitMethod, WayVRSignal, client::WayVRCompositor, comp::send_frames_surface_tree, egl_data, - event_queue::SyncEventQueue, process, smithay_wrapper, time, window, + process, smithay_wrapper, time, window, }; fn generate_auth_key() -> String { diff --git a/wlx-overlay-s/src/backend/wayvr/mod.rs b/wlx-overlay-s/src/backend/wayvr/mod.rs index 2c2b511..06f7d89 100644 --- a/wlx-overlay-s/src/backend/wayvr/mod.rs +++ b/wlx-overlay-s/src/backend/wayvr/mod.rs @@ -3,20 +3,16 @@ mod comp; pub mod display; pub mod egl_data; mod egl_ex; -pub mod event_queue; mod handle; -mod process; -pub mod server_ipc; +pub mod process; mod smithay_wrapper; mod time; -mod window; +pub mod window; use anyhow::Context; use comp::Application; use display::{Display, DisplayInitParams, DisplayVec}; -use event_queue::SyncEventQueue; use process::ProcessVec; use serde::Deserialize; -use server_ipc::WayVRServer; use smallvec::SmallVec; use smithay::{ backend::{ @@ -48,6 +44,7 @@ use wayvr_ipc::{ use xkbcommon::xkb; use crate::{ + ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal}, state::AppState, subsystem::hid::{MODS_TO_KEYS, WheelDelta}, }; @@ -87,19 +84,6 @@ pub enum WayVRTask { ProcessTerminationRequest(process::ProcessHandle), } -#[derive(Clone)] -pub enum WayVRSignal { - DisplayVisibility(display::DisplayHandle, bool), - DisplayWindowLayout( - display::DisplayHandle, - packet_server::WvrDisplayWindowLayout, - ), - BroadcastStateChanged(packet_server::WvrStateChanged), - DropOverlay(crate::windowing::OverlayID), - Haptics(super::input::Haptics), - CustomTask(crate::backend::task::ModifyPanelTask), -} - pub enum BlitMethod { Dmabuf, Software, @@ -127,20 +111,19 @@ pub struct WayVRState { time_start: u64, pub displays: display::DisplayVec, pub manager: client::WayVRCompositor, - wm: Rc>, + pub wm: Rc>, egl_data: Rc, pub processes: process::ProcessVec, pub config: Config, dashboard_display: Option, pub tasks: SyncEventQueue, - pub signals: SyncEventQueue, ticks: u64, cur_modifiers: u8, + signals: SyncEventQueue, } pub struct WayVR { pub state: WayVRState, - pub ipc_server: WayVRServer, } pub enum MouseIndex { @@ -159,7 +142,7 @@ pub enum TickTask { impl WayVR { #[allow(clippy::too_many_lines, clippy::cognitive_complexity)] - pub fn new(config: Config) -> anyhow::Result { + pub fn new(config: Config, signals: SyncEventQueue) -> anyhow::Result { log::info!("Initializing WayVR"); let display: wayland_server::Display = wayland_server::Display::new()?; let dh = display.handle(); @@ -264,8 +247,6 @@ impl WayVR { let time_start = get_millis(); - let ipc_server = WayVRServer::new()?; - let state = WayVRState { time_start, manager: client::WayVRCompositor::new(state, display, seat_keyboard, seat_pointer)?, @@ -277,11 +258,11 @@ impl WayVR { dashboard_display: None, ticks: 0, tasks, - signals: SyncEventQueue::new(), cur_modifiers: 0, + signals, }; - Ok(Self { state, ipc_server }) + Ok(Self { state }) } pub fn render_display(&mut self, display: display::DisplayHandle) -> anyhow::Result { @@ -312,13 +293,14 @@ impl WayVR { } #[allow(clippy::too_many_lines, clippy::cognitive_complexity)] - pub fn tick_events(&mut self, app: &AppState) -> anyhow::Result> { + pub fn tick_events(&mut self, app: &mut AppState) -> anyhow::Result> { let mut tasks: Vec = Vec::new(); - self.ipc_server.tick(&mut server_ipc::TickParams { - state: &mut self.state, + app.ipc_server.tick(&mut ipc_server::TickParams { + wayland_state: &mut self.state, + input_state: &app.input_state, tasks: &mut tasks, - app, + signals: &app.wayvr_signals, }); // Check for redraw events @@ -357,11 +339,11 @@ impl WayVR { } for (handle, display) in self.state.displays.iter_mut() { - display.tick(&self.state.config, &handle, &mut self.state.signals); + display.tick(&self.state.config, &handle, &mut app.wayvr_signals); } if !to_remove.is_empty() { - self.state.signals.send(WayVRSignal::BroadcastStateChanged( + app.wayvr_signals.send(WayVRSignal::BroadcastStateChanged( packet_server::WvrStateChanged::ProcessRemoved, )); } @@ -402,7 +384,7 @@ impl WayVR { }; display.add_window(window_handle, process_handle, &toplevel); - self.state.signals.send(WayVRSignal::BroadcastStateChanged( + app.wayvr_signals.send(WayVRSignal::BroadcastStateChanged( packet_server::WvrStateChanged::WindowCreated, )); } diff --git a/wlx-overlay-s/src/config_wayvr.rs b/wlx-overlay-s/src/config_wayvr.rs index c75cf0c..34dcf74 100644 --- a/wlx-overlay-s/src/config_wayvr.rs +++ b/wlx-overlay-s/src/config_wayvr.rs @@ -19,6 +19,7 @@ use crate::{ }, config::load_config_with_conf_d, config_io, + ipc::{event_queue::SyncEventQueue, signal::WayVRSignal}, overlays::wayvr::{WayVRData, executable_exists_in_path}, }; @@ -135,9 +136,6 @@ pub struct WayVRDashboard { #[derive(Deserialize, Serialize)] pub struct WayVRConfig { - #[serde(default = "def_true")] - pub run_compositor_at_start: bool, - #[serde(default = "Default::default")] pub catalogs: HashMap, @@ -203,7 +201,8 @@ impl WayVRConfig { &self, config: &GeneralConfig, tasks: &mut TaskContainer, - ) -> anyhow::Result>>> { + signals: SyncEventQueue, + ) -> anyhow::Result>> { let primary_count = self .displays .iter() @@ -212,10 +211,6 @@ impl WayVRConfig { 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. External Wayland applications will not be attached." - ); } for (catalog_name, catalog) in &self.catalogs { @@ -231,15 +226,10 @@ impl WayVRConfig { } } - if self.run_compositor_at_start { - // Start Wayland server instantly - Ok(Some(Rc::new(RefCell::new(WayVRData::new( - Self::get_wayvr_config(config, self)?, - )?)))) - } else { - // Lazy-init WayVR later if the user requested - Ok(None) - } + Ok(Rc::new(RefCell::new(WayVRData::new( + Self::get_wayvr_config(config, self)?, + signals, + )?))) } } diff --git a/wlx-overlay-s/src/backend/wayvr/event_queue.rs b/wlx-overlay-s/src/ipc/event_queue.rs similarity index 100% rename from wlx-overlay-s/src/backend/wayvr/event_queue.rs rename to wlx-overlay-s/src/ipc/event_queue.rs diff --git a/wlx-overlay-s/src/ipc/events.rs b/wlx-overlay-s/src/ipc/events.rs new file mode 100644 index 0000000..0273ecf --- /dev/null +++ b/wlx-overlay-s/src/ipc/events.rs @@ -0,0 +1,189 @@ +use std::{cell::RefCell, rc::Rc}; + +use crate::ipc::ipc_server; +use wayvr_ipc::packet_server; + +#[cfg(feature = "wayvr")] +use crate::{ + backend::wayvr, config_wayvr, overlays::wayvr::OverlayToCreate, overlays::wayvr::WayVRData, +}; + +use crate::{ + backend::{ + self, + task::{OverlayTask, TaskType}, + }, + ipc::signal::WayVRSignal, + overlays::{self}, + state::AppState, + windowing::{OverlaySelector, manager::OverlayWindowManager}, +}; + +#[cfg(feature = "wayvr")] +fn process_tick_tasks( + app: &mut AppState, + tick_tasks: Vec, + r_wayvr: &Rc>, +) -> anyhow::Result<()> { + for tick_task in tick_tasks { + match tick_task { + backend::wayvr::TickTask::NewExternalProcess(request) => { + let config = &app.session.wayvr_config; + + let disp_name = request.env.display_name.map_or_else( + || { + config + .get_default_display() + .map(|(display_name, _)| display_name) + }, + |display_name| { + config + .get_display(display_name.as_str()) + .map(|_| display_name) + }, + ); + + if let Some(disp_name) = disp_name { + let mut wayvr = r_wayvr.borrow_mut(); + + log::info!("Registering external process with PID {}", request.pid); + + let disp_handle = overlays::wayvr::get_or_create_display_by_name( + app, &mut wayvr, &disp_name, + )?; + + wayvr + .data + .state + .add_external_process(disp_handle, request.pid); + + wayvr + .data + .state + .manager + .add_client(wayvr::client::WayVRClient { + client: request.client, + display_handle: disp_handle, + pid: request.pid, + }); + } + } + wayvr::TickTask::NewDisplay(cpar, disp_handle) => { + log::info!("Creating new display with name \"{}\"", cpar.name); + + let mut wayvr = r_wayvr.borrow_mut(); + + let unique_name = wayvr.get_unique_display_name(cpar.name); + + let disp_handle = match disp_handle { + Some(d) => d, + None => wayvr.data.state.create_display( + cpar.width, + cpar.height, + &unique_name, + false, + )?, + }; + + wayvr.overlays_to_create.push(OverlayToCreate { + disp_handle, + conf_display: config_wayvr::WayVRDisplay { + attach_to: Some(config_wayvr::AttachTo::from_packet(&cpar.attach_to)), + width: cpar.width, + height: cpar.height, + pos: None, + primary: None, + rotation: None, + scale: cpar.scale, + }, + }); + } + } + } + + Ok(()) +} + +#[allow(clippy::too_many_lines)] +pub fn tick_events( + app: &mut AppState, + overlays: &mut OverlayWindowManager, +) -> anyhow::Result<()> +where + O: Default, +{ + #[cfg(feature = "wayvr")] + let r_wayvr = app.wayvr.clone(); + #[cfg(feature = "wayvr")] + let mut wayvr = r_wayvr.borrow_mut(); + + while let Some(signal) = app.wayvr_signals.read() { + match signal { + #[cfg(feature = "wayvr")] + WayVRSignal::DisplayVisibility(display_handle, visible) => { + if let Some(overlay_id) = wayvr.display_handle_map.get(&display_handle) { + let overlay_id = *overlay_id; + wayvr + .data + .state + .set_display_visible(display_handle, visible); + app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify( + OverlaySelector::Id(overlay_id), + Box::new(move |app, o| { + if visible == o.is_active() { + return; + } + if visible { + o.activate(app); + } else { + o.deactivate(); + } + }), + ))); + } + } + #[cfg(feature = "wayvr")] + WayVRSignal::DisplayWindowLayout(display_handle, layout) => { + wayvr.data.state.set_display_layout(display_handle, layout); + } + #[cfg(feature = "wayvr")] + WayVRSignal::BroadcastStateChanged(packet) => { + app.ipc_server + .broadcast(packet_server::PacketServer::WvrStateChanged(packet)); + } + #[cfg(feature = "wayvr")] + WayVRSignal::Haptics(haptics) => { + wayvr.pending_haptics = Some(haptics); + } + WayVRSignal::DropOverlay(overlay_id) => { + app.tasks + .enqueue(TaskType::Overlay(OverlayTask::Drop(OverlaySelector::Id( + overlay_id, + )))); + } + WayVRSignal::CustomTask(custom_task) => { + app.tasks + .enqueue(TaskType::Overlay(OverlayTask::ModifyPanel(custom_task))); + } + } + } + + #[cfg(feature = "wayvr")] + { + let tick_tasks = wayvr.data.tick_events(app)?; + process_tick_tasks(app, tick_tasks, &r_wayvr)?; + } + + #[cfg(not(feature = "wayvr"))] + { + app.ipc_server.tick(&mut ipc_server::TickParams { + input_state: &app.input_state, + signals: &app.wayvr_signals, + }); + } + + #[cfg(feature = "wayvr")] + overlays::wayvr::create_queued_displays(app, &mut wayvr, overlays)?; + + Ok(()) +} diff --git a/wlx-overlay-s/src/backend/wayvr/server_ipc.rs b/wlx-overlay-s/src/ipc/ipc_server.rs similarity index 82% rename from wlx-overlay-s/src/backend/wayvr/server_ipc.rs rename to wlx-overlay-s/src/ipc/ipc_server.rs index ed320d5..e5aee89 100644 --- a/wlx-overlay-s/src/backend/wayvr/server_ipc.rs +++ b/wlx-overlay-s/src/ipc/ipc_server.rs @@ -1,6 +1,10 @@ -use crate::state::AppState; +#[cfg(feature = "wayvr")] +use crate::backend::wayvr::{self, WayVRState}; -use super::{TickTask, WayVRSignal, display, process, window}; +use crate::{ + backend::input::InputState, + ipc::{event_queue::SyncEventQueue, signal::WayVRSignal}, +}; use bytes::BufMut; use glam::Vec3A; use interprocess::local_socket::{self, ToNsName, traits::Listener}; @@ -71,9 +75,12 @@ fn read_payload(conn: &mut local_socket::Stream, size: u32) -> Option { } pub struct TickParams<'a> { - pub state: &'a mut super::WayVRState, - pub tasks: &'a mut Vec, - pub app: &'a AppState, + #[cfg(feature = "wayvr")] + pub wayland_state: &'a mut WayVRState, + #[cfg(feature = "wayvr")] + pub tasks: &'a mut Vec, + pub signals: &'a SyncEventQueue, + pub input_state: &'a InputState, } pub fn gen_args_vec(input: &str) -> Vec<&str> { @@ -149,13 +156,14 @@ impl Connection { Ok(()) } + #[cfg(feature = "wayvr")] fn handle_wvr_display_list( &mut self, params: &TickParams, serial: ipc::Serial, ) -> anyhow::Result<()> { let list: Vec = params - .state + .wayland_state .displays .vec .iter() @@ -165,7 +173,10 @@ impl Connection { return None; }; let display = &cell.obj; - Some(display.as_packet(display::DisplayHandle::new(idx as u32, cell.generation))) + Some(display.as_packet(wayvr::display::DisplayHandle::new( + idx as u32, + cell.generation, + ))) }) .collect(); @@ -185,8 +196,6 @@ impl Connection { params: &TickParams, serial: ipc::Serial, ) -> anyhow::Result<()> { - let input_state = ¶ms.app.input_state; - let to_arr = |vec: &Vec3A| -> [f32; 3] { [vec.x, vec.y, vec.z] }; send_packet( @@ -194,12 +203,12 @@ impl Connection { &ipc::data_encode(&PacketServer::WlxInputStateResponse( serial, packet_server::WlxInputState { - hmd_pos: to_arr(&input_state.hmd.translation), + hmd_pos: to_arr(¶ms.input_state.hmd.translation), left: WlxInputStatePointer { - pos: to_arr(&input_state.pointers[0].raw_pose.translation), + pos: to_arr(¶ms.input_state.pointers[0].raw_pose.translation), }, right: WlxInputStatePointer { - pos: to_arr(&input_state.pointers[0].raw_pose.translation), + pos: to_arr(¶ms.input_state.pointers[0].raw_pose.translation), }, }, )), @@ -208,22 +217,24 @@ impl Connection { Ok(()) } + #[cfg(feature = "wayvr")] fn handle_wvr_display_create( &mut self, params: &mut TickParams, serial: ipc::Serial, packet_params: packet_client::WvrDisplayCreateParams, ) -> anyhow::Result<()> { - let display_handle = params.state.create_display( + let display_handle = params.wayland_state.create_display( packet_params.width, packet_params.height, &packet_params.name, false, )?; - params - .tasks - .push(TickTask::NewDisplay(packet_params, Some(display_handle))); + params.tasks.push(wayvr::TickTask::NewDisplay( + packet_params, + Some(display_handle), + )); send_packet( &mut self.conn, @@ -235,6 +246,7 @@ impl Connection { Ok(()) } + #[cfg(feature = "wayvr")] fn handle_wvr_display_remove( &mut self, params: &mut TickParams, @@ -242,8 +254,8 @@ impl Connection { handle: packet_server::WvrDisplayHandle, ) -> anyhow::Result<()> { let res = params - .state - .destroy_display(display::DisplayHandle::from_packet(handle)) + .wayland_state + .destroy_display(wayvr::display::DisplayHandle::from_packet(handle)) .map_err(|e| format!("{e:?}")); send_packet( @@ -253,28 +265,31 @@ impl Connection { Ok(()) } + #[cfg(feature = "wayvr")] fn handle_wvr_display_set_visible( params: &mut TickParams, handle: packet_server::WvrDisplayHandle, visible: bool, ) { - params.state.signals.send(WayVRSignal::DisplayVisibility( - display::DisplayHandle::from_packet(handle), + params.signals.send(WayVRSignal::DisplayVisibility( + wayvr::display::DisplayHandle::from_packet(handle), visible, )); } + #[cfg(feature = "wayvr")] fn handle_wvr_display_set_window_layout( params: &mut TickParams, handle: packet_server::WvrDisplayHandle, layout: packet_server::WvrDisplayWindowLayout, ) { - params.state.signals.send(WayVRSignal::DisplayWindowLayout( - display::DisplayHandle::from_packet(handle), + params.signals.send(WayVRSignal::DisplayWindowLayout( + wayvr::display::DisplayHandle::from_packet(handle), layout, )); } + #[cfg(feature = "wayvr")] fn handle_wvr_display_window_list( &mut self, params: &mut TickParams, @@ -288,10 +303,13 @@ impl Connection { ) }; - let Some(display) = params - .state - .displays - .get(&display::DisplayHandle::from_packet(display_handle.clone())) + let Some(display) = + params + .wayland_state + .displays + .get(&wayvr::display::DisplayHandle::from_packet( + display_handle.clone(), + )) else { return send(None); }; @@ -302,14 +320,14 @@ impl Connection { .iter() .filter_map(|disp_win| { params - .state + .wayland_state .wm .borrow_mut() .windows .get(&disp_win.window_handle) .map(|win| packet_server::WvrWindow { - handle: window::WindowHandle::as_packet(&disp_win.window_handle), - process_handle: process::ProcessHandle::as_packet( + handle: wayvr::window::WindowHandle::as_packet(&disp_win.window_handle), + process_handle: wayvr::process::ProcessHandle::as_packet( &disp_win.process_handle, ), pos_x: win.pos_x, @@ -324,17 +342,18 @@ impl Connection { })) } + #[cfg(feature = "wayvr")] fn handle_wvr_window_set_visible( params: &mut TickParams, handle: packet_server::WvrWindowHandle, visible: bool, ) { let to_resize = if let Some(window) = params - .state + .wayland_state .wm .borrow_mut() .windows - .get_mut(&window::WindowHandle::from_packet(handle)) + .get_mut(&wayvr::window::WindowHandle::from_packet(handle)) { window.visible = visible; Some(window.display_handle) @@ -343,13 +362,14 @@ impl Connection { }; if let Some(to_resize) = to_resize - && let Some(display) = params.state.displays.get_mut(&to_resize) + && let Some(display) = params.wayland_state.displays.get_mut(&to_resize) { display.reposition_windows(); display.trigger_rerender(); } } + #[cfg(feature = "wayvr")] fn handle_wvr_process_launch( &mut self, params: &mut TickParams, @@ -359,8 +379,8 @@ impl Connection { let args_vec = gen_args_vec(&packet_params.args); let env_vec = gen_env_vec(&packet_params.env); - let res = params.state.spawn_process( - super::display::DisplayHandle::from_packet(packet_params.target_display), + let res = params.wayland_state.spawn_process( + wayvr::display::DisplayHandle::from_packet(packet_params.target_display), &packet_params.exec, &args_vec, &env_vec, @@ -378,15 +398,16 @@ impl Connection { Ok(()) } + #[cfg(feature = "wayvr")] fn handle_wvr_display_get( &mut self, params: &TickParams, serial: ipc::Serial, display_handle: packet_server::WvrDisplayHandle, ) -> anyhow::Result<()> { - let native_handle = &display::DisplayHandle::from_packet(display_handle); + let native_handle = &wayvr::display::DisplayHandle::from_packet(display_handle); let disp = params - .state + .wayland_state .displays .get(native_handle) .map(|disp| disp.as_packet(*native_handle)); @@ -399,13 +420,14 @@ impl Connection { Ok(()) } + #[cfg(feature = "wayvr")] fn handle_wvr_process_list( &mut self, params: &TickParams, serial: ipc::Serial, ) -> anyhow::Result<()> { let list: Vec = params - .state + .wayland_state .processes .vec .iter() @@ -415,7 +437,10 @@ impl Connection { return None; }; let process = &cell.obj; - Some(process.to_packet(process::ProcessHandle::new(idx as u32, cell.generation))) + Some(process.to_packet(wayvr::process::ProcessHandle::new( + idx as u32, + cell.generation, + ))) }) .collect(); @@ -431,12 +456,13 @@ impl Connection { } // This request doesn't return anything to the client + #[cfg(feature = "wayvr")] fn handle_wvr_process_terminate( params: &mut TickParams, process_handle: packet_server::WvrProcessHandle, ) { - let native_handle = &process::ProcessHandle::from_packet(process_handle); - let process = params.state.processes.get_mut(native_handle); + let native_handle = &wayvr::process::ProcessHandle::from_packet(process_handle); + let process = params.wayland_state.processes.get_mut(native_handle); let Some(process) = process else { return; @@ -445,15 +471,16 @@ impl Connection { process.terminate(); } + #[cfg(feature = "wayvr")] fn handle_wvr_process_get( &mut self, params: &TickParams, serial: ipc::Serial, process_handle: packet_server::WvrProcessHandle, ) -> anyhow::Result<()> { - let native_handle = &process::ProcessHandle::from_packet(process_handle); + let native_handle = &wayvr::process::ProcessHandle::from_packet(process_handle); let process = params - .state + .wayland_state .processes .get(native_handle) .map(|process| process.to_packet(*native_handle)); @@ -466,17 +493,18 @@ impl Connection { Ok(()) } + #[cfg(feature = "wayvr")] fn handle_wlx_haptics( params: &mut TickParams, haptics_params: packet_client::WlxHapticsParams, ) { - params.state.signals.send(super::WayVRSignal::Haptics( - crate::backend::input::Haptics { + params + .signals + .send(WayVRSignal::Haptics(crate::backend::input::Haptics { duration: haptics_params.duration, frequency: haptics_params.frequency, intensity: haptics_params.intensity, - }, - )); + })); } fn handle_wlx_panel( @@ -486,9 +514,8 @@ impl Connection { use crate::backend::task::{ModifyPanelCommand, ModifyPanelTask}; params - .state .signals - .send(super::WayVRSignal::CustomTask(ModifyPanelTask { + .send(WayVRSignal::CustomTask(ModifyPanelTask { overlay: custom_params.overlay, element: custom_params.element, command: match custom_params.command { @@ -511,6 +538,9 @@ impl Connection { })); } + // FIXME: we should probably respond an error to the client in case if wayland server feature is disabled + // fix this after we're done with the webkit-based wayvr-dashboard + #[allow(unused_variables)] fn process_payload(&mut self, params: &mut TickParams, payload: Payload) -> anyhow::Result<()> { let packet: PacketClient = ipc::data_decode(&payload)?; @@ -525,42 +555,55 @@ impl Connection { self.handle_wlx_input_state(params, serial)?; } PacketClient::WvrDisplayList(serial) => { + #[cfg(feature = "wayvr")] self.handle_wvr_display_list(params, serial)?; } PacketClient::WvrDisplayGet(serial, display_handle) => { + #[cfg(feature = "wayvr")] self.handle_wvr_display_get(params, serial, display_handle)?; } PacketClient::WvrDisplayRemove(serial, display_handle) => { + #[cfg(feature = "wayvr")] self.handle_wvr_display_remove(params, serial, display_handle)?; } PacketClient::WvrDisplaySetVisible(display_handle, visible) => { + #[cfg(feature = "wayvr")] Self::handle_wvr_display_set_visible(params, display_handle, visible); } PacketClient::WvrDisplaySetWindowLayout(display_handle, layout) => { + #[cfg(feature = "wayvr")] Self::handle_wvr_display_set_window_layout(params, display_handle, layout); } PacketClient::WvrDisplayWindowList(serial, display_handle) => { + #[cfg(feature = "wayvr")] self.handle_wvr_display_window_list(params, serial, display_handle)?; } PacketClient::WvrWindowSetVisible(window_handle, visible) => { + #[cfg(feature = "wayvr")] Self::handle_wvr_window_set_visible(params, window_handle, visible); } PacketClient::WvrProcessGet(serial, process_handle) => { + #[cfg(feature = "wayvr")] self.handle_wvr_process_get(params, serial, process_handle)?; } PacketClient::WvrProcessList(serial) => { + #[cfg(feature = "wayvr")] self.handle_wvr_process_list(params, serial)?; } PacketClient::WvrProcessLaunch(serial, packet_params) => { + #[cfg(feature = "wayvr")] self.handle_wvr_process_launch(params, serial, packet_params)?; } PacketClient::WvrDisplayCreate(serial, packet_params) => { + #[cfg(feature = "wayvr")] self.handle_wvr_display_create(params, serial, packet_params)?; } PacketClient::WvrProcessTerminate(process_handle) => { + #[cfg(feature = "wayvr")] Self::handle_wvr_process_terminate(params, process_handle); } PacketClient::WlxHaptics(haptics_params) => { + #[cfg(feature = "wayvr")] Self::handle_wlx_haptics(params, haptics_params); } PacketClient::WlxModifyPanel(custom_params) => { diff --git a/wlx-overlay-s/src/ipc/mod.rs b/wlx-overlay-s/src/ipc/mod.rs new file mode 100644 index 0000000..c1fd500 --- /dev/null +++ b/wlx-overlay-s/src/ipc/mod.rs @@ -0,0 +1,4 @@ +pub mod event_queue; +pub mod events; +pub mod ipc_server; +pub mod signal; diff --git a/wlx-overlay-s/src/ipc/signal.rs b/wlx-overlay-s/src/ipc/signal.rs new file mode 100644 index 0000000..1bb4289 --- /dev/null +++ b/wlx-overlay-s/src/ipc/signal.rs @@ -0,0 +1,19 @@ +#[cfg(feature = "wayvr")] +use crate::backend::wayvr; + +#[derive(Clone)] +pub enum WayVRSignal { + #[cfg(feature = "wayvr")] + DisplayVisibility(wayvr::display::DisplayHandle, bool), + #[cfg(feature = "wayvr")] + DisplayWindowLayout( + wayvr::display::DisplayHandle, + wayvr_ipc::packet_server::WvrDisplayWindowLayout, + ), + #[cfg(feature = "wayvr")] + BroadcastStateChanged(wayvr_ipc::packet_server::WvrStateChanged), + #[cfg(feature = "wayvr")] + Haptics(crate::backend::input::Haptics), + DropOverlay(crate::windowing::OverlayID), + CustomTask(crate::backend::task::ModifyPanelTask), +} diff --git a/wlx-overlay-s/src/main.rs b/wlx-overlay-s/src/main.rs index aea98e1..252cc06 100644 --- a/wlx-overlay-s/src/main.rs +++ b/wlx-overlay-s/src/main.rs @@ -22,6 +22,7 @@ mod config; mod config_io; mod graphics; mod gui; +mod ipc; mod overlays; mod shaders; mod state; diff --git a/wlx-overlay-s/src/overlays/wayvr.rs b/wlx-overlay-s/src/overlays/wayvr.rs index f5e6d69..3590ba6 100644 --- a/wlx-overlay-s/src/overlays/wayvr.rs +++ b/wlx-overlay-s/src/overlays/wayvr.rs @@ -22,13 +22,11 @@ use crate::{ backend::{ input::{self, HoverResult}, task::{OverlayTask, TaskType}, - wayvr::{ - self, WayVR, WayVRAction, WayVRDisplayClickAction, display, - server_ipc::{gen_args_vec, gen_env_vec}, - }, + wayvr::{self, WayVR, WayVRAction, WayVRDisplayClickAction, display}, }, config_wayvr, graphics::{Vert2Uv, dmabuf::WGfxDmabuf}, + ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal}, state::{self, AppState}, subsystem::{hid::WheelDelta, input::KeyboardFocus}, windowing::{ @@ -63,31 +61,34 @@ impl WayVRContext { } } -struct OverlayToCreate { +pub struct OverlayToCreate { pub conf_display: config_wayvr::WayVRDisplay, pub disp_handle: display::DisplayHandle, } pub struct WayVRData { - display_handle_map: HashMap, - overlays_to_create: Vec, - dashboard_executed: bool, + pub display_handle_map: HashMap, + pub overlays_to_create: Vec, + pub dashboard_executed: bool, pub data: WayVR, - pending_haptics: Option, + pub pending_haptics: Option, } impl WayVRData { - pub fn new(config: wayvr::Config) -> anyhow::Result { + pub fn new( + config: wayvr::Config, + signals: SyncEventQueue, + ) -> anyhow::Result { Ok(Self { display_handle_map: HashMap::default(), - data: WayVR::new(config)?, + data: WayVR::new(config, signals)?, overlays_to_create: Vec::new(), dashboard_executed: false, pending_haptics: None, }) } - fn get_unique_display_name(&self, mut candidate: String) -> String { + pub fn get_unique_display_name(&self, mut candidate: String) -> String { let mut num = 0; while !self @@ -179,7 +180,7 @@ impl WayVRBackend { } } -fn get_or_create_display_by_name( +pub fn get_or_create_display_by_name( app: &mut AppState, wayvr: &mut WayVRData, disp_name: &str, @@ -290,12 +291,12 @@ where let args_vec = &conf_dash .args .as_ref() - .map_or_else(Vec::new, |args| gen_args_vec(args.as_str())); + .map_or_else(Vec::new, |args| ipc_server::gen_args_vec(args.as_str())); let env_vec = &conf_dash .env .as_ref() - .map_or_else(Vec::new, |env| gen_env_vec(env)); + .map_or_else(Vec::new, |env| ipc_server::gen_env_vec(env)); let mut userdata = HashMap::new(); userdata.insert(String::from("type"), String::from("dashboard")); @@ -322,9 +323,7 @@ where let cur_visibility = !display.visible; - wayvr - .data - .ipc_server + app.ipc_server .broadcast(PacketServer::WvrStateChanged(if cur_visibility { WvrStateChanged::DashboardShown } else { @@ -379,7 +378,7 @@ fn create_overlay( Ok(overlay) } -fn create_queued_displays( +pub fn create_queued_displays( app: &mut AppState, data: &mut WayVRData, overlays: &mut OverlayWindowManager, @@ -405,152 +404,6 @@ where Ok(()) } -#[allow(clippy::too_many_lines)] -pub fn tick_events( - app: &mut AppState, - overlays: &mut OverlayWindowManager, -) -> anyhow::Result<()> -where - O: Default, -{ - let Some(r_wayvr) = app.wayvr.clone() else { - return Ok(()); - }; - - let mut wayvr = r_wayvr.borrow_mut(); - - while let Some(signal) = wayvr.data.state.signals.read() { - match signal { - wayvr::WayVRSignal::DisplayVisibility(display_handle, visible) => { - if let Some(overlay_id) = wayvr.display_handle_map.get(&display_handle) { - let overlay_id = *overlay_id; - wayvr - .data - .state - .set_display_visible(display_handle, visible); - app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify( - OverlaySelector::Id(overlay_id), - Box::new(move |app, o| { - if visible == o.is_active() { - return; - } - if visible { - o.activate(app); - } else { - o.deactivate(); - } - }), - ))); - } - } - wayvr::WayVRSignal::DisplayWindowLayout(display_handle, layout) => { - wayvr.data.state.set_display_layout(display_handle, layout); - } - wayvr::WayVRSignal::BroadcastStateChanged(packet) => { - wayvr - .data - .ipc_server - .broadcast(packet_server::PacketServer::WvrStateChanged(packet)); - } - wayvr::WayVRSignal::DropOverlay(overlay_id) => { - app.tasks - .enqueue(TaskType::Overlay(OverlayTask::Drop(OverlaySelector::Id( - overlay_id, - )))); - } - wayvr::WayVRSignal::Haptics(haptics) => { - wayvr.pending_haptics = Some(haptics); - } - wayvr::WayVRSignal::CustomTask(custom_task) => { - app.tasks - .enqueue(TaskType::Overlay(OverlayTask::ModifyPanel(custom_task))); - } - } - } - - let res = wayvr.data.tick_events(app)?; - drop(wayvr); - - for result in res { - match result { - wayvr::TickTask::NewExternalProcess(request) => { - let config = &app.session.wayvr_config; - - let disp_name = request.env.display_name.map_or_else( - || { - config - .get_default_display() - .map(|(display_name, _)| display_name) - }, - |display_name| { - config - .get_display(display_name.as_str()) - .map(|_| display_name) - }, - ); - - if let Some(disp_name) = disp_name { - let mut wayvr = r_wayvr.borrow_mut(); - - log::info!("Registering external process with PID {}", request.pid); - - let disp_handle = get_or_create_display_by_name(app, &mut wayvr, &disp_name)?; - - wayvr - .data - .state - .add_external_process(disp_handle, request.pid); - - wayvr - .data - .state - .manager - .add_client(wayvr::client::WayVRClient { - client: request.client, - display_handle: disp_handle, - pid: request.pid, - }); - } - } - wayvr::TickTask::NewDisplay(cpar, disp_handle) => { - log::info!("Creating new display with name \"{}\"", cpar.name); - - let mut wayvr = r_wayvr.borrow_mut(); - - let unique_name = wayvr.get_unique_display_name(cpar.name); - - let disp_handle = match disp_handle { - Some(d) => d, - None => wayvr.data.state.create_display( - cpar.width, - cpar.height, - &unique_name, - false, - )?, - }; - - wayvr.overlays_to_create.push(OverlayToCreate { - disp_handle, - conf_display: config_wayvr::WayVRDisplay { - attach_to: Some(config_wayvr::AttachTo::from_packet(&cpar.attach_to)), - width: cpar.width, - height: cpar.height, - pos: None, - primary: None, - rotation: None, - scale: cpar.scale, - }, - }); - } - } - } - - let mut wayvr = r_wayvr.borrow_mut(); - create_queued_displays(app, &mut wayvr, overlays)?; - - Ok(()) -} - impl WayVRBackend { fn ensure_software_data( &mut self, @@ -805,7 +658,7 @@ pub fn create_wayvr_display_overlay( display_scale: f32, name: &str, ) -> anyhow::Result { - let wayvr = app.get_wayvr()?; + let wayvr = app.wayvr.clone(); let backend = Box::new(WayVRBackend::new( app, @@ -866,7 +719,7 @@ fn action_app_click( where O: Default, { - let wayvr = app.get_wayvr()?; + let wayvr = app.wayvr.clone(); let catalog = app .session @@ -887,12 +740,12 @@ where let args_vec = &app_entry .args .as_ref() - .map_or_else(Vec::new, |args| gen_args_vec(args.as_str())); + .map_or_else(Vec::new, |args| ipc_server::gen_args_vec(args.as_str())); let env_vec = &app_entry .env .as_ref() - .map_or_else(Vec::new, |env| gen_env_vec(env)); + .map_or_else(Vec::new, |env| ipc_server::gen_env_vec(env)); // Terminate existing process if required if let Some(process_handle) = @@ -930,7 +783,7 @@ pub fn action_display_click( where O: Default, { - let wayvr = app.get_wayvr()?; + let wayvr = app.wayvr.clone(); let mut wayvr = wayvr.borrow_mut(); let Some(handle) = WayVR::get_display_by_name(&wayvr.data.state.displays, display_name) else { @@ -990,14 +843,7 @@ pub fn wayvr_action( } } WayVRAction::ToggleDashboard => { - let wayvr = match app.get_wayvr() { - Ok(wayvr) => wayvr, - Err(e) => { - log::error!("WayVR Error: {e:?}"); - return; - } - }; - + let wayvr = app.wayvr.clone(); let mut wayvr = wayvr.borrow_mut(); if let Err(e) = toggle_dashboard::(app, overlays, &mut wayvr) { diff --git a/wlx-overlay-s/src/res/wayvr.yaml b/wlx-overlay-s/src/res/wayvr.yaml index 954e353..0f7767b 100644 --- a/wlx-overlay-s/src/res/wayvr.yaml +++ b/wlx-overlay-s/src/res/wayvr.yaml @@ -12,11 +12,6 @@ version: 1 # "software": Read pixel data to memory via glReadPixels() every time a content has been updated. Minor performance impact on large resolutions blit_method: "dmabuf" -# Set to true if you want to make Wyland server instantly available. -# By default, WayVR starts only when it's needed. -# (this option is primarily used for remote starting external processes and development purposes) -run_compositor_at_start: false - # Automatically close overlays with zero window count? auto_hide: true diff --git a/wlx-overlay-s/src/state.rs b/wlx-overlay-s/src/state.rs index 0c083f1..e5453cb 100644 --- a/wlx-overlay-s/src/state.rs +++ b/wlx-overlay-s/src/state.rs @@ -27,6 +27,7 @@ use crate::{ config_io::{self, get_config_file_path}, graphics::WGfxExtras, gui, + ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal}, subsystem::{audio::AudioOutput, dbus::DbusConnector, input::HidWrapper}, }; @@ -53,11 +54,16 @@ pub struct AppState { pub xr_backend: XrBackend, + pub ipc_server: ipc_server::WayVRServer, + pub wayvr_signals: SyncEventQueue, + #[cfg(feature = "osc")] pub osc_sender: Option, + // wayland server + // TODO: rename to wayland_server? #[cfg(feature = "wayvr")] - pub wayvr: Option>>, // Dynamically created if requested + pub wayvr: Rc>, } #[allow(unused_mut)] @@ -71,18 +77,18 @@ impl AppState { let mut tasks = TaskContainer::new(); let session = AppSession::load(); + let wayvr_signals = SyncEventQueue::new(); #[cfg(feature = "wayvr")] - let wayvr = session - .wayvr_config - .post_load(&session.config, &mut tasks)?; + let wayvr = + session + .wayvr_config + .post_load(&session.config, &mut tasks, wayvr_signals.clone())?; let mut hid_provider = HidWrapper::new(); #[cfg(feature = "wayvr")] - if let Some(wayvr) = wayvr.as_ref() { - hid_provider.set_wayvr(wayvr.clone()); - } + hid_provider.set_wayvr(wayvr.clone()); #[cfg(feature = "osc")] let osc_sender = crate::subsystem::osc::OscSender::new(session.config.osc_out_port).ok(); @@ -114,6 +120,8 @@ impl AppState { let dbus = DbusConnector::default(); + let ipc_server = ipc_server::WayVRServer::new()?; + Ok(Self { session, tasks, @@ -135,6 +143,8 @@ impl AppState { )?, dbus, xr_backend, + ipc_server, + wayvr_signals, #[cfg(feature = "osc")] osc_sender, @@ -144,21 +154,6 @@ impl AppState { }) } - #[cfg(feature = "wayvr")] - #[allow(dead_code)] - pub fn get_wayvr(&mut self) -> anyhow::Result>> { - if let Some(wvr) = &self.wayvr { - Ok(wvr.clone()) - } else { - let wayvr = Rc::new(RefCell::new(WayVRData::new( - WayVRConfig::get_wayvr_config(&self.session.config, &self.session.wayvr_config)?, - )?)); - self.hid_provider.set_wayvr(wayvr.clone()); - self.wayvr = Some(wayvr.clone()); - Ok(wayvr) - } - } - pub fn try_load_bytes(path: &str, fallback_data: &'static [u8]) -> &'static [u8] { if path.is_empty() { return fallback_data; @@ -187,7 +182,7 @@ pub struct AppSession { pub config: GeneralConfig, #[cfg(feature = "wayvr")] - pub wayvr_config: WayVRConfig, + pub wayvr_config: WayVRConfig, // TODO: rename to "wayland_server_config" pub toast_topics: IdMap, }