diff --git a/wayvr-ipc/src/client.rs b/wayvr-ipc/src/client.rs index b742ef49..500973a6 100644 --- a/wayvr-ipc/src/client.rs +++ b/wayvr-ipc/src/client.rs @@ -315,93 +315,16 @@ impl WayVRClient { } } - pub async fn fn_wvr_display_list( + pub async fn fn_wvr_window_list( client: WayVRClientMutex, serial: Serial, - ) -> anyhow::Result> { - Ok( - send_and_wait!( - client, - serial, - &PacketClient::WvrDisplayList(serial), - WvrDisplayListResponse - ) - .list, - ) - } - - pub async fn fn_wvr_display_get( - client: WayVRClientMutex, - serial: Serial, - handle: packet_server::WvrDisplayHandle, - ) -> anyhow::Result> { - Ok(send_and_wait!( - client, - serial, - &PacketClient::WvrDisplayGet(serial, handle), - WvrDisplayGetResponse - )) - } - - pub async fn fn_wvr_display_remove( - client: WayVRClientMutex, - serial: Serial, - handle: packet_server::WvrDisplayHandle, - ) -> anyhow::Result<()> { - send_and_wait!( - client, - serial, - &PacketClient::WvrDisplayRemove(serial, handle), - WvrDisplayRemoveResponse - ) - .map_err(|e| anyhow::anyhow!("{}", e)) - } - - pub async fn fn_wvr_display_create( - client: WayVRClientMutex, - serial: Serial, - params: packet_client::WvrDisplayCreateParams, - ) -> anyhow::Result { - Ok(send_and_wait!( - client, - serial, - &PacketClient::WvrDisplayCreate(serial, params), - WvrDisplayCreateResponse - )) - } - - pub async fn fn_wvr_display_set_visible( - client: WayVRClientMutex, - handle: packet_server::WvrDisplayHandle, - visible: bool, - ) -> anyhow::Result<()> { - send_only!(client, &PacketClient::WvrDisplaySetVisible(handle, visible)); - Ok(()) - } - - pub async fn fn_wvr_display_set_layout( - client: WayVRClientMutex, - handle: packet_server::WvrDisplayHandle, - layout: packet_server::WvrDisplayWindowLayout, - ) -> anyhow::Result<()> { - send_only!( - client, - &PacketClient::WvrDisplaySetWindowLayout(handle, layout) - ); - Ok(()) - } - - pub async fn fn_wvr_display_window_list( - client: WayVRClientMutex, - serial: Serial, - handle: packet_server::WvrDisplayHandle, ) -> anyhow::Result>> { Ok( send_and_wait!( client, serial, - &PacketClient::WvrDisplayWindowList(serial, handle), - WvrDisplayWindowListResponse + &PacketClient::WvrWindowList(serial), + WvrWindowListResponse ) .map(|res| res.list), ) @@ -478,14 +401,6 @@ impl WayVRClient { )) } - pub async fn fn_wlx_haptics( - client: WayVRClientMutex, - params: packet_client::WlxHapticsParams, - ) -> anyhow::Result<()> { - send_only!(client, &PacketClient::WlxHaptics(params)); - Ok(()) - } - pub async fn fn_wlx_device_haptics( client: WayVRClientMutex, device: usize, diff --git a/wayvr-ipc/src/packet_client.rs b/wayvr-ipc/src/packet_client.rs index 3cb046b7..e1482f08 100644 --- a/wayvr-ipc/src/packet_client.rs +++ b/wayvr-ipc/src/packet_client.rs @@ -67,22 +67,12 @@ pub struct WlxModifyPanelParams { #[derive(Debug, Serialize, Deserialize)] pub enum PacketClient { Handshake(Handshake), - WvrDisplayCreate(Serial, WvrDisplayCreateParams), - WvrDisplayGet(Serial, packet_server::WvrDisplayHandle), - WvrDisplayList(Serial), - WvrDisplayRemove(Serial, packet_server::WvrDisplayHandle), - WvrDisplaySetVisible(packet_server::WvrDisplayHandle, bool), - WvrDisplayWindowList(Serial, packet_server::WvrDisplayHandle), - WvrDisplaySetWindowLayout( - packet_server::WvrDisplayHandle, - packet_server::WvrDisplayWindowLayout, - ), + WvrWindowList(Serial), WvrWindowSetVisible(packet_server::WvrWindowHandle, bool), WvrProcessGet(Serial, packet_server::WvrProcessHandle), WvrProcessLaunch(Serial, WvrProcessLaunchParams), WvrProcessList(Serial), WvrProcessTerminate(packet_server::WvrProcessHandle), - WlxHaptics(WlxHapticsParams), WlxInputState(Serial), WlxModifyPanel(WlxModifyPanelParams), WlxDeviceHaptics(usize, WlxHapticsParams), diff --git a/wayvr-ipc/src/packet_server.rs b/wayvr-ipc/src/packet_server.rs index f25ae8ed..f26fbc6b 100644 --- a/wayvr-ipc/src/packet_server.rs +++ b/wayvr-ipc/src/packet_server.rs @@ -48,14 +48,11 @@ pub struct WvrDisplay { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WvrWindow { - pub pos_x: i32, - pub pos_y: i32, pub size_x: u32, pub size_y: u32, pub visible: bool, pub handle: WvrWindowHandle, pub process_handle: WvrProcessHandle, - pub display_handle: WvrDisplayHandle, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -71,7 +68,6 @@ pub struct WvrWindowList { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WvrProcess { pub name: String, - pub display_handle: WvrDisplayHandle, pub handle: WvrProcessHandle, pub userdata: HashMap, } @@ -103,8 +99,6 @@ pub enum WvrDisplayWindowLayout { #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] pub enum WvrStateChanged { - DisplayCreated, - DisplayRemoved, ProcessCreated, ProcessRemoved, WindowCreated, @@ -132,11 +126,7 @@ pub enum PacketServer { Disconnect(Disconnect), HandshakeSuccess(HandshakeSuccess), WlxInputStateResponse(Serial, WlxInputState), - WvrDisplayCreateResponse(Serial, WvrDisplayHandle), - WvrDisplayGetResponse(Serial, Option), - WvrDisplayListResponse(Serial, WvrDisplayList), - WvrDisplayRemoveResponse(Serial, Result<(), String>), - WvrDisplayWindowListResponse(Serial, Option), + WvrWindowListResponse(Serial, Option), WvrProcessGetResponse(Serial, Option), WvrProcessLaunchResponse(Serial, Result), WvrProcessListResponse(Serial, WvrProcessList), @@ -149,11 +139,7 @@ impl PacketServer { PacketServer::Disconnect(_) => None, PacketServer::HandshakeSuccess(_) => None, PacketServer::WlxInputStateResponse(serial, _) => Some(serial), - PacketServer::WvrDisplayCreateResponse(serial, _) => Some(serial), - PacketServer::WvrDisplayGetResponse(serial, _) => Some(serial), - PacketServer::WvrDisplayListResponse(serial, _) => Some(serial), - PacketServer::WvrDisplayRemoveResponse(serial, _) => Some(serial), - PacketServer::WvrDisplayWindowListResponse(serial, _) => Some(serial), + PacketServer::WvrWindowListResponse(serial, _) => Some(serial), PacketServer::WvrProcessGetResponse(serial, _) => Some(serial), PacketServer::WvrProcessLaunchResponse(serial, _) => Some(serial), PacketServer::WvrProcessListResponse(serial, _) => Some(serial), diff --git a/wayvrctl/src/helper.rs b/wayvrctl/src/helper.rs index 7ab0d170..38203179 100644 --- a/wayvrctl/src/helper.rs +++ b/wayvrctl/src/helper.rs @@ -37,104 +37,18 @@ fn handle_result(pretty_print: bool, result: anyhow::Result) { } } -pub async fn wvr_display_create( - state: &mut WayVRClientState, - width: u16, - height: u16, - name: String, - scale: Option, - attach_to: packet_client::AttachTo, -) { +pub async fn wvr_window_list(state: &mut WayVRClientState) { handle_result( state.pretty_print, - WayVRClient::fn_wvr_display_create( + WayVRClient::fn_wvr_window_list( state.wayvr_client.clone(), state.serial_generator.increment_get(), - packet_client::WvrDisplayCreateParams { - width, - height, - name, - scale, - attach_to, - }, - ) - .await - .context("failed to create display"), - ); -} - -pub async fn wvr_display_list(state: &mut WayVRClientState) { - handle_result( - state.pretty_print, - WayVRClient::fn_wvr_display_list( - state.wayvr_client.clone(), - state.serial_generator.increment_get(), - ) - .await - .context("failed to fetch displays"), - ); -} - -pub async fn wvr_display_get( - state: &mut WayVRClientState, - handle: packet_server::WvrDisplayHandle, -) { - handle_result( - state.pretty_print, - WayVRClient::fn_wvr_display_get( - state.wayvr_client.clone(), - state.serial_generator.increment_get(), - handle, - ) - .await - .context("failed to fetch display"), - ); -} - -pub async fn wvr_display_window_list( - state: &mut WayVRClientState, - handle: packet_server::WvrDisplayHandle, -) { - handle_result( - state.pretty_print, - WayVRClient::fn_wvr_display_window_list( - state.wayvr_client.clone(), - state.serial_generator.increment_get(), - handle, ) .await .context("failed to list window displays"), ); } -pub async fn wvr_display_remove( - state: &mut WayVRClientState, - handle: packet_server::WvrDisplayHandle, -) { - handle_result( - state.pretty_print, - WayVRClient::fn_wvr_display_remove( - state.wayvr_client.clone(), - state.serial_generator.increment_get(), - handle, - ) - .await - .context("failed to remove display"), - ); -} - -pub async fn wvr_display_set_visible( - state: &mut WayVRClientState, - handle: packet_server::WvrDisplayHandle, - visible: bool, -) { - handle_empty_result( - WayVRClient::fn_wvr_display_set_visible(state.wayvr_client.clone(), handle, visible) - .await - .context("failed to set display visibility"), - ) -} - pub async fn wvr_window_set_visible( state: &mut WayVRClientState, handle: packet_server::WvrWindowHandle, diff --git a/wayvrctl/src/main.rs b/wayvrctl/src/main.rs index 1676bd4e..06e2ab43 100644 --- a/wayvrctl/src/main.rs +++ b/wayvrctl/src/main.rs @@ -10,10 +10,9 @@ use env_logger::Env; use wayvr_ipc::{client::WayVRClient, ipc, packet_client}; use crate::helper::{ - WayVRClientState, wlx_device_haptics, wlx_input_state, wlx_panel_modify, wvr_display_create, - wvr_display_get, wvr_display_list, wvr_display_remove, wvr_display_set_visible, - wvr_display_window_list, wvr_process_get, wvr_process_launch, wvr_process_list, - wvr_process_terminate, wvr_window_set_visible, + WayVRClientState, wlx_device_haptics, wlx_input_state, wlx_panel_modify, wvr_process_get, + wvr_process_launch, wvr_process_list, wvr_process_terminate, wvr_window_list, + wvr_window_set_visible, }; mod helper; @@ -76,12 +75,12 @@ async fn run_batch(state: &mut WayVRClientState, fail_fast: bool) -> anyhow::Res } async fn parse_run_line(state: &mut WayVRClientState, line: &str) -> anyhow::Result<()> { - let mut argv = shell_words::split(&line).with_context(|| format!("parse error"))?; + let mut argv = shell_words::split(line).context("parse error")?; // clap expects argv[0] to be the binary name argv.insert(0, env!("CARGO_PKG_NAME").to_string()); - let args = Args::try_parse_from(argv).with_context(|| format!("invalid arguments"))?; + let args = Args::try_parse_from(argv).context("invalid arguments")?; run_once(state, args).await?; Ok(()) @@ -95,43 +94,8 @@ async fn run_once(state: &mut WayVRClientState, args: Args) -> anyhow::Result<() Subcommands::InputState => { wlx_input_state(state).await; } - Subcommands::DisplayCreate { - width, - height, - name, - scale, - } => { - wvr_display_create( - state, - width, - height, - name, - scale, - packet_client::AttachTo::None, - ) - .await; - } - Subcommands::DisplayList => { - wvr_display_list(state).await; - } - Subcommands::DisplayGet { handle } => { - let handle = serde_json::from_str(&handle).context("Invalid handle")?; - wvr_display_get(state, handle).await; - } - Subcommands::DisplayWindowList { handle } => { - let handle = serde_json::from_str(&handle).context("Invalid handle")?; - wvr_display_window_list(state, handle).await; - } - Subcommands::DisplayRemove { handle } => { - let handle = serde_json::from_str(&handle).context("Invalid handle")?; - wvr_display_remove(state, handle).await; - } - Subcommands::DisplaySetVisible { - handle, - visible_0_or_1, - } => { - let handle = serde_json::from_str(&handle).context("Invalid handle")?; - wvr_display_set_visible(state, handle, visible_0_or_1 != 0).await; + Subcommands::WindowList => { + wvr_window_list(state).await; } Subcommands::WindowSetVisible { handle, @@ -221,39 +185,9 @@ enum Subcommands { }, /// Get the positions of HMD & controllers InputState, - /// Create a new WayVR display - DisplayCreate { - width: u16, - height: u16, - name: String, - #[arg(short, long)] - scale: Option, - //attach_to: packet_client::AttachTo, - }, - /// List WayVR displays - DisplayList, - /// Retrieve information about a single WayVR display - DisplayGet { - /// A display handle JSON returned by DisplayList or DisplayCreate - handle: String, - }, - /// List windows attached to a WayVR display - DisplayWindowList { - /// A display handle JSON returned by DisplayList or DisplayCreate - handle: String, - }, + /// List WayVR windows + WindowList, /// Delete a WayVR display - DisplayRemove { - /// A display handle JSON returned by DisplayList or DisplayCreate - handle: String, - }, - /// Change the visibility of a WayVR display - DisplaySetVisible { - /// A display handle JSON returned by DisplayList or DisplayCreate - handle: String, - visible_0_or_1: u8, - }, - // DisplaySetLayout skipped /// Change the visibility of a window on a WayVR display WindowSetVisible { diff --git a/wlx-capture/src/frame.rs b/wlx-capture/src/frame.rs index 7dfe5bd0..37470edf 100644 --- a/wlx-capture/src/frame.rs +++ b/wlx-capture/src/frame.rs @@ -1,47 +1,6 @@ -use std::{fmt::Display, os::fd::RawFd}; +use std::os::fd::RawFd; -#[derive(Debug, Clone, Copy, Default)] -pub struct FourCC { - pub value: u32, -} - -impl PartialEq for FourCC { - fn eq(&self, other: &Self) -> bool { - self.value == other.value - } -} - -impl From for FourCC { - fn from(value: u32) -> Self { - Self { value } - } -} - -impl From for u32 { - fn from(fourcc: FourCC) -> Self { - fourcc.value - } -} - -impl Display for FourCC { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for i in 0..4 { - if let Some(c) = char::from_u32((self.value >> (i * 8)) & 0xFF) { - write!(f, "{c}")? - } else { - write!(f, "?")? - } - } - Ok(()) - } -} - -pub const DRM_FORMAT_ARGB8888: u32 = 0x34325241; // AR24 -pub const DRM_FORMAT_ABGR8888: u32 = 0x34324241; // AB24 -pub const DRM_FORMAT_XRGB8888: u32 = 0x34325258; // XR24 -pub const DRM_FORMAT_XBGR8888: u32 = 0x34324258; // XB24 -pub const DRM_FORMAT_ABGR2101010: u32 = 0x30334241; // AB30 -pub const DRM_FORMAT_XBGR2101010: u32 = 0x30334258; // XB30 +use drm_fourcc::{DrmFormat, DrmModifier}; #[cfg(feature = "egl")] #[rustfmt::skip] @@ -73,24 +32,27 @@ pub enum Transform { Flipped270, } -#[derive(Debug, Clone, Copy, Default)] +#[derive(Debug, Clone, Copy)] pub struct FrameFormat { pub width: u32, pub height: u32, - pub fourcc: FourCC, - pub modifier: u64, + pub drm_format: DrmFormat, pub transform: Transform, } impl FrameFormat { + #[must_use] pub fn get_mod_hi(&self) -> u32 { - (self.modifier >> 32) as _ + let m = u64::from(self.drm_format.modifier); + (m >> 32) as _ } + #[must_use] pub fn get_mod_lo(&self) -> u32 { - (self.modifier & 0xFFFFFFFF) as _ + let m = u64::from(self.drm_format.modifier); + (m & 0xFFFFFFFF) as _ } pub fn set_mod(&mut self, mod_hi: u32, mod_low: u32) { - self.modifier = ((mod_hi as u64) << 32) + mod_low as u64; + self.drm_format.modifier = DrmModifier::from(((mod_hi as u64) << 32) + mod_low as u64); } } @@ -101,13 +63,6 @@ pub struct FramePlane { pub stride: i32, } -#[derive(Default, Clone)] -pub struct DrmFormat { - pub fourcc: FourCC, - pub modifiers: Vec, -} - -#[derive(Default)] pub struct DmabufFrame { pub format: FrameFormat, pub num_planes: usize, @@ -126,7 +81,7 @@ impl DmabufFrame { 0x3056, // HEIGHT self.format.height as _, 0x3271, // LINUX_DRM_FOURCC_EXT, - self.format.fourcc.value as _, + self.format.drm_format.code as _, ]; for i in 0..self.num_planes { @@ -162,14 +117,12 @@ impl DmabufFrame { } } -#[derive(Default)] pub struct MemFdFrame { pub format: FrameFormat, pub plane: FramePlane, pub mouse: Option, } -#[derive(Default)] pub struct MemPtrFrame { pub format: FrameFormat, pub ptr: usize, diff --git a/wlx-capture/src/lib.rs b/wlx-capture/src/lib.rs index 26c8162d..edbeaa84 100644 --- a/wlx-capture/src/lib.rs +++ b/wlx-capture/src/lib.rs @@ -1,7 +1,8 @@ #![allow(dead_code)] #![allow(clippy::expect_fun_call)] -use frame::{DrmFormat, WlxFrame}; +pub use drm_fourcc::{DrmFormat, DrmFourcc, DrmModifier}; +use frame::WlxFrame; pub mod frame; diff --git a/wlx-capture/src/pipewire.rs b/wlx-capture/src/pipewire.rs index 1358411d..840e0df3 100644 --- a/wlx-capture/src/pipewire.rs +++ b/wlx-capture/src/pipewire.rs @@ -1,4 +1,5 @@ use std::any::Any; +use std::collections::HashMap; use std::sync::Arc; use std::sync::atomic::AtomicU32; use std::sync::atomic::Ordering; @@ -12,6 +13,9 @@ use ashpd::desktop::{ pub use ashpd::Error as AshpdError; +use drm_fourcc::DrmFormat; +use drm_fourcc::DrmFourcc; +use drm_fourcc::DrmModifier; use pipewire as pw; use pw::spa; @@ -33,14 +37,6 @@ use spa::utils::ChoiceEnum; use spa::utils::ChoiceFlags; use crate::WlxCapture; -use crate::frame::DRM_FORMAT_ABGR8888; -use crate::frame::DRM_FORMAT_ABGR2101010; -use crate::frame::DRM_FORMAT_ARGB8888; -use crate::frame::DRM_FORMAT_XBGR8888; -use crate::frame::DRM_FORMAT_XBGR2101010; -use crate::frame::DRM_FORMAT_XRGB8888; -use crate::frame::DrmFormat; -use crate::frame::FourCC; use crate::frame::FrameFormat; use crate::frame::MouseMeta; use crate::frame::Transform; @@ -297,7 +293,15 @@ where )?; let _listener = stream - .add_local_listener_with_user_data(FrameFormat::default()) + .add_local_listener_with_user_data(FrameFormat { + width: 0, + height: 0, + drm_format: DrmFormat { + code: DrmFourcc::Argb8888, + modifier: DrmModifier::Invalid, + }, + transform: Transform::Undefined, + }) .state_changed({ let name = name.clone(); move |_, _, old, new| { @@ -320,10 +324,10 @@ where format.width = info.size().width; format.height = info.size().height; - format.fourcc = spa_to_fourcc(info.format()); - format.modifier = info.modifier(); + format.drm_format.code = spa_to_fourcc(info.format()); + format.drm_format.modifier = DrmModifier::from(info.modifier()); - let kind = if format.modifier != 0 { + let kind = if info.modifier() != 0 { "DMA-buf" } else { "SHM" @@ -425,7 +429,7 @@ where format: *format, num_planes: planes.len(), mouse: mouse_meta, - ..Default::default() + planes: Default::default(), }; dmabuf.planes[..planes.len()].copy_from_slice(&planes[..planes.len()]); @@ -494,7 +498,17 @@ where }) .register()?; - let mut format_params: Vec> = dmabuf_formats + let mut fourcc_mods: HashMap> = HashMap::new(); + + for f in dmabuf_formats.iter() { + if let Some(v) = fourcc_mods.get_mut(&f.code) { + v.push(f.modifier); + } else { + fourcc_mods.insert(f.code, vec![f.modifier]); + } + } + + let mut format_params: Vec> = fourcc_mods .iter() .filter_map(|f| obj_to_bytes(get_format_params(Some(f))).ok()) .collect(); @@ -591,7 +605,7 @@ fn get_meta_object(key: u32, size: usize) -> Object { ) } -fn get_format_params(fmt: Option<&DrmFormat>) -> Object { +fn get_format_params(fmt: Option<(&DrmFourcc, &Vec)>) -> Object { let mut obj = spa::pod::object!( spa::utils::SpaTypes::ObjectParamFormat, spa::param::ParamType::EnumFormat, @@ -638,7 +652,7 @@ fn get_format_params(fmt: Option<&DrmFormat>) -> Object { ); if let Some(fmt) = fmt { - let spa_fmt = fourcc_to_spa(fmt.fourcc); + let spa_fmt = fourcc_to_spa(*fmt.0); let prop = spa::pod::property!( spa::param::format::FormatProperties::VideoFormat, @@ -657,8 +671,8 @@ fn get_format_params(fmt: Option<&DrmFormat>) -> Object { value: Value::Choice(ChoiceValue::Long(Choice( ChoiceFlags::empty(), ChoiceEnum::Enum { - default: fmt.modifiers[0] as _, - alternatives: fmt.modifiers.iter().map(|m| *m as _).collect(), + default: u64::from(fmt.1[0]) as _, + alternatives: fmt.1.iter().map(|m| u64::from(*m) as _).collect(), }, ))), }; @@ -683,27 +697,27 @@ fn get_format_params(fmt: Option<&DrmFormat>) -> Object { obj } -fn fourcc_to_spa(fourcc: FourCC) -> VideoFormat { - match fourcc.value { - DRM_FORMAT_ARGB8888 => VideoFormat::BGRA, - DRM_FORMAT_ABGR8888 => VideoFormat::RGBA, - DRM_FORMAT_XRGB8888 => VideoFormat::BGRx, - DRM_FORMAT_XBGR8888 => VideoFormat::RGBx, - DRM_FORMAT_ABGR2101010 => VideoFormat::ABGR_210LE, - DRM_FORMAT_XBGR2101010 => VideoFormat::xBGR_210LE, +fn fourcc_to_spa(fourcc: DrmFourcc) -> VideoFormat { + match fourcc{ + DrmFourcc::Argb8888 => VideoFormat::BGRA, + DrmFourcc::Abgr8888 => VideoFormat::RGBA, + DrmFourcc::Xrgb8888 => VideoFormat::BGRx, + DrmFourcc::Xbgr8888 => VideoFormat::RGBx, + DrmFourcc::Abgr2101010 => VideoFormat::ABGR_210LE, + DrmFourcc::Xbgr2101010 => VideoFormat::xBGR_210LE, _ => panic!("Unsupported format"), } } #[allow(non_upper_case_globals)] -fn spa_to_fourcc(spa: VideoFormat) -> FourCC { +fn spa_to_fourcc(spa: VideoFormat) -> DrmFourcc { match spa { - VideoFormat::BGRA => DRM_FORMAT_ARGB8888.into(), - VideoFormat::RGBA => DRM_FORMAT_ABGR8888.into(), - VideoFormat::BGRx => DRM_FORMAT_XRGB8888.into(), - VideoFormat::RGBx => DRM_FORMAT_XBGR8888.into(), - VideoFormat::ABGR_210LE => DRM_FORMAT_ABGR2101010.into(), - VideoFormat::xBGR_210LE => DRM_FORMAT_XBGR2101010.into(), + VideoFormat::BGRA => DrmFourcc::Argb8888, + VideoFormat::RGBA => DrmFourcc::Abgr8888, + VideoFormat::BGRx => DrmFourcc::Xrgb8888, + VideoFormat::RGBx => DrmFourcc::Xbgr8888, + VideoFormat::ABGR_210LE => DrmFourcc::Abgr2101010, + VideoFormat::xBGR_210LE => DrmFourcc::Xbgr2101010, _ => panic!("Unsupported format"), } } diff --git a/wlx-capture/src/wlr_dmabuf.rs b/wlx-capture/src/wlr_dmabuf.rs index 1725ff7a..68e23196 100644 --- a/wlx-capture/src/wlr_dmabuf.rs +++ b/wlx-capture/src/wlr_dmabuf.rs @@ -6,12 +6,13 @@ use std::{ thread::JoinHandle, }; +use drm_fourcc::{DrmFormat, DrmFourcc, DrmModifier}; use smithay_client_toolkit::reexports::protocols_wlr::export_dmabuf::v1::client::zwlr_export_dmabuf_frame_v1::{self, ZwlrExportDmabufFrameV1}; use wayland_client::{Connection, QueueHandle, Dispatch, Proxy}; use crate::{ WlxCapture, - frame::{DmabufFrame, DrmFormat, FramePlane, WlxFrame}, + frame::{DmabufFrame, FrameFormat, FramePlane, WlxFrame}, wayland::WlxClient, }; @@ -168,13 +169,27 @@ fn request_dmabuf_frame( num_objects, .. } => { - let mut new_frame = DmabufFrame::default(); - new_frame.format.width = width; - new_frame.format.height = height; - new_frame.format.fourcc.value = format; + let mut new_frame = DmabufFrame { + format: FrameFormat { + width, + height, + drm_format: DrmFormat { + code: match DrmFourcc::try_from(format) { + Ok(code) => code, + Err(e) => { + log::error!("Unrecognized fourcc: {e:?}"); + return; + } + }, + modifier: DrmModifier::Invalid, + }, + transform, + }, + mouse: None, + num_planes: num_objects as _, + planes: Default::default(), + }; new_frame.format.set_mod(mod_high, mod_low); - new_frame.format.transform = transform; - new_frame.num_planes = num_objects as _; frame = Some(new_frame); } zwlr_export_dmabuf_frame_v1::Event::Object { diff --git a/wlx-capture/src/wlr_screencopy.rs b/wlx-capture/src/wlr_screencopy.rs index a54ae805..c9cc9ae8 100644 --- a/wlx-capture/src/wlr_screencopy.rs +++ b/wlx-capture/src/wlr_screencopy.rs @@ -1,3 +1,4 @@ +use drm_fourcc::{DrmFormat, DrmFourcc, DrmModifier}; use libc::{O_CREAT, O_RDWR, S_IRUSR, S_IWUSR}; use std::{ any::Any, @@ -18,10 +19,7 @@ use smithay_client_toolkit::reexports::protocols_wlr::screencopy::v1::client::zw use crate::{ WlxCapture, - frame::{ - DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888, DrmFormat, FourCC, FrameFormat, FramePlane, - MemFdFrame, WlxFrame, - }, + frame::{FrameFormat, FramePlane, MemFdFrame, WlxFrame}, wayland::WlxClient, }; @@ -44,7 +42,7 @@ impl Drop for BufData { enum ScreenCopyEvent { Buffer { data: BufData, - fourcc: FourCC, + drm_format: DrmFormat, width: u32, height: u32, stride: u32, @@ -213,7 +211,7 @@ where match event { ScreenCopyEvent::Buffer { data, - fourcc, + drm_format, width, height, stride, @@ -222,16 +220,15 @@ where format: FrameFormat { width, height, - fourcc, + drm_format, transform, - ..Default::default() }, plane: FramePlane { fd: Some(data.fd), offset: 0, stride: stride as _, }, - ..Default::default() + mouse: None, }; log::trace!("{}: Received screencopy buffer, copying", name.as_ref()); if wait_for_damage { @@ -331,7 +328,10 @@ impl Dispatch> for WlxClient wl_pool, fd, }, - fourcc, + drm_format: DrmFormat { + code: fourcc, + modifier: DrmModifier::Invalid, + }, width, height, stride, @@ -346,12 +346,12 @@ impl Dispatch> for WlxClient } } -fn fourcc_from_wlshm(shm_format: Format) -> Option { +fn fourcc_from_wlshm(shm_format: Format) -> Option { match shm_format { - Format::Argb8888 => Some(FourCC::from(DRM_FORMAT_ARGB8888)), - Format::Xrgb8888 => Some(FourCC::from(DRM_FORMAT_XRGB8888)), - Format::Abgr8888 => Some(FourCC::from(DRM_FORMAT_ARGB8888)), - Format::Xbgr8888 => Some(FourCC::from(DRM_FORMAT_XRGB8888)), + Format::Argb8888 => Some(DrmFourcc::Argb8888), + Format::Xrgb8888 => Some(DrmFourcc::Xrgb8888), + Format::Abgr8888 => Some(DrmFourcc::Abgr8888), + Format::Xbgr8888 => Some(DrmFourcc::Xbgr8888), _ => None, } } diff --git a/wlx-capture/src/xshm.rs b/wlx-capture/src/xshm.rs index 3f0b05fb..ff4908d2 100644 --- a/wlx-capture/src/xshm.rs +++ b/wlx-capture/src/xshm.rs @@ -8,11 +8,12 @@ use std::{ }, }; +use drm_fourcc::{DrmFormat, DrmFourcc, DrmModifier}; use rxscreen::monitor::Monitor; use crate::{ WlxCapture, - frame::{DRM_FORMAT_XRGB8888, DrmFormat, FrameFormat, MemPtrFrame, MouseMeta, WlxFrame}, + frame::{FrameFormat, MemPtrFrame, MouseMeta, Transform, WlxFrame}, }; pub struct XshmScreen { @@ -101,8 +102,11 @@ where format: FrameFormat { width: image.width() as _, height: image.height() as _, - fourcc: DRM_FORMAT_XRGB8888.into(), - ..Default::default() + drm_format: DrmFormat { + code: DrmFourcc::Xrgb8888, + modifier: DrmModifier::Invalid, + }, + transform: Transform::Normal, }, ptr: unsafe { image.as_ptr() as _ }, size, diff --git a/wlx-overlay-s/src/backend/openvr/mod.rs b/wlx-overlay-s/src/backend/openvr/mod.rs index f7dd8ba9..4eda9c88 100644 --- a/wlx-overlay-s/src/backend/openvr/mod.rs +++ b/wlx-overlay-s/src/backend/openvr/mod.rs @@ -26,7 +26,7 @@ use crate::{ manifest::{install_manifest, uninstall_manifest}, overlay::OpenVrOverlayData, }, - task::{InputTask, OpenVrTask, OverlayTask, TaskType}, + task::{OpenVrTask, OverlayTask, TaskType}, }, config::save_state, graphics::{GpuFutures, init_openvr_graphics}, @@ -42,9 +42,6 @@ use crate::{ }, }; -#[cfg(feature = "wayvr")] -use crate::{backend::wayvr::WayVRAction, overlays::wayvr::wayvr_action}; - pub mod helpers; pub mod input; pub mod lines; @@ -157,51 +154,50 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr } FRAME_COUNTER.fetch_add(1, Ordering::Relaxed); - // extremely cursed - const VREVENT_QUIT: u32 = EVREventType::VREvent_Quit as u32; - const VREVENT_TRACKED_ACTIVATED: u32 = EVREventType::VREvent_TrackedDeviceActivated as u32; - const VREVENT_TRACKED_DEACTIVATED: u32 = - EVREventType::VREvent_TrackedDeviceDeactivated as u32; - const VREVENT_TRACKED_UPDATED: u32 = EVREventType::VREvent_TrackedDeviceUpdated as u32; - const VREVENT_SEATED_ZERO: u32 = EVREventType::VREvent_SeatedZeroPoseReset as u32; - const VREVENT_STANDING_ZERO: u32 = EVREventType::VREvent_StandingZeroPoseReset as u32; - const VREVENT_CHAPERONE_CHANGED: u32 = - EVREventType::VREvent_ChaperoneUniverseHasChanged as u32; - const VREVENT_SCENE_APP_CHANGED: u32 = EVREventType::VREvent_SceneApplicationChanged as u32; - const VREVENT_IPD_CHANGED: u32 = EVREventType::VREvent_IpdChanged as u32; + { + // extremely cursed + const EV_QUIT: u32 = EVREventType::VREvent_Quit as u32; + const EV_DEV_ACTIVATED: u32 = EVREventType::VREvent_TrackedDeviceActivated as u32; + const EV_DEV_DEACTIVATED: u32 = EVREventType::VREvent_TrackedDeviceDeactivated as u32; + const EV_DEV_UPDATED: u32 = EVREventType::VREvent_TrackedDeviceUpdated as u32; + const EV_SEAT_ZERO: u32 = EVREventType::VREvent_SeatedZeroPoseReset as u32; + const EV_STAND_ZERO: u32 = EVREventType::VREvent_StandingZeroPoseReset as u32; + const EV_CHAP_CHANGED: u32 = EVREventType::VREvent_ChaperoneUniverseHasChanged as u32; + const EV_SCENE_CHANGED: u32 = EVREventType::VREvent_SceneApplicationChanged as u32; + const EV_IPD_CHANGED: u32 = EVREventType::VREvent_IpdChanged as u32; - while let Some(event) = system_mgr.poll_next_event() { - match event.event_type { - VREVENT_QUIT => { - log::warn!("Received quit event, shutting down."); - break 'main_loop; - } - VREVENT_TRACKED_ACTIVATED - | VREVENT_TRACKED_DEACTIVATED - | VREVENT_TRACKED_UPDATED => { - next_device_update = Instant::now(); - } - VREVENT_SEATED_ZERO - | VREVENT_STANDING_ZERO - | VREVENT_CHAPERONE_CHANGED - | VREVENT_SCENE_APP_CHANGED => { - playspace.playspace_changed(&mut compositor_mgr, &mut chaperone_mgr); - } - VREVENT_IPD_CHANGED => { - if let Ok(ipd) = system_mgr.get_tracked_device_property::( - TrackedDeviceIndex::HMD, - ETrackedDeviceProperty::Prop_UserIpdMeters_Float, - ) { - let ipd = (ipd * 1000.0).round(); - if (ipd - app.input_state.ipd).abs() > 0.05 { - log::info!("IPD: {:.1} mm -> {:.1} mm", app.input_state.ipd, ipd); - Toast::new(ToastTopic::IpdChange, "IPD".into(), format!("{ipd:.1} mm")) - .submit(&mut app); - } - app.input_state.ipd = ipd; + while let Some(event) = system_mgr.poll_next_event() { + match event.event_type { + EV_QUIT => { + log::warn!("Received quit event, shutting down."); + break 'main_loop; } + EV_DEV_ACTIVATED | EV_DEV_DEACTIVATED | EV_DEV_UPDATED => { + next_device_update = Instant::now(); + } + EV_SEAT_ZERO | EV_STAND_ZERO | EV_CHAP_CHANGED | EV_SCENE_CHANGED => { + playspace.playspace_changed(&mut compositor_mgr, &mut chaperone_mgr); + } + EV_IPD_CHANGED => { + if let Ok(ipd) = system_mgr.get_tracked_device_property::( + TrackedDeviceIndex::HMD, + ETrackedDeviceProperty::Prop_UserIpdMeters_Float, + ) { + let ipd = (ipd * 1000.0).round(); + if (ipd - app.input_state.ipd).abs() > 0.05 { + log::info!("IPD: {:.1} mm -> {:.1} mm", app.input_state.ipd, ipd); + Toast::new( + ToastTopic::IpdChange, + "IPD".into(), + format!("{ipd:.1} mm"), + ) + .submit(&mut app); + } + app.input_state.ipd = ipd; + } + } + _ => {} } - _ => {} } } @@ -235,9 +231,7 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr } }, #[cfg(feature = "wayvr")] - TaskType::WayVR(action) => { - wayvr_action(&mut app, &mut overlays, &action); - } + TaskType::WayVR(_action) => { /* TODO */ } } } @@ -267,9 +261,7 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr .pointers .iter() .any(|p| p.now.toggle_dashboard && !p.before.toggle_dashboard) - { - wayvr_action(&mut app, &mut overlays, &WayVRAction::ToggleDashboard); - } + { /* TODO */ } overlays .values_mut() @@ -337,11 +329,6 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr .values_mut() .for_each(|o| o.after_render(universe.clone(), &mut overlay_mgr, &app.gfx)); - #[cfg(feature = "wayvr")] - if let Some(wayland_server) = app.wayland_server.as_ref() { - wayland_server.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 c14b993f..9645ec50 100644 --- a/wlx-overlay-s/src/backend/openxr/mod.rs +++ b/wlx-overlay-s/src/backend/openxr/mod.rs @@ -35,9 +35,6 @@ use crate::{ }, }; -#[cfg(feature = "wayvr")] -use crate::{backend::wayvr::WayVRAction, overlays::wayvr::wayvr_action}; - mod blocker; mod helpers; mod input; @@ -149,8 +146,8 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr }; let pointer_lines = [ - lines.allocate(&xr_state, &mut app)?, - lines.allocate(&xr_state, &mut app)?, + lines.allocate(&xr_state, &app)?, + lines.allocate(&xr_state, &app)?, ]; let watch_id = overlays.lookup(WATCH_NAME).unwrap(); // want panic @@ -303,9 +300,7 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr .pointers .iter() .any(|p| p.now.toggle_dashboard && !p.before.toggle_dashboard) - { - wayvr_action(&mut app, &mut overlays, &WayVRAction::ToggleDashboard); - } + { /* TODO */ } watch_fade(&mut app, overlays.mut_by_id(watch_id).unwrap()); // want panic if let Some(ref mut space_mover) = playspace { @@ -458,11 +453,6 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr } // End layer composition - #[cfg(feature = "wayvr")] - if let Some(wayland_server) = app.wayland_server.as_ref() { - wayland_server.borrow_mut().data.tick_finish()?; - } - // Begin layer submit layers.sort_by(|a, b| b.0.total_cmp(&a.0)); @@ -502,10 +492,7 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr } #[cfg(feature = "openvr")] TaskType::OpenVR(_) => {} - #[cfg(feature = "wayvr")] - TaskType::WayVR(action) => { - wayvr_action(&mut app, &mut overlays, &action); - } + TaskType::WayVR(_action) => { /* TODO */ } } } diff --git a/wlx-overlay-s/src/backend/wayvr/client.rs b/wlx-overlay-s/src/backend/wayvr/client.rs index 39b1a9d2..862fcc5d 100644 --- a/wlx-overlay-s/src/backend/wayvr/client.rs +++ b/wlx-overlay-s/src/backend/wayvr/client.rs @@ -14,12 +14,11 @@ use crate::backend::wayvr::{ExternalProcessRequest, WayVRTask}; use super::{ ProcessWayVREnv, comp::{self, ClientState}, - display, process, + process, }; pub struct WayVRClient { pub client: wayland_server::Client, - pub display_handle: display::DisplayHandle, pub pid: u32, } @@ -103,10 +102,13 @@ impl WayVRCompositor { }); } + pub fn cleanup_handles(&mut self) { + self.state.cleanup(); + } + fn accept_connection( &mut self, stream: UnixStream, - displays: &mut display::DisplayVec, processes: &mut process::ProcessVec, ) -> anyhow::Result<()> { let client = self @@ -126,16 +128,12 @@ impl WayVRCompositor { { // 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(()); - } + // Add client + self.add_client(WayVRClient { + client, + pid: creds.pid as u32, + }); + return Ok(()); } } } @@ -158,13 +156,9 @@ impl WayVRCompositor { Ok(()) } - fn accept_connections( - &mut self, - displays: &mut display::DisplayVec, - processes: &mut process::ProcessVec, - ) -> anyhow::Result<()> { + fn accept_connections(&mut self, processes: &mut process::ProcessVec) -> anyhow::Result<()> { if let Some(stream) = self.listener.accept()? - && let Err(e) = self.accept_connection(stream, displays, processes) + && let Err(e) = self.accept_connection(stream, processes) { log::error!("Failed to accept connection: {e}"); } @@ -172,12 +166,8 @@ impl WayVRCompositor { Ok(()) } - pub fn tick_wayland( - &mut self, - displays: &mut display::DisplayVec, - processes: &mut process::ProcessVec, - ) -> anyhow::Result<()> { - if let Err(e) = self.accept_connections(displays, processes) { + pub fn tick_wayland(&mut self, processes: &mut process::ProcessVec) -> anyhow::Result<()> { + if let Err(e) = self.accept_connections(processes) { log::error!("accept_connections failed: {e}"); } diff --git a/wlx-overlay-s/src/backend/wayvr/comp.rs b/wlx-overlay-s/src/backend/wayvr/comp.rs index b945a519..bd392e05 100644 --- a/wlx-overlay-s/src/backend/wayvr/comp.rs +++ b/wlx-overlay-s/src/backend/wayvr/comp.rs @@ -1,18 +1,17 @@ use smithay::backend::allocator::dmabuf::Dmabuf; -use smithay::backend::renderer::ImportDma; -use smithay::backend::renderer::gles::GlesRenderer; -use smithay::backend::renderer::utils::on_commit_buffer_handler; +use smithay::backend::renderer::{BufferType, buffer_type}; use smithay::input::{Seat, SeatHandler, SeatState}; use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel; use smithay::reexports::wayland_server; use smithay::reexports::wayland_server::Resource; -use smithay::reexports::wayland_server::protocol::{wl_buffer, wl_seat, wl_surface}; +use smithay::reexports::wayland_server::protocol::{wl_buffer, wl_output, wl_seat, wl_surface}; use smithay::wayland::buffer::BufferHandler; use smithay::wayland::dmabuf::{ - DmabufFeedback, DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier, + DmabufFeedback, DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier, get_dmabuf, }; use smithay::wayland::output::OutputHandler; -use smithay::wayland::shm::{ShmHandler, ShmState}; +use smithay::wayland::shm::{ShmHandler, ShmState, with_buffer_contents}; +use smithay::wayland::single_pixel_buffer::get_single_pixel_buffer; use smithay::{ delegate_compositor, delegate_data_device, delegate_dmabuf, delegate_output, delegate_seat, delegate_shm, delegate_xdg_shell, @@ -23,7 +22,7 @@ use std::sync::{Arc, Mutex}; use smithay::utils::Serial; use smithay::wayland::compositor::{ - self, SurfaceAttributes, TraversalAction, with_surface_tree_downward, + self, BufferAssignment, SurfaceAttributes, TraversalAction, with_surface_tree_downward, }; use smithay::wayland::selection::SelectionHandler; @@ -37,12 +36,14 @@ use wayland_server::Client; use wayland_server::backend::{ClientData, ClientId, DisconnectReason}; use wayland_server::protocol::wl_surface::WlSurface; +use crate::backend::wayvr::SurfaceBufWithImage; +use crate::backend::wayvr::image_importer::ImageImporter; use crate::ipc::event_queue::SyncEventQueue; use super::WayVRTask; pub struct Application { - pub gles_renderer: GlesRenderer, + pub image_importer: ImageImporter, pub dmabuf_state: (DmabufState, DmabufGlobal, Option), pub compositor: compositor::CompositorState, pub xdg_shell: XdgShellState, @@ -57,6 +58,10 @@ impl Application { pub fn check_redraw(&mut self, surface: &WlSurface) -> bool { self.redraw_requests.remove(&surface.id()) } + + pub fn cleanup(&mut self) { + self.image_importer.cleanup(); + } } impl compositor::CompositorHandler for Application { @@ -72,7 +77,74 @@ impl compositor::CompositorHandler for Application { } fn commit(&mut self, surface: &WlSurface) { - on_commit_buffer_handler::(surface); + smithay::wayland::compositor::with_states(surface, |states| { + let mut guard = states.cached_state.get::(); + let attrs = guard.current(); + + match attrs.buffer.take() { + Some(BufferAssignment::NewBuffer(buffer)) => { + let current = SurfaceBufWithImage::get_from_surface(states); + + if current.is_none_or(|c| c.buffer != buffer) { + match buffer_type(&buffer) { + Some(BufferType::Dma) => { + let dmabuf = get_dmabuf(&buffer).unwrap(); // always Ok due to buffer_type + if let Ok(image) = + self.image_importer.get_or_import_dmabuf(dmabuf.clone()) + { + let sbwi = SurfaceBufWithImage { + image, + buffer, + transform: wl_transform_to_frame_transform( + attrs.buffer_transform, + ), + scale: attrs.buffer_scale, + }; + sbwi.apply_to_surface(states); + } + } + Some(BufferType::Shm) => { + with_buffer_contents(&buffer, |data, size, buf| { + if let Ok(image) = + self.image_importer.import_shm(data, size, buf) + { + let sbwi = SurfaceBufWithImage { + image, + buffer: buffer.clone(), + transform: wl_transform_to_frame_transform( + attrs.buffer_transform, + ), + scale: attrs.buffer_scale, + }; + sbwi.apply_to_surface(states); + } + }); + } + Some(BufferType::SinglePixel) => { + let spb = get_single_pixel_buffer(&buffer).unwrap(); // always Ok + if let Ok(image) = self.image_importer.import_spb(spb) { + let sbwi = SurfaceBufWithImage { + image, + buffer, + transform: wl_transform_to_frame_transform( + // does this even matter + attrs.buffer_transform, + ), + scale: attrs.buffer_scale, + }; + sbwi.apply_to_surface(states); + } + } + Some(other) => log::warn!("Unsupported wl_buffer format: {other:?}"), + None => { /* don't draw anything */ } + } + } + } + Some(BufferAssignment::Removed) => {} + None => {} + } + }); + self.redraw_requests.insert(surface.id()); } } @@ -198,7 +270,7 @@ impl DmabufHandler for Application { dmabuf: Dmabuf, notifier: ImportNotifier, ) { - if self.gles_renderer.import_dmabuf(&dmabuf, None).is_ok() { + if self.image_importer.get_or_import_dmabuf(dmabuf).is_ok() { let _ = notifier.successful::(); } else { notifier.failed(); @@ -235,3 +307,19 @@ pub fn send_frames_surface_tree(surface: &wl_surface::WlSurface, time: u32) { |_, _, &()| true, ); } + +fn wl_transform_to_frame_transform( + transform: wl_output::Transform, +) -> wlx_capture::frame::Transform { + match transform { + wl_output::Transform::Normal => wlx_capture::frame::Transform::Normal, + wl_output::Transform::_90 => wlx_capture::frame::Transform::Rotated90, + wl_output::Transform::_180 => wlx_capture::frame::Transform::Rotated180, + wl_output::Transform::_270 => wlx_capture::frame::Transform::Rotated270, + wl_output::Transform::Flipped => wlx_capture::frame::Transform::Flipped, + wl_output::Transform::Flipped90 => wlx_capture::frame::Transform::Flipped90, + wl_output::Transform::Flipped180 => wlx_capture::frame::Transform::Flipped180, + wl_output::Transform::Flipped270 => wlx_capture::frame::Transform::Flipped270, + _ => wlx_capture::frame::Transform::Undefined, + } +} diff --git a/wlx-overlay-s/src/backend/wayvr/display.rs b/wlx-overlay-s/src/backend/wayvr/display.rs deleted file mode 100644 index 9a4d7635..00000000 --- a/wlx-overlay-s/src/backend/wayvr/display.rs +++ /dev/null @@ -1,606 +0,0 @@ -use std::{cell::RefCell, rc::Rc, sync::Arc}; - -use smithay::{ - backend::renderer::{ - Bind, Color32F, Frame, Renderer, - element::{ - Kind, - surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree}, - }, - gles::{GlesRenderer, GlesTexture, ffi}, - utils::draw_render_elements, - }, - input, - utils::{Logical, Point, Rectangle, Size, Transform}, - wayland::shell::xdg::ToplevelSurface, -}; -use wayvr_ipc::packet_server; - -use crate::{ - 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, - process, smithay_wrapper, time, window, -}; - -fn generate_auth_key() -> String { - let uuid = uuid::Uuid::new_v4(); - uuid.to_string() -} - -#[derive(Debug)] -pub struct DisplayWindow { - pub window_handle: window::WindowHandle, - pub toplevel: ToplevelSurface, - pub process_handle: process::ProcessHandle, -} - -pub struct SpawnProcessResult { - pub auth_key: String, - pub child: std::process::Child, -} - -#[derive(Debug)] -pub enum DisplayTask { - ProcessCleanup(process::ProcessHandle), -} - -const MAX_DISPLAY_SIZE: u16 = 8192; - -#[derive(Debug)] -pub struct Display { - // Display info stuff - pub width: u16, - pub height: u16, - pub name: String, - pub visible: bool, - pub layout: packet_server::WvrDisplayWindowLayout, - pub overlay_id: Option, - pub wants_redraw: bool, - pub rendered_frame_count: u32, - pub primary: bool, - pub wm: Rc>, - pub displayed_windows: Vec, - wayland_env: super::WaylandEnv, - last_pressed_time_ms: u64, - pub no_windows_since: Option, - - // Render data stuff - gles_texture: GlesTexture, // TODO: drop texture - egl_image: khronos_egl::Image, - egl_data: Rc, - - pub render_data: egl_data::RenderData, - - pub tasks: SyncEventQueue, -} - -impl Drop for Display { - fn drop(&mut self) { - let _ = self - .egl_data - .egl - .destroy_image(self.egl_data.display, self.egl_image); - } -} - -pub struct DisplayInitParams<'a> { - pub wm: Rc>, - pub config: &'a super::Config, - pub renderer: &'a mut GlesRenderer, - pub egl_data: Rc, - pub wayland_env: super::WaylandEnv, - pub width: u16, - pub height: u16, - pub name: &'a str, - pub primary: bool, -} - -impl Display { - pub fn new(params: DisplayInitParams) -> anyhow::Result { - if params.width > MAX_DISPLAY_SIZE { - anyhow::bail!( - "display width ({}) is larger than {}", - params.width, - MAX_DISPLAY_SIZE - ); - } - - if params.height > MAX_DISPLAY_SIZE { - anyhow::bail!( - "display height ({}) is larger than {}", - params.height, - MAX_DISPLAY_SIZE - ); - } - - let tex_format = ffi::RGBA; - let internal_format = ffi::RGBA8; - - let tex_id = params.renderer.with_context(|gl| { - smithay_wrapper::create_framebuffer_texture( - gl, - u32::from(params.width), - u32::from(params.height), - tex_format, - internal_format, - ) - })?; - - let egl_image = params.egl_data.create_egl_image(tex_id)?; - - let render_data = match params.config.blit_method { - BlitMethod::Dmabuf => match params.egl_data.create_dmabuf_data(&egl_image) { - Ok(dmabuf_data) => egl_data::RenderData::Dmabuf(dmabuf_data), - Err(e) => { - log::error!( - "create_dmabuf_data failed: {e:?}. Using software blitting (This will be slow!)" - ); - egl_data::RenderData::Software(None) - } - }, - BlitMethod::Software => egl_data::RenderData::Software(None), - }; - - let opaque = false; - let size = (i32::from(params.width), i32::from(params.height)).into(); - let gles_texture = unsafe { - GlesTexture::from_raw(params.renderer, Some(tex_format), opaque, tex_id, size) - }; - - Ok(Self { - egl_data: params.egl_data, - width: params.width, - height: params.height, - name: String::from(params.name), - primary: params.primary, - wayland_env: params.wayland_env, - wm: params.wm, - displayed_windows: Vec::new(), - render_data, - egl_image, - gles_texture, - last_pressed_time_ms: 0, - no_windows_since: None, - overlay_id: None, - tasks: SyncEventQueue::new(), - visible: true, - wants_redraw: true, - rendered_frame_count: 0, - layout: packet_server::WvrDisplayWindowLayout::Tiling, - }) - } - - pub fn as_packet(&self, handle: DisplayHandle) -> packet_server::WvrDisplay { - packet_server::WvrDisplay { - width: self.width, - height: self.height, - name: self.name.clone(), - visible: self.visible, - handle: handle.as_packet(), - } - } - - pub fn add_window( - &mut self, - window_handle: window::WindowHandle, - process_handle: process::ProcessHandle, - toplevel: &ToplevelSurface, - ) { - log::debug!("Attaching toplevel surface into display"); - self.displayed_windows.push(DisplayWindow { - window_handle, - process_handle, - toplevel: toplevel.clone(), - }); - self.reposition_windows(); - } - - pub fn remove_window(&mut self, window_handle: window::WindowHandle) { - self.displayed_windows - .retain(|disp| disp.window_handle != window_handle); - } - - pub fn reposition_windows(&mut self) { - let window_count = self.displayed_windows.len(); - - match &self.layout { - packet_server::WvrDisplayWindowLayout::Tiling => { - let mut i = 0; - for win in &mut self.displayed_windows { - if let Some(window) = self.wm.borrow_mut().windows.get_mut(&win.window_handle) { - if !window.visible { - continue; - } - let d_cur = i as f32 / window_count as f32; - let d_next = (i + 1) as f32 / window_count as f32; - - let left = (d_cur * f32::from(self.width)) as i32; - let right = (d_next * f32::from(self.width)) as i32; - - window.set_pos(left, 0); - window.set_size((right - left) as u32, u32::from(self.height)); - i += 1; - } - } - } - packet_server::WvrDisplayWindowLayout::Stacking(opts) => { - let do_margins = |margins: &packet_server::Margins, window: &mut window::Window| { - let top = i32::from(margins.top); - let bottom = i32::from(self.height) - i32::from(margins.bottom); - let left = i32::from(margins.left); - let right = i32::from(self.width) - i32::from(margins.right); - let width = right - left; - let height = bottom - top; - if width < 0 || height < 0 { - return; // wrong parameters, do nothing! - } - - window.set_pos(left, top); - window.set_size(width as u32, height as u32); - }; - - let mut i = 0; - for win in &mut self.displayed_windows { - if let Some(window) = self.wm.borrow_mut().windows.get_mut(&win.window_handle) { - if !window.visible { - continue; - } - do_margins( - if i == 0 { - &opts.margins_first - } else { - &opts.margins_rest - }, - window, - ); - i += 1; - } - } - } - } - } - - pub fn tick( - &mut self, - config: &super::Config, - handle: &DisplayHandle, - signals: &mut SyncEventQueue, - ) { - if self.visible { - if !self.displayed_windows.is_empty() { - self.no_windows_since = None; - } else if let Some(auto_hide_delay) = config.auto_hide_delay - && let Some(s) = self.no_windows_since - && s + u64::from(auto_hide_delay) < get_millis() - { - // Auto-hide after specific time - signals.send(WayVRSignal::DisplayVisibility(*handle, false)); - } - } - - while let Some(task) = self.tasks.read() { - match task { - DisplayTask::ProcessCleanup(process_handle) => { - let count = self.displayed_windows.len(); - self.displayed_windows - .retain(|win| win.process_handle != process_handle); - log::info!( - "Cleanup finished for display \"{}\". Current window count: {}", - self.name, - self.displayed_windows.len() - ); - self.no_windows_since = Some(get_millis()); - - if count != self.displayed_windows.len() { - signals.send(WayVRSignal::BroadcastStateChanged( - packet_server::WvrStateChanged::WindowRemoved, - )); - } - - self.reposition_windows(); - } - } - } - } - - #[allow(clippy::significant_drop_tightening)] - pub fn tick_render(&mut self, renderer: &mut GlesRenderer, time_ms: u64) -> anyhow::Result<()> { - let mut gles_texture = self.gles_texture.clone(); - let mut target = renderer.bind(&mut gles_texture)?; - - let size = Size::from((i32::from(self.width), i32::from(self.height))); - let damage: Rectangle = Rectangle::from_size(size); - - let elements: Vec> = self - .displayed_windows - .iter() - .flat_map(|display_window| { - let wm = self.wm.borrow_mut(); - if let Some(window) = wm.windows.get(&display_window.window_handle) { - if !window.visible { - return vec![]; - } - render_elements_from_surface_tree( - renderer, - display_window.toplevel.wl_surface(), - (window.pos_x, window.pos_y), - 1.0, - 1.0, - Kind::Unspecified, - ) - } else { - // Failed to fetch window - vec![] - } - }) - .collect(); - - let mut frame = renderer.render(&mut target, size, Transform::Normal)?; - - let clear_color = if self.displayed_windows.is_empty() { - Color32F::new(0.5, 0.5, 0.5, 0.5) - } else { - Color32F::new(0.0, 0.0, 0.0, 0.0) - }; - - frame.clear(clear_color, &[damage])?; - - draw_render_elements(&mut frame, 1.0, &elements, &[damage])?; - - let _sync_point = frame.finish()?; - - for window in &self.displayed_windows { - send_frames_surface_tree(window.toplevel.wl_surface(), time_ms as u32); - } - - if let egl_data::RenderData::Software(_) = &self.render_data { - // Read OpenGL texture into memory. Slow! - let pixel_data = renderer.with_context(|gl| unsafe { - gl.BindTexture(ffi::TEXTURE_2D, self.gles_texture.tex_id()); - - let len = self.width as usize * self.height as usize * 4; - let mut data: Box<[u8]> = Box::new_uninit_slice(len).assume_init(); - gl.ReadPixels( - 0, - 0, - i32::from(self.width), - i32::from(self.height), - ffi::RGBA, - ffi::UNSIGNED_BYTE, - data.as_mut_ptr().cast(), - ); - - let data: Arc<[u8]> = Arc::from(data); - data - })?; - - self.render_data = - egl_data::RenderData::Software(Some(egl_data::RenderSoftwarePixelsData { - data: pixel_data, - width: self.width, - height: self.height, - })); - } - - self.rendered_frame_count += 1; - - Ok(()) - } - - fn get_hovered_window(&self, cursor_x: u32, cursor_y: u32) -> Option { - let wm = self.wm.borrow(); - - for cell in self.displayed_windows.iter().rev() { - if let Some(window) = wm.windows.get(&cell.window_handle) { - if !window.visible { - continue; - } - - if (cursor_x as i32) >= window.pos_x - && (cursor_x as i32) < window.pos_x + window.size_x as i32 - && (cursor_y as i32) >= window.pos_y - && (cursor_y as i32) < window.pos_y + window.size_y as i32 - { - return Some(cell.window_handle); - } - } - } - None - } - - pub const fn trigger_rerender(&mut self) { - self.wants_redraw = true; - } - - pub fn set_visible(&mut self, visible: bool) { - log::info!("Display \"{}\" visible: {}", self.name.as_str(), visible); - if self.visible == visible { - return; - } - self.visible = visible; - if visible { - self.no_windows_since = None; - self.trigger_rerender(); - } - } - - pub fn set_layout(&mut self, layout: packet_server::WvrDisplayWindowLayout) { - log::info!("Display \"{}\" layout: {:?}", self.name.as_str(), layout); - if self.layout == layout { - return; - } - self.layout = layout; - self.trigger_rerender(); - self.reposition_windows(); - } - - pub fn send_mouse_move( - &self, - config: &super::Config, - manager: &mut WayVRCompositor, - x: u32, - y: u32, - ) { - let current_ms = time::get_millis(); - if self.last_pressed_time_ms + u64::from(config.click_freeze_time_ms) > current_ms { - return; - } - - if let Some(window_handle) = self.get_hovered_window(x, y) { - let wm = self.wm.borrow(); - if let Some(window) = wm.windows.get(&window_handle) { - let surf = window.toplevel.wl_surface().clone(); - let point = Point::::from(( - f64::from(x as i32 - window.pos_x), - f64::from(y as i32 - window.pos_y), - )); - - manager.seat_pointer.motion( - &mut manager.state, - Some((surf, Point::from((0.0, 0.0)))), - &input::pointer::MotionEvent { - serial: manager.serial_counter.next_serial(), - time: 0, - location: point, - }, - ); - - manager.seat_pointer.frame(&mut manager.state); - } - } - } - - const fn get_mouse_index_number(index: super::MouseIndex) -> u32 { - match index { - super::MouseIndex::Left => 0x110, /* BTN_LEFT */ - super::MouseIndex::Center => 0x112, /* BTN_MIDDLE */ - super::MouseIndex::Right => 0x111, /* BTN_RIGHT */ - } - } - - pub fn send_mouse_down(&mut self, manager: &mut WayVRCompositor, index: super::MouseIndex) { - // Change keyboard focus to pressed window - let loc = manager.seat_pointer.current_location(); - - self.last_pressed_time_ms = time::get_millis(); - - if let Some(window_handle) = - self.get_hovered_window(loc.x.max(0.0) as u32, loc.y.max(0.0) as u32) - { - let wm = self.wm.borrow(); - if let Some(window) = wm.windows.get(&window_handle) { - let surf = window.toplevel.wl_surface().clone(); - - manager.seat_keyboard.set_focus( - &mut manager.state, - Some(surf), - manager.serial_counter.next_serial(), - ); - } - } - - manager.seat_pointer.button( - &mut manager.state, - &input::pointer::ButtonEvent { - button: Self::get_mouse_index_number(index), - serial: manager.serial_counter.next_serial(), - time: 0, - state: smithay::backend::input::ButtonState::Pressed, - }, - ); - - manager.seat_pointer.frame(&mut manager.state); - } - - pub fn send_mouse_up(manager: &mut WayVRCompositor, index: super::MouseIndex) { - manager.seat_pointer.button( - &mut manager.state, - &input::pointer::ButtonEvent { - button: Self::get_mouse_index_number(index), - serial: manager.serial_counter.next_serial(), - time: 0, - state: smithay::backend::input::ButtonState::Released, - }, - ); - - manager.seat_pointer.frame(&mut manager.state); - } - - pub fn send_mouse_scroll(manager: &mut WayVRCompositor, delta: WheelDelta) { - manager.seat_pointer.axis( - &mut manager.state, - input::pointer::AxisFrame { - source: None, - relative_direction: ( - smithay::backend::input::AxisRelativeDirection::Identical, - smithay::backend::input::AxisRelativeDirection::Identical, - ), - time: 0, - axis: (f64::from(delta.x), f64::from(-delta.y)), - v120: Some((0, (delta.y * -64.0) as i32)), - stop: (false, false), - }, - ); - manager.seat_pointer.frame(&mut manager.state); - } - - fn configure_env(&self, cmd: &mut std::process::Command, auth_key: &str) { - cmd.env_remove("DISPLAY"); // Goodbye X11 - cmd.env("WAYLAND_DISPLAY", self.wayland_env.display_num_string()); - cmd.env("WAYVR_DISPLAY_AUTH", auth_key); - } - - pub fn spawn_process( - &mut self, - exec_path: &str, - args: &[&str], - env: &[(&str, &str)], - working_dir: Option<&str>, - ) -> anyhow::Result { - log::info!("Spawning subprocess with exec path \"{exec_path}\""); - - let auth_key = generate_auth_key(); - - let mut cmd = std::process::Command::new(exec_path); - self.configure_env(&mut cmd, auth_key.as_str()); - cmd.args(args); - if let Some(working_dir) = working_dir { - cmd.current_dir(working_dir); - } - - for e in env { - cmd.env(e.0, e.1); - } - - match cmd.spawn() { - Ok(child) => Ok(SpawnProcessResult { auth_key, child }), - Err(e) => { - anyhow::bail!( - "Failed to launch process with path \"{exec_path}\": {e}. Make sure your exec path exists." - ); - } - } - } -} - -gen_id!(DisplayVec, Display, DisplayCell, DisplayHandle); - -impl DisplayHandle { - pub const fn from_packet(handle: packet_server::WvrDisplayHandle) -> Self { - Self { - generation: handle.generation, - idx: handle.idx, - } - } - - pub const fn as_packet(&self) -> packet_server::WvrDisplayHandle { - packet_server::WvrDisplayHandle { - idx: self.idx, - generation: self.generation, - } - } -} diff --git a/wlx-overlay-s/src/backend/wayvr/egl_data.rs b/wlx-overlay-s/src/backend/wayvr/egl_data.rs deleted file mode 100644 index d4b844c3..00000000 --- a/wlx-overlay-s/src/backend/wayvr/egl_data.rs +++ /dev/null @@ -1,315 +0,0 @@ -use std::sync::Arc; - -use crate::backend::wayvr::egl_ex::{ - PFNEGLGETPLATFORMDISPLAYEXTPROC, PFNEGLQUERYDMABUFFORMATSEXTPROC, - PFNEGLQUERYDMABUFMODIFIERSEXTPROC, -}; - -use super::egl_ex; -use anyhow::Context; - -#[derive(Debug)] -pub struct EGLData { - pub egl: khronos_egl::Instance, - pub display: khronos_egl::Display, - pub config: khronos_egl::Config, - pub context: khronos_egl::Context, -} - -#[macro_export] -macro_rules! bind_egl_function { - ($func_type:ident, $func:expr) => { - std::mem::transmute_copy::<_, $func_type>($func).unwrap() - }; -} - -#[derive(Debug, Clone)] -pub struct DMAbufModifierInfo { - pub modifiers: Vec, - pub fourcc: u32, -} - -#[derive(Debug, Clone)] -pub struct RenderDMAbufData { - pub fd: i32, - pub stride: i32, - pub offset: i32, - pub mod_info: DMAbufModifierInfo, -} - -#[derive(Debug, Clone)] -pub struct RenderSoftwarePixelsData { - pub data: Arc<[u8]>, - pub width: u16, - pub height: u16, -} - -#[derive(Debug, Clone)] -pub enum RenderData { - Dmabuf(RenderDMAbufData), - Software(Option), // will be set if the next image data is available -} - -fn load_egl_func( - egl: &khronos_egl::Instance, - func_name: &str, -) -> anyhow::Result { - let raw_fn = egl - .get_proc_address(func_name) - .ok_or_else(|| anyhow::anyhow!("Required EGL function {func_name} not found"))?; - Ok(raw_fn) -} - -fn get_disp( - egl: &khronos_egl::Instance, -) -> anyhow::Result { - unsafe { - if let Ok(func) = load_egl_func(egl, "eglGetPlatformDisplayEXT") { - let egl_get_platform_display_ext = - bind_egl_function!(PFNEGLGETPLATFORMDISPLAYEXTPROC, &func); - - let display_ext = egl_get_platform_display_ext( - egl_ex::EGL_PLATFORM_WAYLAND_EXT, // platform - std::ptr::null_mut(), // void *native_display - std::ptr::null_mut(), // EGLint *attrib_list - ); - - if display_ext.is_null() { - log::warn!("eglGetPlatformDisplayEXT failed, using eglGetDisplay instead"); - } else { - return Ok(khronos_egl::Display::from_ptr(display_ext)); - } - } - - egl - .get_display(khronos_egl::DEFAULT_DISPLAY) - .context( - "Both eglGetPlatformDisplayEXT and eglGetDisplay failed. This shouldn't happen unless you don't have any display manager running. Cannot continue, check your EGL installation." - ) - } -} - -impl EGLData { - pub fn new() -> anyhow::Result { - let egl = khronos_egl::Instance::new(khronos_egl::Static); - let display = get_disp(&egl)?; - - let (major, minor) = egl.initialize(display)?; - log::debug!("EGL version: {major}.{minor}"); - - let attrib_list = [ - khronos_egl::RED_SIZE, - 8, - khronos_egl::GREEN_SIZE, - 8, - khronos_egl::BLUE_SIZE, - 8, - khronos_egl::SURFACE_TYPE, - khronos_egl::WINDOW_BIT, - khronos_egl::RENDERABLE_TYPE, - khronos_egl::OPENGL_BIT, - khronos_egl::NONE, - ]; - - let config = egl - .choose_first_config(display, &attrib_list)? - .context("Failed to get EGL config")?; - - egl.bind_api(khronos_egl::OPENGL_ES_API)?; - - log::debug!("eglCreateContext"); - - // Require OpenGL ES 3.0 - let context_attrib_list = [ - khronos_egl::CONTEXT_MAJOR_VERSION, - 3, - khronos_egl::CONTEXT_MINOR_VERSION, - 0, - khronos_egl::NONE, - ]; - - let context = egl.create_context(display, config, None, &context_attrib_list)?; - - log::debug!("eglMakeCurrent"); - - egl.make_current(display, None, None, Some(context))?; - - Ok(Self { - egl, - display, - config, - context, - }) - } - - fn query_dmabuf_mod_info(&self) -> anyhow::Result { - let target_fourcc = 0x3432_4258; //XB24 - - unsafe { - let egl_query_dmabuf_formats_ext = bind_egl_function!( - PFNEGLQUERYDMABUFFORMATSEXTPROC, - &load_egl_func(&self.egl, "eglQueryDmaBufFormatsEXT")? - ); - - // Query format count - let mut num_formats: khronos_egl::Int = 0; - egl_query_dmabuf_formats_ext( - self.display.as_ptr(), - 0, - std::ptr::null_mut(), - &raw mut num_formats, - ); - - // Retrieve format list - let mut formats: Vec = vec![0; num_formats as usize]; - egl_query_dmabuf_formats_ext( - self.display.as_ptr(), - num_formats, - formats.as_mut_ptr(), - &raw mut num_formats, - ); - - /*for (idx, format) in formats.iter().enumerate() { - let bytes = format.to_le_bytes(); - log::trace!( - "idx {}, format {}{}{}{} (hex {:#x})", - idx, - bytes[0] as char, - bytes[1] as char, - bytes[2] as char, - bytes[3] as char, - format - ); - }*/ - - let egl_query_dmabuf_modifiers_ext = bind_egl_function!( - PFNEGLQUERYDMABUFMODIFIERSEXTPROC, - &load_egl_func(&self.egl, "eglQueryDmaBufModifiersEXT")? - ); - - let mut num_mods: khronos_egl::Int = 0; - - // Query modifier count - egl_query_dmabuf_modifiers_ext( - self.display.as_ptr(), - target_fourcc, - 0, - std::ptr::null_mut(), - std::ptr::null_mut(), - &raw mut num_mods, - ); - - if num_mods == 0 { - anyhow::bail!("eglQueryDmaBufModifiersEXT modifier count is zero"); - } - - let mut mods: Vec = vec![0; num_mods as usize]; - egl_query_dmabuf_modifiers_ext( - self.display.as_ptr(), - target_fourcc, - num_mods, - mods.as_mut_ptr(), - std::ptr::null_mut(), - &raw mut num_mods, - ); - - if mods[0] == 0xFFFF_FFFF_FFFF_FFFF { - anyhow::bail!("modifier is -1") - } - - log::trace!("Modifier list:"); - for modifier in &mods { - log::trace!("{modifier:#x}"); - } - - // We should not change these modifier values. Passing all of them to the Vulkan dmabuf - // texture system causes significant graphical corruption due to invalid memory layout and - // tiling on this specific GPU model (very probably others also have the same issue). - // It is not guaranteed that this modifier will be present in other models. - // If not, the full list of modifiers will be passed. Further testing is required. - // For now, it looks like only NAVI32-based gpus have this problem. - let mod_whitelist: [u64; 2] = [ - 0x200_0000_2086_bf04, /* AMD RX 7800 XT, Navi32 */ - 0x200_0000_1866_bf04, /* AMD RX 7600 XT, Navi33 */ - ]; - - for modifier in &mod_whitelist { - if mods.contains(modifier) { - log::warn!("Using whitelisted dmabuf tiling modifier: {modifier:#x}"); - mods = vec![*modifier, 0x0 /* also important (???) */]; - break; - } - } - - Ok(DMAbufModifierInfo { - modifiers: mods, - fourcc: target_fourcc as u32, - }) - } - } - - pub fn create_dmabuf_data( - &self, - egl_image: &khronos_egl::Image, - ) -> anyhow::Result { - use egl_ex::PFNEGLEXPORTDMABUFIMAGEMESAPROC as FUNC; - unsafe { - let egl_export_dmabuf_image_mesa = - bind_egl_function!(FUNC, &load_egl_func(&self.egl, "eglExportDMABUFImageMESA")?); - - let mut fds: [i32; 3] = [0; 3]; - let mut strides: [i32; 3] = [0; 3]; - let mut offsets: [i32; 3] = [0; 3]; - - let ret = egl_export_dmabuf_image_mesa( - self.display.as_ptr(), - egl_image.as_ptr(), - fds.as_mut_ptr(), - strides.as_mut_ptr(), - offsets.as_mut_ptr(), - ); - - if ret != khronos_egl::TRUE { - anyhow::bail!("eglExportDMABUFImageMESA failed with return code {ret}"); - } - - if fds[0] <= 0 { - anyhow::bail!("fd is <=0 (got {})", fds[0]); - } - - // many planes in RGB data? - if fds[1] != 0 || strides[1] != 0 || offsets[1] != 0 { - anyhow::bail!("multi-planar data received, packed RGB expected"); - } - - if strides[0] < 0 { - anyhow::bail!("strides is < 0"); - } - - if offsets[0] < 0 { - anyhow::bail!("offsets is < 0"); - } - - let mod_info = self.query_dmabuf_mod_info()?; - - Ok(RenderDMAbufData { - fd: fds[0], - stride: strides[0], - offset: offsets[0], - mod_info, - }) - } - } - - pub fn create_egl_image(&self, gl_tex_id: u32) -> anyhow::Result { - unsafe { - Ok(self.egl.create_image( - self.display, - self.context, - khronos_egl::GL_TEXTURE_2D as std::ffi::c_uint, - khronos_egl::ClientBuffer::from_ptr(gl_tex_id as *mut std::ffi::c_void), - &[khronos_egl::ATTRIB_NONE], - )?) - } - } -} diff --git a/wlx-overlay-s/src/backend/wayvr/egl_ex.rs b/wlx-overlay-s/src/backend/wayvr/egl_ex.rs deleted file mode 100644 index bf264920..00000000 --- a/wlx-overlay-s/src/backend/wayvr/egl_ex.rs +++ /dev/null @@ -1,49 +0,0 @@ -#![allow(clippy::all)] - -pub const EGL_PLATFORM_WAYLAND_EXT: khronos_egl::Enum = 0x31D8; - -// eglGetPlatformDisplayEXT -// https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_platform_base.txt -pub type PFNEGLGETPLATFORMDISPLAYEXTPROC = Option< - unsafe extern "C" fn( - platform: khronos_egl::Enum, - native_display: *mut std::ffi::c_void, - attrib_list: *mut khronos_egl::Enum, - ) -> khronos_egl::EGLDisplay, ->; - -// eglExportDMABUFImageMESA -// https://registry.khronos.org/EGL/extensions/MESA/EGL_MESA_image_dma_buf_export.txt -pub type PFNEGLEXPORTDMABUFIMAGEMESAPROC = Option< - unsafe extern "C" fn( - dpy: khronos_egl::EGLDisplay, - image: khronos_egl::EGLImage, - fds: *mut i32, - strides: *mut khronos_egl::Int, - offsets: *mut khronos_egl::Int, - ) -> khronos_egl::Boolean, ->; - -// eglQueryDmaBufModifiersEXT -// https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt -pub type PFNEGLQUERYDMABUFMODIFIERSEXTPROC = Option< - unsafe extern "C" fn( - dpy: khronos_egl::EGLDisplay, - format: khronos_egl::Int, - max_modifiers: khronos_egl::Int, - modifiers: *mut u64, - external_only: *mut khronos_egl::Boolean, - num_modifiers: *mut khronos_egl::Int, - ) -> khronos_egl::Boolean, ->; - -// eglQueryDmaBufFormatsEXT -// https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt -pub type PFNEGLQUERYDMABUFFORMATSEXTPROC = Option< - unsafe extern "C" fn( - dpy: khronos_egl::EGLDisplay, - max_formats: khronos_egl::Int, - formats: *mut khronos_egl::Int, - num_formats: *mut khronos_egl::Int, - ) -> khronos_egl::Boolean, ->; diff --git a/wlx-overlay-s/src/backend/wayvr/image_importer.rs b/wlx-overlay-s/src/backend/wayvr/image_importer.rs new file mode 100644 index 00000000..53a2b56a --- /dev/null +++ b/wlx-overlay-s/src/backend/wayvr/image_importer.rs @@ -0,0 +1,111 @@ +use std::{collections::HashMap, os::fd::AsRawFd, sync::Arc}; + +use anyhow::Context; +use smithay::{ + backend::allocator::{ + Buffer, + dmabuf::{Dmabuf, WeakDmabuf}, + }, + wayland::{ + shm::{BufferData, shm_format_to_fourcc}, + single_pixel_buffer::SinglePixelBufferUserData, + }, +}; +use vulkano::{format::Format, image::view::ImageView}; +use wgui::gfx::WGfx; +use wlx_capture::frame::{DmabufFrame, FrameFormat, Transform}; + +use crate::graphics::dmabuf::{WGfxDmabuf, fourcc_to_vk}; + +pub struct ImageImporter { + gfx: Arc, + dmabufs: HashMap>, +} + +impl ImageImporter { + pub fn new(gfx: Arc) -> Self { + Self { + gfx, + dmabufs: HashMap::new(), + } + } + + pub fn import_spb( + &mut self, + spb: &SinglePixelBufferUserData, + ) -> anyhow::Result> { + let mut cmd_buf = self.gfx.create_xfer_command_buffer( + vulkano::command_buffer::CommandBufferUsage::OneTimeSubmit, + )?; + + let rgba = spb.rgba8888(); + let image = cmd_buf.upload_image(1, 1, Format::R8G8B8A8_UNORM, &rgba)?; + + cmd_buf.build_and_execute_now()?; //TODO: async + + let image_view = ImageView::new_default(image)?; + Ok(image_view) + } + + pub fn import_shm( + &mut self, + data: *const u8, + size: usize, + bd: BufferData, + ) -> anyhow::Result> { + let mut cmd_buf = self.gfx.create_xfer_command_buffer( + vulkano::command_buffer::CommandBufferUsage::OneTimeSubmit, + )?; + + let fourcc = shm_format_to_fourcc(bd.format) + .with_context(|| format!("Could not conver {:?} to fourcc", bd.format))?; + + let format = fourcc_to_vk(fourcc) + .with_context(|| format!("Could not convert {fourcc} to vkFormat"))?; + + let data = unsafe { std::slice::from_raw_parts(data, size) }; + let image = cmd_buf.upload_image(bd.width as _, bd.height as _, format, data)?; + + cmd_buf.build_and_execute_now()?; //TODO: async + + let image_view = ImageView::new_default(image)?; + Ok(image_view) + } + + pub fn get_or_import_dmabuf(&mut self, dmabuf: Dmabuf) -> anyhow::Result> { + let mut frame = DmabufFrame { + format: FrameFormat { + width: dmabuf.width(), + height: dmabuf.height(), + drm_format: dmabuf.format(), + transform: Transform::Undefined, + }, + num_planes: dmabuf.num_planes(), + planes: Default::default(), + mouse: None, + }; + + for (i, handle) in dmabuf.handles().enumerate() { + // even if the original OwnedFd is dropped, the vkImage will hold reference on the DMA-buf + frame.planes[i].fd = Some(handle.as_raw_fd()); + } + + for (i, offset) in dmabuf.offsets().enumerate() { + frame.planes[i].offset = offset; + } + + for (i, stride) in dmabuf.strides().enumerate() { + frame.planes[i].stride = stride as _; + } + + let image = self.gfx.dmabuf_texture(frame)?; + let image_view = ImageView::new_default(image)?; + self.dmabufs.insert(dmabuf.weak(), image_view.clone()); + + Ok(image_view) + } + + pub fn cleanup(&mut self) { + self.dmabufs.retain(|k, _| !k.is_gone()); + } +} diff --git a/wlx-overlay-s/src/backend/wayvr/mod.rs b/wlx-overlay-s/src/backend/wayvr/mod.rs index 06f7d89a..98f35d57 100644 --- a/wlx-overlay-s/src/backend/wayvr/mod.rs +++ b/wlx-overlay-s/src/backend/wayvr/mod.rs @@ -1,29 +1,22 @@ pub mod client; mod comp; -pub mod display; -pub mod egl_data; -mod egl_ex; mod handle; +mod image_importer; pub mod process; -mod smithay_wrapper; mod time; pub mod window; use anyhow::Context; use comp::Application; -use display::{Display, DisplayInitParams, DisplayVec}; use process::ProcessVec; use serde::Deserialize; +use slotmap::SecondaryMap; use smallvec::SmallVec; use smithay::{ - backend::{ - egl, - renderer::{ImportDma, gles::GlesRenderer}, - }, input::{SeatState, keyboard::XkbConfig}, output::{Mode, Output}, - reexports::wayland_server::{self, backend::ClientId}, + reexports::wayland_server::{self, backend::ClientId, protocol::wl_buffer}, wayland::{ - compositor, + compositor::{self, SurfaceData}, dmabuf::{DmabufFeedbackBuilder, DmabufState}, selection::data_device::DataDeviceState, shell::xdg::{ToplevelSurface, XdgShellState}, @@ -33,20 +26,28 @@ use smithay::{ use std::{ cell::RefCell, collections::{HashMap, HashSet}, + mem::MaybeUninit, rc::Rc, sync::Arc, + time::{Duration, Instant}, }; use time::get_millis; -use wayvr_ipc::{ - packet_client::{self}, - packet_server, -}; +use vulkano::image::view::ImageView; +use wayvr_ipc::packet_server; +use wgui::gfx::WGfx; +use wlx_capture::frame::Transform; use xkbcommon::xkb; use crate::{ + backend::{ + task::{OverlayTask, TaskType}, + wayvr::{image_importer::ImageImporter, window::Window}, + }, + graphics::WGfxExtras, ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal}, state::AppState, subsystem::hid::{MODS_TO_KEYS, WheelDelta}, + windowing::{OverlayID, OverlaySelector}, }; const STR_INVALID_HANDLE_DISP: &str = "Invalid display handle"; @@ -109,17 +110,17 @@ pub struct Config { pub struct WayVRState { time_start: u64, - pub displays: display::DisplayVec, pub manager: client::WayVRCompositor, pub wm: Rc>, - egl_data: Rc, pub processes: process::ProcessVec, pub config: Config, - dashboard_display: Option, pub tasks: SyncEventQueue, ticks: u64, cur_modifiers: u8, signals: SyncEventQueue, + mouse_freeze: Instant, + window_to_overlay: HashMap, + overlay_to_window: SecondaryMap, } pub struct WayVR { @@ -134,15 +135,16 @@ pub enum MouseIndex { pub enum TickTask { NewExternalProcess(ExternalProcessRequest), // Call WayVRCompositor::add_client after receiving this message - NewDisplay( - packet_client::WvrDisplayCreateParams, - Option, /* existing handle? */ - ), } impl WayVR { #[allow(clippy::too_many_lines, clippy::cognitive_complexity)] - pub fn new(config: Config, signals: SyncEventQueue) -> anyhow::Result { + pub fn new( + gfx: Arc, + gfx_extras: &WGfxExtras, + config: Config, + signals: SyncEventQueue, + ) -> anyhow::Result { log::info!("Initializing WayVR"); let display: wayland_server::Display = wayland_server::Display::new()?; let dh = display.handle(); @@ -153,8 +155,8 @@ impl WayVR { let data_device = DataDeviceState::new::(&dh); let mut seat = seat_state.new_wl_seat(&dh, "wayvr"); - let dummy_width = 1280; - let dummy_height = 720; + let dummy_width = 1920; + let dummy_height = 1080; let dummy_milli_hz = 60000; /* refresh rate in millihertz */ let output = Output::new( @@ -176,53 +178,38 @@ impl WayVR { output.change_current_state(Some(mode), None, None, None); output.set_preferred(mode); - let egl_data = egl_data::EGLData::new()?; - - let smithay_display = smithay_wrapper::get_egl_display(&egl_data)?; - let smithay_context = smithay_wrapper::get_egl_context(&egl_data, &smithay_display)?; - - let render_node = egl::EGLDevice::device_for_display(&smithay_display) - .and_then(|device| device.try_get_render_node()); - - let gles_renderer = unsafe { GlesRenderer::new(smithay_context)? }; - - let dmabuf_default_feedback = match render_node { - Ok(Some(node)) => { - let dmabuf_formats = gles_renderer.dmabuf_formats(); - let dmabuf_default_feedback = - DmabufFeedbackBuilder::new(node.dev_id(), dmabuf_formats) - .build() - .unwrap(); - Some(dmabuf_default_feedback) - } - Ok(None) => { - log::warn!("dmabuf: Failed to query render node"); - None - } - Err(err) => { - log::warn!("dmabuf: Failed to get egl device for display: {err}"); - None - } + let main_device = { + let (major, minor) = gfx_extras.drm_device.as_ref().context("No DRM device!")?; + libc::makedev(*major as _, *minor as _) }; - let dmabuf_state = dmabuf_default_feedback.map_or_else( - || { - let dmabuf_formats = gles_renderer.dmabuf_formats(); - let mut dmabuf_state = DmabufState::new(); - let dmabuf_global = - dmabuf_state.create_global::(&display.handle(), dmabuf_formats); - (dmabuf_state, dmabuf_global, None) - }, - |default_feedback| { - let mut dmabuf_state = DmabufState::new(); - let dmabuf_global = dmabuf_state - .create_global_with_default_feedback::( - &display.handle(), - &default_feedback, - ); - (dmabuf_state, dmabuf_global, Some(default_feedback)) - }, - ); + // this will throw a compile-time error if smithay's drm-fourcc is out of sync with wlx-capture's + let mut formats: Vec = vec![]; + + for f in gfx_extras.drm_formats.iter() { + formats.push(f.clone()); + } + + let dmabuf_state = DmabufFeedbackBuilder::new(main_device, formats.clone()) + .build() + .map_or_else( + |_| { + log::info!("Falling back to zwp_linux_dmabuf_v1 version 3."); + let mut dmabuf_state = DmabufState::new(); + let dmabuf_global = + dmabuf_state.create_global::(&display.handle(), formats); + (dmabuf_state, dmabuf_global, None) + }, + |default_feedback| { + let mut dmabuf_state = DmabufState::new(); + let dmabuf_global = dmabuf_state + .create_global_with_default_feedback::( + &display.handle(), + &default_feedback, + ); + (dmabuf_state, dmabuf_global, Some(default_feedback)) + }, + ); let seat_keyboard = seat.add_keyboard( XkbConfig::default(), @@ -233,7 +220,10 @@ impl WayVR { let tasks = SyncEventQueue::new(); + let dma_importer = ImageImporter::new(gfx); + let state = Application { + image_importer: dma_importer, compositor, xdg_shell, seat_state, @@ -242,7 +232,6 @@ impl WayVR { wayvr_tasks: tasks.clone(), redraw_requests: HashSet::new(), dmabuf_state, - gles_renderer, }; let time_start = get_millis(); @@ -250,48 +239,21 @@ impl WayVR { let state = WayVRState { time_start, manager: client::WayVRCompositor::new(state, display, seat_keyboard, seat_pointer)?, - displays: DisplayVec::new(), processes: ProcessVec::new(), - egl_data: Rc::new(egl_data), wm: Rc::new(RefCell::new(window::WindowManager::new())), config, - dashboard_display: None, ticks: 0, tasks, cur_modifiers: 0, signals, + mouse_freeze: Instant::now(), + window_to_overlay: HashMap::new(), + overlay_to_window: SecondaryMap::new(), }; Ok(Self { state }) } - pub fn render_display(&mut self, display: display::DisplayHandle) -> anyhow::Result { - let display = self - .state - .displays - .get_mut(&display) - .context(STR_INVALID_HANDLE_DISP)?; - - /* Buffer warm-up is required, always two first calls of this function are always rendered */ - if !display.wants_redraw && display.rendered_frame_count >= 2 { - // Nothing changed, do not render - return Ok(false); - } - - if !display.visible { - // Display is invisible, do not render - return Ok(false); - } - - // millis since the start of wayvr - let time_ms = get_millis() - self.state.time_start; - - display.tick_render(&mut self.state.manager.state.gles_renderer, time_ms)?; - display.wants_redraw = false; - - Ok(true) - } - #[allow(clippy::too_many_lines, clippy::cognitive_complexity)] pub fn tick_events(&mut self, app: &mut AppState) -> anyhow::Result> { let mut tasks: Vec = Vec::new(); @@ -303,43 +265,17 @@ impl WayVR { signals: &app.wayvr_signals, }); - // Check for redraw events - for (_, disp) in self.state.displays.iter_mut() { - for disp_window in &disp.displayed_windows { - if self - .state - .manager - .state - .check_redraw(disp_window.toplevel.wl_surface()) - { - disp.wants_redraw = true; - } - } - } - // Tick all child processes - let mut to_remove: SmallVec<[(process::ProcessHandle, display::DisplayHandle); 2]> = - SmallVec::new(); + let mut to_remove: SmallVec<[process::ProcessHandle; 2]> = SmallVec::new(); for (handle, process) in self.state.processes.iter_mut() { if !process.is_running() { - to_remove.push((handle, process.display_handle())); + to_remove.push(handle); } } - for (p_handle, disp_handle) in &to_remove { + for p_handle in &to_remove { self.state.processes.remove(p_handle); - - if let Some(display) = self.state.displays.get_mut(disp_handle) { - display - .tasks - .send(display::DisplayTask::ProcessCleanup(*p_handle)); - display.wants_redraw = true; - } - } - - for (handle, display) in self.state.displays.iter_mut() { - display.tick(&self.state.config, &handle, &mut app.wayvr_signals); } if !to_remove.is_empty() { @@ -374,16 +310,10 @@ impl WayVR { .state .wm .borrow_mut() - .create_window(client.display_handle, &toplevel); + .create_window(&toplevel, process_handle); - let Some(display) = self.state.displays.get_mut(&client.display_handle) - else { - // This shouldn't happen, scream if it does - log::error!("Could not attach window handle into display"); - continue; - }; + //TODO: create overlay - display.add_window(window_handle, process_handle, &toplevel); app.wayvr_signals.send(WayVRSignal::BroadcastStateChanged( packet_server::WvrStateChanged::WindowCreated, )); @@ -401,18 +331,15 @@ impl WayVR { continue; }; - let Some(display) = self.state.displays.get_mut(&client.display_handle) - else { - log::warn!("DropToplevel: Couldn't find matching display"); - continue; - }; + if let Some(oid) = self.state.window_to_overlay.get(&window_handle) { + app.tasks.enqueue(TaskType::Overlay(OverlayTask::Drop( + OverlaySelector::Id(*oid), + ))); + } - display.remove_window(window_handle); wm.remove_window(window_handle); drop(wm); - - display.reposition_windows(); } } WayVRTask::ProcessTerminationRequest(process_handle) => { @@ -423,12 +350,11 @@ impl WayVR { } } - self.state - .manager - .tick_wayland(&mut self.state.displays, &mut self.state.processes)?; + self.state.manager.tick_wayland(&mut self.state.processes)?; if self.state.ticks.is_multiple_of(200) { self.state.manager.cleanup_clients(); + self.state.manager.cleanup_handles(); } self.state.ticks += 1; @@ -436,44 +362,6 @@ impl WayVR { Ok(tasks) } - pub fn tick_finish(&mut self) -> anyhow::Result<()> { - self.state - .manager - .state - .gles_renderer - .with_context(|gl| unsafe { - gl.Flush(); - gl.Finish(); - })?; - Ok(()) - } - - #[allow(dead_code)] - pub fn get_primary_display(displays: &DisplayVec) -> Option { - for (idx, cell) in displays.vec.iter().enumerate() { - if let Some(cell) = cell - && 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 - && cell.obj.name == name - { - return Some(DisplayVec::get_handle(cell, idx)); - } - } - None - } - pub fn terminate_process(&mut self, process_handle: process::ProcessHandle) { self.state .tasks @@ -482,24 +370,30 @@ impl WayVR { } impl WayVRState { - pub fn send_mouse_move(&mut self, display: display::DisplayHandle, x: u32, y: u32) { - if let Some(display) = self.displays.get(&display) { - display.send_mouse_move(&self.config, &mut self.manager, x, y); + pub fn send_mouse_move(&mut self, handle: window::WindowHandle, x: u32, y: u32) { + if self.mouse_freeze > Instant::now() { + return; + } + if let Some(window) = self.wm.borrow_mut().windows.get_mut(&handle) { + window.send_mouse_move(&mut self.manager, x, y); } } - pub fn send_mouse_down(&mut self, display: display::DisplayHandle, index: MouseIndex) { - if let Some(display) = self.displays.get_mut(&display) { - display.send_mouse_down(&mut self.manager, index); + pub fn send_mouse_down(&mut self, handle: window::WindowHandle, index: MouseIndex) { + self.mouse_freeze = + Instant::now() + Duration::from_millis(self.config.click_freeze_time_ms as _); + + if let Some(window) = self.wm.borrow_mut().windows.get_mut(&handle) { + window.send_mouse_down(&mut self.manager, index); } } pub fn send_mouse_up(&mut self, index: MouseIndex) { - Display::send_mouse_up(&mut self.manager, index); + Window::send_mouse_up(&mut self.manager, index); } pub fn send_mouse_scroll(&mut self, delta: WheelDelta) { - Display::send_mouse_scroll(&mut self.manager, delta); + Window::send_mouse_scroll(&mut self.manager, delta); } pub fn send_key(&mut self, virtual_key: u32, down: bool) { @@ -523,125 +417,9 @@ impl WayVRState { self.cur_modifiers = modifiers; } - pub fn set_display_visible(&mut self, display: display::DisplayHandle, visible: bool) { - if let Some(display) = self.displays.get_mut(&display) { - display.set_visible(visible); - } - } - - pub fn set_display_layout( - &mut self, - display: display::DisplayHandle, - layout: packet_server::WvrDisplayWindowLayout, - ) { - if let Some(display) = self.displays.get_mut(&display) { - display.set_layout(layout); - } - } - - pub fn get_render_data( - &self, - display: display::DisplayHandle, - ) -> Option<&egl_data::RenderData> { - self.displays - .get(&display) - .map(|display| &display.render_data) - } - - pub fn create_display( - &mut self, - width: u16, - height: u16, - name: &str, - primary: bool, - ) -> anyhow::Result { - let display = display::Display::new(DisplayInitParams { - wm: self.wm.clone(), - egl_data: self.egl_data.clone(), - renderer: &mut self.manager.state.gles_renderer, - wayland_env: self.manager.wayland_env.clone(), - config: &self.config, - width, - height, - name, - primary, - })?; - let handle = self.displays.add(display); - - self.signals.send(WayVRSignal::BroadcastStateChanged( - packet_server::WvrStateChanged::DisplayCreated, - )); - - Ok(handle) - } - - pub fn destroy_display(&mut self, handle: display::DisplayHandle) -> anyhow::Result<()> { - let Some(display) = self.displays.get(&handle) else { - anyhow::bail!("Display not found"); - }; - - if let Some(overlay_id) = display.overlay_id { - self.signals.send(WayVRSignal::DropOverlay(overlay_id)); - } else { - log::warn!("Destroying display without OverlayID set"); // This shouldn't happen, but log it anyways. - } - - let mut process_names = Vec::::new(); - - for (_, process) in self.processes.iter_mut() { - if process.display_handle() == handle { - process_names.push(process.get_name()); - } - } - - if !display.displayed_windows.is_empty() || !process_names.is_empty() { - anyhow::bail!( - "Display is not empty. Attached processes: {}", - process_names.join(", ") - ); - } - - self.manager.cleanup_clients(); - - for client in &self.manager.clients { - if client.display_handle == handle { - // This shouldn't happen, but make sure we are all set to destroy this display - anyhow::bail!("Wayland client still exists"); - } - } - - self.displays.remove(&handle); - - self.signals.send(WayVRSignal::BroadcastStateChanged( - packet_server::WvrStateChanged::DisplayRemoved, - )); - - Ok(()) - } - - pub fn get_or_create_dashboard_display( - &mut self, - width: u16, - height: u16, - name: &str, - ) -> anyhow::Result<(bool /* newly created? */, display::DisplayHandle)> { - if let Some(handle) = &self.dashboard_display { - // ensure it still exists - if self.displays.get(handle).is_some() { - return Ok((false, *handle)); - } - } - - let new_disp = self.create_display(width, height, name, false)?; - self.dashboard_display = Some(new_disp); - - Ok((true, new_disp)) - } - // Check if process with given arguments already exists pub fn process_query( &self, - display_handle: display::DisplayHandle, exec_path: &str, args: &[&str], _env: &[(&str, &str)], @@ -650,10 +428,7 @@ impl WayVRState { if let Some(cell) = &cell && let process::Process::Managed(process) = &cell.obj { - if process.display_handle != display_handle - || process.exec_path != exec_path - || process.args != args - { + if process.exec_path != exec_path || process.args != args { continue; } return Some(process::ProcessVec::get_handle(cell, idx)); @@ -663,40 +438,39 @@ impl WayVRState { None } - pub fn add_external_process( - &mut self, - display_handle: display::DisplayHandle, - pid: u32, - ) -> process::ProcessHandle { + pub fn add_external_process(&mut self, pid: u32) -> process::ProcessHandle { self.processes - .add(process::Process::External(process::ExternalProcess { - pid, - display_handle, - })) + .add(process::Process::External(process::ExternalProcess { pid })) } pub fn spawn_process( &mut self, - display_handle: display::DisplayHandle, exec_path: &str, args: &[&str], env: &[(&str, &str)], working_dir: Option<&str>, userdata: HashMap, ) -> anyhow::Result { - let display = self - .displays - .get_mut(&display_handle) - .context(STR_INVALID_HANDLE_DISP)?; + let auth_key = generate_auth_key(); - let res = display.spawn_process(exec_path, args, env, working_dir)?; + let mut cmd = std::process::Command::new(exec_path); + self.configure_env(&mut cmd, auth_key.as_str()); + cmd.args(args); + if let Some(working_dir) = working_dir { + cmd.current_dir(working_dir); + } + + for e in env { + cmd.env(e.0, e.1); + } + + let child = cmd.spawn().context("Failed to spawn child process")?; let handle = self .processes .add(process::Process::Managed(process::WayVRProcess { - auth_key: res.auth_key, - child: res.child, - display_handle, + auth_key, + child, exec_path: String::from(exec_path), userdata, args: args.iter().map(|x| String::from(*x)).collect(), @@ -713,6 +487,25 @@ impl WayVRState { Ok(handle) } + + fn configure_env(&self, cmd: &mut std::process::Command, auth_key: &str) { + cmd.env_remove("DISPLAY"); // Goodbye X11 + cmd.env( + "WAYLAND_DISPLAY", + self.manager.wayland_env.display_num_string(), + ); + cmd.env("WAYVR_DISPLAY_AUTH", auth_key); + } +} + +fn generate_auth_key() -> String { + let uuid = uuid::Uuid::new_v4(); + uuid.to_string() +} + +pub struct SpawnProcessResult { + pub auth_key: String, + pub child: std::process::Child, } #[derive(Deserialize, Clone)] @@ -733,3 +526,34 @@ pub enum WayVRAction { }, ToggleDashboard, } + +struct SurfaceBufWithImageContainer { + inner: RefCell, +} + +#[derive(Clone)] +pub struct SurfaceBufWithImage { + buffer: wl_buffer::WlBuffer, + pub image: Arc, + pub transform: Transform, + pub scale: i32, +} + +impl SurfaceBufWithImage { + fn apply_to_surface(self, surface_data: &SurfaceData) { + let container = surface_data.data_map.get_or_insert(|| unsafe { + SurfaceBufWithImageContainer { + inner: RefCell::new(MaybeUninit::uninit().assume_init()), + } + }); + + container.inner.replace(self); + } + + pub fn get_from_surface(surface_data: &SurfaceData) -> Option { + surface_data + .data_map + .get::() + .map(|x| x.inner.borrow().clone()) + } +} diff --git a/wlx-overlay-s/src/backend/wayvr/process.rs b/wlx-overlay-s/src/backend/wayvr/process.rs index 1d5dded4..44f9623d 100644 --- a/wlx-overlay-s/src/backend/wayvr/process.rs +++ b/wlx-overlay-s/src/backend/wayvr/process.rs @@ -4,14 +4,11 @@ use wayvr_ipc::packet_server; use crate::gen_id; -use super::display; - #[derive(Debug)] #[allow(dead_code)] pub struct WayVRProcess { pub auth_key: String, pub child: std::process::Child, - pub display_handle: display::DisplayHandle, pub exec_path: String, pub args: Vec, @@ -24,7 +21,6 @@ pub struct WayVRProcess { #[derive(Debug)] pub struct ExternalProcess { pub pid: u32, - pub display_handle: display::DisplayHandle, } #[derive(Debug)] @@ -34,13 +30,6 @@ pub enum Process { } impl Process { - pub const fn display_handle(&self) -> display::DisplayHandle { - match self { - Self::Managed(p) => p.display_handle, - Self::External(p) => p.display_handle, - } - } - pub fn is_running(&mut self) -> bool { match self { Self::Managed(p) => p.is_running(), @@ -67,13 +56,11 @@ impl Process { Self::Managed(p) => packet_server::WvrProcess { name: p.get_name().unwrap_or_else(|| String::from("unknown")), userdata: p.userdata.clone(), - display_handle: p.display_handle.as_packet(), handle: handle.as_packet(), }, Self::External(p) => packet_server::WvrProcess { name: p.get_name().unwrap_or_else(|| String::from("unknown")), userdata: HashMap::default(), - display_handle: p.display_handle.as_packet(), handle: handle.as_packet(), }, } diff --git a/wlx-overlay-s/src/backend/wayvr/smithay_wrapper.rs b/wlx-overlay-s/src/backend/wayvr/smithay_wrapper.rs deleted file mode 100644 index 145b27ce..00000000 --- a/wlx-overlay-s/src/backend/wayvr/smithay_wrapper.rs +++ /dev/null @@ -1,54 +0,0 @@ -use super::egl_data; -use smithay::backend::{egl as smithay_egl, renderer::gles::ffi}; - -pub fn get_egl_display(data: &egl_data::EGLData) -> anyhow::Result { - Ok(unsafe { smithay_egl::EGLDisplay::from_raw(data.display.as_ptr(), data.config.as_ptr())? }) -} - -pub fn get_egl_context( - data: &egl_data::EGLData, - display: &smithay_egl::EGLDisplay, -) -> anyhow::Result { - let display_ptr = display.get_display_handle().handle; - debug_assert!(std::ptr::eq(display_ptr, data.display.as_ptr())); - let config_ptr = data.config.as_ptr(); - let context_ptr = data.context.as_ptr(); - Ok(unsafe { smithay_egl::EGLContext::from_raw(display_ptr, config_ptr, context_ptr)? }) -} - -pub fn create_framebuffer_texture( - gl: &ffi::Gles2, - width: u32, - height: u32, - tex_format: u32, - internal_format: u32, -) -> u32 { - unsafe { - let mut tex = 0; - gl.GenTextures(1, &raw mut tex); - gl.BindTexture(ffi::TEXTURE_2D, tex); - gl.TexParameteri( - ffi::TEXTURE_2D, - ffi::TEXTURE_MIN_FILTER, - ffi::NEAREST as i32, - ); - gl.TexParameteri( - ffi::TEXTURE_2D, - ffi::TEXTURE_MAG_FILTER, - ffi::NEAREST as i32, - ); - gl.TexImage2D( - ffi::TEXTURE_2D, - 0, - internal_format as i32, - width as i32, - height as i32, - 0, - tex_format, - ffi::UNSIGNED_BYTE, - std::ptr::null(), - ); - gl.BindTexture(ffi::TEXTURE_2D, 0); - tex - } -} diff --git a/wlx-overlay-s/src/backend/wayvr/window.rs b/wlx-overlay-s/src/backend/wayvr/window.rs index 1b0afed3..96f09871 100644 --- a/wlx-overlay-s/src/backend/wayvr/window.rs +++ b/wlx-overlay-s/src/backend/wayvr/window.rs @@ -1,39 +1,36 @@ -use smithay::wayland::shell::xdg::ToplevelSurface; +use smithay::{ + input, + utils::{Logical, Point}, + wayland::shell::xdg::ToplevelSurface, +}; use wayvr_ipc::packet_server; -use crate::gen_id; - -use super::display; +use crate::{ + backend::wayvr::{client::WayVRCompositor, process}, + gen_id, + subsystem::hid::WheelDelta, +}; #[derive(Debug)] pub struct Window { - pub pos_x: i32, - pub pos_y: i32, pub size_x: u32, pub size_y: u32, pub visible: bool, pub toplevel: ToplevelSurface, - pub display_handle: display::DisplayHandle, + pub process: process::ProcessHandle, } impl Window { - pub fn new(display_handle: display::DisplayHandle, toplevel: &ToplevelSurface) -> Self { + fn new(toplevel: &ToplevelSurface, process: process::ProcessHandle) -> Self { Self { - pos_x: 0, - pos_y: 0, size_x: 0, size_y: 0, visible: true, toplevel: toplevel.clone(), - display_handle, + process, } } - pub const fn set_pos(&mut self, pos_x: i32, pos_y: i32) { - self.pos_x = pos_x; - self.pos_y = pos_y; - } - pub fn set_size(&mut self, size_x: u32, size_y: u32) { self.toplevel.with_pending_state(|state| { //state.bounds = Some((size_x as i32, size_y as i32).into()); @@ -44,6 +41,86 @@ impl Window { self.size_x = size_x; self.size_y = size_y; } + + pub fn send_mouse_move(&self, manager: &mut WayVRCompositor, x: u32, y: u32) { + let surf = self.toplevel.wl_surface().clone(); + let point = Point::::from((f64::from(x as i32), f64::from(y as i32))); + + manager.seat_pointer.motion( + &mut manager.state, + Some((surf, Point::from((0.0, 0.0)))), + &input::pointer::MotionEvent { + serial: manager.serial_counter.next_serial(), + time: 0, + location: point, + }, + ); + + manager.seat_pointer.frame(&mut manager.state); + } + + const fn get_mouse_index_number(index: super::MouseIndex) -> u32 { + match index { + super::MouseIndex::Left => 0x110, /* BTN_LEFT */ + super::MouseIndex::Center => 0x112, /* BTN_MIDDLE */ + super::MouseIndex::Right => 0x111, /* BTN_RIGHT */ + } + } + + pub fn send_mouse_down(&mut self, manager: &mut WayVRCompositor, index: super::MouseIndex) { + let surf = self.toplevel.wl_surface().clone(); + + // Change keyboard focus to pressed window + manager.seat_keyboard.set_focus( + &mut manager.state, + Some(surf), + manager.serial_counter.next_serial(), + ); + + manager.seat_pointer.button( + &mut manager.state, + &input::pointer::ButtonEvent { + button: Self::get_mouse_index_number(index), + serial: manager.serial_counter.next_serial(), + time: 0, + state: smithay::backend::input::ButtonState::Pressed, + }, + ); + + manager.seat_pointer.frame(&mut manager.state); + } + + pub fn send_mouse_up(manager: &mut WayVRCompositor, index: super::MouseIndex) { + manager.seat_pointer.button( + &mut manager.state, + &input::pointer::ButtonEvent { + button: Self::get_mouse_index_number(index), + serial: manager.serial_counter.next_serial(), + time: 0, + state: smithay::backend::input::ButtonState::Released, + }, + ); + + manager.seat_pointer.frame(&mut manager.state); + } + + pub fn send_mouse_scroll(manager: &mut WayVRCompositor, delta: WheelDelta) { + manager.seat_pointer.axis( + &mut manager.state, + input::pointer::AxisFrame { + source: None, + relative_direction: ( + smithay::backend::input::AxisRelativeDirection::Identical, + smithay::backend::input::AxisRelativeDirection::Identical, + ), + time: 0, + axis: (f64::from(delta.x), f64::from(-delta.y)), + v120: Some((0, (delta.y * -64.0) as i32)), + stop: (false, false), + }, + ); + manager.seat_pointer.frame(&mut manager.state); + } } #[derive(Debug)] @@ -72,10 +149,10 @@ impl WindowManager { pub fn create_window( &mut self, - display_handle: display::DisplayHandle, toplevel: &ToplevelSurface, + process: process::ProcessHandle, ) -> WindowHandle { - self.windows.add(Window::new(display_handle, toplevel)) + self.windows.add(Window::new(toplevel, process)) } pub fn remove_window(&mut self, window_handle: WindowHandle) { diff --git a/wlx-overlay-s/src/config_wayvr.rs b/wlx-overlay-s/src/config_wayvr.rs index 34dcf742..caed1a36 100644 --- a/wlx-overlay-s/src/config_wayvr.rs +++ b/wlx-overlay-s/src/config_wayvr.rs @@ -10,6 +10,7 @@ use std::{ use anyhow::Context; use serde::{Deserialize, Serialize}; +use wgui::gfx::WGfx; use wlx_common::{common::LeftRight, config::GeneralConfig, windowing::Positioning}; use crate::{ @@ -19,8 +20,9 @@ use crate::{ }, config::load_config_with_conf_d, config_io, + graphics::WGfxExtras, ipc::{event_queue::SyncEventQueue, signal::WayVRSignal}, - overlays::wayvr::{WayVRData, executable_exists_in_path}, + overlays::wayvr::WayVRData, }; // Flat version of RelativeTo @@ -199,6 +201,8 @@ impl WayVRConfig { pub fn post_load( &self, + gfx: Arc, + gfx_extras: &WGfxExtras, config: &GeneralConfig, tasks: &mut TaskContainer, signals: SyncEventQueue, @@ -227,6 +231,8 @@ impl WayVRConfig { } Ok(Rc::new(RefCell::new(WayVRData::new( + gfx, + gfx_extras, Self::get_wayvr_config(config, self)?, signals, )?))) @@ -248,6 +254,19 @@ fn get_default_dashboard_exec() -> ( (String::from("wayvr-dashboard"), None) } +pub fn executable_exists_in_path(command: &str) -> bool { + let Ok(path) = std::env::var("PATH") else { + return false; // very unlikely to happen + }; + for dir in path.split(':') { + let exec_path = std::path::PathBuf::from(dir).join(command); + if exec_path.exists() && exec_path.is_file() { + return true; // executable found + } + } + false +} + pub fn load_wayvr() -> WayVRConfig { let config_root_path = config_io::ConfigRoot::WayVR.ensure_dir(); log::info!("WayVR Config root path: {}", config_root_path.display()); diff --git a/wlx-overlay-s/src/graphics/dmabuf.rs b/wlx-overlay-s/src/graphics/dmabuf.rs index c0f30a88..671be50e 100644 --- a/wlx-overlay-s/src/graphics/dmabuf.rs +++ b/wlx-overlay-s/src/graphics/dmabuf.rs @@ -5,7 +5,7 @@ use std::{ }; use anyhow::Context; -use smallvec::SmallVec; +use smallvec::{SmallVec, smallvec}; use vulkano::{ VulkanError, VulkanObject, device::Device, @@ -19,12 +19,7 @@ use vulkano::{ sync::Sharing, }; use wgui::gfx::WGfx; -use wlx_capture::frame::{ - DRM_FORMAT_ABGR8888, DRM_FORMAT_ABGR2101010, DRM_FORMAT_ARGB8888, DRM_FORMAT_XBGR8888, - DRM_FORMAT_XBGR2101010, DRM_FORMAT_XRGB8888, DmabufFrame, DrmFormat, FourCC, -}; - -pub const DRM_FORMAT_MOD_INVALID: u64 = 0xff_ffff_ffff_ffff; +use wlx_capture::{DrmFormat, DrmFourcc, DrmModifier, frame::DmabufFrame}; pub trait WGfxDmabuf { fn dmabuf_texture_ex( @@ -47,7 +42,7 @@ impl WGfxDmabuf for WGfx { modifiers: &[u64], ) -> anyhow::Result> { let extent = [frame.format.width, frame.format.height, 1]; - let format = fourcc_to_vk(frame.format.fourcc)?; + let format = fourcc_to_vk(frame.format.drm_format.code)?; let image = unsafe { create_dmabuf_image( @@ -116,11 +111,11 @@ impl WGfxDmabuf for WGfx { } fn dmabuf_texture(&self, frame: DmabufFrame) -> anyhow::Result> { - let mut modifiers: Vec = vec![]; + let mut modifiers: SmallVec<[u64; 4]> = smallvec![]; let mut tiling: ImageTiling = ImageTiling::Optimal; let mut layouts: Vec = vec![]; - if frame.format.modifier != DRM_FORMAT_MOD_INVALID { + if !matches!(frame.format.drm_format.modifier, DrmModifier::Invalid) { (0..frame.num_planes).for_each(|i| { let plane = &frame.planes[i]; layouts.push(SubresourceLayout { @@ -130,7 +125,7 @@ impl WGfxDmabuf for WGfx { array_pitch: None, depth_pitch: None, }); - modifiers.push(frame.format.modifier); + modifiers.push(frame.format.drm_format.modifier.into()); }); tiling = ImageTiling::DrmFormatModifier; } @@ -304,50 +299,56 @@ pub(super) unsafe fn create_dmabuf_image( } } -pub fn get_drm_formats(device: Arc) -> Vec { +pub(super) fn get_drm_formats(device: Arc) -> Vec { let possible_formats = [ - DRM_FORMAT_ABGR8888.into(), - DRM_FORMAT_XBGR8888.into(), - DRM_FORMAT_ARGB8888.into(), - DRM_FORMAT_XRGB8888.into(), - DRM_FORMAT_ABGR2101010.into(), - DRM_FORMAT_XBGR2101010.into(), + DrmFourcc::Abgr8888, + DrmFourcc::Xbgr8888, + DrmFourcc::Argb8888, + DrmFourcc::Xrgb8888, + DrmFourcc::Abgr2101010, + DrmFourcc::Xbgr2101010, ]; - let mut final_formats = vec![]; + let mut out_formats = vec![]; - for &f in &possible_formats { - let Ok(vk_fmt) = fourcc_to_vk(f) else { + for &code in &possible_formats { + let Ok(vk_fmt) = fourcc_to_vk(code) else { continue; }; let Ok(props) = device.physical_device().format_properties(vk_fmt) else { continue; }; - let mut fmt = DrmFormat { - fourcc: f, - modifiers: props - .drm_format_modifier_properties - .iter() - // important bit: only allow single-plane - .filter(|m| m.drm_format_modifier_plane_count == 1) - .map(|m| m.drm_format_modifier) - .collect(), - }; - fmt.modifiers.push(DRM_FORMAT_MOD_INVALID); // implicit modifiers support - final_formats.push(fmt); + + for m in props + .drm_format_modifier_properties + .iter() + .filter(|m| m.drm_format_modifier_plane_count == 1) + .map(|m| m.drm_format_modifier) + { + out_formats.push(DrmFormat { + code, + modifier: DrmModifier::from(m), + }); + } + + // accept implicit modifiers + out_formats.push(DrmFormat { + code, + modifier: DrmModifier::Invalid, + }); } log::debug!("Supported DRM formats:"); - for f in &final_formats { - log::debug!(" {} {:?}", f.fourcc, f.modifiers); + for f in &out_formats { + log::debug!(" {} {:?}", f.code, f.modifier); } - final_formats + out_formats } -pub fn fourcc_to_vk(fourcc: FourCC) -> anyhow::Result { - match fourcc.value { - DRM_FORMAT_ABGR8888 | DRM_FORMAT_XBGR8888 => Ok(Format::R8G8B8A8_UNORM), - DRM_FORMAT_ARGB8888 | DRM_FORMAT_XRGB8888 => Ok(Format::B8G8R8A8_UNORM), - DRM_FORMAT_ABGR2101010 | DRM_FORMAT_XBGR2101010 => Ok(Format::A2B10G10R10_UNORM_PACK32), +pub fn fourcc_to_vk(fourcc: DrmFourcc) -> anyhow::Result { + match fourcc { + DrmFourcc::Abgr8888 | DrmFourcc::Xbgr8888 => Ok(Format::R8G8B8A8_UNORM), + DrmFourcc::Argb8888 | DrmFourcc::Xrgb8888 => Ok(Format::B8G8R8A8_UNORM), + DrmFourcc::Abgr2101010 | DrmFourcc::Xbgr2101010 => Ok(Format::A2B10G10R10_UNORM_PACK32), _ => anyhow::bail!("Unsupported format {fourcc}"), } } diff --git a/wlx-overlay-s/src/graphics/mod.rs b/wlx-overlay-s/src/graphics/mod.rs index a73a7942..f25a5bd9 100644 --- a/wlx-overlay-s/src/graphics/mod.rs +++ b/wlx-overlay-s/src/graphics/mod.rs @@ -19,7 +19,7 @@ use wgui::gfx::WGfx; #[cfg(feature = "openvr")] use vulkano::instance::InstanceCreateFlags; -use wlx_capture::frame::DrmFormat; +use wlx_capture::DrmFormat; use crate::shaders::{frag_color, frag_grid, frag_screen, frag_srgb, vert_quad}; @@ -73,6 +73,7 @@ pub struct WGfxExtras { pub queue_capture: Option>, pub quad_verts: Vert2Buf, pub fallback_image: Arc, + pub drm_device: Option<(i64, i64)>, } impl WGfxExtras { @@ -135,12 +136,23 @@ impl WGfxExtras { let fallback_image = ImageView::new_default(fallback_image)?; + let p = gfx.device.physical_device().properties(); + + let drm_device = if let (Some(maj), Some(min)) = (p.render_major, p.render_minor) { + log::info!("DRM render device: {maj} {min}"); + Some((maj, min)) + } else { + log::warn!("No DRM device."); + None + }; + Ok(Self { shaders, drm_formats, queue_capture, quad_verts, fallback_image, + drm_device, }) } } @@ -271,6 +283,13 @@ pub fn init_openxr_graphics( .ext_image_drm_format_modifier; } + if physical_device + .supported_extensions() + .ext_physical_device_drm + { + device_extensions.ext_physical_device_drm = true; + } + let device_extensions_raw = device_extensions .into_iter() .filter_map(|(name, enabled)| { @@ -432,6 +451,11 @@ pub fn init_openvr_graphics( my_extensions.ext_filter_cubic = true; } + if p.supported_extensions().ext_physical_device_drm { + // needed for wayland_server + my_extensions.ext_physical_device_drm = true; + } + log::debug!( "Device exts for {}: {:?}", p.properties().device_name, diff --git a/wlx-overlay-s/src/ipc/events.rs b/wlx-overlay-s/src/ipc/events.rs index 4c87e3e9..456c6fd9 100644 --- a/wlx-overlay-s/src/ipc/events.rs +++ b/wlx-overlay-s/src/ipc/events.rs @@ -3,9 +3,7 @@ use std::{cell::RefCell, rc::Rc}; use wayvr_ipc::packet_server; #[cfg(feature = "wayvr")] -use crate::{ - backend::wayvr, config_wayvr, overlays::wayvr::OverlayToCreate, overlays::wayvr::WayVRData, -}; +use crate::{backend::wayvr, overlays::wayvr::WayVRData}; use crate::{ backend::{ @@ -13,89 +11,32 @@ use crate::{ task::{InputTask, 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); + log::info!("Registering external process with PID {}", request.pid); - let disp_handle = match disp_handle { - Some(d) => d, - None => wayvr.data.state.create_display( - cpar.width, - cpar.height, - &unique_name, - false, - )?, - }; + wayvr.data.state.add_external_process(request.pid); - 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, - }, - }); + wayvr + .data + .state + .manager + .add_client(wayvr::client::WayVRClient { + client: request.client, + pid: request.pid, + }); } } } @@ -103,10 +44,9 @@ fn process_tick_tasks( Ok(()) } -#[allow(clippy::too_many_lines)] pub fn tick_events( app: &mut AppState, - overlays: &mut OverlayWindowManager, + _overlays: &mut OverlayWindowManager, ) -> anyhow::Result<()> where O: Default, @@ -116,51 +56,11 @@ where while let Some(signal) = app.wayvr_signals.read() { match signal { - #[cfg(feature = "wayvr")] - WayVRSignal::DisplayVisibility(display_handle, visible) => { - if let Some(mut wayland_server) = wayland_server.as_ref().map(|r| r.borrow_mut()) - && let Some(overlay_id) = wayland_server.display_handle_map.get(&display_handle) - { - let overlay_id = *overlay_id; - wayland_server - .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) => { - if let Some(mut wayland_server) = wayland_server.as_ref().map(|r| r.borrow_mut()) { - wayland_server - .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) => { - if let Some(mut wayland_server) = wayland_server.as_ref().map(|r| r.borrow_mut()) { - wayland_server.pending_haptics = Some(haptics); - } - } WayVRSignal::DeviceHaptics(device, haptics) => { app.tasks .enqueue(TaskType::Input(InputTask::Haptics { device, haptics })); @@ -182,13 +82,7 @@ where { if let Some(wayland_server) = wayland_server { let tick_tasks = wayland_server.borrow_mut().data.tick_events(app)?; - process_tick_tasks(app, tick_tasks, &wayland_server)?; - - overlays::wayvr::create_queued_displays( - app, - &mut wayland_server.borrow_mut(), - overlays, - )?; + process_tick_tasks(tick_tasks, &wayland_server)?; } } diff --git a/wlx-overlay-s/src/ipc/ipc_server.rs b/wlx-overlay-s/src/ipc/ipc_server.rs index 9ee919d1..3532d285 100644 --- a/wlx-overlay-s/src/ipc/ipc_server.rs +++ b/wlx-overlay-s/src/ipc/ipc_server.rs @@ -156,41 +156,6 @@ impl Connection { Ok(()) } - #[cfg(feature = "wayvr")] - fn handle_wvr_display_list( - &mut self, - params: &TickParams, - serial: ipc::Serial, - ) -> anyhow::Result<()> { - let list: Vec = params - .wayland_state - .displays - .vec - .iter() - .enumerate() - .filter_map(|(idx, opt_cell)| { - let Some(cell) = opt_cell else { - return None; - }; - let display = &cell.obj; - Some(display.as_packet(wayvr::display::DisplayHandle::new( - idx as u32, - cell.generation, - ))) - }) - .collect(); - - send_packet( - &mut self.conn, - &ipc::data_encode(&PacketServer::WvrDisplayListResponse( - serial, - packet_server::WvrDisplayList { list }, - )), - )?; - - Ok(()) - } - fn handle_wlx_input_state( &mut self, params: &TickParams, @@ -218,125 +183,31 @@ impl Connection { } #[cfg(feature = "wayvr")] - fn handle_wvr_display_create( + fn handle_wvr_window_list( &mut self, params: &mut TickParams, serial: ipc::Serial, - packet_params: packet_client::WvrDisplayCreateParams, - ) -> anyhow::Result<()> { - let display_handle = params.wayland_state.create_display( - packet_params.width, - packet_params.height, - &packet_params.name, - false, - )?; - - params.tasks.push(wayvr::TickTask::NewDisplay( - packet_params, - Some(display_handle), - )); - - send_packet( - &mut self.conn, - &ipc::data_encode(&PacketServer::WvrDisplayCreateResponse( - serial, - display_handle.as_packet(), - )), - )?; - Ok(()) - } - - #[cfg(feature = "wayvr")] - fn handle_wvr_display_remove( - &mut self, - params: &mut TickParams, - serial: ipc::Serial, - handle: packet_server::WvrDisplayHandle, - ) -> anyhow::Result<()> { - let res = params - .wayland_state - .destroy_display(wayvr::display::DisplayHandle::from_packet(handle)) - .map_err(|e| format!("{e:?}")); - - send_packet( - &mut self.conn, - &ipc::data_encode(&PacketServer::WvrDisplayRemoveResponse(serial, res)), - )?; - Ok(()) - } - - #[cfg(feature = "wayvr")] - fn handle_wvr_display_set_visible( - params: &mut TickParams, - handle: packet_server::WvrDisplayHandle, - visible: bool, - ) { - 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.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, - serial: ipc::Serial, - display_handle: packet_server::WvrDisplayHandle, ) -> anyhow::Result<()> { let mut send = |list: Option| -> anyhow::Result<()> { send_packet( &mut self.conn, - &ipc::data_encode(&PacketServer::WvrDisplayWindowListResponse(serial, list)), + &ipc::data_encode(&PacketServer::WvrWindowListResponse(serial, list)), ) }; - let Some(display) = - params - .wayland_state - .displays - .get(&wayvr::display::DisplayHandle::from_packet( - display_handle.clone(), - )) - else { - return send(None); - }; - send(Some(packet_server::WvrWindowList { - list: display - .displayed_windows + list: params + .wayland_state + .wm + .borrow_mut() + .windows .iter() - .filter_map(|disp_win| { - params - .wayland_state - .wm - .borrow_mut() - .windows - .get(&disp_win.window_handle) - .map(|win| packet_server::WvrWindow { - 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, - pos_y: win.pos_y, - size_x: win.size_x, - size_y: win.size_y, - visible: win.visible, - display_handle: display_handle.clone(), - }) + .map(|(handle, win)| packet_server::WvrWindow { + handle: wayvr::window::WindowHandle::as_packet(&handle), + process_handle: wayvr::process::ProcessHandle::as_packet(&win.process), + size_x: win.size_x, + size_y: win.size_y, + visible: win.visible, }) .collect::>(), })) @@ -348,7 +219,7 @@ impl Connection { handle: packet_server::WvrWindowHandle, visible: bool, ) { - let to_resize = if let Some(window) = params + if let Some(window) = params .wayland_state .wm .borrow_mut() @@ -356,16 +227,6 @@ impl Connection { .get_mut(&wayvr::window::WindowHandle::from_packet(handle)) { window.visible = visible; - Some(window.display_handle) - } else { - None - }; - - if let Some(to_resize) = to_resize - && let Some(display) = params.wayland_state.displays.get_mut(&to_resize) - { - display.reposition_windows(); - display.trigger_rerender(); } } @@ -380,7 +241,6 @@ impl Connection { let env_vec = gen_env_vec(&packet_params.env); let res = params.wayland_state.spawn_process( - wayvr::display::DisplayHandle::from_packet(packet_params.target_display), &packet_params.exec, &args_vec, &env_vec, @@ -398,28 +258,6 @@ 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 = &wayvr::display::DisplayHandle::from_packet(display_handle); - let disp = params - .wayland_state - .displays - .get(native_handle) - .map(|disp| disp.as_packet(*native_handle)); - - send_packet( - &mut self.conn, - &ipc::data_encode(&PacketServer::WvrDisplayGetResponse(serial, disp)), - )?; - - Ok(()) - } - #[cfg(feature = "wayvr")] fn handle_wvr_process_list( &mut self, @@ -493,20 +331,6 @@ impl Connection { Ok(()) } - #[cfg(feature = "wayvr")] - fn handle_wlx_haptics( - params: &mut TickParams, - haptics_params: packet_client::WlxHapticsParams, - ) { - params - .signals - .send(WayVRSignal::Haptics(crate::backend::input::Haptics { - duration: haptics_params.duration, - frequency: haptics_params.frequency, - intensity: haptics_params.intensity, - })); - } - fn handle_wlx_device_haptics( params: &mut TickParams, device: usize, @@ -569,29 +393,9 @@ impl Connection { PacketClient::WlxInputState(serial) => { self.handle_wlx_input_state(params, serial)?; } - PacketClient::WvrDisplayList(serial) => { + PacketClient::WvrWindowList(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)?; + self.handle_wvr_window_list(params, serial)?; } PacketClient::WvrWindowSetVisible(window_handle, visible) => { #[cfg(feature = "wayvr")] @@ -609,18 +413,10 @@ impl Connection { #[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::WlxDeviceHaptics(device, haptics_params) => { Self::handle_wlx_device_haptics(params, device, haptics_params); } diff --git a/wlx-overlay-s/src/ipc/signal.rs b/wlx-overlay-s/src/ipc/signal.rs index 9cdb69a6..aaac9d00 100644 --- a/wlx-overlay-s/src/ipc/signal.rs +++ b/wlx-overlay-s/src/ipc/signal.rs @@ -1,19 +1,7 @@ -#[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), DeviceHaptics(usize, crate::backend::input::Haptics), DropOverlay(crate::windowing::OverlayID), CustomTask(crate::backend::task::ModifyPanelTask), diff --git a/wlx-overlay-s/src/overlays/screen/backend.rs b/wlx-overlay-s/src/overlays/screen/backend.rs index 4f943be5..b2abbcfb 100644 --- a/wlx-overlay-s/src/overlays/screen/backend.rs +++ b/wlx-overlay-s/src/overlays/screen/backend.rs @@ -222,9 +222,13 @@ impl OverlayBackend for ScreenBackend { fn render(&mut self, app: &mut AppState, rdr: &mut RenderResources) -> anyhow::Result<()> { // want panic; must be some if should_render was not Unable let capture = self.cur_frame.as_ref().unwrap(); + let image = capture.image.clone(); // want panic; must be Some if cur_frame is also Some - self.pipeline.as_mut().unwrap().render(&capture, app, rdr)?; + self.pipeline + .as_mut() + .unwrap() + .render(image, capture.mouse.as_ref(), app, rdr)?; self.capture.request_new_frame(); Ok(()) } diff --git a/wlx-overlay-s/src/overlays/screen/capture.rs b/wlx-overlay-s/src/overlays/screen/capture.rs index baee1c34..b56eb90d 100644 --- a/wlx-overlay-s/src/overlays/screen/capture.rs +++ b/wlx-overlay-s/src/overlays/screen/capture.rs @@ -17,8 +17,8 @@ use wgui::gfx::{ pipeline::{WGfxPipeline, WPipelineCreateInfo}, }; use wlx_capture::{ - WlxCapture, - frame::{self as wlx_frame, DrmFormat, FrameFormat, MouseMeta, Transform, WlxFrame}, + DrmFormat, WlxCapture, + frame::{self as wlx_frame, FrameFormat, MouseMeta, WlxFrame}, }; use wlx_common::{config::GeneralConfig, overlays::StereoMode}; @@ -39,7 +39,8 @@ struct BufPass { buf_vert: Subbuffer<[Vert2Uv]>, } -pub(super) struct ScreenPipeline { +/// A render pipeline that supports mouse + stereo +pub struct ScreenPipeline { mouse: BufPass, pass: SmallVec<[BufPass; 2]>, pipeline: Arc>, @@ -49,11 +50,7 @@ pub(super) struct ScreenPipeline { } impl ScreenPipeline { - pub(super) fn new( - meta: &FrameMeta, - app: &mut AppState, - stereo: StereoMode, - ) -> anyhow::Result { + pub fn new(meta: &FrameMeta, app: &mut AppState, stereo: StereoMode) -> anyhow::Result { let extentf = [meta.extent[0] as f32, meta.extent[1] as f32]; let pipeline = app.gfx.create_pipeline( @@ -198,13 +195,13 @@ impl ScreenPipeline { Ok(BufPass { pass, buf_vert }) } - pub(super) fn render( + pub fn render( &mut self, - capture: &WlxCaptureOut, + image: Arc, + mouse: Option<&MouseMeta>, app: &mut AppState, rdr: &mut RenderResources, ) -> anyhow::Result<()> { - let view = ImageView::new_default(capture.image.clone())?; self.buf_alpha.write()?[0] = rdr.alpha; for (eye, cmd_buf) in rdr.cmd_bufs.iter_mut().enumerate() { @@ -212,11 +209,11 @@ impl ScreenPipeline { current .pass - .update_sampler(0, view.clone(), app.gfx.texture_filter)?; + .update_sampler(0, image.clone(), app.gfx.texture_filter)?; cmd_buf.run_ref(¤t.pass)?; - if let Some(mouse) = capture.mouse.as_ref() { + if let Some(mouse) = mouse.as_ref() { let size = CURSOR_SIZE * self.extentf[1]; let half_size = size * 0.5; @@ -325,10 +322,10 @@ impl WlxCaptureIn { } #[derive(Clone)] -pub struct WlxCaptureOut { - image: Arc, - format: FrameFormat, - mouse: Option, +pub(super) struct WlxCaptureOut { + pub(super) image: Arc, + pub(super) format: FrameFormat, + pub(super) mouse: Option, } impl WlxCaptureOut { @@ -340,10 +337,6 @@ impl WlxCaptureOut { format: self.image.format(), } } - - pub(super) const fn get_transform(&self) -> Transform { - self.format.transform - } } fn upload_image( @@ -390,7 +383,7 @@ pub(super) fn receive_callback(me: &WlxCaptureIn, frame: WlxFrame) -> Option Some(WlxCaptureOut { - image, + image: ImageView::new_default(image).ok()?, format, mouse: None, }), @@ -406,7 +399,7 @@ pub(super) fn receive_callback(me: &WlxCaptureIn, frame: WlxFrame) -> Option x, Err(e) => { log::error!("{}: {}", me.name, e); @@ -439,7 +432,7 @@ pub(super) fn receive_callback(me: &WlxCaptureIn, frame: WlxFrame) -> Option Option { log::trace!("{}: New MemPtr frame", me.name); - let format = match fourcc_to_vk(frame.format.fourcc) { + let format = match fourcc_to_vk(frame.format.drm_format.code) { Ok(x) => x, Err(e) => { log::error!("{}: {}", me.name, e); @@ -459,7 +452,7 @@ pub(super) fn receive_callback(me: &WlxCaptureIn, frame: WlxFrame) -> Option>, - display: wayvr::display::DisplayHandle, -} - -impl WayVRContext { - pub const fn new(wvr: Rc>, display: wayvr::display::DisplayHandle) -> Self { - Self { - wayvr: wvr, - display, - } - } -} - -pub struct OverlayToCreate { - pub conf_display: config_wayvr::WayVRDisplay, - pub disp_handle: display::DisplayHandle, -} - pub struct WayVRData { - pub display_handle_map: HashMap, - pub overlays_to_create: Vec, - pub dashboard_executed: bool, + pub window_handle_map: HashMap, pub data: WayVR, - pub pending_haptics: Option, } impl WayVRData { pub fn new( + gfx: Arc, + gfx_extras: &WGfxExtras, config: wayvr::Config, signals: SyncEventQueue, ) -> anyhow::Result { Ok(Self { - display_handle_map: HashMap::default(), - data: WayVR::new(config, signals)?, - overlays_to_create: Vec::new(), - dashboard_executed: false, - pending_haptics: None, + window_handle_map: HashMap::default(), + data: WayVR::new(gfx, &gfx_extras, config, signals)?, }) } - - pub fn get_unique_display_name(&self, mut candidate: String) -> String { - let mut num = 0; - - while !self - .data - .state - .displays - .vec - .iter() - .flatten() - .any(|d| d.obj.name == candidate) - { - if num > 0 { - candidate = format!("{candidate} ({num})"); - } - num += 1; - } - - candidate - } - - fn set_overlay_display_handle(&mut self, id: OverlayID, disp_handle: display::DisplayHandle) { - self.display_handle_map.insert(disp_handle, id); - let display = self.data.state.displays.get_mut(&disp_handle).unwrap(); // Never fails - display.overlay_id = Some(id); - } -} - -struct ImageData { - vk_image: Arc, - vk_image_view: Arc, } pub struct WayVRBackend { - pipeline: Arc>, - pass: WGfxPass, - buf_alpha: Subbuffer<[f32]>, - image: Option, - context: Rc>, - graphics: Arc, - resolution: [u16; 2], + name: Arc, + pipeline: Option, mouse_transform: Affine2, interaction_transform: Option, + window: wayvr::window::WindowHandle, + wayvr: Rc>, + wm: Rc>, + just_resumed: bool, + meta: Option, + stereo: Option, + cur_image: Option>, } impl WayVRBackend { pub fn new( - app: &state::AppState, - wvr: Rc>, - display: wayvr::display::DisplayHandle, - resolution: [u16; 2], + name: Arc, + xr_backend: XrBackend, + wayvr: Rc>, + window: wayvr::window::WindowHandle, ) -> anyhow::Result { - let pipeline = app.gfx.create_pipeline( - app.gfx_extras.shaders.get("vert_quad").unwrap(), // want panic - app.gfx_extras.shaders.get("frag_srgb").unwrap(), // want panic - WPipelineCreateInfo::new(app.gfx.surface_format) - .use_updatable_descriptors(smallvec![0]), - )?; - - let buf_alpha = app - .gfx - .empty_buffer(BufferUsage::TRANSFER_DST | BufferUsage::UNIFORM_BUFFER, 1)?; - - let set0 = pipeline.uniform_sampler( - 0, - app.gfx_extras.fallback_image.clone(), - app.gfx.texture_filter, - )?; - let set1 = pipeline.buffer(1, buf_alpha.clone())?; - let pass = pipeline.create_pass( - [resolution[0] as _, resolution[1] as _], - app.gfx_extras.quad_verts.clone(), - 0..4, - 0..1, - vec![set0, set1], - &Default::default(), - )?; - + let wm = wayvr.borrow().data.state.wm.clone(); Ok(Self { - pipeline, - pass, - buf_alpha, - context: Rc::new(RefCell::new(WayVRContext::new(wvr, display))), - graphics: app.gfx.clone(), - image: None, - resolution, + name, + pipeline: None, + wayvr, + wm, + window, mouse_transform: Affine2::IDENTITY, - interaction_transform: Some(ui_transform([resolution[0] as _, resolution[1] as _])), //TODO:dynamic + interaction_transform: None, + just_resumed: false, + meta: None, + stereo: if matches!(xr_backend, XrBackend::OpenXR) { + Some(StereoMode::None) + } else { + None + }, + cur_image: None, }) } } -pub fn get_or_create_display_by_name( - app: &mut AppState, - wayvr: &mut WayVRData, - disp_name: &str, -) -> anyhow::Result { - let disp_handle = - if let Some(disp) = WayVR::get_display_by_name(&wayvr.data.state.displays, disp_name) { - disp - } else { - let conf_display = app - .session - .wayvr_config - .get_display(disp_name) - .ok_or_else(|| anyhow::anyhow!("Cannot find display named \"{disp_name}\""))? - .clone(); - - let disp_handle = wayvr.data.state.create_display( - conf_display.width, - conf_display.height, - disp_name, - conf_display.primary.unwrap_or(false), - )?; - - wayvr.overlays_to_create.push(OverlayToCreate { - conf_display, - disp_handle, - }); - - disp_handle - }; - - Ok(disp_handle) -} - -pub fn executable_exists_in_path(command: &str) -> bool { - let Ok(path) = std::env::var("PATH") else { - return false; // very unlikely to happen - }; - for dir in path.split(':') { - let exec_path = std::path::PathBuf::from(dir).join(command); - if exec_path.exists() && exec_path.is_file() { - return true; // executable found - } - } - false -} - -fn toggle_dashboard( - app: &mut AppState, - overlays: &mut OverlayWindowManager, - wayvr: &mut WayVRData, -) -> anyhow::Result<()> -where - O: Default, -{ - let Some(conf_dash) = app.session.wayvr_config.dashboard.clone() else { - anyhow::bail!("Dashboard is not configured"); - }; - - if !wayvr.dashboard_executed && !executable_exists_in_path(&conf_dash.exec) { - anyhow::bail!("Executable \"{}\" not found", &conf_dash.exec); - } - - let (newly_created, disp_handle) = wayvr.data.state.get_or_create_dashboard_display( - DASHBOARD_WIDTH, - DASHBOARD_HEIGHT, - DASHBOARD_DISPLAY_NAME, - )?; - - if newly_created { - log::info!("Creating dashboard overlay"); - - let overlay = OverlayWindowData::from_config(OverlayWindowConfig { - default_state: OverlayWindowState { - interactable: true, - grabbable: true, - curvature: Some(0.15), - transform: Affine3A::from_scale_rotation_translation( - Vec3::ONE * 2.0, - Quat::IDENTITY, - vec3(0.0, -0.35, -1.75), - ), - ..OverlayWindowState::default() - }, - z_order: Z_ORDER_DASHBOARD, - show_on_spawn: true, - global: true, - ..create_overlay( - app, - DASHBOARD_DISPLAY_NAME, - OverlayToCreate { - disp_handle, - conf_display: config_wayvr::WayVRDisplay { - attach_to: None, - width: DASHBOARD_WIDTH, - height: DASHBOARD_HEIGHT, - scale: None, - rotation: None, - pos: None, - primary: None, - }, - }, - )? - }); - - let overlay_id = overlays.add(overlay, app); - wayvr.set_overlay_display_handle(overlay_id, disp_handle); - - let args_vec = &conf_dash - .args - .as_ref() - .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| ipc_server::gen_env_vec(env)); - - let mut userdata = HashMap::new(); - userdata.insert(String::from("type"), String::from("dashboard")); - - // Start dashboard specified in the WayVR config - let _process_handle_unused = wayvr.data.state.spawn_process( - disp_handle, - &conf_dash.exec, - args_vec, - env_vec, - conf_dash.working_dir.as_deref(), - userdata, - )?; - - wayvr.dashboard_executed = true; - - return Ok(()); - } - - let display = wayvr.data.state.displays.get(&disp_handle).unwrap(); // safe - let Some(overlay_id) = display.overlay_id else { - anyhow::bail!("Overlay ID not set for dashboard display"); - }; - - let cur_visibility = !display.visible; - - app.ipc_server - .broadcast(PacketServer::WvrStateChanged(if cur_visibility { - WvrStateChanged::DashboardShown - } else { - WvrStateChanged::DashboardHidden - })); - - app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify( - OverlaySelector::Id(overlay_id), - Box::new(move |app, o| { - o.toggle(app); - }), - ))); - - Ok(()) -} - -fn create_overlay( - app: &mut AppState, - name: &str, - cell: OverlayToCreate, -) -> anyhow::Result { - let conf_display = &cell.conf_display; - let disp_handle = cell.disp_handle; - - let mut overlay = create_wayvr_display_overlay( - app, - conf_display.width, - conf_display.height, - disp_handle, - conf_display.scale.unwrap_or(1.0), - name, - )?; - - if let Some(attach_to) = &conf_display.attach_to { - overlay.default_state.positioning = attach_to.get_positioning(); - } - - let rot = conf_display - .rotation - .as_ref() - .map_or(glam::Quat::IDENTITY, |rot| { - glam::Quat::from_axis_angle(Vec3::from_slice(&rot.axis), f32::to_radians(rot.angle)) - }); - - let pos = conf_display - .pos - .as_ref() - .map_or(Vec3::NEG_Z, |pos| Vec3::from_slice(pos)); - - overlay.default_state.transform = Affine3A::from_rotation_translation(rot, pos); - - Ok(overlay) -} - -pub fn create_queued_displays( - app: &mut AppState, - data: &mut WayVRData, - overlays: &mut OverlayWindowManager, -) -> anyhow::Result<()> -where - O: Default, -{ - let overlays_to_create = std::mem::take(&mut data.overlays_to_create); - - for cell in overlays_to_create { - let Some(disp) = data.data.state.displays.get(&cell.disp_handle) else { - continue; // this shouldn't happen - }; - - let name = disp.name.clone(); - - let disp_handle = cell.disp_handle; - let overlay = OverlayWindowData::from_config(create_overlay(app, name.as_str(), cell)?); - let overlay_id = overlays.add(overlay, app); // Insert freshly created WayVR overlay into wlx stack - data.set_overlay_display_handle(overlay_id, disp_handle); - } - - Ok(()) -} - -impl WayVRBackend { - fn ensure_software_data( - &mut self, - data: &wayvr::egl_data::RenderSoftwarePixelsData, - ) -> anyhow::Result<()> { - let mut upload = self - .graphics - .create_xfer_command_buffer(CommandBufferUsage::OneTimeSubmit)?; - - let tex = upload.upload_image( - u32::from(data.width), - u32::from(data.height), - Format::R8G8B8A8_UNORM, - &data.data, - )?; - - // FIXME: can we use _buffers_ here? - upload.build_and_execute_now()?; - - //buffers.push(upload.build()?); - self.image = Some(ImageData { - vk_image: tex.clone(), - vk_image_view: ImageView::new_default(tex).unwrap(), - }); - Ok(()) - } - - fn ensure_dmabuf_data( - &mut self, - data: &wayvr::egl_data::RenderDMAbufData, - ) -> anyhow::Result<()> { - if self.image.is_some() { - return Ok(()); // already initialized and automatically updated due to direct zero-copy textue access - } - - // First init - let mut planes = [FramePlane::default(); 4]; - planes[0].fd = Some(data.fd); - planes[0].offset = data.offset as u32; - planes[0].stride = data.stride; - - let ctx = self.context.borrow_mut(); - let wayvr = ctx.wayvr.borrow_mut(); - let Some(disp) = wayvr.data.state.displays.get(&ctx.display) else { - anyhow::bail!("Failed to fetch WayVR display") - }; - - let frame = DmabufFrame { - format: FrameFormat { - width: u32::from(disp.width), - height: u32::from(disp.height), - fourcc: FourCC { - value: data.mod_info.fourcc, - }, - modifier: data.mod_info.modifiers[0], /* possibly not proper? */ - ..Default::default() - }, - num_planes: 1, - planes, - ..Default::default() - }; - - drop(wayvr); - - let layouts: Vec = vec![SubresourceLayout { - offset: data.offset as _, - size: 0, - row_pitch: data.stride as _, - array_pitch: None, - depth_pitch: None, - }]; - - let tex = self.graphics.dmabuf_texture_ex( - frame, - ImageTiling::DrmFormatModifier, - layouts, - &data.mod_info.modifiers, - )?; - - self.image = Some(ImageData { - vk_image: tex.clone(), - vk_image_view: ImageView::new_default(tex).unwrap(), - }); - Ok(()) - } -} - impl OverlayBackend for WayVRBackend { fn init(&mut self, _app: &mut state::AppState) -> anyhow::Result<()> { Ok(()) } fn pause(&mut self, _app: &mut state::AppState) -> anyhow::Result<()> { - let ctx = self.context.borrow_mut(); - let wayvr = &mut ctx.wayvr.borrow_mut().data; - wayvr.state.set_display_visible(ctx.display, false); Ok(()) } fn resume(&mut self, _app: &mut state::AppState) -> anyhow::Result<()> { - let ctx = self.context.borrow_mut(); - let wayvr = &mut ctx.wayvr.borrow_mut().data; - wayvr.state.set_display_visible(ctx.display, true); + self.just_resumed = true; Ok(()) } - fn should_render(&mut self, _app: &mut AppState) -> anyhow::Result { - let ctx = self.context.borrow(); - let mut wayvr = ctx.wayvr.borrow_mut(); - let redrawn = match wayvr.data.render_display(ctx.display) { - Ok(r) => r, - Err(e) => { - log::error!("render_display failed: {e}"); - return Ok(ShouldRender::Unable); - } + fn should_render(&mut self, app: &mut AppState) -> anyhow::Result { + let wm = self.wm.borrow(); + let Some(window) = wm.windows.get(&self.window) else { + log::debug!( + "{:?}: WayVR overlay without matching window entry", + self.name + ); + return Ok(ShouldRender::Unable); }; - if redrawn { - Ok(ShouldRender::Should) - } else { - Ok(ShouldRender::Can) - } + with_states(window.toplevel.wl_surface(), |states| { + if let Some(surf) = SurfaceBufWithImage::get_from_surface(states) { + let mut meta = FrameMeta { + extent: surf.image.image().extent(), + format: surf.image.format(), + ..Default::default() + }; + + if let Some(pipeline) = self.pipeline.as_mut() { + meta.extent[2] = pipeline.get_depth(); + if self + .meta + .is_some_and(|old| old.extent[..2] != meta.extent[..2]) + { + pipeline.set_extent(app, [meta.extent[0] as _, meta.extent[1] as _])?; + self.interaction_transform = + Some(ui_transform(meta.extent.extent_u32arr())); + } + } else { + let pipeline = + ScreenPipeline::new(&meta, app, self.stereo.unwrap_or(StereoMode::None))?; + meta.extent[2] = pipeline.get_depth(); + self.pipeline = Some(pipeline); + self.interaction_transform = Some(ui_transform(meta.extent.extent_u32arr())); + } + + self.meta = Some(meta); + if self + .cur_image + .as_ref() + .is_none_or(|i| *i.image() != *surf.image.image()) + { + self.cur_image = Some(surf.image); + Ok(ShouldRender::Should) + } else { + Ok(ShouldRender::Can) + } + } else { + Ok(ShouldRender::Unable) + } + }) } fn render( &mut self, - _app: &mut state::AppState, + app: &mut state::AppState, rdr: &mut RenderResources, ) -> anyhow::Result<()> { - let ctx = self.context.borrow(); - let wayvr = ctx.wayvr.borrow_mut(); + let mouse = None; //TODO: mouse cursor + let image = self.cur_image.as_ref().unwrap().clone(); - let data = wayvr - .data - .state - .get_render_data(ctx.display) - .context("Failed to fetch render data")? - .clone(); + self.pipeline + .as_mut() + .unwrap() + .render(image, mouse, app, rdr)?; - drop(wayvr); - drop(ctx); - - match data { - wayvr::egl_data::RenderData::Dmabuf(data) => { - self.ensure_dmabuf_data(&data)?; - } - wayvr::egl_data::RenderData::Software(data) => { - if let Some(new_frame) = &data { - self.ensure_software_data(new_frame)?; - } - } - } - - let image = self.image.as_ref().unwrap(); - self.pass - .update_sampler(0, image.vk_image_view.clone(), self.graphics.texture_filter)?; - self.buf_alpha.write()?[0] = rdr.alpha; - rdr.cmd_buf_single().run_ref(&self.pass)?; Ok(()) } fn frame_meta(&mut self) -> Option { - Some(FrameMeta { - extent: [self.resolution[0] as u32, self.resolution[1] as u32, 1], - ..Default::default() - }) + self.meta } fn notify( @@ -581,24 +182,17 @@ impl OverlayBackend for WayVRBackend { } fn on_hover(&mut self, _app: &mut state::AppState, hit: &input::PointerHit) -> HoverResult { - let ctx = self.context.borrow(); - - let wayvr = &mut ctx.wayvr.borrow_mut(); - - if let Some(disp) = wayvr.data.state.displays.get(&ctx.display) { + if let Some(window) = self.wm.borrow().windows.get(&self.window) { let pos = self.mouse_transform.transform_point2(hit.uv); - let x = ((pos.x * f32::from(disp.width)) as i32).max(0); - let y = ((pos.y * f32::from(disp.height)) as i32).max(0); + let x = ((pos.x * (window.size_x as f32)) as u32).max(0); + let y = ((pos.y * (window.size_y as f32)) as u32).max(0); - let ctx = self.context.borrow(); - wayvr - .data - .state - .send_mouse_move(ctx.display, x as u32, y as u32); + let wayvr = &mut self.wayvr.borrow_mut().data; + wayvr.state.send_mouse_move(self.window, x, y); } HoverResult { - haptics: wayvr.pending_haptics.take(), + haptics: None, // haptics are handled via task consume: true, } } @@ -617,10 +211,9 @@ impl OverlayBackend for WayVRBackend { None } } { - let ctx = self.context.borrow(); - let wayvr = &mut ctx.wayvr.borrow_mut().data; + let wayvr = &mut self.wayvr.borrow_mut().data; if pressed { - wayvr.state.send_mouse_down(ctx.display, index); + wayvr.state.send_mouse_down(self.window, index); } else { wayvr.state.send_mouse_up(index); } @@ -633,233 +226,34 @@ impl OverlayBackend for WayVRBackend { _hit: &input::PointerHit, delta: WheelDelta, ) { - let ctx = self.context.borrow(); - ctx.wayvr.borrow_mut().data.state.send_mouse_scroll(delta); + self.wayvr.borrow_mut().data.state.send_mouse_scroll(delta); } fn get_interaction_transform(&mut self) -> Option { self.interaction_transform } - fn get_attrib(&self, _attrib: BackendAttrib) -> Option { - None - } - fn set_attrib(&mut self, _app: &mut AppState, _value: BackendAttribValue) -> bool { - false - } -} - -#[allow(dead_code)] -pub fn create_wayvr_display_overlay( - app: &mut state::AppState, - display_width: u16, - display_height: u16, - display_handle: wayvr::display::DisplayHandle, - display_scale: f32, - name: &str, -) -> anyhow::Result { - let wayland_server = app - .wayland_server - .as_ref() - .map(|r| r.clone()) - .context("wayland_server unavailable")?; - - let backend = Box::new(WayVRBackend::new( - app, - wayland_server, - display_handle, - [display_width, display_height], - )?); - - let category = if name == DASHBOARD_DISPLAY_NAME { - OverlayCategory::Dashboard - } else { - OverlayCategory::WayVR - }; - - Ok(OverlayWindowConfig { - name: format!("WVR-{name}").into(), - keyboard_focus: Some(KeyboardFocus::WayVR), - category, - default_state: OverlayWindowState { - interactable: true, - grabbable: true, - transform: Affine3A::from_scale_rotation_translation( - Vec3::ONE * display_scale, - Quat::IDENTITY, - vec3(0.0, -0.1, -1.0), - ), - ..OverlayWindowState::default() - }, - ..OverlayWindowConfig::from_backend(backend) - }) -} - -fn show_display( - wayvr: &mut WayVRData, - overlays: &mut OverlayWindowManager, - app: &mut AppState, - display_name: &str, -) where - O: Default, -{ - if let Some(display) = WayVR::get_display_by_name(&wayvr.data.state.displays, display_name) { - if let Some(overlay_id) = wayvr.display_handle_map.get(&display) - && let Some(overlay) = overlays.mut_by_id(*overlay_id) - { - overlay.config.activate(app); - } - - wayvr.data.state.set_display_visible(display, true); - } -} - -fn action_app_click( - app: &mut AppState, - overlays: &mut OverlayWindowManager, - catalog_name: &Arc, - app_name: &Arc, -) -> anyhow::Result<()> -where - O: Default, -{ - let wayland_server = app - .wayland_server - .as_ref() - .map(|r| r.clone()) - .context("wayland_server unavailable")?; - - let catalog = app - .session - .wayvr_config - .get_catalog(catalog_name) - .ok_or_else(|| anyhow::anyhow!("Failed to get catalog \"{catalog_name}\""))? - .clone(); - - if let Some(app_entry) = catalog.get_app(app_name) { - let mut wayvr = wayland_server.borrow_mut(); - - let disp_handle = get_or_create_display_by_name( - app, - &mut wayvr, - &app_entry.target_display.to_lowercase(), - )?; - - let args_vec = &app_entry - .args - .as_ref() - .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| ipc_server::gen_env_vec(env)); - - // Terminate existing process if required - if let Some(process_handle) = - wayvr - .data - .state - .process_query(disp_handle, &app_entry.exec, args_vec, env_vec) - { - // Terminate process - wayvr.data.terminate_process(process_handle); - } else { - // Spawn process - wayvr.data.state.spawn_process( - disp_handle, - &app_entry.exec, - args_vec, - env_vec, - None, - HashMap::default(), - )?; - - show_display::(&mut wayvr, overlays, app, app_entry.target_display.as_str()); + fn get_attrib(&self, attrib: BackendAttrib) -> Option { + match attrib { + BackendAttrib::Stereo => self.stereo.map(|s| BackendAttribValue::Stereo(s)), + _ => None, } } - - Ok(()) -} - -pub fn action_display_click( - app: &mut AppState, - overlays: &mut OverlayWindowManager, - display_name: &Arc, - action: &WayVRDisplayClickAction, -) -> anyhow::Result<()> -where - O: Default, -{ - let wayland_server = app - .wayland_server - .clone() - .context("wayland_server unavailable")?; - let mut wayvr = wayland_server.borrow_mut(); - - let Some(handle) = WayVR::get_display_by_name(&wayvr.data.state.displays, display_name) else { - return Ok(()); - }; - - let Some(display) = wayvr.data.state.displays.get_mut(&handle) else { - return Ok(()); - }; - - let Some(overlay_id) = display.overlay_id else { - return Ok(()); - }; - - let Some(overlay) = overlays.mut_by_id(overlay_id) else { - return Ok(()); - }; - - match action { - WayVRDisplayClickAction::ToggleVisibility => { - // Toggle visibility - overlay.config.toggle(app); - } - WayVRDisplayClickAction::Reset => { - // Show it at the front - overlay.config.reset(app, true); - } - } - - Ok(()) -} - -pub fn wayvr_action( - app: &mut AppState, - overlays: &mut OverlayWindowManager, - action: &WayVRAction, -) where - O: Default, -{ - match action { - WayVRAction::AppClick { - catalog_name, - app_name, - } => { - if let Err(e) = action_app_click(app, overlays, catalog_name, app_name) { - // Happens if something went wrong with initialization - // or input exec path is invalid. Do nothing, just print an error - error_toast(app, "action_app_click failed", e); - } - } - WayVRAction::DisplayClick { - display_name, - action, - } => { - if let Err(e) = action_display_click::(app, overlays, display_name, action) { - error_toast(app, "action_display_click failed", e); - } - } - WayVRAction::ToggleDashboard => { - if let Some(wayland_server) = app.wayland_server.as_ref().map(|r| r.clone()) - && let Err(e) = - toggle_dashboard::(app, overlays, &mut wayland_server.borrow_mut()) - { - error_toast(app, "toggle_dashboard failed", e); + fn set_attrib(&mut self, app: &mut AppState, value: BackendAttribValue) -> bool { + match value { + BackendAttribValue::Stereo(new) => { + if let Some(stereo) = self.stereo.as_mut() { + log::debug!("{}: stereo: {stereo:?} → {new:?}", self.name); + *stereo = new; + if let Some(pipeline) = self.pipeline.as_mut() { + pipeline.set_stereo(app, new).unwrap(); // only panics if gfx is dead + } + true + } else { + false + } } + _ => false, } } } diff --git a/wlx-overlay-s/src/state.rs b/wlx-overlay-s/src/state.rs index c25f433a..fd85fd38 100644 --- a/wlx-overlay-s/src/state.rs +++ b/wlx-overlay-s/src/state.rs @@ -80,7 +80,13 @@ impl AppState { #[cfg(feature = "wayvr")] let wayland_server = session .wayvr_config - .post_load(&session.config, &mut tasks, wayvr_signals.clone()) + .post_load( + gfx.clone(), + &gfx_extras, + &session.config, + &mut tasks, + wayvr_signals.clone(), + ) .inspect_err(|e| log::error!("Could not initialize wayland server: {e:?}")) .ok();