WayVR: Process cleanup support, Refactoring
This commit is contained in:
@@ -9,13 +9,13 @@ use smithay::{
|
||||
|
||||
use super::{
|
||||
comp::{self},
|
||||
display,
|
||||
display, process,
|
||||
};
|
||||
|
||||
pub struct WayVRClient {
|
||||
pub client: wayland_server::Client,
|
||||
pub display_handle: display::DisplayHandle,
|
||||
pub pid: i32,
|
||||
pub pid: u32,
|
||||
}
|
||||
|
||||
pub struct WayVRManager {
|
||||
@@ -28,6 +28,8 @@ pub struct WayVRManager {
|
||||
display: wayland_server::Display<comp::Application>,
|
||||
listener: wayland_server::ListeningSocket,
|
||||
|
||||
toplevel_surf_count: u32, // for logging purposes
|
||||
|
||||
pub clients: Vec<WayVRClient>,
|
||||
}
|
||||
|
||||
@@ -67,6 +69,7 @@ impl WayVRManager {
|
||||
wayland_env,
|
||||
serial_counter: SerialCounter::new(),
|
||||
clients: Vec::new(),
|
||||
toplevel_surf_count: 0,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -74,6 +77,7 @@ impl WayVRManager {
|
||||
&mut self,
|
||||
stream: UnixStream,
|
||||
displays: &mut display::DisplayVec,
|
||||
processes: &mut process::ProcessVec,
|
||||
) -> anyhow::Result<()> {
|
||||
let client = self
|
||||
.display
|
||||
@@ -84,28 +88,34 @@ impl WayVRManager {
|
||||
let creds = client.get_credentials(&self.display.handle())?;
|
||||
let auth_key = get_display_auth_from_pid(creds.pid)?;
|
||||
|
||||
for (idx, cell) in displays.vec.iter().enumerate() {
|
||||
if let Some(cell) = &cell {
|
||||
let display = &cell.obj;
|
||||
if display.auth_key_matches(auth_key.as_str()) {
|
||||
let display_handle = display::DisplayVec::get_handle(cell, idx);
|
||||
// Find suitable auth key from the process list
|
||||
for process in processes.vec.iter().flatten() {
|
||||
let process = &process.obj;
|
||||
|
||||
// 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.clients.push(WayVRClient {
|
||||
client,
|
||||
display_handle,
|
||||
pid: creds.pid,
|
||||
display_handle: process.display_handle,
|
||||
pid: creds.pid as u32,
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anyhow::bail!("Process auth key is invalid or selected display is non-existent");
|
||||
}
|
||||
|
||||
fn accept_connections(&mut self, displays: &mut display::DisplayVec) -> anyhow::Result<()> {
|
||||
fn accept_connections(
|
||||
&mut self,
|
||||
displays: &mut display::DisplayVec,
|
||||
processes: &mut process::ProcessVec,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(stream) = self.listener.accept()? {
|
||||
if let Err(e) = self.accept_connection(stream, displays) {
|
||||
if let Err(e) = self.accept_connection(stream, displays, processes) {
|
||||
log::error!("Failed to accept connection: {}", e);
|
||||
}
|
||||
}
|
||||
@@ -113,14 +123,24 @@ impl WayVRManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn tick_wayland(&mut self, displays: &mut display::DisplayVec) -> anyhow::Result<()> {
|
||||
if let Err(e) = self.accept_connections(displays) {
|
||||
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) {
|
||||
log::error!("accept_connections failed: {}", e);
|
||||
}
|
||||
|
||||
self.display.dispatch_clients(&mut self.state)?;
|
||||
self.display.flush_clients()?;
|
||||
|
||||
let surf_count = self.state.xdg_shell.toplevel_surfaces().len() as u32;
|
||||
if surf_count != self.toplevel_surf_count {
|
||||
self.toplevel_surf_count = surf_count;
|
||||
log::info!("Toplevel surface count changed: {}", surf_count);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ use wayland_server::protocol::wl_surface::WlSurface;
|
||||
use wayland_server::Client;
|
||||
|
||||
use super::event_queue::SyncEventQueue;
|
||||
use super::WayVRTask;
|
||||
|
||||
pub struct Application {
|
||||
pub compositor: compositor::CompositorState,
|
||||
@@ -35,7 +36,7 @@ pub struct Application {
|
||||
pub shm: ShmState,
|
||||
pub data_device: DataDeviceState,
|
||||
|
||||
pub queue_new_toplevel: SyncEventQueue<(ClientId, ToplevelSurface)>,
|
||||
pub wayvr_tasks: SyncEventQueue<WayVRTask>,
|
||||
}
|
||||
|
||||
impl compositor::CompositorHandler for Application {
|
||||
@@ -125,7 +126,8 @@ impl XdgShellHandler for Application {
|
||||
|
||||
fn new_toplevel(&mut self, surface: ToplevelSurface) {
|
||||
if let Some(client) = surface.wl_surface().client() {
|
||||
self.queue_new_toplevel.send((client.id(), surface.clone()));
|
||||
self.wayvr_tasks
|
||||
.send(WayVRTask::NewToplevel(client.id(), surface.clone()));
|
||||
}
|
||||
surface.with_pending_state(|state| {
|
||||
state.states.set(xdg_toplevel::State::Activated);
|
||||
|
||||
@@ -18,7 +18,8 @@ use smithay::{
|
||||
use crate::{backend::overlay::OverlayID, gen_id};
|
||||
|
||||
use super::{
|
||||
client::WayVRManager, comp::send_frames_surface_tree, egl_data, smithay_wrapper, window,
|
||||
client::WayVRManager, comp::send_frames_surface_tree, egl_data, event_queue::SyncEventQueue,
|
||||
process, smithay_wrapper, window,
|
||||
};
|
||||
|
||||
fn generate_auth_key() -> String {
|
||||
@@ -26,22 +27,21 @@ fn generate_auth_key() -> String {
|
||||
uuid.to_string()
|
||||
}
|
||||
|
||||
struct Process {
|
||||
auth_key: String,
|
||||
child: std::process::Child,
|
||||
}
|
||||
|
||||
impl Drop for Process {
|
||||
fn drop(&mut self) {
|
||||
let _dont_care = self.child.kill();
|
||||
}
|
||||
}
|
||||
|
||||
struct DisplayWindow {
|
||||
handle: window::WindowHandle,
|
||||
window_handle: window::WindowHandle,
|
||||
process_handle: process::ProcessHandle,
|
||||
toplevel: ToplevelSurface,
|
||||
}
|
||||
|
||||
pub struct SpawnProcessResult {
|
||||
pub auth_key: String,
|
||||
pub child: std::process::Child,
|
||||
}
|
||||
|
||||
pub enum DisplayTask {
|
||||
ProcessCleanup(process::ProcessHandle),
|
||||
}
|
||||
|
||||
pub struct Display {
|
||||
// Display info stuff
|
||||
pub width: u32,
|
||||
@@ -59,7 +59,7 @@ pub struct Display {
|
||||
egl_data: Rc<egl_data::EGLData>,
|
||||
pub dmabuf_data: egl_data::DMAbufData,
|
||||
|
||||
processes: Vec<Process>,
|
||||
pub tasks: SyncEventQueue<DisplayTask>,
|
||||
}
|
||||
|
||||
impl Drop for Display {
|
||||
@@ -113,25 +113,22 @@ impl Display {
|
||||
egl_image,
|
||||
gles_texture,
|
||||
wayland_env,
|
||||
processes: Vec::new(),
|
||||
visible: true,
|
||||
overlay_id: None,
|
||||
tasks: SyncEventQueue::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn auth_key_matches(&self, auth_key: &str) -> bool {
|
||||
for process in &self.processes {
|
||||
if process.auth_key.as_str() == auth_key {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn add_window(&mut self, window_handle: window::WindowHandle, toplevel: &ToplevelSurface) {
|
||||
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 {
|
||||
handle: window_handle,
|
||||
window_handle,
|
||||
process_handle,
|
||||
toplevel: toplevel.clone(),
|
||||
});
|
||||
self.reposition_windows();
|
||||
@@ -141,7 +138,7 @@ impl Display {
|
||||
let window_count = self.displayed_windows.len();
|
||||
|
||||
for (i, win) in self.displayed_windows.iter_mut().enumerate() {
|
||||
if let Some(window) = self.wm.borrow_mut().windows.get_mut(&win.handle) {
|
||||
if let Some(window) = self.wm.borrow_mut().windows.get_mut(&win.window_handle) {
|
||||
let d_cur = i as f32 / window_count as f32;
|
||||
let d_next = (i + 1) as f32 / window_count as f32;
|
||||
|
||||
@@ -154,6 +151,24 @@ impl Display {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick(&mut self) {
|
||||
while let Some(task) = self.tasks.read() {
|
||||
match task {
|
||||
DisplayTask::ProcessCleanup(process_handle) => {
|
||||
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.reposition_windows();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick_render(&self, renderer: &mut GlesRenderer, time_ms: u64) -> anyhow::Result<()> {
|
||||
renderer.bind(self.gles_texture.clone())?;
|
||||
|
||||
@@ -166,7 +181,7 @@ impl Display {
|
||||
.iter()
|
||||
.flat_map(|display_window| {
|
||||
let wm = self.wm.borrow_mut();
|
||||
if let Some(window) = wm.windows.get(&display_window.handle) {
|
||||
if let Some(window) = wm.windows.get(&display_window.window_handle) {
|
||||
render_elements_from_surface_tree(
|
||||
renderer,
|
||||
display_window.toplevel.wl_surface(),
|
||||
@@ -207,13 +222,13 @@ impl Display {
|
||||
let wm = self.wm.borrow();
|
||||
|
||||
for cell in self.displayed_windows.iter() {
|
||||
if let Some(window) = wm.windows.get(&cell.handle) {
|
||||
if let Some(window) = wm.windows.get(&cell.window_handle) {
|
||||
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.handle);
|
||||
return Some(cell.window_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,7 +350,7 @@ impl Display {
|
||||
exec_path: &str,
|
||||
args: &[&str],
|
||||
env: &[(&str, &str)],
|
||||
) -> anyhow::Result<()> {
|
||||
) -> anyhow::Result<SpawnProcessResult> {
|
||||
log::info!("Spawning subprocess with exec path \"{}\"", exec_path);
|
||||
|
||||
let auth_key = generate_auth_key();
|
||||
@@ -349,9 +364,7 @@ impl Display {
|
||||
}
|
||||
|
||||
match cmd.spawn() {
|
||||
Ok(child) => {
|
||||
self.processes.push(Process { child, auth_key });
|
||||
}
|
||||
Ok(child) => Ok(SpawnProcessResult { auth_key, child }),
|
||||
Err(e) => {
|
||||
anyhow::bail!(
|
||||
"Failed to launch process with path \"{}\": {}. Make sure your exec path exists.",
|
||||
@@ -360,8 +373,6 @@ impl Display {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +1,34 @@
|
||||
#![allow(clippy::all)]
|
||||
|
||||
//eglExportDMABUFImageMESA
|
||||
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,
|
||||
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
|
||||
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,
|
||||
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
|
||||
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,
|
||||
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,
|
||||
>;
|
||||
|
||||
@@ -1,151 +1,163 @@
|
||||
#[macro_export]
|
||||
macro_rules! gen_id {
|
||||
(
|
||||
(
|
||||
$container_name:ident,
|
||||
$instance_name:ident,
|
||||
$cell_name:ident,
|
||||
$handle_name:ident) => {
|
||||
//ThingCell
|
||||
pub struct $cell_name {
|
||||
pub obj: $instance_name,
|
||||
generation: u64,
|
||||
}
|
||||
//ThingCell
|
||||
pub struct $cell_name {
|
||||
pub obj: $instance_name,
|
||||
generation: u64,
|
||||
}
|
||||
|
||||
//ThingVec
|
||||
pub struct $container_name {
|
||||
// Vec<Option<ThingCell>>
|
||||
pub vec: Vec<Option<$cell_name>>,
|
||||
//ThingVec
|
||||
pub struct $container_name {
|
||||
// Vec<Option<ThingCell>>
|
||||
pub vec: Vec<Option<$cell_name>>,
|
||||
|
||||
cur_generation: u64,
|
||||
}
|
||||
cur_generation: u64,
|
||||
}
|
||||
|
||||
//ThingHandle
|
||||
#[derive(Default, Clone, Copy, PartialEq)]
|
||||
pub struct $handle_name {
|
||||
idx: u32,
|
||||
generation: u64,
|
||||
}
|
||||
//ThingHandle
|
||||
#[derive(Default, Clone, Copy, PartialEq)]
|
||||
pub struct $handle_name {
|
||||
idx: u32,
|
||||
generation: u64,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl $handle_name {
|
||||
pub fn reset(&mut self) {
|
||||
self.generation = 0;
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
impl $handle_name {
|
||||
pub fn reset(&mut self) {
|
||||
self.generation = 0;
|
||||
}
|
||||
|
||||
pub fn is_set(&self) -> bool {
|
||||
self.generation > 0
|
||||
}
|
||||
pub fn is_set(&self) -> bool {
|
||||
self.generation > 0
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u32 {
|
||||
self.idx
|
||||
}
|
||||
}
|
||||
pub fn id(&self) -> u32 {
|
||||
self.idx
|
||||
}
|
||||
}
|
||||
|
||||
//ThingVec
|
||||
#[allow(dead_code)]
|
||||
impl $container_name {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
vec: Vec::new(),
|
||||
cur_generation: 0,
|
||||
}
|
||||
}
|
||||
//ThingVec
|
||||
#[allow(dead_code)]
|
||||
impl $container_name {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
vec: Vec::new(),
|
||||
cur_generation: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self, callback: &mut dyn FnMut($handle_name, &$instance_name)) {
|
||||
for (idx, opt_cell) in self.vec.iter().enumerate() {
|
||||
if let Some(cell) = opt_cell {
|
||||
let handle = $container_name::get_handle(&cell, idx);
|
||||
callback(handle, &cell.obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn iter(&self, callback: &dyn Fn($handle_name, &$instance_name)) {
|
||||
for (idx, opt_cell) in self.vec.iter().enumerate() {
|
||||
if let Some(cell) = opt_cell {
|
||||
let handle = $container_name::get_handle(&cell, idx);
|
||||
callback(handle, &cell.obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_handle(cell: &$cell_name, idx: usize) -> $handle_name {
|
||||
$handle_name {
|
||||
idx: idx as u32,
|
||||
generation: cell.generation,
|
||||
}
|
||||
}
|
||||
pub fn iter_mut(
|
||||
&mut self,
|
||||
callback: &mut dyn FnMut($handle_name, &mut $instance_name),
|
||||
) {
|
||||
for (idx, opt_cell) in self.vec.iter_mut().enumerate() {
|
||||
if let Some(cell) = opt_cell {
|
||||
let handle = $container_name::get_handle(&cell, idx);
|
||||
callback(handle, &mut cell.obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_unused_idx(&mut self) -> Option<u32> {
|
||||
for (num, obj) in self.vec.iter().enumerate() {
|
||||
if obj.is_none() {
|
||||
return Some(num as u32);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn get_handle(cell: &$cell_name, idx: usize) -> $handle_name {
|
||||
$handle_name {
|
||||
idx: idx as u32,
|
||||
generation: cell.generation,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, obj: $instance_name) -> $handle_name {
|
||||
self.cur_generation += 1;
|
||||
let generation = self.cur_generation;
|
||||
fn find_unused_idx(&mut self) -> Option<u32> {
|
||||
for (num, obj) in self.vec.iter().enumerate() {
|
||||
if obj.is_none() {
|
||||
return Some(num as u32);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
let unused_idx = self.find_unused_idx();
|
||||
pub fn add(&mut self, obj: $instance_name) -> $handle_name {
|
||||
self.cur_generation += 1;
|
||||
let generation = self.cur_generation;
|
||||
|
||||
let idx = if let Some(idx) = unused_idx {
|
||||
idx
|
||||
} else {
|
||||
self.vec.len() as u32
|
||||
};
|
||||
let unused_idx = self.find_unused_idx();
|
||||
|
||||
let handle = $handle_name { idx, generation };
|
||||
let idx = if let Some(idx) = unused_idx {
|
||||
idx
|
||||
} else {
|
||||
self.vec.len() as u32
|
||||
};
|
||||
|
||||
let cell = $cell_name { obj, generation };
|
||||
let handle = $handle_name { idx, generation };
|
||||
|
||||
if let Some(idx) = unused_idx {
|
||||
self.vec[idx as usize] = Some(cell);
|
||||
} else {
|
||||
self.vec.push(Some(cell))
|
||||
}
|
||||
let cell = $cell_name { obj, generation };
|
||||
|
||||
handle
|
||||
}
|
||||
if let Some(idx) = unused_idx {
|
||||
self.vec[idx as usize] = Some(cell);
|
||||
} else {
|
||||
self.vec.push(Some(cell))
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, handle: &$handle_name) {
|
||||
// Out of bounds, ignore
|
||||
if handle.idx as usize >= self.vec.len() {
|
||||
return;
|
||||
}
|
||||
handle
|
||||
}
|
||||
|
||||
// Remove only if the generation matches
|
||||
if let Some(cell) = &self.vec[handle.idx as usize] {
|
||||
if cell.generation == handle.generation {
|
||||
self.vec[handle.idx as usize] = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn remove(&mut self, handle: &$handle_name) {
|
||||
// Out of bounds, ignore
|
||||
if handle.idx as usize >= self.vec.len() {
|
||||
return;
|
||||
}
|
||||
|
||||
pub fn get(&self, handle: &$handle_name) -> Option<&$instance_name> {
|
||||
// Out of bounds, ignore
|
||||
if handle.idx as usize >= self.vec.len() {
|
||||
return None;
|
||||
}
|
||||
// Remove only if the generation matches
|
||||
if let Some(cell) = &self.vec[handle.idx as usize] {
|
||||
if cell.generation == handle.generation {
|
||||
self.vec[handle.idx as usize] = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(cell) = &self.vec[handle.idx as usize] {
|
||||
if cell.generation == handle.generation {
|
||||
return Some(&cell.obj);
|
||||
}
|
||||
}
|
||||
pub fn get(&self, handle: &$handle_name) -> Option<&$instance_name> {
|
||||
// Out of bounds, ignore
|
||||
if handle.idx as usize >= self.vec.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
if let Some(cell) = &self.vec[handle.idx as usize] {
|
||||
if cell.generation == handle.generation {
|
||||
return Some(&cell.obj);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, handle: &$handle_name) -> Option<&mut $instance_name> {
|
||||
// Out of bounds, ignore
|
||||
if handle.idx as usize >= self.vec.len() {
|
||||
return None;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
if let Some(cell) = &mut self.vec[handle.idx as usize] {
|
||||
if cell.generation == handle.generation {
|
||||
return Some(&mut cell.obj);
|
||||
}
|
||||
}
|
||||
pub fn get_mut(&mut self, handle: &$handle_name) -> Option<&mut $instance_name> {
|
||||
// Out of bounds, ignore
|
||||
if handle.idx as usize >= self.vec.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Some(cell) = &mut self.vec[handle.idx as usize] {
|
||||
if cell.generation == handle.generation {
|
||||
return Some(&mut cell.obj);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* Example usage:
|
||||
|
||||
@@ -5,6 +5,7 @@ pub mod egl_data;
|
||||
mod egl_ex;
|
||||
mod event_queue;
|
||||
mod handle;
|
||||
mod process;
|
||||
mod smithay_wrapper;
|
||||
mod time;
|
||||
mod window;
|
||||
@@ -14,6 +15,8 @@ use std::{cell::RefCell, rc::Rc};
|
||||
use comp::Application;
|
||||
use display::DisplayVec;
|
||||
use event_queue::SyncEventQueue;
|
||||
use process::ProcessVec;
|
||||
use smallvec::SmallVec;
|
||||
use smithay::{
|
||||
backend::renderer::gles::GlesRenderer,
|
||||
input::SeatState,
|
||||
@@ -27,6 +30,9 @@ use smithay::{
|
||||
};
|
||||
use time::get_millis;
|
||||
|
||||
const STR_INVALID_HANDLE_DISP: &str = "Invalid display handle";
|
||||
const STR_INVALID_HANDLE_PROCESS: &str = "Invalid process handle";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WaylandEnv {
|
||||
pub display_num: u32,
|
||||
@@ -39,6 +45,12 @@ impl WaylandEnv {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum WayVRTask {
|
||||
NewToplevel(ClientId, ToplevelSurface),
|
||||
ProcessTerminationRequest(process::ProcessHandle),
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct WayVR {
|
||||
time_start: u64,
|
||||
@@ -47,8 +59,9 @@ pub struct WayVR {
|
||||
manager: client::WayVRManager,
|
||||
wm: Rc<RefCell<window::WindowManager>>,
|
||||
egl_data: Rc<egl_data::EGLData>,
|
||||
pub processes: process::ProcessVec,
|
||||
|
||||
queue_new_toplevel: SyncEventQueue<(ClientId, ToplevelSurface)>,
|
||||
tasks: SyncEventQueue<WayVRTask>,
|
||||
}
|
||||
|
||||
pub enum MouseIndex {
|
||||
@@ -72,7 +85,7 @@ impl WayVR {
|
||||
let seat_keyboard = seat.add_keyboard(Default::default(), 100, 100)?;
|
||||
let seat_pointer = seat.add_pointer();
|
||||
|
||||
let queue_new_toplevel = SyncEventQueue::new();
|
||||
let tasks = SyncEventQueue::new();
|
||||
|
||||
let state = Application {
|
||||
compositor,
|
||||
@@ -80,7 +93,7 @@ impl WayVR {
|
||||
seat_state,
|
||||
shm,
|
||||
data_device,
|
||||
queue_new_toplevel: queue_new_toplevel.clone(),
|
||||
wayvr_tasks: tasks.clone(),
|
||||
};
|
||||
|
||||
let time_start = get_millis();
|
||||
@@ -94,19 +107,19 @@ impl WayVR {
|
||||
time_start,
|
||||
manager: client::WayVRManager::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())),
|
||||
queue_new_toplevel,
|
||||
tasks,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn tick_display(&mut self, display: display::DisplayHandle) -> anyhow::Result<()> {
|
||||
// millis since the start of wayvr
|
||||
|
||||
let display = self
|
||||
.displays
|
||||
.get(&display)
|
||||
.ok_or(anyhow::anyhow!("Invalid display handle"))?;
|
||||
.ok_or(anyhow::anyhow!(STR_INVALID_HANDLE_DISP))?;
|
||||
|
||||
let time_ms = get_millis() - self.time_start;
|
||||
|
||||
@@ -121,25 +134,68 @@ impl WayVR {
|
||||
}
|
||||
|
||||
pub fn tick_events(&mut self) -> anyhow::Result<()> {
|
||||
// Attach newly created toplevel surfaces to displayes
|
||||
while let Some((client_id, toplevel)) = self.queue_new_toplevel.read() {
|
||||
for client in &self.manager.clients {
|
||||
if client.client.id() == client_id {
|
||||
let window_handle = self.wm.borrow_mut().create_window(&toplevel);
|
||||
// Tick all child processes
|
||||
let mut to_remove: SmallVec<[(process::ProcessHandle, display::DisplayHandle); 2]> =
|
||||
SmallVec::new();
|
||||
self.processes.iter_mut(&mut |handle, process| {
|
||||
if !process.is_running() {
|
||||
to_remove.push((handle, process.display_handle));
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(display) = self.displays.get_mut(&client.display_handle) {
|
||||
display.add_window(window_handle, &toplevel);
|
||||
} else {
|
||||
// This shouldn't happen, scream if it does
|
||||
log::error!("Could not attach window handle into display");
|
||||
for (p_handle, disp_handle) in to_remove {
|
||||
self.processes.remove(&p_handle);
|
||||
|
||||
if let Some(display) = self.displays.get(&disp_handle) {
|
||||
display
|
||||
.tasks
|
||||
.send(display::DisplayTask::ProcessCleanup(p_handle));
|
||||
}
|
||||
}
|
||||
|
||||
for display in self.displays.vec.iter_mut().flatten() {
|
||||
display.obj.tick();
|
||||
}
|
||||
|
||||
while let Some(task) = self.tasks.read() {
|
||||
match task {
|
||||
WayVRTask::NewToplevel(client_id, toplevel) => {
|
||||
// Attach newly created toplevel surfaces to displays
|
||||
for client in &self.manager.clients {
|
||||
if client.client.id() == client_id {
|
||||
let window_handle = self.wm.borrow_mut().create_window(&toplevel);
|
||||
|
||||
if let Some(process_handle) =
|
||||
process::find_by_pid(&self.processes, client.pid)
|
||||
{
|
||||
if let Some(display) = self.displays.get_mut(&client.display_handle)
|
||||
{
|
||||
display.add_window(window_handle, process_handle, &toplevel);
|
||||
} else {
|
||||
// This shouldn't happen, scream if it does
|
||||
log::error!("Could not attach window handle into display");
|
||||
}
|
||||
} else {
|
||||
log::error!(
|
||||
"Failed to find process by PID {}. It was probably spawned externally.",
|
||||
client.pid
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
WayVRTask::ProcessTerminationRequest(process_handle) => {
|
||||
if let Some(process) = self.processes.get_mut(&process_handle) {
|
||||
process.terminate();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.manager.tick_wayland(&mut self.displays)
|
||||
self.manager
|
||||
.tick_wayland(&mut self.displays, &mut self.processes)
|
||||
}
|
||||
|
||||
pub fn tick_finish(&mut self) -> anyhow::Result<()> {
|
||||
@@ -222,16 +278,59 @@ impl WayVR {
|
||||
self.displays.remove(&handle);
|
||||
}
|
||||
|
||||
// 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)],
|
||||
) -> Option<process::ProcessHandle> {
|
||||
for (idx, cell) in self.processes.vec.iter().enumerate() {
|
||||
if let Some(cell) = &cell {
|
||||
let process = &cell.obj;
|
||||
if process.display_handle != display_handle
|
||||
|| process.exec_path != exec_path
|
||||
|| process.args != args
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return Some(process::ProcessVec::get_handle(cell, idx));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn terminate_process(&mut self, process_handle: process::ProcessHandle) {
|
||||
self.tasks
|
||||
.send(WayVRTask::ProcessTerminationRequest(process_handle));
|
||||
}
|
||||
|
||||
pub fn spawn_process(
|
||||
&mut self,
|
||||
display: display::DisplayHandle,
|
||||
display_handle: display::DisplayHandle,
|
||||
exec_path: &str,
|
||||
args: &[&str],
|
||||
env: &[(&str, &str)],
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(display) = self.displays.get_mut(&display) {
|
||||
display.spawn_process(exec_path, args, env)?
|
||||
}
|
||||
Ok(())
|
||||
) -> anyhow::Result<process::ProcessHandle> {
|
||||
let display = self
|
||||
.displays
|
||||
.get_mut(&display_handle)
|
||||
.ok_or(anyhow::anyhow!(STR_INVALID_HANDLE_DISP))?;
|
||||
|
||||
let res = display.spawn_process(exec_path, args, env)?;
|
||||
Ok(self.processes.add(process::Process {
|
||||
auth_key: res.auth_key,
|
||||
child: res.child,
|
||||
display_handle,
|
||||
exec_path: String::from(exec_path),
|
||||
args: args.iter().map(|x| String::from(*x)).collect(),
|
||||
env: env
|
||||
.iter()
|
||||
.map(|(a, b)| (String::from(*a), String::from(*b)))
|
||||
.collect(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
57
src/backend/wayvr/process.rs
Normal file
57
src/backend/wayvr/process.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use crate::gen_id;
|
||||
|
||||
use super::display;
|
||||
|
||||
pub struct Process {
|
||||
pub auth_key: String,
|
||||
pub child: std::process::Child,
|
||||
pub display_handle: display::DisplayHandle,
|
||||
|
||||
pub exec_path: String,
|
||||
pub args: Vec<String>,
|
||||
pub env: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
impl Drop for Process {
|
||||
fn drop(&mut self) {
|
||||
log::info!(
|
||||
"Sending SIGTERM (graceful exit) to process {}",
|
||||
self.exec_path.as_str()
|
||||
);
|
||||
self.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
impl Process {
|
||||
pub fn is_running(&mut self) -> bool {
|
||||
match self.child.try_wait() {
|
||||
Ok(Some(_exit_status)) => false,
|
||||
Ok(None) => true,
|
||||
Err(e) => {
|
||||
// this shouldn't happen
|
||||
log::error!("Child::try_wait failed: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn terminate(&mut self) {
|
||||
unsafe {
|
||||
// Gracefully stop process
|
||||
libc::kill(self.child.id() as i32, libc::SIGTERM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gen_id!(ProcessVec, Process, ProcessCell, ProcessHandle);
|
||||
|
||||
pub fn find_by_pid(processes: &ProcessVec, pid: u32) -> Option<ProcessHandle> {
|
||||
for (idx, cell) in processes.vec.iter().enumerate() {
|
||||
if let Some(cell) = cell {
|
||||
if cell.obj.child.id() == pid {
|
||||
return Some(ProcessVec::get_handle(cell, idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -218,7 +218,7 @@ impl OverlayRenderer for WayVRRenderer {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn create_wayvr<O>(
|
||||
pub fn create_wayvr_display_overlay<O>(
|
||||
app: &mut state::AppState,
|
||||
display_width: u32,
|
||||
display_height: u32,
|
||||
@@ -285,7 +285,7 @@ fn action_app_click<O>(
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
use crate::overlays::wayvr::create_wayvr;
|
||||
use crate::overlays::wayvr::create_wayvr_display_overlay;
|
||||
|
||||
let mut created_overlay: Option<OverlayData<O>> = None;
|
||||
|
||||
@@ -322,7 +322,7 @@ where
|
||||
&app_entry.target_display,
|
||||
)?;
|
||||
|
||||
let overlay = create_wayvr::<O>(
|
||||
let overlay = create_wayvr_display_overlay::<O>(
|
||||
app,
|
||||
conf_display.width,
|
||||
conf_display.height,
|
||||
@@ -354,7 +354,16 @@ where
|
||||
vec![]
|
||||
};
|
||||
|
||||
wayvr.spawn_process(disp_handle, &app_entry.exec, &args_vec, &env_vec)?
|
||||
// Terminate existing process if required
|
||||
if let Some(process_handle) =
|
||||
wayvr.process_query(disp_handle, &app_entry.exec, &args_vec, &env_vec)
|
||||
{
|
||||
// Terminate process
|
||||
wayvr.terminate_process(process_handle);
|
||||
} else {
|
||||
// Spawn process
|
||||
wayvr.spawn_process(disp_handle, &app_entry.exec, &args_vec, &env_vec)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(created_overlay)
|
||||
|
||||
Reference in New Issue
Block a user