Merge Staging into main (#130)

* config: add use_passthrough

* do not use ALPHA_BLEND if passthrough is off

* keyboard.yaml auto_labels

* OSC: Finish XSOverlay parity (#124)

* osc-battery - utilise match return value to set parameter

this just lets parameter be a `&str` instead of a `String`, and reduces repetition.

* osc-battery - fix error where trackers were 1-indexed instead of 0-indexed

* osc-battery: xsoverlay parity! add average tracker and controller battery parameters.

this should now be in parity with xsoverlay's parameters.

gone back to 1-indexing trackers because it's more user-friendly, so other programs can standardise more easily.

* osc battery: ...that was not 1-indexed.

no idea how I made that mistake but the sent tracker parameters were actually still 0-indexed.

* Update Envision section (#123)

add directions for appimage autostart

* Add Flatpak instructions (#127)

* Add Flatpak instructions

fix small inconsistencies between instructions

* Clarify Flatpak Steam setup for SteamVR and Monado/WiVRn users

* Additional instructions

* default AltLayout is None

* WayVR: IPC [1]: Local socket server, handshake receiver

* WayVR : IPC [2]: `ListDisplays`, `GetDisplay` commands

* WayVR: IPC [3]: `ListProcesses`, `TerminateProcess` commands

* WayVR: IPC [4]: Minor refactoring

* WayVR: Move IPC to separate Git repository

* Restore default features in Cargo.toml

* WayVR: Implement `WvrDisplayCreate`, Implement `WvrProcessLaunch`, Refactoring

* WayVR: Dashboard toggle support, minor refactoring

* Update Cargo.toml

* fix formatting for openxr_actions.json5

---------

Co-authored-by: galister <22305755+galister@users.noreply.github.com>
Co-authored-by: Jay <157681441+cubee-cb@users.noreply.github.com>
Co-authored-by: Marcus Howser <mhowser.git@mailbox.org>
Co-authored-by: Joshua Vandaële <joshua@vandaele.software>
This commit is contained in:
Aleksander
2025-01-11 09:46:54 +01:00
committed by GitHub
parent ab6b5c0b46
commit eb3087f230
24 changed files with 1281 additions and 292 deletions
+8
View File
@@ -71,6 +71,13 @@ impl InputState {
if hand.now.show_hide != hand.before.show_hide {
log::debug!("Hand {}: show_hide {}", hand.idx, hand.now.show_hide);
}
if hand.now.toggle_dashboard != hand.before.toggle_dashboard {
log::debug!(
"Hand {}: toggle_dashboard {}",
hand.idx,
hand.now.toggle_dashboard
);
}
if hand.now.space_drag != hand.before.space_drag {
log::debug!("Hand {}: space_drag {}", hand.idx, hand.now.space_drag);
}
@@ -215,6 +222,7 @@ pub struct PointerState {
pub grab: bool,
pub alt_click: bool,
pub show_hide: bool,
pub toggle_dashboard: bool,
pub space_drag: bool,
pub space_rotate: bool,
pub space_reset: bool,
+13 -4
View File
@@ -30,16 +30,17 @@ const PATH_HAPTICS: [&str; 2] = [
"/actions/default/out/HapticsRight",
];
const PATH_ALT_CLICK: &str = "/actions/default/in/AltClick";
const PATH_CLICK_MODIFIER_MIDDLE: &str = "/actions/default/in/ClickModifierMiddle";
const PATH_CLICK_MODIFIER_RIGHT: &str = "/actions/default/in/ClickModifierRight";
const PATH_CLICK: &str = "/actions/default/in/Click";
const PATH_GRAB: &str = "/actions/default/in/Grab";
const PATH_MOVE_MOUSE: &str = "/actions/default/in/MoveMouse";
const PATH_SCROLL: &str = "/actions/default/in/Scroll";
const PATH_ALT_CLICK: &str = "/actions/default/in/AltClick";
const PATH_SHOW_HIDE: &str = "/actions/default/in/ShowHide";
const PATH_SPACE_DRAG: &str = "/actions/default/in/SpaceDrag";
const PATH_SPACE_ROTATE: &str = "/actions/default/in/SpaceRotate";
const PATH_CLICK_MODIFIER_RIGHT: &str = "/actions/default/in/ClickModifierRight";
const PATH_CLICK_MODIFIER_MIDDLE: &str = "/actions/default/in/ClickModifierMiddle";
const PATH_MOVE_MOUSE: &str = "/actions/default/in/MoveMouse";
const PATH_TOGGLE_DASHBOARD: &str = "/actions/default/in/ToggleDashboard";
const INPUT_ANY: InputValueHandle = InputValueHandle(ovr_overlay::sys::k_ulInvalidInputValueHandle);
@@ -51,6 +52,7 @@ pub(super) struct OpenVrInputSource {
scroll_hnd: ActionHandle,
alt_click_hnd: ActionHandle,
show_hide_hnd: ActionHandle,
toggle_dashboard_hnd: ActionHandle,
space_drag_hnd: ActionHandle,
space_rotate_hnd: ActionHandle,
click_modifier_right_hnd: ActionHandle,
@@ -75,6 +77,7 @@ impl OpenVrInputSource {
let scroll_hnd = input.get_action_handle(PATH_SCROLL)?;
let alt_click_hnd = input.get_action_handle(PATH_ALT_CLICK)?;
let show_hide_hnd = input.get_action_handle(PATH_SHOW_HIDE)?;
let toggle_dashboard_hnd = input.get_action_handle(PATH_TOGGLE_DASHBOARD)?;
let space_drag_hnd = input.get_action_handle(PATH_SPACE_DRAG)?;
let space_rotate_hnd = input.get_action_handle(PATH_SPACE_ROTATE)?;
let click_modifier_right_hnd = input.get_action_handle(PATH_CLICK_MODIFIER_RIGHT)?;
@@ -111,6 +114,7 @@ impl OpenVrInputSource {
scroll_hnd,
alt_click_hnd,
show_hide_hnd,
toggle_dashboard_hnd,
space_drag_hnd,
space_rotate_hnd,
click_modifier_right_hnd,
@@ -196,6 +200,11 @@ impl OpenVrInputSource {
.map(|x| x.0.bState)
.unwrap_or(false);
app_hand.now.toggle_dashboard = input
.get_digital_action_data(self.toggle_dashboard_hnd, hand.input_hnd)
.map(|x| x.0.bState)
.unwrap_or(false);
app_hand.now.space_drag = input
.get_digital_action_data(self.space_drag_hnd, hand.input_hnd)
.map(|x| x.0.bState)
+2 -2
View File
@@ -12,7 +12,7 @@ use vulkano::image::view::ImageView;
use vulkano::image::ImageLayout;
use crate::backend::overlay::{
FrameTransform, OverlayData, OverlayRenderer, OverlayState, SplitOverlayBackend,
FrameTransform, OverlayData, OverlayRenderer, OverlayState, SplitOverlayBackend, Z_ORDER_LINES,
};
use crate::graphics::WlxGraphics;
use crate::state::AppState;
@@ -82,7 +82,7 @@ impl LinePool {
},
..Default::default()
};
data.state.z_order = 69;
data.state.z_order = Z_ORDER_LINES;
data.state.dirty = true;
self.lines.insert(id, data);
+12 -2
View File
@@ -43,7 +43,7 @@ use crate::{
};
#[cfg(feature = "wayvr")]
use crate::overlays::wayvr::wayvr_action;
use crate::overlays::wayvr::{wayvr_action, WayVRAction};
pub mod helpers;
pub mod input;
@@ -293,6 +293,16 @@ pub fn openvr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
overlays.show_hide(&mut state);
}
#[cfg(feature = "wayvr")]
if state
.input_state
.pointers
.iter()
.any(|p| p.now.toggle_dashboard && !p.before.toggle_dashboard)
{
wayvr_action(&mut state, &mut overlays, &WayVRAction::ToggleDashboard);
}
overlays
.iter_mut()
.for_each(|o| o.state.auto_movement(&mut state));
@@ -346,7 +356,7 @@ pub fn openvr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
#[cfg(feature = "wayvr")]
if let Some(wayvr) = &state.wayvr {
wayvr.borrow_mut().state.tick_finish()?;
wayvr.borrow_mut().data.tick_finish()?;
}
// chaperone
+17
View File
@@ -154,6 +154,7 @@ pub(super) struct OpenXrHandSource {
action_grab: CustomClickAction,
action_alt_click: CustomClickAction,
action_show_hide: CustomClickAction,
action_toggle_dashboard: CustomClickAction,
action_space_drag: CustomClickAction,
action_space_rotate: CustomClickAction,
action_space_reset: CustomClickAction,
@@ -365,6 +366,12 @@ impl OpenXrHand {
session,
)?;
pointer.now.toggle_dashboard = self.source.action_toggle_dashboard.state(
pointer.before.toggle_dashboard,
xr,
session,
)?;
pointer.now.click_modifier_middle = self.source.action_modifier_middle.state(
pointer.before.click_modifier_middle,
xr,
@@ -422,6 +429,7 @@ impl OpenXrHandSource {
action_scroll,
action_alt_click: CustomClickAction::new(action_set, "alt_click", side)?,
action_show_hide: CustomClickAction::new(action_set, "show_hide", side)?,
action_toggle_dashboard: CustomClickAction::new(action_set, "toggle_dashboard", side)?,
action_space_drag: CustomClickAction::new(action_set, "space_drag", side)?,
action_space_rotate: CustomClickAction::new(action_set, "space_rotate", side)?,
action_space_reset: CustomClickAction::new(action_set, "space_reset", side)?,
@@ -578,6 +586,14 @@ fn suggest_bindings(instance: &xr::Instance, hands: &[&OpenXrHandSource; 2]) ->
instance
);
add_custom!(
profile.toggle_dashboard,
&hands[0].action_toggle_dashboard,
&hands[1].action_toggle_dashboard,
bindings,
instance
);
add_custom!(
profile.space_drag,
&hands[0].action_space_drag,
@@ -655,6 +671,7 @@ struct OpenXrActionConfProfile {
grab: Option<OpenXrActionConfAction>,
alt_click: Option<OpenXrActionConfAction>,
show_hide: Option<OpenXrActionConfAction>,
toggle_dashboard: Option<OpenXrActionConfAction>,
space_drag: Option<OpenXrActionConfAction>,
space_rotate: Option<OpenXrActionConfAction>,
space_reset: Option<OpenXrActionConfAction>,
+12 -2
View File
@@ -32,7 +32,7 @@ use crate::{
};
#[cfg(feature = "wayvr")]
use crate::overlays::wayvr::wayvr_action;
use crate::overlays::wayvr::{wayvr_action, WayVRAction};
mod helpers;
mod input;
@@ -291,6 +291,16 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
overlays.show_hide(&mut app_state);
}
#[cfg(feature = "wayvr")]
if app_state
.input_state
.pointers
.iter()
.any(|p| p.now.toggle_dashboard && !p.before.toggle_dashboard)
{
wayvr_action(&mut app_state, &mut overlays, &WayVRAction::ToggleDashboard);
}
watch_fade(&mut app_state, overlays.mut_by_id(watch_id).unwrap()); // want panic
if let Some(ref mut space_mover) = playspace {
space_mover.update(
@@ -414,7 +424,7 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
#[cfg(feature = "wayvr")]
if let Some(wayvr) = &app_state.wayvr {
wayvr.borrow_mut().state.tick_finish()?;
wayvr.borrow_mut().data.tick_finish()?;
}
command_buffer.build_and_execute_now()?;
+7
View File
@@ -12,6 +12,9 @@
// -- space_drag --
// move your stage (playspace drag)
//
// -- toggle_dashboard --
// run or toggle visibility of a previously configured WayVR-compatible dashboard
//
// -- space_rotate --
// rotate your stage (playspace rotate, WIP)
//
@@ -127,6 +130,10 @@
left: "/user/hand/left/input/thumbstick/y",
right: "/user/hand/right/input/thumbstick/y"
},
toggle_dashboard: {
double_click: false,
right: "/user/hand/right/input/system/click",
},
show_hide: {
double_click: true,
left: "/user/hand/left/input/b/click",
+6
View File
@@ -28,6 +28,12 @@ pub trait OverlayBackend: OverlayRenderer + InteractionHandler {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Default)]
pub struct OverlayID(pub usize);
pub const Z_ORDER_TOAST: u32 = 70;
pub const Z_ORDER_LINES: u32 = 69;
pub const Z_ORDER_WATCH: u32 = 68;
pub const Z_ORDER_ANCHOR: u32 = 67;
pub const Z_ORDER_DASHBOARD: u32 = 66;
pub struct OverlayState {
pub id: OverlayID,
pub name: Arc<str>,
+2 -2
View File
@@ -20,7 +20,7 @@ pub struct WayVRClient {
pub pid: u32,
}
pub struct WayVRManager {
pub struct WayVRCompositor {
pub state: comp::Application,
pub seat_keyboard: KeyboardHandle<comp::Application>,
pub seat_pointer: PointerHandle<comp::Application>,
@@ -60,7 +60,7 @@ fn get_wayvr_env_from_pid(pid: i32) -> anyhow::Result<ProcessWayVREnv> {
Ok(env)
}
impl WayVRManager {
impl WayVRCompositor {
pub fn new(
state: comp::Application,
display: wayland_server::Display<comp::Application>,
+58 -13
View File
@@ -14,6 +14,7 @@ use smithay::{
utils::{Logical, Point, Rectangle, Size, Transform},
wayland::shell::xdg::ToplevelSurface,
};
use wayvr_ipc::packet_server;
use crate::{
backend::{overlay::OverlayID, wayvr::time::get_millis},
@@ -21,7 +22,7 @@ use crate::{
};
use super::{
client::WayVRManager, comp::send_frames_surface_tree, egl_data, event_queue::SyncEventQueue,
client::WayVRCompositor, comp::send_frames_surface_tree, egl_data, event_queue::SyncEventQueue,
process, smithay_wrapper, time, window, WayVRSignal,
};
@@ -45,10 +46,12 @@ pub enum DisplayTask {
ProcessCleanup(process::ProcessHandle),
}
const MAX_DISPLAY_SIZE: u16 = 8192;
pub struct Display {
// Display info stuff
pub width: u32,
pub height: u32,
pub width: u16,
pub height: u16,
pub name: String,
pub visible: bool,
pub overlay_id: Option<OverlayID>,
@@ -84,25 +87,41 @@ impl Display {
renderer: &mut GlesRenderer,
egl_data: Rc<egl_data::EGLData>,
wayland_env: super::WaylandEnv,
width: u32,
height: u32,
width: u16,
height: u16,
name: &str,
primary: bool,
) -> anyhow::Result<Self> {
if width > MAX_DISPLAY_SIZE {
anyhow::bail!(
"display width ({}) is larger than {}",
width,
MAX_DISPLAY_SIZE
);
}
if height > MAX_DISPLAY_SIZE {
anyhow::bail!(
"display height ({}) is larger than {}",
height,
MAX_DISPLAY_SIZE
);
}
let tex_format = ffi::RGBA;
let internal_format = ffi::RGBA8;
let tex_id = renderer.with_context(|gl| {
smithay_wrapper::create_framebuffer_texture(
gl,
width,
height,
width as u32,
height as u32,
tex_format,
internal_format,
)
})?;
let egl_image = egl_data.create_egl_image(tex_id, width, height)?;
let egl_image = egl_data.create_egl_image(tex_id, width as u32, height as u32)?;
let dmabuf_data = egl_data.create_dmabuf_data(&egl_image)?;
let opaque = false;
@@ -131,6 +150,16 @@ impl Display {
})
}
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,
@@ -158,7 +187,7 @@ impl Display {
let right = (d_next * self.width as f32) as i32;
window.set_pos(left, 0);
window.set_size((right - left) as u32, self.height);
window.set_size((right - left) as u32, self.height as u32);
}
}
}
@@ -279,7 +308,7 @@ impl Display {
pub fn send_mouse_move(
&self,
config: &super::Config,
manager: &mut WayVRManager,
manager: &mut WayVRCompositor,
x: u32,
y: u32,
) {
@@ -320,7 +349,7 @@ impl Display {
}
}
pub fn send_mouse_down(&mut self, manager: &mut WayVRManager, index: super::MouseIndex) {
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();
@@ -356,7 +385,7 @@ impl Display {
manager.seat_pointer.frame(&mut manager.state);
}
pub fn send_mouse_up(&self, manager: &mut WayVRManager, index: super::MouseIndex) {
pub fn send_mouse_up(&self, manager: &mut WayVRCompositor, index: super::MouseIndex) {
manager.seat_pointer.button(
&mut manager.state,
&input::pointer::ButtonEvent {
@@ -370,7 +399,7 @@ impl Display {
manager.seat_pointer.frame(&mut manager.state);
}
pub fn send_mouse_scroll(&self, manager: &mut WayVRManager, delta: f32) {
pub fn send_mouse_scroll(&self, manager: &mut WayVRCompositor, delta: f32) {
manager.seat_pointer.axis(
&mut manager.state,
input::pointer::AxisFrame {
@@ -426,3 +455,19 @@ impl Display {
}
gen_id!(DisplayVec, Display, DisplayCell, DisplayHandle);
impl DisplayHandle {
pub fn from_packet(handle: packet_server::WvrDisplayHandle) -> Self {
Self {
generation: handle.generation,
idx: handle.idx,
}
}
pub fn as_packet(&self) -> packet_server::WvrDisplayHandle {
packet_server::WvrDisplayHandle {
idx: self.idx,
generation: self.generation,
}
}
}
+5 -1
View File
@@ -8,7 +8,7 @@ macro_rules! gen_id {
//ThingCell
pub struct $cell_name {
pub obj: $instance_name,
generation: u64,
pub generation: u64,
}
//ThingVec
@@ -39,6 +39,10 @@ macro_rules! gen_id {
pub fn id(&self) -> u32 {
self.idx
}
pub fn new(idx: u32, generation: u64) -> Self {
Self { idx, generation }
}
}
//ThingVec
+109 -64
View File
@@ -3,19 +3,18 @@ mod comp;
pub mod display;
pub mod egl_data;
mod egl_ex;
mod event_queue;
pub mod event_queue;
mod handle;
mod process;
pub mod server_ipc;
mod smithay_wrapper;
mod time;
mod window;
use std::{cell::RefCell, collections::HashSet, rc::Rc};
use comp::Application;
use display::DisplayVec;
use event_queue::SyncEventQueue;
use process::ProcessVec;
use server_ipc::WayVRServer;
use smallvec::SmallVec;
use smithay::{
backend::renderer::gles::GlesRenderer,
@@ -29,7 +28,9 @@ use smithay::{
shm::ShmState,
},
};
use std::{cell::RefCell, collections::HashSet, rc::Rc};
use time::get_millis;
use wayvr_ipc::packet_client;
const STR_INVALID_HANDLE_DISP: &str = "Invalid display handle";
const STR_INVALID_HANDLE_PROCESS: &str = "Invalid process handle";
@@ -78,17 +79,21 @@ pub struct Config {
pub auto_hide_delay: Option<u32>, // if None, auto-hide is disabled
}
#[allow(dead_code)]
pub struct WayVR {
pub struct WayVRState {
time_start: u64,
gles_renderer: GlesRenderer,
pub displays: display::DisplayVec,
pub manager: client::WayVRManager,
pub manager: client::WayVRCompositor,
wm: Rc<RefCell<window::WindowManager>>,
egl_data: Rc<egl_data::EGLData>,
pub processes: process::ProcessVec,
config: Config,
dashboard_display: Option<display::DisplayHandle>,
}
pub struct WayVR {
pub state: WayVRState,
ipc_server: WayVRServer,
tasks: SyncEventQueue<WayVRTask>,
pub signals: SyncEventQueue<WayVRSignal>,
}
@@ -99,8 +104,9 @@ pub enum MouseIndex {
Right,
}
pub enum TickResult {
NewExternalProcess(ExternalProcessRequest), // Call WayVRManager::add_client after receiving this message
pub enum TickTask {
NewExternalProcess(ExternalProcessRequest), // Call WayVRCompositor::add_client after receiving this message
NewDisplay(packet_client::WvrDisplayCreateParams),
}
impl WayVR {
@@ -163,23 +169,32 @@ impl WayVR {
let smithay_context = smithay_wrapper::get_egl_context(&egl_data, &smithay_display)?;
let gles_renderer = unsafe { GlesRenderer::new(smithay_context)? };
Ok(Self {
let ipc_server = WayVRServer::new()?;
let state = WayVRState {
gles_renderer,
time_start,
manager: client::WayVRManager::new(state, display, seat_keyboard, seat_pointer)?,
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,
};
Ok(Self {
state,
signals: SyncEventQueue::new(),
tasks,
config,
ipc_server,
})
}
pub fn tick_display(&mut self, display: display::DisplayHandle) -> anyhow::Result<()> {
// millis since the start of wayvr
let display = self
.state
.displays
.get_mut(&display)
.ok_or(anyhow::anyhow!(STR_INVALID_HANDLE_DISP))?;
@@ -194,21 +209,27 @@ impl WayVR {
return Ok(());
}
let time_ms = get_millis() - self.time_start;
let time_ms = get_millis() - self.state.time_start;
display.tick_render(&mut self.gles_renderer, time_ms)?;
display.tick_render(&mut self.state.gles_renderer, time_ms)?;
display.wants_redraw = false;
Ok(())
}
pub fn tick_events(&mut self) -> anyhow::Result<Vec<TickResult>> {
let mut res: Vec<TickResult> = Vec::new();
pub fn tick_events(&mut self) -> anyhow::Result<Vec<TickTask>> {
let mut tasks: Vec<TickTask> = Vec::new();
self.ipc_server.tick(&mut server_ipc::TickParams {
state: &mut self.state,
tasks: &mut tasks,
})?;
// Check for redraw events
self.displays.iter_mut(&mut |_, disp| {
self.state.displays.iter_mut(&mut |_, disp| {
for disp_window in &disp.displayed_windows {
if self
.state
.manager
.state
.check_redraw(disp_window.toplevel.wl_surface())
@@ -221,16 +242,17 @@ impl WayVR {
// Tick all child processes
let mut to_remove: SmallVec<[(process::ProcessHandle, display::DisplayHandle); 2]> =
SmallVec::new();
self.processes.iter_mut(&mut |handle, process| {
self.state.processes.iter_mut(&mut |handle, process| {
if !process.is_running() {
to_remove.push((handle, process.display_handle()));
}
});
for (p_handle, disp_handle) in to_remove {
self.processes.remove(&p_handle);
self.state.processes.remove(&p_handle);
if let Some(display) = self.displays.get_mut(&disp_handle) {
if let Some(display) = self.state.displays.get_mut(&disp_handle) {
display
.tasks
.send(display::DisplayTask::ProcessCleanup(p_handle));
@@ -238,25 +260,26 @@ impl WayVR {
}
}
self.displays.iter_mut(&mut |handle, display| {
display.tick(&self.config, &handle, &mut self.signals);
self.state.displays.iter_mut(&mut |handle, display| {
display.tick(&self.state.config, &handle, &mut self.signals);
});
while let Some(task) = self.tasks.read() {
match task {
WayVRTask::NewExternalProcess(req) => {
res.push(TickResult::NewExternalProcess(req));
tasks.push(TickTask::NewExternalProcess(req));
}
WayVRTask::NewToplevel(client_id, toplevel) => {
// Attach newly created toplevel surfaces to displays
for client in &self.manager.clients {
for client in &self.state.manager.clients {
if client.client.id() == client_id {
let window_handle = self.wm.borrow_mut().create_window(&toplevel);
let window_handle = self.state.wm.borrow_mut().create_window(&toplevel);
if let Some(process_handle) =
process::find_by_pid(&self.processes, client.pid)
process::find_by_pid(&self.state.processes, client.pid)
{
if let Some(display) = self.displays.get_mut(&client.display_handle)
if let Some(display) =
self.state.displays.get_mut(&client.display_handle)
{
display.add_window(window_handle, process_handle, &toplevel);
} else {
@@ -275,27 +298,60 @@ impl WayVR {
}
}
WayVRTask::ProcessTerminationRequest(process_handle) => {
if let Some(process) = self.processes.get_mut(&process_handle) {
if let Some(process) = self.state.processes.get_mut(&process_handle) {
process.terminate();
}
}
}
}
self.manager
.tick_wayland(&mut self.displays, &mut self.processes)?;
self.state
.manager
.tick_wayland(&mut self.state.displays, &mut self.state.processes)?;
Ok(res)
Ok(tasks)
}
pub fn tick_finish(&mut self) -> anyhow::Result<()> {
self.gles_renderer.with_context(|gl| unsafe {
self.state.gles_renderer.with_context(|gl| unsafe {
gl.Flush();
gl.Finish();
})?;
Ok(())
}
pub fn get_primary_display(displays: &DisplayVec) -> Option<display::DisplayHandle> {
for (idx, cell) in displays.vec.iter().enumerate() {
if let Some(cell) = cell {
if cell.obj.primary {
return Some(DisplayVec::get_handle(cell, idx));
}
}
}
None
}
pub fn get_display_by_name(
displays: &DisplayVec,
name: &str,
) -> Option<display::DisplayHandle> {
for (idx, cell) in displays.vec.iter().enumerate() {
if let Some(cell) = cell {
if cell.obj.name == name {
return Some(DisplayVec::get_handle(cell, idx));
}
}
}
None
}
pub fn terminate_process(&mut self, process_handle: process::ProcessHandle) {
self.tasks
.send(WayVRTask::ProcessTerminationRequest(process_handle));
}
}
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);
@@ -336,35 +392,10 @@ impl WayVR {
.map(|display| display.dmabuf_data.clone())
}
pub fn get_primary_display(displays: &DisplayVec) -> Option<display::DisplayHandle> {
for (idx, cell) in displays.vec.iter().enumerate() {
if let Some(cell) = cell {
if cell.obj.primary {
return Some(DisplayVec::get_handle(cell, idx));
}
}
}
None
}
pub fn get_display_by_name(
displays: &DisplayVec,
name: &str,
) -> Option<display::DisplayHandle> {
for (idx, cell) in displays.vec.iter().enumerate() {
if let Some(cell) = cell {
if cell.obj.name == name {
return Some(DisplayVec::get_handle(cell, idx));
}
}
}
None
}
pub fn create_display(
&mut self,
width: u32,
height: u32,
width: u16,
height: u16,
name: &str,
primary: bool,
) -> anyhow::Result<display::DisplayHandle> {
@@ -381,6 +412,25 @@ impl WayVR {
Ok(self.displays.add(display))
}
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))
}
pub fn destroy_display(&mut self, handle: display::DisplayHandle) {
self.displays.remove(&handle);
}
@@ -410,11 +460,6 @@ impl WayVR {
None
}
pub fn terminate_process(&mut self, process_handle: process::ProcessHandle) {
self.tasks
.send(WayVRTask::ProcessTerminationRequest(process_handle));
}
pub fn add_external_process(
&mut self,
display_handle: display::DisplayHandle,
+54
View File
@@ -1,3 +1,5 @@
use wayvr_ipc::packet_server;
use crate::gen_id;
use super::display;
@@ -43,6 +45,21 @@ impl Process {
Process::External(p) => p.terminate(),
}
}
pub fn to_packet(&self, handle: ProcessHandle) -> packet_server::WvrProcess {
match self {
Process::Managed(p) => packet_server::WvrProcess {
name: p.get_name().unwrap_or(String::from("unknown")),
display_handle: p.display_handle.as_packet(),
handle: handle.as_packet(),
},
Process::External(p) => packet_server::WvrProcess {
name: p.get_name().unwrap_or(String::from("unknown")),
display_handle: p.display_handle.as_packet(),
handle: handle.as_packet(),
},
}
}
}
impl Drop for WayVRProcess {
@@ -74,6 +91,23 @@ impl WayVRProcess {
libc::kill(self.child.id() as i32, libc::SIGTERM);
}
}
pub fn get_name(&self) -> Option<String> {
get_exec_name_from_pid(self.child.id())
}
}
fn get_exec_name_from_pid(pid: u32) -> Option<String> {
let path = format!("/proc/{}/exe", pid);
match std::fs::read_link(&path) {
Ok(buf) => {
if let Some(process_name) = buf.file_name().and_then(|s| s.to_str()) {
return Some(String::from(process_name));
}
None
}
Err(_) => None,
}
}
impl ExternalProcess {
@@ -94,6 +128,10 @@ impl ExternalProcess {
}
self.pid = 0;
}
pub fn get_name(&self) -> Option<String> {
get_exec_name_from_pid(self.pid)
}
}
gen_id!(ProcessVec, Process, ProcessCell, ProcessHandle);
@@ -117,3 +155,19 @@ pub fn find_by_pid(processes: &ProcessVec, pid: u32) -> Option<ProcessHandle> {
}
None
}
impl ProcessHandle {
pub fn from_packet(handle: packet_server::WvrProcessHandle) -> Self {
Self {
generation: handle.generation,
idx: handle.idx,
}
}
pub fn as_packet(&self) -> packet_server::WvrProcessHandle {
packet_server::WvrProcessHandle {
idx: self.idx,
generation: self.generation,
}
}
}
+418
View File
@@ -0,0 +1,418 @@
use super::{display, process, TickTask};
use bytes::BufMut;
use interprocess::local_socket::{self, traits::Listener, ToNsName};
use smallvec::SmallVec;
use std::io::{Read, Write};
use wayvr_ipc::{
ipc::{self, binary_decode, binary_encode},
packet_client::{self, PacketClient},
packet_server::{self, PacketServer},
};
pub struct Connection {
alive: bool,
conn: local_socket::Stream,
next_packet: Option<u32>,
handshaking: bool,
}
pub fn send_packet(conn: &mut local_socket::Stream, data: &[u8]) -> anyhow::Result<()> {
let mut bytes = bytes::BytesMut::new();
// packet size
bytes.put_u32(data.len() as u32);
// packet data
bytes.put_slice(data);
conn.write_all(&bytes)?;
Ok(())
}
fn read_check(expected_size: u32, res: std::io::Result<usize>) -> bool {
match res {
Ok(count) => {
if count == 0 {
return false;
}
if count as u32 != expected_size {
log::error!("count {} is not {}", count, expected_size);
false
} else {
true // read succeeded
}
}
Err(_e) => {
//log::error!("failed to get packet size: {}", e);
false
}
}
}
type Payload = SmallVec<[u8; 64]>;
fn read_payload(conn: &mut local_socket::Stream, size: u32) -> Option<Payload> {
let mut payload = Payload::new();
payload.resize(size as usize, 0);
if !read_check(size, conn.read(&mut payload)) {
None
} else {
Some(payload)
}
}
pub struct TickParams<'a> {
pub state: &'a mut super::WayVRState,
pub tasks: &'a mut Vec<TickTask>,
}
pub fn gen_args_vec(input: &str) -> Vec<&str> {
input.split_whitespace().collect()
}
pub fn gen_env_vec(input: &Vec<String>) -> Vec<(&str, &str)> {
let res = input
.iter()
.filter_map(|e| e.as_str().split_once('='))
.collect();
res
}
impl Connection {
fn new(conn: local_socket::Stream) -> Self {
Self {
conn,
alive: true,
handshaking: true,
next_packet: None,
}
}
fn kill(&mut self) {
self.alive = false;
}
fn process_handshake(&mut self, payload: Payload) -> anyhow::Result<()> {
let Ok(handshake) = binary_decode::<ipc::Handshake>(&payload) else {
anyhow::bail!("Invalid handshake");
};
if handshake.protocol_version != ipc::PROTOCOL_VERSION {
anyhow::bail!(
"Unsupported protocol version {}",
handshake.protocol_version
);
}
if handshake.magic != ipc::CONNECTION_MAGIC {
anyhow::bail!("Invalid magic");
}
log::info!("Accepted new connection");
self.handshaking = false;
Ok(())
}
fn handle_wvr_display_list(
&mut self,
params: &TickParams,
serial: ipc::Serial,
) -> anyhow::Result<()> {
let list: Vec<packet_server::WvrDisplay> = 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,
&binary_encode(&PacketServer::WvrDisplayListResponse(
serial,
packet_server::WvrDisplayList { list },
)),
)?;
Ok(())
}
fn handle_wvr_display_create(
&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.clone()));
send_packet(
&mut self.conn,
&binary_encode(&PacketServer::WvrDisplayCreateResponse(
serial,
display_handle.as_packet(),
)),
)?;
Ok(())
}
fn handle_wvr_process_launch(
&mut self,
params: &mut TickParams,
serial: ipc::Serial,
packet_params: packet_client::WvrProcessLaunchParams,
) -> anyhow::Result<()> {
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),
&packet_params.exec,
&args_vec,
&env_vec,
);
let res = res.map(|r| r.as_packet()).map_err(|e| e.to_string());
send_packet(
&mut self.conn,
&binary_encode(&PacketServer::WvrProcessLaunchResponse(serial, res)),
)?;
Ok(())
}
fn handle_wvr_process_get(
&mut self,
params: &TickParams,
serial: ipc::Serial,
display_handle: packet_server::WvrDisplayHandle,
) -> anyhow::Result<()> {
let native_handle = &display::DisplayHandle::from_packet(display_handle.clone());
let disp = params
.state
.displays
.get(native_handle)
.map(|disp| disp.as_packet(*native_handle));
send_packet(
&mut self.conn,
&binary_encode(&PacketServer::WvrDisplayGetResponse(serial, disp)),
)?;
Ok(())
}
fn handle_wvr_process_list(
&mut self,
params: &TickParams,
serial: ipc::Serial,
) -> anyhow::Result<()> {
let list: Vec<packet_server::WvrProcess> = params
.state
.processes
.vec
.iter()
.enumerate()
.filter_map(|(idx, opt_cell)| {
let Some(cell) = opt_cell else {
return None;
};
let process = &cell.obj;
Some(process.to_packet(process::ProcessHandle::new(idx as u32, cell.generation)))
})
.collect();
send_packet(
&mut self.conn,
&binary_encode(&PacketServer::WvrProcessListResponse(
serial,
packet_server::WvrProcessList { list },
)),
)?;
Ok(())
}
// This request doesn't return anything to the client
fn handle_wvr_process_terminate(
&mut self,
params: &mut TickParams,
process_handle: packet_server::WvrProcessHandle,
) -> anyhow::Result<()> {
let native_handle = &process::ProcessHandle::from_packet(process_handle.clone());
let process = params.state.processes.get_mut(native_handle);
let Some(process) = process else {
return Ok(());
};
process.terminate();
Ok(())
}
fn process_payload(&mut self, params: &mut TickParams, payload: Payload) -> anyhow::Result<()> {
if self.handshaking {
self.process_handshake(payload)?;
return Ok(());
}
let packet: PacketClient = binary_decode(&payload)?;
match packet {
PacketClient::WvrDisplayList(serial) => {
self.handle_wvr_display_list(params, serial)?;
}
PacketClient::WvrDisplayGet(serial, display_handle) => {
self.handle_wvr_process_get(params, serial, display_handle)?;
}
PacketClient::WvrProcessList(serial) => {
self.handle_wvr_process_list(params, serial)?;
}
PacketClient::WvrProcessLaunch(serial, packet_params) => {
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) => {
self.handle_wvr_process_terminate(params, process_handle)?;
}
}
Ok(())
}
fn process_check_payload(&mut self, params: &mut TickParams, payload: Payload) -> bool {
log::debug!("payload size {}", payload.len());
if let Err(e) = self.process_payload(params, payload) {
log::error!("Invalid payload from the client, closing connection: {}", e);
self.kill();
false
} else {
true
}
}
fn read_packet(&mut self, params: &mut TickParams) -> bool {
if let Some(payload_size) = self.next_packet {
let Some(payload) = read_payload(&mut self.conn, payload_size) else {
// still failed to read payload, try in next tick
return false;
};
if !self.process_check_payload(params, payload) {
return false;
}
self.next_packet = None;
}
let mut buf_packet_header: [u8; 4] = [0; 4];
if !read_check(4, self.conn.read(&mut buf_packet_header)) {
return false;
}
let payload_size = u32::from_be_bytes(buf_packet_header[0..4].try_into().unwrap()); // 0-3 bytes (u32 size)
let size_limit: u32 = 128 * 1024;
if payload_size > size_limit {
// over 128 KiB?
log::error!(
"Client sent a packet header with the size over {} bytes, closing connection.",
size_limit
);
self.kill();
return false;
}
let Some(payload) = read_payload(&mut self.conn, payload_size) else {
// failed to read payload, try in next tick
self.next_packet = Some(payload_size);
return false;
};
if !self.process_check_payload(params, payload) {
return false;
}
true
}
fn tick(&mut self, params: &mut TickParams) {
while self.read_packet(params) {}
}
}
impl Drop for Connection {
fn drop(&mut self) {
log::info!("Connection closed");
}
}
pub struct WayVRServer {
listener: local_socket::Listener,
connections: Vec<Connection>,
}
impl WayVRServer {
pub fn new() -> anyhow::Result<Self> {
let printname = "/tmp/wayvr_ipc.sock";
let name = printname.to_ns_name::<local_socket::GenericNamespaced>()?;
let opts = local_socket::ListenerOptions::new()
.name(name)
.nonblocking(local_socket::ListenerNonblockingMode::Both);
let listener = match opts.create_sync() {
Ok(listener) => listener,
Err(e) => anyhow::bail!("Failed to start WayVRServer IPC listener. Reason: {}", e),
};
log::info!("WayVRServer IPC running at {}", printname);
Ok(Self {
listener,
connections: Vec::new(),
})
}
fn accept_connections(&mut self) {
let Ok(conn) = self.listener.accept() else {
return; // No new connection or other error
};
self.connections.push(Connection::new(conn));
}
fn tick_connections(&mut self, params: &mut TickParams) {
for c in &mut self.connections {
c.tick(params);
}
// remove killed connections
self.connections.retain(|c| c.alive);
}
pub fn tick(&mut self, params: &mut TickParams) -> anyhow::Result<()> {
self.accept_connections();
self.tick_connections(params);
Ok(())
}
}