WayVR: Display removal support

This commit is contained in:
Aleksander
2025-01-19 15:20:13 +01:00
parent 244f8cfa80
commit 80d9b02ac7
10 changed files with 119 additions and 13 deletions

2
Cargo.lock generated
View File

@@ -4582,7 +4582,7 @@ dependencies = [
[[package]] [[package]]
name = "wayvr_ipc" name = "wayvr_ipc"
version = "0.1.0" 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 = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",

View File

@@ -86,7 +86,7 @@ wayland-client = { version = "0.31.6", optional = true }
wayland-egl = { version = "0.32.4", optional = true } wayland-egl = { version = "0.32.4", optional = true }
interprocess = { version = "2.2.2", optional = true } interprocess = { version = "2.2.2", optional = true }
bytes = { version = "1.9.0", 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] [build-dependencies]

View File

@@ -484,6 +484,7 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
TaskType::DropOverlay(sel) => { TaskType::DropOverlay(sel) => {
if let Some(o) = overlays.mut_by_selector(&sel) { if let Some(o) = overlays.mut_by_selector(&sel) {
if o.state.birthframe < cur_frame { if o.state.birthframe < cur_frame {
log::debug!("{}: destroy", o.state.name);
if let Some(o) = overlays.remove_by_selector(&sel) { if let Some(o) = overlays.remove_by_selector(&sel) {
// set for deletion after all images are done showing // set for deletion after all images are done showing
delete_queue.push((o, cur_frame + 5)); delete_queue.push((o, cur_frame + 5));

View File

@@ -10,7 +10,7 @@ use smithay::{
use crate::backend::wayvr::{ExternalProcessRequest, WayVRTask}; use crate::backend::wayvr::{ExternalProcessRequest, WayVRTask};
use super::{ use super::{
comp::{self}, comp::{self, ClientState},
display, process, ProcessWayVREnv, display, process, ProcessWayVREnv,
}; };
@@ -86,6 +86,20 @@ impl WayVRCompositor {
self.clients.push(client); self.clients.push(client);
} }
pub fn cleanup_clients(&mut self) {
self.clients.retain(|client| {
let Some(data) = client.client.get_data::<ClientState>() else {
return false;
};
if *data.disconnected.lock().unwrap() {
return false;
}
true
});
}
fn accept_connection( fn accept_connection(
&mut self, &mut self,
stream: UnixStream, stream: UnixStream,

View File

@@ -19,6 +19,7 @@ use smithay::{
}; };
use std::collections::HashSet; use std::collections::HashSet;
use std::os::fd::OwnedFd; use std::os::fd::OwnedFd;
use std::sync::{Arc, Mutex};
use smithay::utils::Serial; use smithay::utils::Serial;
use smithay::wayland::compositor::{ use smithay::wayland::compositor::{
@@ -116,6 +117,7 @@ impl SelectionHandler for Application {
#[derive(Default)] #[derive(Default)]
pub struct ClientState { pub struct ClientState {
compositor_state: compositor::CompositorClientState, compositor_state: compositor::CompositorClientState,
pub disconnected: Arc<Mutex<bool>>,
} }
impl ClientData for ClientState { impl ClientData for ClientState {
@@ -124,6 +126,7 @@ impl ClientData for ClientState {
} }
fn disconnected(&self, client_id: ClientId, reason: DisconnectReason) { fn disconnected(&self, client_id: ClientId, reason: DisconnectReason) {
*self.disconnected.lock().unwrap() = true;
log::debug!( log::debug!(
"Client ID {:?} disconnected. Reason: {:?}", "Client ID {:?} disconnected. Reason: {:?}",
client_id, client_id,

View File

@@ -60,7 +60,7 @@ pub struct Display {
pub overlay_id: Option<OverlayID>, pub overlay_id: Option<OverlayID>,
pub wants_redraw: bool, pub wants_redraw: bool,
pub primary: bool, pub primary: bool,
wm: Rc<RefCell<window::WindowManager>>, pub wm: Rc<RefCell<window::WindowManager>>,
pub displayed_windows: Vec<DisplayWindow>, pub displayed_windows: Vec<DisplayWindow>,
wayland_env: super::WaylandEnv, wayland_env: super::WaylandEnv,
last_pressed_time_ms: u64, last_pressed_time_ms: u64,

View File

@@ -68,6 +68,7 @@ pub struct ExternalProcessRequest {
pub enum WayVRTask { pub enum WayVRTask {
NewToplevel(ClientId, ToplevelSurface), NewToplevel(ClientId, ToplevelSurface),
NewExternalProcess(ExternalProcessRequest), NewExternalProcess(ExternalProcessRequest),
DropOverlay(super::overlay::OverlayID),
ProcessTerminationRequest(process::ProcessHandle), ProcessTerminationRequest(process::ProcessHandle),
} }
@@ -92,12 +93,13 @@ pub struct WayVRState {
pub processes: process::ProcessVec, pub processes: process::ProcessVec,
config: Config, config: Config,
dashboard_display: Option<display::DisplayHandle>, dashboard_display: Option<display::DisplayHandle>,
tasks: SyncEventQueue<WayVRTask>,
ticks: u64,
} }
pub struct WayVR { pub struct WayVR {
pub state: WayVRState, pub state: WayVRState,
ipc_server: WayVRServer, ipc_server: WayVRServer,
tasks: SyncEventQueue<WayVRTask>,
pub signals: SyncEventQueue<WayVRSignal>, pub signals: SyncEventQueue<WayVRSignal>,
} }
@@ -113,6 +115,7 @@ pub enum TickTask {
packet_client::WvrDisplayCreateParams, packet_client::WvrDisplayCreateParams,
Option<display::DisplayHandle>, /* existing handle? */ Option<display::DisplayHandle>, /* existing handle? */
), ),
DropOverlay(super::overlay::OverlayID),
} }
impl WayVR { impl WayVR {
@@ -230,12 +233,13 @@ impl WayVR {
wm: Rc::new(RefCell::new(window::WindowManager::new())), wm: Rc::new(RefCell::new(window::WindowManager::new())),
config, config,
dashboard_display: None, dashboard_display: None,
ticks: 0,
tasks,
}; };
Ok(Self { Ok(Self {
state, state,
signals: SyncEventQueue::new(), signals: SyncEventQueue::new(),
tasks,
ipc_server, ipc_server,
}) })
} }
@@ -313,11 +317,14 @@ impl WayVR {
display.tick(&self.state.config, &handle, &mut self.signals); 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 { match task {
WayVRTask::NewExternalProcess(req) => { WayVRTask::NewExternalProcess(req) => {
tasks.push(TickTask::NewExternalProcess(req)); tasks.push(TickTask::NewExternalProcess(req));
} }
WayVRTask::DropOverlay(overlay_id) => {
tasks.push(TickTask::DropOverlay(overlay_id));
}
WayVRTask::NewToplevel(client_id, toplevel) => { WayVRTask::NewToplevel(client_id, toplevel) => {
// Attach newly created toplevel surfaces to displays // Attach newly created toplevel surfaces to displays
for client in &self.state.manager.clients { for client in &self.state.manager.clients {
@@ -358,6 +365,12 @@ impl WayVR {
.manager .manager
.tick_wayland(&mut self.state.displays, &mut self.state.processes)?; .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) Ok(tasks)
} }
@@ -399,7 +412,8 @@ impl WayVR {
} }
pub fn terminate_process(&mut self, process_handle: process::ProcessHandle) { pub fn terminate_process(&mut self, process_handle: process::ProcessHandle) {
self.tasks self.state
.tasks
.send(WayVRTask::ProcessTerminationRequest(process_handle)); .send(WayVRTask::ProcessTerminationRequest(process_handle));
} }
} }
@@ -465,6 +479,46 @@ impl WayVRState {
Ok(self.displays.add(display)) 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::<String>::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( pub fn get_or_create_dashboard_display(
&mut self, &mut self,
width: u16, width: u16,
@@ -484,10 +538,6 @@ impl WayVRState {
Ok((true, new_disp)) 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 // Check if process with given arguments already exists
pub fn process_query( pub fn process_query(
&self, &self,

View File

@@ -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 { pub fn to_packet(&self, handle: ProcessHandle) -> packet_server::WvrProcess {
match self { match self {
Process::Managed(p) => packet_server::WvrProcess { Process::Managed(p) => packet_server::WvrProcess {

View File

@@ -205,6 +205,24 @@ impl Connection {
Ok(()) 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( fn handle_wvr_process_launch(
&mut self, &mut self,
params: &mut TickParams, params: &mut TickParams,
@@ -317,6 +335,9 @@ impl Connection {
PacketClient::WvrDisplayGet(serial, display_handle) => { PacketClient::WvrDisplayGet(serial, display_handle) => {
self.handle_wvr_process_get(params, 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) => { PacketClient::WvrProcessList(serial) => {
self.handle_wvr_process_list(params, serial)?; self.handle_wvr_process_list(params, serial)?;
} }

View File

@@ -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 ctx = self.context.borrow();
let mut wayvr = ctx.wayvr.borrow_mut(); 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 let dmabuf_data = wayvr
.data .data