diff --git a/Cargo.lock b/Cargo.lock index dafcc3e..f9b2ae9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -376,16 +376,16 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 1.1.2", + "rustix 1.1.3", "slab", "windows-sys 0.61.2", ] [[package]] name = "async-lock" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" dependencies = [ "event-listener", "event-listener-strategy", @@ -418,7 +418,7 @@ dependencies = [ "cfg-if", "event-listener", "futures-lite", - "rustix 1.1.2", + "rustix 1.1.3", ] [[package]] @@ -444,7 +444,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 1.1.2", + "rustix 1.1.3", "signal-hook-registry", "slab", "windows-sys 0.61.2", @@ -874,7 +874,7 @@ checksum = "cb9f6e1368bd4621d2c86baa7e37de77a938adf5221e5dd3d6133340101b309e" dependencies = [ "bitflags 2.10.0", "polling", - "rustix 1.1.2", + "rustix 1.1.3", "slab", "tracing", ] @@ -898,16 +898,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" dependencies = [ "calloop 0.14.3", - "rustix 1.1.2", + "rustix 1.1.3", "wayland-backend", "wayland-client", ] [[package]] name = "cc" -version = "1.2.49" +version = "1.2.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c" dependencies = [ "find-msvc-tools", "jobserver", @@ -1637,46 +1637,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" -[[package]] -name = "drm" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" -dependencies = [ - "bitflags 2.10.0", - "bytemuck", - "drm-ffi", - "drm-fourcc", - "libc", - "rustix 0.38.44", -] - -[[package]] -name = "drm-ffi" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e41459d99a9b529845f6d2c909eb9adf3b6d2f82635ae40be8de0601726e8b" -dependencies = [ - "drm-sys", - "rustix 0.38.44", -] - [[package]] name = "drm-fourcc" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" -[[package]] -name = "drm-sys" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bafb66c8dbc944d69e15cfcc661df7e703beffbaec8bd63151368b06c5f9858c" -dependencies = [ - "libc", - "linux-raw-sys 0.6.5", -] - [[package]] name = "either" version = "1.15.0" @@ -2216,7 +2182,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ - "rustix 1.1.2", + "rustix 1.1.3", "windows-link 0.2.1", ] @@ -2321,17 +2287,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "gl_generator" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" -dependencies = [ - "khronos_api", - "log", - "xml-rs", -] - [[package]] name = "glam" version = "0.30.9" @@ -2974,9 +2929,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010" [[package]] name = "jiff" @@ -3071,22 +3026,6 @@ dependencies = [ "ucd-trie", ] -[[package]] -name = "khronos-egl" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "khronos_api" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" - [[package]] name = "kurbo" version = "0.11.3" @@ -3234,12 +3173,6 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" -[[package]] -name = "linux-raw-sys" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" - [[package]] name = "linux-raw-sys" version = "0.11.0" @@ -3530,9 +3463,9 @@ dependencies = [ [[package]] name = "ntapi" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081" dependencies = [ "winapi", ] @@ -4315,7 +4248,7 @@ dependencies = [ "concurrent-queue", "hermit-abi 0.5.2", "pin-project-lite", - "rustix 1.1.2", + "rustix 1.1.3", "windows-sys 0.61.2", ] @@ -4541,9 +4474,9 @@ dependencies = [ [[package]] name = "rangemap" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbbbbea733ec66275512d0b9694f34102e7d5406fdbe2ad8d21b28dce92887c" +checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" [[package]] name = "rav1e" @@ -4877,9 +4810,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags 2.10.0", "errno", @@ -4905,9 +4838,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea" [[package]] name = "same-file" @@ -5005,15 +4938,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "6af14725505314343e673e9ecb7cd7e8a36aa9791eb936235a3567cc31447ae4" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -5221,24 +5154,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "740cea6927892bc182d5bf70c8f79806c8bc9f68f2fb96e55a30be171b63af98" dependencies = [ "appendlist", + "ash", "atomic_float", "bitflags 2.10.0", "calloop 0.14.3", "cgmath", "cursor-icon", "downcast-rs", - "drm", - "drm-ffi", "drm-fourcc", "encoding_rs", "errno", - "gl_generator", "indexmap 2.12.1", "libc", - "libloading", "profiling", "rand", - "rustix 1.1.2", + "rustix 1.1.3", "scopeguard", "sha2", "smallvec", @@ -5293,7 +5223,7 @@ dependencies = [ "log", "memmap2", "pkg-config", - "rustix 1.1.2", + "rustix 1.1.3", "thiserror 2.0.17", "wayland-backend", "wayland-client", @@ -5570,14 +5500,14 @@ checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "tempfile" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", "getrandom 0.3.4", "once_cell", - "rustix 1.1.2", + "rustix 1.1.3", "windows-sys 0.61.2", ] @@ -6275,7 +6205,7 @@ checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" dependencies = [ "cc", "downcast-rs", - "rustix 1.1.2", + "rustix 1.1.3", "scoped-tls", "smallvec", "wayland-sys", @@ -6288,7 +6218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ "bitflags 2.10.0", - "rustix 1.1.2", + "rustix 1.1.3", "wayland-backend", "wayland-scanner", ] @@ -6310,21 +6240,11 @@ version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29" dependencies = [ - "rustix 1.1.2", + "rustix 1.1.3", "wayland-client", "xcursor", ] -[[package]] -name = "wayland-egl" -version = "0.32.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d36232ee23ba3ea34a6835d68ca1af91d3ca3d6eddcf9c7147c4e0e66901b9fd" -dependencies = [ - "wayland-backend", - "wayland-sys", -] - [[package]] name = "wayland-protocols" version = "0.32.9" @@ -6411,7 +6331,7 @@ checksum = "fcbd4f3aba6c9fba70445ad2a484c0ef0356c1a9459b1e8e435bedc1971a6222" dependencies = [ "bitflags 2.10.0", "downcast-rs", - "rustix 1.1.2", + "rustix 1.1.3", "wayland-backend", "wayland-scanner", ] @@ -7094,7 +7014,6 @@ dependencies = [ "interprocess", "json", "json5 1.3.0", - "khronos-egl", "libc", "libmonado", "log", @@ -7123,7 +7042,6 @@ dependencies = [ "vulkano", "vulkano-shaders", "wayland-client", - "wayland-egl", "wayvr-ipc", "wgui", "winit", @@ -7162,7 +7080,7 @@ dependencies = [ "libc", "libloading", "once_cell", - "rustix 1.1.2", + "rustix 1.1.3", "x11rb-protocol", ] @@ -7467,6 +7385,12 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "zmij" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e404bcd8afdaf006e529269d3e85a743f9480c3cef60034d77860d02964f3ba" + [[package]] name = "zune-core" version = "0.5.0" @@ -7484,9 +7408,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d915729b0e7d5fe35c2f294c5dc10b30207cc637920e5b59077bfa3da63f28" +checksum = "e35aee689668bf9bd6f6f3a6c60bb29ba1244b3b43adfd50edd554a371da37d5" dependencies = [ "zune-core", ] diff --git a/wayvr-ipc/src/client.rs b/wayvr-ipc/src/client.rs index bba9a98..500973a 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,11 +401,12 @@ impl WayVRClient { )) } - pub async fn fn_wlx_haptics( + pub async fn fn_wlx_device_haptics( client: WayVRClientMutex, + device: usize, params: packet_client::WlxHapticsParams, ) -> anyhow::Result<()> { - send_only!(client, &PacketClient::WlxHaptics(params)); + send_only!(client, &PacketClient::WlxDeviceHaptics(device, params)); Ok(()) } diff --git a/wayvr-ipc/src/packet_client.rs b/wayvr-ipc/src/packet_client.rs index 83d7e91..e1482f0 100644 --- a/wayvr-ipc/src/packet_client.rs +++ b/wayvr-ipc/src/packet_client.rs @@ -67,22 +67,13 @@ 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 f25ae8e..f26fbc6 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 19c9197..3820317 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, @@ -214,15 +128,17 @@ pub async fn wvr_process_launch( ) } -pub async fn wlx_haptics( +pub async fn wlx_device_haptics( state: &mut WayVRClientState, + device: usize, intensity: f32, duration: f32, frequency: f32, ) { handle_empty_result( - WayVRClient::fn_wlx_haptics( + WayVRClient::fn_wlx_device_haptics( state.wayvr_client.clone(), + device, packet_client::WlxHapticsParams { intensity, duration, diff --git a/wayvrctl/src/main.rs b/wayvrctl/src/main.rs index 084fa87..06e2ab4 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_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, @@ -162,11 +126,12 @@ async fn run_once(state: &mut WayVRClientState, args: Args) -> anyhow::Result<() wvr_process_launch(state, exec, name, env, handle, args, HashMap::new()).await; } Subcommands::Haptics { + device, intensity, duration, frequency, } => { - wlx_haptics(state, intensity, duration, frequency).await; + wlx_device_haptics(state, device, intensity, duration, frequency).await; } Subcommands::PanelModify { overlay, @@ -220,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 { @@ -283,6 +218,8 @@ enum Subcommands { }, /// Trigger haptics on the user's controller Haptics { + /// 0 for left, 1 for right controller + device: usize, #[arg(short, long, default_value = "0.25")] intensity: f32, #[arg(short, long, default_value = "0.1")] diff --git a/wlx-capture/src/frame.rs b/wlx-capture/src/frame.rs index 7dfe5bd..37470ed 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 26c8162..edbeaa8 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 1358411..840e0df 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 1725ff7..68e2319 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 a54ae80..c9cc9ae 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 3f0b05f..ff4908d 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/Cargo.toml b/wlx-overlay-s/Cargo.toml index 01d7b5c..75e0a36 100644 --- a/wlx-overlay-s/Cargo.toml +++ b/wlx-overlay-s/Cargo.toml @@ -82,23 +82,18 @@ vulkano = { workspace = true } vulkano-shaders = { workspace = true } wgui = { path = "../wgui" } wayvr_ipc = { workspace = true } +bytes = { version = "1.11.0" } ################################ -#WayVR-only deps +# Wayland Server deps ################################ -khronos-egl = { version = "6.0.0", features = ["static"], optional = true } smithay = { version = "0.7.0", default-features = false, features = [ - "renderer_gl", - "backend_egl", - "backend_drm", + "backend_vulkan", "xwayland", "wayland_frontend", ], optional = true } uuid = { version = "1.19.0", features = ["v4", "fast-rng"], optional = true } wayland-client = { workspace = true, optional = true } -wayland-egl = { version = "0.32.8", optional = true } -bytes = { version = "1.11.0", optional = true } -wayvr-ipc = { path = "../wayvr-ipc", default-features = false, optional = true } rust-embed = { workspace = true } signal-hook = "0.3.18" ################################ @@ -106,6 +101,7 @@ signal-hook = "0.3.18" [build-dependencies] regex = { version = "1.12.2" } +# TODO: rename "wayvr" feature to "wayland-server" [features] default = ["openvr", "openxr", "osc", "x11", "wayland", "wayvr"] openvr = ["dep:ovr_overlay", "dep:json"] @@ -116,13 +112,5 @@ wayland = ["pipewire", "wlx-capture/wlr", "xkbcommon/wayland"] pipewire = ["wlx-capture/pipewire"] uidev = ["dep:winit"] xcb = ["dep:xcb"] -wayvr = [ - "dep:khronos-egl", - "dep:smithay", - "dep:uuid", - "dep:wayland-client", - "dep:wayland-egl", - "dep:bytes", - "dep:wayvr-ipc", -] +wayvr = ["dep:smithay", "dep:uuid", "dep:wayland-client"] as-raw-xcb-connection = [] diff --git a/wlx-overlay-s/src/backend/input.rs b/wlx-overlay-s/src/backend/input.rs index 52453a4..703130a 100644 --- a/wlx-overlay-s/src/backend/input.rs +++ b/wlx-overlay-s/src/backend/input.rs @@ -10,7 +10,7 @@ use smallvec::{SmallVec, smallvec}; use wlx_common::common::LeftRight; use wlx_common::windowing::{OverlayWindowState, Positioning}; -use crate::backend::task::OverlayTask; +use crate::backend::task::{InputTask, OverlayTask}; use crate::overlays::anchor::{ANCHOR_NAME, GRAB_HELP_NAME}; use crate::overlays::watch::WATCH_NAME; use crate::state::{AppSession, AppState}; @@ -65,6 +65,18 @@ impl InputState { } } + pub fn handle_task(&mut self, task: InputTask) { + match task { + InputTask::Haptics { device, haptics } => { + if let Some(pointer) = self.pointers.get_mut(device) { + pointer.pending_haptics = Some(haptics); + } else { + log::warn!("Can't trigger haptics on non-existing device: {device}"); + } + } + } + } + pub const fn pre_update(&mut self) { self.pointers[0].before = self.pointers[0].now; self.pointers[1].before = self.pointers[1].now; @@ -218,6 +230,7 @@ pub struct Pointer { pub now: PointerState, pub before: PointerState, pub last_click: Instant, + pub pending_haptics: Option, pub(super) interaction: InteractionState, } @@ -231,6 +244,7 @@ impl Pointer { now: PointerState::default(), before: PointerState::default(), last_click: Instant::now(), + pending_haptics: None, interaction: InteractionState::default(), } } @@ -340,6 +354,8 @@ where { // already grabbing, ignore everything else let mut pointer = &mut app.input_state.pointers[idx]; + let pending_haptics = pointer.pending_haptics.take(); + if let Some(grab_data) = pointer.interaction.grabbed { if let Some(grabbed) = overlays.mut_by_id(grab_data.grabbed_id) { handle_grabbed(idx, grabbed, app); @@ -347,13 +363,13 @@ where log::warn!("Grabbed overlay {:?} does not exist", grab_data.grabbed_id); pointer.interaction.grabbed = None; } - return (0.1, None); + return (0.1, pending_haptics); } let hovered_id = pointer.interaction.hovered_id.take(); let (Some(mut hit), haptics) = get_nearest_hit(idx, overlays, app) else { handle_no_hit(idx, hovered_id, overlays, app); - return (0.0, None); // no hit + return (0.0, pending_haptics); // no hit }; // focus change @@ -378,7 +394,7 @@ where let Some(hovered) = overlays.mut_by_id(hit.overlay) else { log::warn!("Hit overlay {:?} does not exist", hit.overlay); - return (0.0, None); // no hit + return (0.0, pending_haptics); // no hit }; pointer = &mut app.input_state.pointers[idx]; pointer.interaction.hovered_id = Some(hit.overlay); @@ -447,7 +463,7 @@ where } } - (hit.dist, haptics) + (hit.dist, haptics.or(pending_haptics)) } fn handle_no_hit( diff --git a/wlx-overlay-s/src/backend/openvr/mod.rs b/wlx-overlay-s/src/backend/openvr/mod.rs index f6b2aea..4eda9c8 100644 --- a/wlx-overlay-s/src/backend/openvr/mod.rs +++ b/wlx-overlay-s/src/backend/openvr/mod.rs @@ -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; + } + } + _ => {} } - _ => {} } } @@ -220,6 +216,9 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr while let Some(task) = due_tasks.pop_front() { match task { + TaskType::Input(task) => { + app.input_state.handle_task(task); + } TaskType::Overlay(task) => { overlays.handle_task(&mut app, task)?; } @@ -232,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 */ } } } @@ -264,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() @@ -303,11 +298,10 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr let _ = sender.send_params(&overlays, &app.input_state.devices); } - #[cfg(feature = "wayvr")] if let Err(e) = - crate::overlays::wayvr::tick_events::(&mut app, &mut overlays) + crate::ipc::events::tick_events::(&mut app, &mut overlays) { - log::error!("WayVR tick_events failed: {e:?}"); + log::error!("WayVR IPC tick_events failed: {e:?}"); } log::trace!("Rendering frame"); @@ -335,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(wayvr) = &app.wayvr { - wayvr.borrow_mut().data.tick_finish()?; - } - // chaperone } // main_loop diff --git a/wlx-overlay-s/src/backend/openxr/mod.rs b/wlx-overlay-s/src/backend/openxr/mod.rs index 27fc268..9645ec5 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 { @@ -373,9 +368,9 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr #[cfg(feature = "wayvr")] if let Err(e) = - crate::overlays::wayvr::tick_events::(&mut app, &mut overlays) + crate::ipc::events::tick_events::(&mut app, &mut overlays) { - log::error!("WayVR tick_events failed: {e:?}"); + log::error!("WayVR IPC tick_events failed: {e:?}"); } // Begin rendering @@ -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(wayvr) = &app.wayvr { - wayvr.borrow_mut().data.tick_finish()?; - } - // Begin layer submit layers.sort_by(|a, b| b.0.total_cmp(&a.0)); @@ -489,6 +479,9 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr app.tasks.retrieve_due(&mut due_tasks); while let Some(task) = due_tasks.pop_front() { match task { + TaskType::Input(task) => { + app.input_state.handle_task(task); + } TaskType::Overlay(task) => { overlays.handle_task(&mut app, task)?; } @@ -499,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/task.rs b/wlx-overlay-s/src/backend/task.rs index cd228b9..2ed191a 100644 --- a/wlx-overlay-s/src/backend/task.rs +++ b/wlx-overlay-s/src/backend/task.rs @@ -8,6 +8,7 @@ use std::{ use serde::Deserialize; use crate::{ + backend::input, state::AppState, windowing::{OverlaySelector, window::OverlayWindowConfig}, }; @@ -43,6 +44,13 @@ impl Ord for AppTask { } } +pub enum InputTask { + Haptics { + device: usize, + haptics: input::Haptics, + }, +} + #[cfg(feature = "openvr")] pub enum OpenVrTask { ColorGain(ColorChannel, f32), @@ -87,6 +95,7 @@ pub enum OverlayTask { } pub enum TaskType { + Input(InputTask), Overlay(OverlayTask), Playspace(PlayspaceTask), #[cfg(feature = "openvr")] diff --git a/wlx-overlay-s/src/backend/wayvr/client.rs b/wlx-overlay-s/src/backend/wayvr/client.rs index 39b1a9d..862fcc5 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 1e07bab..bd392e0 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,11 +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; -use super::event_queue::SyncEventQueue; 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, @@ -56,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 { @@ -71,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()); } } @@ -197,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(); @@ -234,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 f5c8909..0000000 --- a/wlx-overlay-s/src/backend/wayvr/display.rs +++ /dev/null @@ -1,605 +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, subsystem::hid::WheelDelta, windowing::OverlayID, -}; - -use super::{ - BlitMethod, WayVRSignal, client::WayVRCompositor, comp::send_frames_surface_tree, egl_data, - event_queue::SyncEventQueue, process, smithay_wrapper, time, window, -}; - -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 d4b844c..0000000 --- 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 bf26492..0000000 --- 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 0000000..53a2b56 --- /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 2c2b511..98f35d5 100644 --- a/wlx-overlay-s/src/backend/wayvr/mod.rs +++ b/wlx-overlay-s/src/backend/wayvr/mod.rs @@ -1,33 +1,22 @@ pub mod client; mod comp; -pub mod display; -pub mod egl_data; -mod egl_ex; -pub mod event_queue; mod handle; -mod process; -pub mod server_ipc; -mod smithay_wrapper; +mod image_importer; +pub mod process; mod time; -mod window; +pub mod window; use anyhow::Context; use comp::Application; -use display::{Display, DisplayInitParams, DisplayVec}; -use event_queue::SyncEventQueue; use process::ProcessVec; use serde::Deserialize; -use server_ipc::WayVRServer; +use 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}, @@ -37,19 +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"; @@ -87,19 +85,6 @@ pub enum WayVRTask { ProcessTerminationRequest(process::ProcessHandle), } -#[derive(Clone)] -pub enum WayVRSignal { - DisplayVisibility(display::DisplayHandle, bool), - DisplayWindowLayout( - display::DisplayHandle, - packet_server::WvrDisplayWindowLayout, - ), - BroadcastStateChanged(packet_server::WvrStateChanged), - DropOverlay(crate::windowing::OverlayID), - Haptics(super::input::Haptics), - CustomTask(crate::backend::task::ModifyPanelTask), -} - pub enum BlitMethod { Dmabuf, Software, @@ -125,22 +110,21 @@ pub struct Config { pub struct WayVRState { time_start: u64, - pub displays: display::DisplayVec, pub manager: client::WayVRCompositor, - wm: Rc>, - egl_data: Rc, + pub wm: Rc>, pub processes: process::ProcessVec, pub config: Config, - dashboard_display: Option, pub tasks: SyncEventQueue, - pub signals: SyncEventQueue, ticks: u64, cur_modifiers: u8, + signals: SyncEventQueue, + mouse_freeze: Instant, + window_to_overlay: HashMap, + overlay_to_window: SecondaryMap, } pub struct WayVR { pub state: WayVRState, - pub ipc_server: WayVRServer, } pub enum MouseIndex { @@ -151,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) -> 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(); @@ -170,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( @@ -193,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(), @@ -250,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, @@ -259,109 +232,54 @@ impl WayVR { wayvr_tasks: tasks.clone(), redraw_requests: HashSet::new(), dmabuf_state, - gles_renderer, }; let time_start = get_millis(); - let ipc_server = WayVRServer::new()?; - let state = WayVRState { time_start, manager: client::WayVRCompositor::new(state, display, seat_keyboard, seat_pointer)?, - 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, - signals: SyncEventQueue::new(), cur_modifiers: 0, + signals, + mouse_freeze: Instant::now(), + window_to_overlay: HashMap::new(), + overlay_to_window: SecondaryMap::new(), }; - Ok(Self { state, ipc_server }) - } - - 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) + Ok(Self { state }) } #[allow(clippy::too_many_lines, clippy::cognitive_complexity)] - pub fn tick_events(&mut self, app: &AppState) -> anyhow::Result> { + pub fn tick_events(&mut self, app: &mut AppState) -> anyhow::Result> { let mut tasks: Vec = Vec::new(); - self.ipc_server.tick(&mut server_ipc::TickParams { - state: &mut self.state, + app.ipc_server.tick(&mut ipc_server::TickParams { + wayland_state: &mut self.state, + input_state: &app.input_state, tasks: &mut tasks, - app, + signals: &app.wayvr_signals, }); - // Check for redraw events - 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 self.state.signals); } if !to_remove.is_empty() { - self.state.signals.send(WayVRSignal::BroadcastStateChanged( + app.wayvr_signals.send(WayVRSignal::BroadcastStateChanged( packet_server::WvrStateChanged::ProcessRemoved, )); } @@ -392,17 +310,11 @@ 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); - self.state.signals.send(WayVRSignal::BroadcastStateChanged( + app.wayvr_signals.send(WayVRSignal::BroadcastStateChanged( packet_server::WvrStateChanged::WindowCreated, )); } @@ -419,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) => { @@ -441,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; @@ -454,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 @@ -500,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) { @@ -541,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)], @@ -668,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)); @@ -681,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(), @@ -731,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)] @@ -751,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 1d5dded..44f9623 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 145b27c..0000000 --- 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 1b0afed..96f0987 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 c75cf0c..caed1a3 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,7 +20,9 @@ use crate::{ }, config::load_config_with_conf_d, config_io, - overlays::wayvr::{WayVRData, executable_exists_in_path}, + graphics::WGfxExtras, + ipc::{event_queue::SyncEventQueue, signal::WayVRSignal}, + overlays::wayvr::WayVRData, }; // Flat version of RelativeTo @@ -135,9 +138,6 @@ pub struct WayVRDashboard { #[derive(Deserialize, Serialize)] pub struct WayVRConfig { - #[serde(default = "def_true")] - pub run_compositor_at_start: bool, - #[serde(default = "Default::default")] pub catalogs: HashMap, @@ -201,9 +201,12 @@ impl WayVRConfig { pub fn post_load( &self, + gfx: Arc, + gfx_extras: &WGfxExtras, config: &GeneralConfig, tasks: &mut TaskContainer, - ) -> anyhow::Result>>> { + signals: SyncEventQueue, + ) -> anyhow::Result>> { let primary_count = self .displays .iter() @@ -212,10 +215,6 @@ impl WayVRConfig { if primary_count > 1 { anyhow::bail!("Number of primary displays is more than 1") - } else if primary_count == 0 { - log::warn!( - "No primary display specified. External Wayland applications will not be attached." - ); } for (catalog_name, catalog) in &self.catalogs { @@ -231,15 +230,12 @@ impl WayVRConfig { } } - if self.run_compositor_at_start { - // Start Wayland server instantly - Ok(Some(Rc::new(RefCell::new(WayVRData::new( - Self::get_wayvr_config(config, self)?, - )?)))) - } else { - // Lazy-init WayVR later if the user requested - Ok(None) - } + Ok(Rc::new(RefCell::new(WayVRData::new( + gfx, + gfx_extras, + Self::get_wayvr_config(config, self)?, + signals, + )?))) } } @@ -258,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 c0f30a8..671be50 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 a73a794..f25a5bd 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/backend/wayvr/event_queue.rs b/wlx-overlay-s/src/ipc/event_queue.rs similarity index 100% rename from wlx-overlay-s/src/backend/wayvr/event_queue.rs rename to wlx-overlay-s/src/ipc/event_queue.rs diff --git a/wlx-overlay-s/src/ipc/events.rs b/wlx-overlay-s/src/ipc/events.rs new file mode 100644 index 0000000..456c6fd --- /dev/null +++ b/wlx-overlay-s/src/ipc/events.rs @@ -0,0 +1,99 @@ +use std::{cell::RefCell, rc::Rc}; + +use wayvr_ipc::packet_server; + +#[cfg(feature = "wayvr")] +use crate::{backend::wayvr, overlays::wayvr::WayVRData}; + +use crate::{ + backend::{ + self, + task::{InputTask, OverlayTask, TaskType}, + }, + ipc::signal::WayVRSignal, + state::AppState, + windowing::{OverlaySelector, manager::OverlayWindowManager}, +}; + +#[cfg(feature = "wayvr")] +fn process_tick_tasks( + tick_tasks: Vec, + r_wayvr: &Rc>, +) -> anyhow::Result<()> { + for tick_task in tick_tasks { + match tick_task { + backend::wayvr::TickTask::NewExternalProcess(request) => { + let mut wayvr = r_wayvr.borrow_mut(); + + log::info!("Registering external process with PID {}", request.pid); + + wayvr.data.state.add_external_process(request.pid); + + wayvr + .data + .state + .manager + .add_client(wayvr::client::WayVRClient { + client: request.client, + pid: request.pid, + }); + } + } + } + + Ok(()) +} + +pub fn tick_events( + app: &mut AppState, + _overlays: &mut OverlayWindowManager, +) -> anyhow::Result<()> +where + O: Default, +{ + #[cfg(feature = "wayvr")] + let wayland_server = app.wayland_server.clone(); + + while let Some(signal) = app.wayvr_signals.read() { + match signal { + #[cfg(feature = "wayvr")] + WayVRSignal::BroadcastStateChanged(packet) => { + app.ipc_server + .broadcast(packet_server::PacketServer::WvrStateChanged(packet)); + } + WayVRSignal::DeviceHaptics(device, haptics) => { + app.tasks + .enqueue(TaskType::Input(InputTask::Haptics { device, haptics })); + } + WayVRSignal::DropOverlay(overlay_id) => { + app.tasks + .enqueue(TaskType::Overlay(OverlayTask::Drop(OverlaySelector::Id( + overlay_id, + )))); + } + WayVRSignal::CustomTask(custom_task) => { + app.tasks + .enqueue(TaskType::Overlay(OverlayTask::ModifyPanel(custom_task))); + } + } + } + + #[cfg(feature = "wayvr")] + { + if let Some(wayland_server) = wayland_server { + let tick_tasks = wayland_server.borrow_mut().data.tick_events(app)?; + process_tick_tasks(tick_tasks, &wayland_server)?; + } + } + + #[cfg(not(feature = "wayvr"))] + { + use super::ipc_server::TickParams; + app.ipc_server.tick(&mut TickParams { + input_state: &app.input_state, + signals: &app.wayvr_signals, + }); + } + + Ok(()) +} diff --git a/wlx-overlay-s/src/backend/wayvr/server_ipc.rs b/wlx-overlay-s/src/ipc/ipc_server.rs similarity index 65% rename from wlx-overlay-s/src/backend/wayvr/server_ipc.rs rename to wlx-overlay-s/src/ipc/ipc_server.rs index ed320d5..3532d28 100644 --- a/wlx-overlay-s/src/backend/wayvr/server_ipc.rs +++ b/wlx-overlay-s/src/ipc/ipc_server.rs @@ -1,6 +1,10 @@ -use crate::state::AppState; +#[cfg(feature = "wayvr")] +use crate::backend::wayvr::{self, WayVRState}; -use super::{TickTask, WayVRSignal, display, process, window}; +use crate::{ + backend::input::InputState, + ipc::{event_queue::SyncEventQueue, signal::WayVRSignal}, +}; use bytes::BufMut; use glam::Vec3A; use interprocess::local_socket::{self, ToNsName, traits::Listener}; @@ -71,9 +75,12 @@ fn read_payload(conn: &mut local_socket::Stream, size: u32) -> Option { } pub struct TickParams<'a> { - pub state: &'a mut super::WayVRState, - pub tasks: &'a mut Vec, - pub app: &'a AppState, + #[cfg(feature = "wayvr")] + pub wayland_state: &'a mut WayVRState, + #[cfg(feature = "wayvr")] + pub tasks: &'a mut Vec, + pub signals: &'a SyncEventQueue, + pub input_state: &'a InputState, } pub fn gen_args_vec(input: &str) -> Vec<&str> { @@ -149,44 +156,11 @@ impl Connection { Ok(()) } - fn handle_wvr_display_list( - &mut self, - params: &TickParams, - serial: ipc::Serial, - ) -> anyhow::Result<()> { - let list: Vec = params - .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(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, serial: ipc::Serial, ) -> anyhow::Result<()> { - let input_state = ¶ms.app.input_state; - let to_arr = |vec: &Vec3A| -> [f32; 3] { [vec.x, vec.y, vec.z] }; send_packet( @@ -194,12 +168,12 @@ impl Connection { &ipc::data_encode(&PacketServer::WlxInputStateResponse( serial, packet_server::WlxInputState { - hmd_pos: to_arr(&input_state.hmd.translation), + hmd_pos: to_arr(¶ms.input_state.hmd.translation), left: WlxInputStatePointer { - pos: to_arr(&input_state.pointers[0].raw_pose.translation), + pos: to_arr(¶ms.input_state.pointers[0].raw_pose.translation), }, right: WlxInputStatePointer { - pos: to_arr(&input_state.pointers[0].raw_pose.translation), + pos: to_arr(¶ms.input_state.pointers[0].raw_pose.translation), }, }, )), @@ -208,148 +182,55 @@ impl Connection { Ok(()) } - fn handle_wvr_display_create( + #[cfg(feature = "wayvr")] + fn handle_wvr_window_list( &mut self, params: &mut TickParams, serial: ipc::Serial, - packet_params: packet_client::WvrDisplayCreateParams, - ) -> anyhow::Result<()> { - let display_handle = params.state.create_display( - packet_params.width, - packet_params.height, - &packet_params.name, - false, - )?; - - params - .tasks - .push(TickTask::NewDisplay(packet_params, Some(display_handle))); - - send_packet( - &mut self.conn, - &ipc::data_encode(&PacketServer::WvrDisplayCreateResponse( - serial, - display_handle.as_packet(), - )), - )?; - Ok(()) - } - - fn handle_wvr_display_remove( - &mut self, - params: &mut TickParams, - serial: ipc::Serial, - handle: packet_server::WvrDisplayHandle, - ) -> anyhow::Result<()> { - let res = params - .state - .destroy_display(display::DisplayHandle::from_packet(handle)) - .map_err(|e| format!("{e:?}")); - - send_packet( - &mut self.conn, - &ipc::data_encode(&PacketServer::WvrDisplayRemoveResponse(serial, res)), - )?; - Ok(()) - } - - fn handle_wvr_display_set_visible( - params: &mut TickParams, - handle: packet_server::WvrDisplayHandle, - visible: bool, - ) { - params.state.signals.send(WayVRSignal::DisplayVisibility( - display::DisplayHandle::from_packet(handle), - visible, - )); - } - - fn handle_wvr_display_set_window_layout( - params: &mut TickParams, - handle: packet_server::WvrDisplayHandle, - layout: packet_server::WvrDisplayWindowLayout, - ) { - params.state.signals.send(WayVRSignal::DisplayWindowLayout( - display::DisplayHandle::from_packet(handle), - layout, - )); - } - - 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 - .state - .displays - .get(&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 - .state - .wm - .borrow_mut() - .windows - .get(&disp_win.window_handle) - .map(|win| packet_server::WvrWindow { - handle: window::WindowHandle::as_packet(&disp_win.window_handle), - process_handle: process::ProcessHandle::as_packet( - &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::>(), })) } + #[cfg(feature = "wayvr")] fn handle_wvr_window_set_visible( params: &mut TickParams, handle: packet_server::WvrWindowHandle, visible: bool, ) { - let to_resize = if let Some(window) = params - .state + if let Some(window) = params + .wayland_state .wm .borrow_mut() .windows - .get_mut(&window::WindowHandle::from_packet(handle)) + .get_mut(&wayvr::window::WindowHandle::from_packet(handle)) { window.visible = visible; - Some(window.display_handle) - } else { - None - }; - - if let Some(to_resize) = to_resize - && let Some(display) = params.state.displays.get_mut(&to_resize) - { - display.reposition_windows(); - display.trigger_rerender(); } } + #[cfg(feature = "wayvr")] fn handle_wvr_process_launch( &mut self, params: &mut TickParams, @@ -359,8 +240,7 @@ impl Connection { let args_vec = gen_args_vec(&packet_params.args); let env_vec = gen_env_vec(&packet_params.env); - let res = params.state.spawn_process( - super::display::DisplayHandle::from_packet(packet_params.target_display), + let res = params.wayland_state.spawn_process( &packet_params.exec, &args_vec, &env_vec, @@ -378,34 +258,14 @@ impl Connection { Ok(()) } - fn handle_wvr_display_get( - &mut self, - params: &TickParams, - serial: ipc::Serial, - display_handle: packet_server::WvrDisplayHandle, - ) -> anyhow::Result<()> { - let native_handle = &display::DisplayHandle::from_packet(display_handle); - let disp = params - .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, params: &TickParams, serial: ipc::Serial, ) -> anyhow::Result<()> { let list: Vec = params - .state + .wayland_state .processes .vec .iter() @@ -415,7 +275,10 @@ impl Connection { return None; }; let process = &cell.obj; - Some(process.to_packet(process::ProcessHandle::new(idx as u32, cell.generation))) + Some(process.to_packet(wayvr::process::ProcessHandle::new( + idx as u32, + cell.generation, + ))) }) .collect(); @@ -431,12 +294,13 @@ impl Connection { } // This request doesn't return anything to the client + #[cfg(feature = "wayvr")] fn handle_wvr_process_terminate( params: &mut TickParams, process_handle: packet_server::WvrProcessHandle, ) { - let native_handle = &process::ProcessHandle::from_packet(process_handle); - let process = params.state.processes.get_mut(native_handle); + let native_handle = &wayvr::process::ProcessHandle::from_packet(process_handle); + let process = params.wayland_state.processes.get_mut(native_handle); let Some(process) = process else { return; @@ -445,15 +309,16 @@ impl Connection { process.terminate(); } + #[cfg(feature = "wayvr")] fn handle_wvr_process_get( &mut self, params: &TickParams, serial: ipc::Serial, process_handle: packet_server::WvrProcessHandle, ) -> anyhow::Result<()> { - let native_handle = &process::ProcessHandle::from_packet(process_handle); + let native_handle = &wayvr::process::ProcessHandle::from_packet(process_handle); let process = params - .state + .wayland_state .processes .get(native_handle) .map(|process| process.to_packet(*native_handle)); @@ -466,11 +331,13 @@ impl Connection { Ok(()) } - fn handle_wlx_haptics( + fn handle_wlx_device_haptics( params: &mut TickParams, + device: usize, haptics_params: packet_client::WlxHapticsParams, ) { - params.state.signals.send(super::WayVRSignal::Haptics( + params.signals.send(WayVRSignal::DeviceHaptics( + device, crate::backend::input::Haptics { duration: haptics_params.duration, frequency: haptics_params.frequency, @@ -486,9 +353,8 @@ impl Connection { use crate::backend::task::{ModifyPanelCommand, ModifyPanelTask}; params - .state .signals - .send(super::WayVRSignal::CustomTask(ModifyPanelTask { + .send(WayVRSignal::CustomTask(ModifyPanelTask { overlay: custom_params.overlay, element: custom_params.element, command: match custom_params.command { @@ -511,6 +377,9 @@ impl Connection { })); } + // FIXME: we should probably respond an error to the client in case if wayland server feature is disabled + // fix this after we're done with the webkit-based wayvr-dashboard + #[allow(unused_variables)] fn process_payload(&mut self, params: &mut TickParams, payload: Payload) -> anyhow::Result<()> { let packet: PacketClient = ipc::data_decode(&payload)?; @@ -524,44 +393,32 @@ impl Connection { PacketClient::WlxInputState(serial) => { self.handle_wlx_input_state(params, serial)?; } - PacketClient::WvrDisplayList(serial) => { - self.handle_wvr_display_list(params, serial)?; - } - PacketClient::WvrDisplayGet(serial, display_handle) => { - self.handle_wvr_display_get(params, serial, display_handle)?; - } - PacketClient::WvrDisplayRemove(serial, display_handle) => { - self.handle_wvr_display_remove(params, serial, display_handle)?; - } - PacketClient::WvrDisplaySetVisible(display_handle, visible) => { - Self::handle_wvr_display_set_visible(params, display_handle, visible); - } - PacketClient::WvrDisplaySetWindowLayout(display_handle, layout) => { - Self::handle_wvr_display_set_window_layout(params, display_handle, layout); - } - PacketClient::WvrDisplayWindowList(serial, display_handle) => { - self.handle_wvr_display_window_list(params, serial, display_handle)?; + PacketClient::WvrWindowList(serial) => { + #[cfg(feature = "wayvr")] + self.handle_wvr_window_list(params, serial)?; } PacketClient::WvrWindowSetVisible(window_handle, visible) => { + #[cfg(feature = "wayvr")] Self::handle_wvr_window_set_visible(params, window_handle, visible); } PacketClient::WvrProcessGet(serial, process_handle) => { + #[cfg(feature = "wayvr")] self.handle_wvr_process_get(params, serial, process_handle)?; } PacketClient::WvrProcessList(serial) => { + #[cfg(feature = "wayvr")] self.handle_wvr_process_list(params, serial)?; } PacketClient::WvrProcessLaunch(serial, packet_params) => { + #[cfg(feature = "wayvr")] self.handle_wvr_process_launch(params, serial, packet_params)?; } - PacketClient::WvrDisplayCreate(serial, packet_params) => { - 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) => { - Self::handle_wlx_haptics(params, haptics_params); + PacketClient::WlxDeviceHaptics(device, haptics_params) => { + Self::handle_wlx_device_haptics(params, device, haptics_params); } PacketClient::WlxModifyPanel(custom_params) => { Self::handle_wlx_panel(params, custom_params); diff --git a/wlx-overlay-s/src/ipc/mod.rs b/wlx-overlay-s/src/ipc/mod.rs new file mode 100644 index 0000000..c1fd500 --- /dev/null +++ b/wlx-overlay-s/src/ipc/mod.rs @@ -0,0 +1,4 @@ +pub mod event_queue; +pub mod events; +pub mod ipc_server; +pub mod signal; diff --git a/wlx-overlay-s/src/ipc/signal.rs b/wlx-overlay-s/src/ipc/signal.rs new file mode 100644 index 0000000..aaac9d0 --- /dev/null +++ b/wlx-overlay-s/src/ipc/signal.rs @@ -0,0 +1,8 @@ +#[derive(Clone)] +pub enum WayVRSignal { + #[cfg(feature = "wayvr")] + BroadcastStateChanged(wayvr_ipc::packet_server::WvrStateChanged), + DeviceHaptics(usize, crate::backend::input::Haptics), + DropOverlay(crate::windowing::OverlayID), + CustomTask(crate::backend::task::ModifyPanelTask), +} diff --git a/wlx-overlay-s/src/main.rs b/wlx-overlay-s/src/main.rs index aea98e1..252cc06 100644 --- a/wlx-overlay-s/src/main.rs +++ b/wlx-overlay-s/src/main.rs @@ -22,6 +22,7 @@ mod config; mod config_io; mod graphics; mod gui; +mod ipc; mod overlays; mod shaders; mod state; diff --git a/wlx-overlay-s/src/overlays/screen/backend.rs b/wlx-overlay-s/src/overlays/screen/backend.rs index 4f943be..b2abbcf 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 baee1c3..b56eb90 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, - } - } -} - -struct OverlayToCreate { - pub conf_display: config_wayvr::WayVRDisplay, - pub disp_handle: display::DisplayHandle, -} - pub struct WayVRData { - display_handle_map: HashMap, - overlays_to_create: Vec, - dashboard_executed: bool, + pub window_handle_map: HashMap, pub data: WayVR, - pending_haptics: Option, } impl WayVRData { - pub fn new(config: wayvr::Config) -> anyhow::Result { + 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)?, - overlays_to_create: Vec::new(), - dashboard_executed: false, - pending_haptics: None, + window_handle_map: HashMap::default(), + data: WayVR::new(gfx, &gfx_extras, config, signals)?, }) } - - 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, }) } } -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| gen_args_vec(args.as_str())); - - let env_vec = &conf_dash - .env - .as_ref() - .map_or_else(Vec::new, |env| 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; - - wayvr - .data - .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) -} - -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(()) -} - -#[allow(clippy::too_many_lines)] -pub fn tick_events( - app: &mut AppState, - overlays: &mut OverlayWindowManager, -) -> anyhow::Result<()> -where - O: Default, -{ - let Some(r_wayvr) = app.wayvr.clone() else { - return Ok(()); - }; - - let mut wayvr = r_wayvr.borrow_mut(); - - while let Some(signal) = wayvr.data.state.signals.read() { - match signal { - wayvr::WayVRSignal::DisplayVisibility(display_handle, visible) => { - if let Some(overlay_id) = wayvr.display_handle_map.get(&display_handle) { - let overlay_id = *overlay_id; - wayvr - .data - .state - .set_display_visible(display_handle, visible); - app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify( - OverlaySelector::Id(overlay_id), - Box::new(move |app, o| { - if visible == o.is_active() { - return; - } - if visible { - o.activate(app); - } else { - o.deactivate(); - } - }), - ))); - } - } - wayvr::WayVRSignal::DisplayWindowLayout(display_handle, layout) => { - wayvr.data.state.set_display_layout(display_handle, layout); - } - wayvr::WayVRSignal::BroadcastStateChanged(packet) => { - wayvr - .data - .ipc_server - .broadcast(packet_server::PacketServer::WvrStateChanged(packet)); - } - wayvr::WayVRSignal::DropOverlay(overlay_id) => { - app.tasks - .enqueue(TaskType::Overlay(OverlayTask::Drop(OverlaySelector::Id( - overlay_id, - )))); - } - wayvr::WayVRSignal::Haptics(haptics) => { - wayvr.pending_haptics = Some(haptics); - } - wayvr::WayVRSignal::CustomTask(custom_task) => { - app.tasks - .enqueue(TaskType::Overlay(OverlayTask::ModifyPanel(custom_task))); - } - } - } - - let res = wayvr.data.tick_events(app)?; - drop(wayvr); - - for result in res { - match result { - wayvr::TickTask::NewExternalProcess(request) => { - let config = &app.session.wayvr_config; - - let disp_name = request.env.display_name.map_or_else( - || { - config - .get_default_display() - .map(|(display_name, _)| display_name) - }, - |display_name| { - config - .get_display(display_name.as_str()) - .map(|_| display_name) - }, - ); - - if let Some(disp_name) = disp_name { - let mut wayvr = r_wayvr.borrow_mut(); - - log::info!("Registering external process with PID {}", request.pid); - - let disp_handle = get_or_create_display_by_name(app, &mut wayvr, &disp_name)?; - - wayvr - .data - .state - .add_external_process(disp_handle, request.pid); - - wayvr - .data - .state - .manager - .add_client(wayvr::client::WayVRClient { - client: request.client, - display_handle: disp_handle, - pid: request.pid, - }); - } - } - wayvr::TickTask::NewDisplay(cpar, disp_handle) => { - log::info!("Creating new display with name \"{}\"", cpar.name); - - let mut wayvr = r_wayvr.borrow_mut(); - - let unique_name = wayvr.get_unique_display_name(cpar.name); - - let disp_handle = match disp_handle { - Some(d) => d, - None => wayvr.data.state.create_display( - cpar.width, - cpar.height, - &unique_name, - false, - )?, - }; - - wayvr.overlays_to_create.push(OverlayToCreate { - disp_handle, - conf_display: config_wayvr::WayVRDisplay { - attach_to: Some(config_wayvr::AttachTo::from_packet(&cpar.attach_to)), - width: cpar.width, - height: cpar.height, - pos: None, - primary: None, - rotation: None, - scale: cpar.scale, - }, - }); - } - } - } - - let mut wayvr = r_wayvr.borrow_mut(); - create_queued_displays(app, &mut wayvr, overlays)?; - - Ok(()) -} - -impl WayVRBackend { - fn ensure_software_data( - &mut self, - 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( @@ -728,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, } } @@ -764,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); } @@ -780,229 +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 wayvr = app.get_wayvr()?; - - let backend = Box::new(WayVRBackend::new( - app, - wayvr, - 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 wayvr = app.get_wayvr()?; - - 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 = wayvr.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| gen_args_vec(args.as_str())); - - let env_vec = &app_entry - .env - .as_ref() - .map_or_else(Vec::new, |env| 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 wayvr = app.get_wayvr()?; - let mut wayvr = wayvr.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 => { - let wayvr = match app.get_wayvr() { - Ok(wayvr) => wayvr, - Err(e) => { - log::error!("WayVR Error: {e:?}"); - return; + 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 } - }; - - let mut wayvr = wayvr.borrow_mut(); - - if let Err(e) = toggle_dashboard::(app, overlays, &mut wayvr) { - error_toast(app, "toggle_dashboard failed", e); } + _ => false, } } } diff --git a/wlx-overlay-s/src/res/wayvr.yaml b/wlx-overlay-s/src/res/wayvr.yaml index 954e353..0f7767b 100644 --- a/wlx-overlay-s/src/res/wayvr.yaml +++ b/wlx-overlay-s/src/res/wayvr.yaml @@ -12,11 +12,6 @@ version: 1 # "software": Read pixel data to memory via glReadPixels() every time a content has been updated. Minor performance impact on large resolutions blit_method: "dmabuf" -# Set to true if you want to make Wyland server instantly available. -# By default, WayVR starts only when it's needed. -# (this option is primarily used for remote starting external processes and development purposes) -run_compositor_at_start: false - # Automatically close overlays with zero window count? auto_hide: true diff --git a/wlx-overlay-s/src/state.rs b/wlx-overlay-s/src/state.rs index 0c083f1..fd85fd3 100644 --- a/wlx-overlay-s/src/state.rs +++ b/wlx-overlay-s/src/state.rs @@ -27,6 +27,7 @@ use crate::{ config_io::{self, get_config_file_path}, graphics::WGfxExtras, gui, + ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal}, subsystem::{audio::AudioOutput, dbus::DbusConnector, input::HidWrapper}, }; @@ -53,11 +54,14 @@ pub struct AppState { pub xr_backend: XrBackend, + pub ipc_server: ipc_server::WayVRServer, + pub wayvr_signals: SyncEventQueue, + #[cfg(feature = "osc")] pub osc_sender: Option, #[cfg(feature = "wayvr")] - pub wayvr: Option>>, // Dynamically created if requested + pub wayland_server: Option>>, } #[allow(unused_mut)] @@ -71,17 +75,26 @@ impl AppState { let mut tasks = TaskContainer::new(); let session = AppSession::load(); + let wayvr_signals = SyncEventQueue::new(); #[cfg(feature = "wayvr")] - let wayvr = session + let wayland_server = session .wayvr_config - .post_load(&session.config, &mut tasks)?; + .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(); let mut hid_provider = HidWrapper::new(); #[cfg(feature = "wayvr")] - if let Some(wayvr) = wayvr.as_ref() { - hid_provider.set_wayvr(wayvr.clone()); + if let Some(wayland_server) = wayland_server.as_ref() { + hid_provider.set_wayvr(wayland_server.clone()); } #[cfg(feature = "osc")] @@ -114,6 +127,8 @@ impl AppState { let dbus = DbusConnector::default(); + let ipc_server = ipc_server::WayVRServer::new()?; + Ok(Self { session, tasks, @@ -135,30 +150,17 @@ impl AppState { )?, dbus, xr_backend, + ipc_server, + wayvr_signals, #[cfg(feature = "osc")] osc_sender, #[cfg(feature = "wayvr")] - wayvr, + wayland_server, }) } - #[cfg(feature = "wayvr")] - #[allow(dead_code)] - pub fn get_wayvr(&mut self) -> anyhow::Result>> { - if let Some(wvr) = &self.wayvr { - Ok(wvr.clone()) - } else { - let wayvr = Rc::new(RefCell::new(WayVRData::new( - WayVRConfig::get_wayvr_config(&self.session.config, &self.session.wayvr_config)?, - )?)); - self.hid_provider.set_wayvr(wayvr.clone()); - self.wayvr = Some(wayvr.clone()); - Ok(wayvr) - } - } - pub fn try_load_bytes(path: &str, fallback_data: &'static [u8]) -> &'static [u8] { if path.is_empty() { return fallback_data; @@ -187,7 +189,7 @@ pub struct AppSession { pub config: GeneralConfig, #[cfg(feature = "wayvr")] - pub wayvr_config: WayVRConfig, + pub wayvr_config: WayVRConfig, // TODO: rename to "wayland_server_config" pub toast_topics: IdMap, }