From 80d9b02ac7f8ec16cfc8bd709d4bec17b0e23f70 Mon Sep 17 00:00:00 2001 From: Aleksander Date: Sun, 19 Jan 2025 15:20:13 +0100 Subject: [PATCH] WayVR: Display removal support --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/backend/openxr/mod.rs | 1 + src/backend/wayvr/client.rs | 16 +++++++- src/backend/wayvr/comp.rs | 3 ++ src/backend/wayvr/display.rs | 2 +- src/backend/wayvr/mod.rs | 66 +++++++++++++++++++++++++++++---- src/backend/wayvr/process.rs | 7 ++++ src/backend/wayvr/server_ipc.rs | 21 +++++++++++ src/overlays/wayvr.rs | 12 +++++- 10 files changed, 119 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2eca8e5..6b82e58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4582,7 +4582,7 @@ dependencies = [ [[package]] name = "wayvr_ipc" version = "0.1.0" -source = "git+https://github.com/olekolek1000/wayvr-ipc.git?rev=b484d1f0607b3f67e2c9e49797819738344bcb5c#b484d1f0607b3f67e2c9e49797819738344bcb5c" +source = "git+https://github.com/olekolek1000/wayvr-ipc.git?rev=94ab6125ec07c091099bf71b028d701b58eccccf#94ab6125ec07c091099bf71b028d701b58eccccf" dependencies = [ "anyhow", "bytes", diff --git a/Cargo.toml b/Cargo.toml index d117752..83dea61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,7 +86,7 @@ wayland-client = { version = "0.31.6", optional = true } wayland-egl = { version = "0.32.4", optional = true } interprocess = { version = "2.2.2", optional = true } bytes = { version = "1.9.0", optional = true } -wayvr_ipc = { git = "https://github.com/olekolek1000/wayvr-ipc.git", rev = "b484d1f0607b3f67e2c9e49797819738344bcb5c", default-features = false, optional = true } +wayvr_ipc = { git = "https://github.com/olekolek1000/wayvr-ipc.git", rev = "94ab6125ec07c091099bf71b028d701b58eccccf", default-features = false, optional = true } ################################ [build-dependencies] diff --git a/src/backend/openxr/mod.rs b/src/backend/openxr/mod.rs index eebf3af..bd1acc7 100644 --- a/src/backend/openxr/mod.rs +++ b/src/backend/openxr/mod.rs @@ -484,6 +484,7 @@ pub fn openxr_run(running: Arc, show_by_default: bool) -> Result<(), TaskType::DropOverlay(sel) => { if let Some(o) = overlays.mut_by_selector(&sel) { if o.state.birthframe < cur_frame { + log::debug!("{}: destroy", o.state.name); if let Some(o) = overlays.remove_by_selector(&sel) { // set for deletion after all images are done showing delete_queue.push((o, cur_frame + 5)); diff --git a/src/backend/wayvr/client.rs b/src/backend/wayvr/client.rs index d060379..603829b 100644 --- a/src/backend/wayvr/client.rs +++ b/src/backend/wayvr/client.rs @@ -10,7 +10,7 @@ use smithay::{ use crate::backend::wayvr::{ExternalProcessRequest, WayVRTask}; use super::{ - comp::{self}, + comp::{self, ClientState}, display, process, ProcessWayVREnv, }; @@ -86,6 +86,20 @@ impl WayVRCompositor { self.clients.push(client); } + pub fn cleanup_clients(&mut self) { + self.clients.retain(|client| { + let Some(data) = client.client.get_data::() else { + return false; + }; + + if *data.disconnected.lock().unwrap() { + return false; + } + + true + }); + } + fn accept_connection( &mut self, stream: UnixStream, diff --git a/src/backend/wayvr/comp.rs b/src/backend/wayvr/comp.rs index 1b07031..2af7ddc 100644 --- a/src/backend/wayvr/comp.rs +++ b/src/backend/wayvr/comp.rs @@ -19,6 +19,7 @@ use smithay::{ }; use std::collections::HashSet; use std::os::fd::OwnedFd; +use std::sync::{Arc, Mutex}; use smithay::utils::Serial; use smithay::wayland::compositor::{ @@ -116,6 +117,7 @@ impl SelectionHandler for Application { #[derive(Default)] pub struct ClientState { compositor_state: compositor::CompositorClientState, + pub disconnected: Arc>, } impl ClientData for ClientState { @@ -124,6 +126,7 @@ impl ClientData for ClientState { } fn disconnected(&self, client_id: ClientId, reason: DisconnectReason) { + *self.disconnected.lock().unwrap() = true; log::debug!( "Client ID {:?} disconnected. Reason: {:?}", client_id, diff --git a/src/backend/wayvr/display.rs b/src/backend/wayvr/display.rs index c07939f..f42c767 100644 --- a/src/backend/wayvr/display.rs +++ b/src/backend/wayvr/display.rs @@ -60,7 +60,7 @@ pub struct Display { pub overlay_id: Option, pub wants_redraw: bool, pub primary: bool, - wm: Rc>, + pub wm: Rc>, pub displayed_windows: Vec, wayland_env: super::WaylandEnv, last_pressed_time_ms: u64, diff --git a/src/backend/wayvr/mod.rs b/src/backend/wayvr/mod.rs index b5395c5..7807ad0 100644 --- a/src/backend/wayvr/mod.rs +++ b/src/backend/wayvr/mod.rs @@ -68,6 +68,7 @@ pub struct ExternalProcessRequest { pub enum WayVRTask { NewToplevel(ClientId, ToplevelSurface), NewExternalProcess(ExternalProcessRequest), + DropOverlay(super::overlay::OverlayID), ProcessTerminationRequest(process::ProcessHandle), } @@ -92,12 +93,13 @@ pub struct WayVRState { pub processes: process::ProcessVec, config: Config, dashboard_display: Option, + tasks: SyncEventQueue, + ticks: u64, } pub struct WayVR { pub state: WayVRState, ipc_server: WayVRServer, - tasks: SyncEventQueue, pub signals: SyncEventQueue, } @@ -113,6 +115,7 @@ pub enum TickTask { packet_client::WvrDisplayCreateParams, Option, /* existing handle? */ ), + DropOverlay(super::overlay::OverlayID), } impl WayVR { @@ -230,12 +233,13 @@ impl WayVR { wm: Rc::new(RefCell::new(window::WindowManager::new())), config, dashboard_display: None, + ticks: 0, + tasks, }; Ok(Self { state, signals: SyncEventQueue::new(), - tasks, ipc_server, }) } @@ -313,11 +317,14 @@ impl WayVR { display.tick(&self.state.config, &handle, &mut self.signals); }); - while let Some(task) = self.tasks.read() { + while let Some(task) = self.state.tasks.read() { match task { WayVRTask::NewExternalProcess(req) => { tasks.push(TickTask::NewExternalProcess(req)); } + WayVRTask::DropOverlay(overlay_id) => { + tasks.push(TickTask::DropOverlay(overlay_id)); + } WayVRTask::NewToplevel(client_id, toplevel) => { // Attach newly created toplevel surfaces to displays for client in &self.state.manager.clients { @@ -358,6 +365,12 @@ impl WayVR { .manager .tick_wayland(&mut self.state.displays, &mut self.state.processes)?; + if self.state.ticks % 200 == 0 { + self.state.manager.cleanup_clients(); + } + + self.state.ticks += 1; + Ok(tasks) } @@ -399,7 +412,8 @@ impl WayVR { } pub fn terminate_process(&mut self, process_handle: process::ProcessHandle) { - self.tasks + self.state + .tasks .send(WayVRTask::ProcessTerminationRequest(process_handle)); } } @@ -465,6 +479,46 @@ impl WayVRState { Ok(self.displays.add(display)) } + 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.tasks.send(WayVRTask::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(); + + self.processes.iter_mut(&mut |_, process| { + 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.iter() { + 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); + + Ok(()) + } + pub fn get_or_create_dashboard_display( &mut self, width: u16, @@ -484,10 +538,6 @@ impl WayVRState { Ok((true, new_disp)) } - pub fn destroy_display(&mut self, handle: display::DisplayHandle) { - self.displays.remove(&handle); - } - // Check if process with given arguments already exists pub fn process_query( &self, diff --git a/src/backend/wayvr/process.rs b/src/backend/wayvr/process.rs index ef0a956..62114df 100644 --- a/src/backend/wayvr/process.rs +++ b/src/backend/wayvr/process.rs @@ -49,6 +49,13 @@ impl Process { } } + pub fn get_name(&self) -> String { + match self { + Process::Managed(p) => p.get_name().unwrap_or(String::from("unknown")), + Process::External(p) => p.get_name().unwrap_or(String::from("unknown")), + } + } + pub fn to_packet(&self, handle: ProcessHandle) -> packet_server::WvrProcess { match self { Process::Managed(p) => packet_server::WvrProcess { diff --git a/src/backend/wayvr/server_ipc.rs b/src/backend/wayvr/server_ipc.rs index 7ac1e0c..a624526 100644 --- a/src/backend/wayvr/server_ipc.rs +++ b/src/backend/wayvr/server_ipc.rs @@ -205,6 +205,24 @@ impl Connection { 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_process_launch( &mut self, params: &mut TickParams, @@ -317,6 +335,9 @@ impl Connection { PacketClient::WvrDisplayGet(serial, display_handle) => { self.handle_wvr_process_get(params, serial, display_handle)?; } + PacketClient::WvrDisplayRemove(serial, display_handle) => { + self.handle_wvr_display_remove(params, serial, display_handle)?; + } PacketClient::WvrProcessList(serial) => { self.handle_wvr_process_list(params, serial)?; } diff --git a/src/overlays/wayvr.rs b/src/overlays/wayvr.rs index 9050e9f..9de7c40 100644 --- a/src/overlays/wayvr.rs +++ b/src/overlays/wayvr.rs @@ -495,6 +495,10 @@ where }, }); } + wayvr::TickTask::DropOverlay(overlay_id) => { + app.tasks + .enqueue(TaskType::DropOverlay(OverlaySelector::Id(overlay_id))); + } } } @@ -580,7 +584,13 @@ impl OverlayRenderer for WayVRRenderer { let ctx = self.context.borrow(); let mut wayvr = ctx.wayvr.borrow_mut(); - wayvr.data.tick_display(ctx.display)?; + match wayvr.data.tick_display(ctx.display) { + Ok(_) => {} + Err(e) => { + log::error!("tick_display failed: {}", e); + return Ok(()); // do not proceed further + } + } let dmabuf_data = wayvr .data