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