From 7118cea810a34a0dd9489fe2ea07afe566c35a85 Mon Sep 17 00:00:00 2001 From: Aleksander Date: Wed, 10 Dec 2025 22:11:57 +0100 Subject: [PATCH] DashInterface, DashInterfaceEmulated --- Cargo.lock | 3 + Cargo.toml | 1 + dash-frontend/src/frontend.rs | 5 +- uidev/Cargo.toml | 1 + uidev/src/testbed/testbed_dashboard.rs | 3 + wlx-common/Cargo.toml | 2 + wlx-common/src/dash_interface.rs | 25 +++ wlx-common/src/dash_interface_emulated.rs | 173 +++++++++++++++++++++ wlx-common/src/handle.rs | 176 ++++++++++++++++++++++ wlx-common/src/lib.rs | 3 + wlx-overlay-s/Cargo.toml | 3 +- 11 files changed, 392 insertions(+), 3 deletions(-) create mode 100644 wlx-common/src/dash_interface.rs create mode 100644 wlx-common/src/dash_interface_emulated.rs create mode 100644 wlx-common/src/handle.rs diff --git a/Cargo.lock b/Cargo.lock index a2e6a7d..aa7f727 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5848,6 +5848,7 @@ dependencies = [ "vulkano-shaders", "wgui", "winit", + "wlx-common", ] [[package]] @@ -6841,11 +6842,13 @@ dependencies = [ name = "wlx-common" version = "0.1.0" dependencies = [ + "anyhow", "chrono", "glam", "idmap", "idmap-derive", "serde", + "wayvr_ipc", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 5c89cda..b1d75a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,3 +34,4 @@ vulkano = { version = "0.35.2", default-features = false, features = [ ] } vulkano-shaders = "0.35.0" wayland-client = { version = "0.31.11" } +wayvr_ipc = { git = "https://github.com/olekolek1000/wayvr-ipc.git", rev = "a72587d23f3bb8624d9aeb1f13c0a21e65350f51", default-features = false } diff --git a/dash-frontend/src/frontend.rs b/dash-frontend/src/frontend.rs index f1397c7..f2a1355 100644 --- a/dash-frontend/src/frontend.rs +++ b/dash-frontend/src/frontend.rs @@ -13,7 +13,7 @@ use wgui::{ widget::{label::WidgetLabel, rectangle::WidgetRectangle}, windowing::{WguiWindow, WguiWindowParams, WguiWindowParamsExtra, WguiWindowPlacement}, }; -use wlx_common::timestep::Timestep; +use wlx_common::{dash_interface, timestep::Timestep}; use crate::{ assets, settings, @@ -41,6 +41,7 @@ pub struct Frontend { globals: WguiGlobals, pub settings: Box, + pub interface: Box, #[allow(dead_code)] state: ParserState, @@ -62,6 +63,7 @@ pub struct Frontend { pub struct InitParams { pub settings: Box, + pub interface: Box, } pub type RcFrontend = Rc>; @@ -139,6 +141,7 @@ impl Frontend { }, timestep, settings: params.settings, + interface: params.interface, popup_manager, toast_manager, window_audio_settings: WguiWindow::default(), diff --git a/uidev/Cargo.toml b/uidev/Cargo.toml index b63d260..8d06993 100644 --- a/uidev/Cargo.toml +++ b/uidev/Cargo.toml @@ -15,3 +15,4 @@ winit = "0.30.12" vulkano = { workspace = true } vulkano-shaders = { workspace = true } dash-frontend = { path = "../dash-frontend/" } +wlx-common = { path = "../wlx-common" } diff --git a/uidev/src/testbed/testbed_dashboard.rs b/uidev/src/testbed/testbed_dashboard.rs index 3f8d7a4..daf7657 100644 --- a/uidev/src/testbed/testbed_dashboard.rs +++ b/uidev/src/testbed/testbed_dashboard.rs @@ -4,6 +4,7 @@ use dash_frontend::{ settings::{self, SettingsIO}, }; use wgui::layout::RcLayout; +use wlx_common::dash_interface_emulated::DashInterfaceEmulated; struct SimpleSettingsIO { settings: settings::Settings, @@ -59,9 +60,11 @@ pub struct TestbedDashboard { impl TestbedDashboard { pub fn new() -> anyhow::Result { let settings = SimpleSettingsIO::new(); + let interface = DashInterfaceEmulated::new(); let (frontend, layout) = frontend::Frontend::new(frontend::InitParams { settings: Box::new(settings), + interface: Box::new(interface), })?; Ok(Self { frontend, layout }) } diff --git a/wlx-common/Cargo.toml b/wlx-common/Cargo.toml index 88768e5..467f39f 100644 --- a/wlx-common/Cargo.toml +++ b/wlx-common/Cargo.toml @@ -10,3 +10,5 @@ glam = { workspace = true } chrono = "0.4.42" idmap = { workspace = true, features = ["serde"] } idmap-derive = { workspace = true } +wayvr_ipc = { workspace = true } +anyhow = { workspace = true } diff --git a/wlx-common/src/dash_interface.rs b/wlx-common/src/dash_interface.rs new file mode 100644 index 0000000..1cd79c3 --- /dev/null +++ b/wlx-common/src/dash_interface.rs @@ -0,0 +1,25 @@ +use wayvr_ipc::{ + packet_client::{WvrDisplayCreateParams, WvrProcessLaunchParams}, + packet_server::{ + WvrDisplay, WvrDisplayHandle, WvrDisplayWindowLayout, WvrProcess, WvrProcessHandle, WvrWindow, WvrWindowHandle, + }, +}; + +pub trait DashInterface { + fn display_create(&mut self, params: WvrDisplayCreateParams) -> anyhow::Result; + fn display_get(&mut self, handle: WvrDisplayHandle) -> Option; + fn display_list(&mut self) -> anyhow::Result>; + fn display_remove(&mut self, handle: WvrDisplayHandle) -> anyhow::Result<()>; + fn display_set_visible(&mut self, handle: WvrDisplayHandle, visible: bool) -> anyhow::Result<()>; + fn display_set_window_layout( + &mut self, + handle: WvrDisplayHandle, + layout: WvrDisplayWindowLayout, + ) -> anyhow::Result<()>; + fn display_window_list(&mut self, handle: WvrDisplayHandle) -> anyhow::Result>; + fn process_get(&mut self, handle: WvrProcessHandle) -> Option; + fn process_launch(&mut self, params: WvrProcessLaunchParams) -> anyhow::Result; + fn process_list(&mut self) -> anyhow::Result>; + fn process_terminate(&mut self, handle: WvrProcessHandle) -> anyhow::Result<()>; + fn window_set_visible(&mut self, handle: WvrWindowHandle, visible: bool) -> anyhow::Result<()>; +} diff --git a/wlx-common/src/dash_interface_emulated.rs b/wlx-common/src/dash_interface_emulated.rs new file mode 100644 index 0000000..c1106f5 --- /dev/null +++ b/wlx-common/src/dash_interface_emulated.rs @@ -0,0 +1,173 @@ +use wayvr_ipc::{ + packet_client::{WvrDisplayCreateParams, WvrProcessLaunchParams}, + packet_server::{ + WvrDisplay, WvrDisplayHandle, WvrDisplayWindowLayout, WvrProcess, WvrProcessHandle, WvrWindow, WvrWindowHandle, + }, +}; + +use crate::{dash_interface::DashInterface, gen_id}; + +#[derive(Debug)] +pub struct EmuProcess { + display_handle: WvrDisplayHandle, + name: String, +} + +impl EmuProcess { + fn to(&self, handle: EmuProcessHandle) -> WvrProcess { + WvrProcess { + display_handle: self.display_handle.clone(), + handle: WvrProcessHandle { + generation: handle.generation, + idx: handle.idx, + }, + name: self.name.clone(), + userdata: Default::default(), + } + } +} + +#[derive(Debug)] +pub struct EmuDisplay { + width: u16, + height: u16, + name: String, + visible: bool, + layout: WvrDisplayWindowLayout, +} + +impl EmuDisplay { + fn to(&self, handle: EmuDisplayHandle) -> WvrDisplay { + WvrDisplay { + width: self.width, + height: self.height, + name: self.name.clone(), + visible: self.visible, + handle: WvrDisplayHandle { + generation: handle.generation, + idx: handle.idx, + }, + } + } +} + +gen_id!(EmuDisplayVec, EmuDisplay, EmuDisplayCell, EmuDisplayHandle); +gen_id!(EmuProcessVec, EmuProcess, EmuProcessCell, EmuProcessHandle); + +pub struct DashInterfaceEmulated { + displays: EmuDisplayVec, + processes: EmuProcessVec, +} + +impl DashInterfaceEmulated { + pub fn new() -> Self { + Self { + displays: EmuDisplayVec::new(), + processes: EmuProcessVec::new(), + } + } +} + +impl DashInterface for DashInterfaceEmulated { + fn display_create(&mut self, params: WvrDisplayCreateParams) -> anyhow::Result { + let res = self.displays.add(EmuDisplay { + width: params.width, + height: params.height, + name: params.name, + visible: true, + layout: WvrDisplayWindowLayout::Tiling, + }); + + Ok(WvrDisplayHandle { + generation: res.generation, + idx: res.idx, + }) + } + + fn display_get(&mut self, handle: WvrDisplayHandle) -> Option { + let emu_handle = EmuDisplayHandle::new(handle.idx, handle.generation); + self.displays.get(&emu_handle).map(|disp| disp.to(emu_handle)) + } + + fn display_list(&mut self) -> anyhow::Result> { + Ok(self.displays.iter().map(|(handle, disp)| disp.to(handle)).collect()) + } + + fn display_remove(&mut self, handle: WvrDisplayHandle) -> anyhow::Result<()> { + self + .displays + .remove(&EmuDisplayHandle::new(handle.idx, handle.generation)); + Ok(()) + } + + fn display_set_visible(&mut self, handle: WvrDisplayHandle, visible: bool) -> anyhow::Result<()> { + let Some(disp) = self + .displays + .get_mut(&EmuDisplayHandle::new(handle.idx, handle.generation)) + else { + anyhow::bail!("Display not found"); + }; + + disp.visible = visible; + Ok(()) + } + + fn display_set_window_layout( + &mut self, + handle: WvrDisplayHandle, + layout: WvrDisplayWindowLayout, + ) -> anyhow::Result<()> { + let Some(disp) = self + .displays + .get_mut(&EmuDisplayHandle::new(handle.idx, handle.generation)) + else { + anyhow::bail!("Display not found"); + }; + + disp.layout = layout; + Ok(()) + } + + fn display_window_list(&mut self, _handle: WvrDisplayHandle) -> anyhow::Result> { + // stub! + Ok(Vec::new()) + } + + fn process_get(&mut self, handle: WvrProcessHandle) -> Option { + let emu_handle = EmuProcessHandle::new(handle.idx, handle.generation); + self.processes.get(&emu_handle).map(|process| process.to(emu_handle)) + } + + fn process_launch(&mut self, params: WvrProcessLaunchParams) -> anyhow::Result { + let res = self.processes.add(EmuProcess { + display_handle: params.target_display, + name: params.name, + }); + Ok(WvrProcessHandle { + generation: res.generation, + idx: res.idx, + }) + } + + fn process_list(&mut self) -> anyhow::Result> { + Ok( + self + .processes + .iter() + .map(|(handle, process)| process.to(handle)) + .collect(), + ) + } + + fn process_terminate(&mut self, handle: WvrProcessHandle) -> anyhow::Result<()> { + self + .processes + .remove(&EmuProcessHandle::new(handle.idx, handle.generation)); + Ok(()) + } + + fn window_set_visible(&mut self, _handle: WvrWindowHandle, _visible: bool) -> anyhow::Result<()> { + // stub! + Ok(()) + } +} diff --git a/wlx-common/src/handle.rs b/wlx-common/src/handle.rs new file mode 100644 index 0000000..0eea013 --- /dev/null +++ b/wlx-common/src/handle.rs @@ -0,0 +1,176 @@ +#[macro_export] +macro_rules! gen_id { + ( + $container_name:ident, + $instance_name:ident, + $cell_name:ident, + $handle_name:ident) => { + //ThingCell + #[derive(Debug)] + pub struct $cell_name { + pub obj: $instance_name, + pub generation: u64, + } + + //ThingVec + #[derive(Debug)] + pub struct $container_name { + // Vec> + pub vec: Vec>, + + cur_generation: u64, + } + + //ThingHandle + #[derive(Default, Debug, Clone, Copy, PartialEq, Hash, Eq)] + pub struct $handle_name { + idx: u32, + generation: u64, + } + + #[allow(dead_code)] + impl $handle_name { + pub const fn reset(&mut self) { + self.generation = 0; + } + + pub const fn is_set(&self) -> bool { + self.generation > 0 + } + + pub const fn id(&self) -> u32 { + self.idx + } + + pub const fn new(idx: u32, generation: u64) -> Self { + Self { idx, generation } + } + } + + //ThingVec + impl $container_name { + pub const fn new() -> Self { + Self { + vec: Vec::new(), + cur_generation: 0, + } + } + + pub fn iter(&self) -> impl Iterator { + self.vec.iter().enumerate().filter_map(|(idx, opt_cell)| { + opt_cell.as_ref().map(|cell| { + let handle = $container_name::get_handle(&cell, idx); + (handle, &cell.obj) + }) + }) + } + + pub fn iter_mut( + &mut self, + ) -> impl Iterator { + self.vec + .iter_mut() + .enumerate() + .filter_map(|(idx, opt_cell)| { + opt_cell.as_mut().map(|cell| { + let handle = $container_name::get_handle(&cell, idx); + (handle, &mut cell.obj) + }) + }) + } + + pub const fn get_handle(cell: &$cell_name, idx: usize) -> $handle_name { + $handle_name { + idx: idx as u32, + generation: cell.generation, + } + } + + fn find_unused_idx(&mut self) -> Option { + for (num, obj) in self.vec.iter().enumerate() { + if obj.is_none() { + return Some(num as u32); + } + } + None + } + + pub fn add(&mut self, obj: $instance_name) -> $handle_name { + self.cur_generation += 1; + let generation = self.cur_generation; + + let unused_idx = self.find_unused_idx(); + + let idx = if let Some(idx) = unused_idx { + idx + } else { + self.vec.len() as u32 + }; + + let handle = $handle_name { idx, generation }; + + let cell = $cell_name { obj, generation }; + + if let Some(idx) = unused_idx { + self.vec[idx as usize] = Some(cell); + } else { + self.vec.push(Some(cell)) + } + + handle + } + + pub fn remove(&mut self, handle: &$handle_name) { + // Out of bounds, ignore + if handle.idx as usize >= self.vec.len() { + return; + } + + // Remove only if the generation matches + if let Some(cell) = &self.vec[handle.idx as usize] { + if cell.generation == handle.generation { + self.vec[handle.idx as usize] = None; + } + } + } + + pub fn get(&self, handle: &$handle_name) -> Option<&$instance_name> { + // Out of bounds, ignore + if handle.idx as usize >= self.vec.len() { + return None; + } + + if let Some(cell) = &self.vec[handle.idx as usize] { + if cell.generation == handle.generation { + return Some(&cell.obj); + } + } + + None + } + + pub fn get_mut(&mut self, handle: &$handle_name) -> Option<&mut $instance_name> { + // Out of bounds, ignore + if handle.idx as usize >= self.vec.len() { + return None; + } + + if let Some(cell) = &mut self.vec[handle.idx as usize] { + if cell.generation == handle.generation { + return Some(&mut cell.obj); + } + } + + None + } + } + }; +} + +/* Example usage: +gen_id!(ThingVec, ThingInstance, ThingCell, ThingHandle); + +struct ThingInstance {} + +impl ThingInstance {} + */ diff --git a/wlx-common/src/lib.rs b/wlx-common/src/lib.rs index 369c20a..db5ea04 100644 --- a/wlx-common/src/lib.rs +++ b/wlx-common/src/lib.rs @@ -1,6 +1,9 @@ pub mod astr_containers; pub mod common; pub mod config; +pub mod dash_interface; +pub mod dash_interface_emulated; +mod handle; pub mod overlays; pub mod timestep; pub mod windowing; diff --git a/wlx-overlay-s/Cargo.toml b/wlx-overlay-s/Cargo.toml index 0e41aaf..fe02f5b 100644 --- a/wlx-overlay-s/Cargo.toml +++ b/wlx-overlay-s/Cargo.toml @@ -80,6 +80,7 @@ tracing = "0.1.43" vulkano = { workspace = true } vulkano-shaders = { workspace = true } wgui = { path = "../wgui" } +wayvr_ipc = { workspace = true } ################################ #WayVR-only deps @@ -96,7 +97,6 @@ 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 = { git = "https://github.com/olekolek1000/wayvr-ipc.git", rev = "a72587d23f3bb8624d9aeb1f13c0a21e65350f51", default-features = false, optional = true } rust-embed = { workspace = true } signal-hook = "0.3.18" ################################ @@ -121,6 +121,5 @@ wayvr = [ "dep:wayland-client", "dep:wayland-egl", "dep:bytes", - "dep:wayvr_ipc", ] as-raw-xcb-connection = []