Make WayVR IPC independent of the built-in Wayland server

- Rename state -> wayland_state
- Move WayVR IPC server outside "wayvr" (wayland server) feature
- WayVR IPC server is now available without "wayvr" (wayland server) feature
- Remove run_compositor_at_start (run wayland server instantly)
This commit is contained in:
Aleksander
2025-12-23 23:35:00 +01:00
committed by galister
parent 5afe85a3b3
commit 36576122a9
18 changed files with 387 additions and 328 deletions

View File

@@ -17,7 +17,8 @@ members = [
"wlx-overlay-s",
"wlx-capture",
"dash-frontend",
"wayvr-ipc", "wayvrctl",
"wayvr-ipc",
"wayvrctl",
]
resolver = "3"

View File

@@ -382,7 +382,7 @@ impl Layout {
let mut iter = |idx: usize| -> anyhow::Result<bool> {
let child_id = self.state.tree.get_child_id(parent_node_id, idx);
self.push_event_widget(child_id, event, event_result, alterables, user_data, false)?;
self.push_event_widget(child_id, event, event_result, alterables, user_data)?;
Ok(!event_result.can_propagate())
};
@@ -403,7 +403,6 @@ impl Layout {
event_result: &mut EventResult,
alterables: &mut EventAlterables,
user_data: &mut (&'a mut U1, &'a mut U2),
is_root_node: bool,
) -> anyhow::Result<()> {
let l = self.state.tree.layout(node_id)?;
let Some(widget_id) = self.state.tree.get_node_context(node_id).copied() else {
@@ -495,7 +494,6 @@ impl Layout {
&mut event_result,
&mut alterables,
&mut (user1, user2),
true,
)?;
self.process_alterables(alterables)?;
Ok(event_result)

View File

@@ -81,9 +81,11 @@ tracing = "0.1.43"
vulkano = { workspace = true }
vulkano-shaders = { workspace = true }
wgui = { path = "../wgui" }
bytes = { version = "1.11.0" }
wayvr-ipc = { path = "../wayvr-ipc", default-features = false }
################################
#WayVR-only deps
# Wayland Server deps
################################
khronos-egl = { version = "6.0.0", features = ["static"], optional = true }
smithay = { version = "0.7.0", default-features = false, features = [
@@ -96,8 +98,6 @@ smithay = { version = "0.7.0", default-features = false, features = [
uuid = { version = "1.19.0", features = ["v4", "fast-rng"], optional = true }
wayland-client = { workspace = true, optional = true }
wayland-egl = { version = "0.32.8", optional = true }
bytes = { version = "1.11.0", optional = true }
wayvr-ipc = { path = "../wayvr-ipc", default-features = false, optional = true }
rust-embed = { workspace = true }
signal-hook = "0.3.18"
################################
@@ -105,6 +105,7 @@ signal-hook = "0.3.18"
[build-dependencies]
regex = { version = "1.12.2" }
# TODO: rename "wayvr" feature to "wayland-server"
[features]
default = ["openvr", "openxr", "osc", "x11", "wayland", "wayvr"]
openvr = ["dep:ovr_overlay", "dep:json"]
@@ -121,7 +122,5 @@ wayvr = [
"dep:uuid",
"dep:wayland-client",
"dep:wayland-egl",
"dep:bytes",
"dep:wayvr-ipc",
]
as-raw-xcb-connection = []

View File

@@ -303,11 +303,10 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
let _ = sender.send_params(&overlays, &app.input_state.devices);
}
#[cfg(feature = "wayvr")]
if let Err(e) =
crate::overlays::wayvr::tick_events::<OpenVrOverlayData>(&mut app, &mut overlays)
crate::ipc::events::tick_events::<OpenVrOverlayData>(&mut app, &mut overlays)
{
log::error!("WayVR tick_events failed: {e:?}");
log::error!("WayVR IPC tick_events failed: {e:?}");
}
log::trace!("Rendering frame");
@@ -336,9 +335,7 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
.for_each(|o| o.after_render(universe.clone(), &mut overlay_mgr, &app.gfx));
#[cfg(feature = "wayvr")]
if let Some(wayvr) = &app.wayvr {
wayvr.borrow_mut().data.tick_finish()?;
}
app.wayvr.borrow_mut().data.tick_finish()?;
// chaperone
} // main_loop

View File

@@ -373,9 +373,9 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
#[cfg(feature = "wayvr")]
if let Err(e) =
crate::overlays::wayvr::tick_events::<OpenXrOverlayData>(&mut app, &mut overlays)
crate::ipc::events::tick_events::<OpenXrOverlayData>(&mut app, &mut overlays)
{
log::error!("WayVR tick_events failed: {e:?}");
log::error!("WayVR IPC tick_events failed: {e:?}");
}
// Begin rendering
@@ -459,9 +459,7 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
// End layer composition
#[cfg(feature = "wayvr")]
if let Some(wayvr) = &app.wayvr {
wayvr.borrow_mut().data.tick_finish()?;
}
app.wayvr.borrow_mut().data.tick_finish()?;
// Begin layer submit
layers.sort_by(|a, b| b.0.total_cmp(&a.0));

View File

@@ -37,8 +37,9 @@ use wayland_server::Client;
use wayland_server::backend::{ClientData, ClientId, DisconnectReason};
use wayland_server::protocol::wl_surface::WlSurface;
use crate::ipc::event_queue::SyncEventQueue;
use super::WayVRTask;
use super::event_queue::SyncEventQueue;
pub struct Application {
pub gles_renderer: GlesRenderer,

View File

@@ -17,12 +17,13 @@ use smithay::{
use wayvr_ipc::packet_server;
use crate::{
backend::wayvr::time::get_millis, gen_id, subsystem::hid::WheelDelta, windowing::OverlayID,
backend::wayvr::time::get_millis, gen_id, ipc::event_queue::SyncEventQueue,
subsystem::hid::WheelDelta, windowing::OverlayID,
};
use super::{
BlitMethod, WayVRSignal, client::WayVRCompositor, comp::send_frames_surface_tree, egl_data,
event_queue::SyncEventQueue, process, smithay_wrapper, time, window,
process, smithay_wrapper, time, window,
};
fn generate_auth_key() -> String {

View File

@@ -3,20 +3,16 @@ mod comp;
pub mod display;
pub mod egl_data;
mod egl_ex;
pub mod event_queue;
mod handle;
mod process;
pub mod server_ipc;
pub mod process;
mod smithay_wrapper;
mod time;
mod window;
pub mod window;
use anyhow::Context;
use comp::Application;
use display::{Display, DisplayInitParams, DisplayVec};
use event_queue::SyncEventQueue;
use process::ProcessVec;
use serde::Deserialize;
use server_ipc::WayVRServer;
use smallvec::SmallVec;
use smithay::{
backend::{
@@ -48,6 +44,7 @@ use wayvr_ipc::{
use xkbcommon::xkb;
use crate::{
ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal},
state::AppState,
subsystem::hid::{MODS_TO_KEYS, WheelDelta},
};
@@ -87,19 +84,6 @@ pub enum WayVRTask {
ProcessTerminationRequest(process::ProcessHandle),
}
#[derive(Clone)]
pub enum WayVRSignal {
DisplayVisibility(display::DisplayHandle, bool),
DisplayWindowLayout(
display::DisplayHandle,
packet_server::WvrDisplayWindowLayout,
),
BroadcastStateChanged(packet_server::WvrStateChanged),
DropOverlay(crate::windowing::OverlayID),
Haptics(super::input::Haptics),
CustomTask(crate::backend::task::ModifyPanelTask),
}
pub enum BlitMethod {
Dmabuf,
Software,
@@ -127,20 +111,19 @@ pub struct WayVRState {
time_start: u64,
pub displays: display::DisplayVec,
pub manager: client::WayVRCompositor,
wm: Rc<RefCell<window::WindowManager>>,
pub wm: Rc<RefCell<window::WindowManager>>,
egl_data: Rc<egl_data::EGLData>,
pub processes: process::ProcessVec,
pub config: Config,
dashboard_display: Option<display::DisplayHandle>,
pub tasks: SyncEventQueue<WayVRTask>,
pub signals: SyncEventQueue<WayVRSignal>,
ticks: u64,
cur_modifiers: u8,
signals: SyncEventQueue<WayVRSignal>,
}
pub struct WayVR {
pub state: WayVRState,
pub ipc_server: WayVRServer,
}
pub enum MouseIndex {
@@ -159,7 +142,7 @@ pub enum TickTask {
impl WayVR {
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
pub fn new(config: Config) -> anyhow::Result<Self> {
pub fn new(config: Config, signals: SyncEventQueue<WayVRSignal>) -> anyhow::Result<Self> {
log::info!("Initializing WayVR");
let display: wayland_server::Display<Application> = wayland_server::Display::new()?;
let dh = display.handle();
@@ -264,8 +247,6 @@ impl WayVR {
let time_start = get_millis();
let ipc_server = WayVRServer::new()?;
let state = WayVRState {
time_start,
manager: client::WayVRCompositor::new(state, display, seat_keyboard, seat_pointer)?,
@@ -277,11 +258,11 @@ impl WayVR {
dashboard_display: None,
ticks: 0,
tasks,
signals: SyncEventQueue::new(),
cur_modifiers: 0,
signals,
};
Ok(Self { state, ipc_server })
Ok(Self { state })
}
pub fn render_display(&mut self, display: display::DisplayHandle) -> anyhow::Result<bool> {
@@ -312,13 +293,14 @@ impl WayVR {
}
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
pub fn tick_events(&mut self, app: &AppState) -> anyhow::Result<Vec<TickTask>> {
pub fn tick_events(&mut self, app: &mut AppState) -> anyhow::Result<Vec<TickTask>> {
let mut tasks: Vec<TickTask> = Vec::new();
self.ipc_server.tick(&mut server_ipc::TickParams {
state: &mut self.state,
app.ipc_server.tick(&mut ipc_server::TickParams {
wayland_state: &mut self.state,
input_state: &app.input_state,
tasks: &mut tasks,
app,
signals: &app.wayvr_signals,
});
// Check for redraw events
@@ -357,11 +339,11 @@ impl WayVR {
}
for (handle, display) in self.state.displays.iter_mut() {
display.tick(&self.state.config, &handle, &mut self.state.signals);
display.tick(&self.state.config, &handle, &mut app.wayvr_signals);
}
if !to_remove.is_empty() {
self.state.signals.send(WayVRSignal::BroadcastStateChanged(
app.wayvr_signals.send(WayVRSignal::BroadcastStateChanged(
packet_server::WvrStateChanged::ProcessRemoved,
));
}
@@ -402,7 +384,7 @@ impl WayVR {
};
display.add_window(window_handle, process_handle, &toplevel);
self.state.signals.send(WayVRSignal::BroadcastStateChanged(
app.wayvr_signals.send(WayVRSignal::BroadcastStateChanged(
packet_server::WvrStateChanged::WindowCreated,
));
}

View File

@@ -19,6 +19,7 @@ use crate::{
},
config::load_config_with_conf_d,
config_io,
ipc::{event_queue::SyncEventQueue, signal::WayVRSignal},
overlays::wayvr::{WayVRData, executable_exists_in_path},
};
@@ -135,9 +136,6 @@ pub struct WayVRDashboard {
#[derive(Deserialize, Serialize)]
pub struct WayVRConfig {
#[serde(default = "def_true")]
pub run_compositor_at_start: bool,
#[serde(default = "Default::default")]
pub catalogs: HashMap<String, WayVRCatalog>,
@@ -203,7 +201,8 @@ impl WayVRConfig {
&self,
config: &GeneralConfig,
tasks: &mut TaskContainer,
) -> anyhow::Result<Option<Rc<RefCell<WayVRData>>>> {
signals: SyncEventQueue<WayVRSignal>,
) -> anyhow::Result<Rc<RefCell<WayVRData>>> {
let primary_count = self
.displays
.iter()
@@ -212,10 +211,6 @@ impl WayVRConfig {
if primary_count > 1 {
anyhow::bail!("Number of primary displays is more than 1")
} else if primary_count == 0 {
log::warn!(
"No primary display specified. External Wayland applications will not be attached."
);
}
for (catalog_name, catalog) in &self.catalogs {
@@ -231,15 +226,10 @@ impl WayVRConfig {
}
}
if self.run_compositor_at_start {
// Start Wayland server instantly
Ok(Some(Rc::new(RefCell::new(WayVRData::new(
Self::get_wayvr_config(config, self)?,
)?))))
} else {
// Lazy-init WayVR later if the user requested
Ok(None)
}
Ok(Rc::new(RefCell::new(WayVRData::new(
Self::get_wayvr_config(config, self)?,
signals,
)?)))
}
}

View File

@@ -0,0 +1,189 @@
use std::{cell::RefCell, rc::Rc};
use crate::ipc::ipc_server;
use wayvr_ipc::packet_server;
#[cfg(feature = "wayvr")]
use crate::{
backend::wayvr, config_wayvr, overlays::wayvr::OverlayToCreate, overlays::wayvr::WayVRData,
};
use crate::{
backend::{
self,
task::{OverlayTask, TaskType},
},
ipc::signal::WayVRSignal,
overlays::{self},
state::AppState,
windowing::{OverlaySelector, manager::OverlayWindowManager},
};
#[cfg(feature = "wayvr")]
fn process_tick_tasks(
app: &mut AppState,
tick_tasks: Vec<backend::wayvr::TickTask>,
r_wayvr: &Rc<RefCell<WayVRData>>,
) -> anyhow::Result<()> {
for tick_task in tick_tasks {
match tick_task {
backend::wayvr::TickTask::NewExternalProcess(request) => {
let config = &app.session.wayvr_config;
let disp_name = request.env.display_name.map_or_else(
|| {
config
.get_default_display()
.map(|(display_name, _)| display_name)
},
|display_name| {
config
.get_display(display_name.as_str())
.map(|_| display_name)
},
);
if let Some(disp_name) = disp_name {
let mut wayvr = r_wayvr.borrow_mut();
log::info!("Registering external process with PID {}", request.pid);
let disp_handle = overlays::wayvr::get_or_create_display_by_name(
app, &mut wayvr, &disp_name,
)?;
wayvr
.data
.state
.add_external_process(disp_handle, request.pid);
wayvr
.data
.state
.manager
.add_client(wayvr::client::WayVRClient {
client: request.client,
display_handle: disp_handle,
pid: request.pid,
});
}
}
wayvr::TickTask::NewDisplay(cpar, disp_handle) => {
log::info!("Creating new display with name \"{}\"", cpar.name);
let mut wayvr = r_wayvr.borrow_mut();
let unique_name = wayvr.get_unique_display_name(cpar.name);
let disp_handle = match disp_handle {
Some(d) => d,
None => wayvr.data.state.create_display(
cpar.width,
cpar.height,
&unique_name,
false,
)?,
};
wayvr.overlays_to_create.push(OverlayToCreate {
disp_handle,
conf_display: config_wayvr::WayVRDisplay {
attach_to: Some(config_wayvr::AttachTo::from_packet(&cpar.attach_to)),
width: cpar.width,
height: cpar.height,
pos: None,
primary: None,
rotation: None,
scale: cpar.scale,
},
});
}
}
}
Ok(())
}
#[allow(clippy::too_many_lines)]
pub fn tick_events<O>(
app: &mut AppState,
overlays: &mut OverlayWindowManager<O>,
) -> anyhow::Result<()>
where
O: Default,
{
#[cfg(feature = "wayvr")]
let r_wayvr = app.wayvr.clone();
#[cfg(feature = "wayvr")]
let mut wayvr = r_wayvr.borrow_mut();
while let Some(signal) = app.wayvr_signals.read() {
match signal {
#[cfg(feature = "wayvr")]
WayVRSignal::DisplayVisibility(display_handle, visible) => {
if let Some(overlay_id) = wayvr.display_handle_map.get(&display_handle) {
let overlay_id = *overlay_id;
wayvr
.data
.state
.set_display_visible(display_handle, visible);
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Id(overlay_id),
Box::new(move |app, o| {
if visible == o.is_active() {
return;
}
if visible {
o.activate(app);
} else {
o.deactivate();
}
}),
)));
}
}
#[cfg(feature = "wayvr")]
WayVRSignal::DisplayWindowLayout(display_handle, layout) => {
wayvr.data.state.set_display_layout(display_handle, layout);
}
#[cfg(feature = "wayvr")]
WayVRSignal::BroadcastStateChanged(packet) => {
app.ipc_server
.broadcast(packet_server::PacketServer::WvrStateChanged(packet));
}
#[cfg(feature = "wayvr")]
WayVRSignal::Haptics(haptics) => {
wayvr.pending_haptics = Some(haptics);
}
WayVRSignal::DropOverlay(overlay_id) => {
app.tasks
.enqueue(TaskType::Overlay(OverlayTask::Drop(OverlaySelector::Id(
overlay_id,
))));
}
WayVRSignal::CustomTask(custom_task) => {
app.tasks
.enqueue(TaskType::Overlay(OverlayTask::ModifyPanel(custom_task)));
}
}
}
#[cfg(feature = "wayvr")]
{
let tick_tasks = wayvr.data.tick_events(app)?;
process_tick_tasks(app, tick_tasks, &r_wayvr)?;
}
#[cfg(not(feature = "wayvr"))]
{
app.ipc_server.tick(&mut ipc_server::TickParams {
input_state: &app.input_state,
signals: &app.wayvr_signals,
});
}
#[cfg(feature = "wayvr")]
overlays::wayvr::create_queued_displays(app, &mut wayvr, overlays)?;
Ok(())
}

View File

@@ -1,6 +1,10 @@
use crate::state::AppState;
#[cfg(feature = "wayvr")]
use crate::backend::wayvr::{self, WayVRState};
use super::{TickTask, WayVRSignal, display, process, window};
use crate::{
backend::input::InputState,
ipc::{event_queue::SyncEventQueue, signal::WayVRSignal},
};
use bytes::BufMut;
use glam::Vec3A;
use interprocess::local_socket::{self, ToNsName, traits::Listener};
@@ -71,9 +75,12 @@ fn read_payload(conn: &mut local_socket::Stream, size: u32) -> Option<Payload> {
}
pub struct TickParams<'a> {
pub state: &'a mut super::WayVRState,
pub tasks: &'a mut Vec<TickTask>,
pub app: &'a AppState,
#[cfg(feature = "wayvr")]
pub wayland_state: &'a mut WayVRState,
#[cfg(feature = "wayvr")]
pub tasks: &'a mut Vec<wayvr::TickTask>,
pub signals: &'a SyncEventQueue<WayVRSignal>,
pub input_state: &'a InputState,
}
pub fn gen_args_vec(input: &str) -> Vec<&str> {
@@ -149,13 +156,14 @@ impl Connection {
Ok(())
}
#[cfg(feature = "wayvr")]
fn handle_wvr_display_list(
&mut self,
params: &TickParams,
serial: ipc::Serial,
) -> anyhow::Result<()> {
let list: Vec<packet_server::WvrDisplay> = params
.state
.wayland_state
.displays
.vec
.iter()
@@ -165,7 +173,10 @@ impl Connection {
return None;
};
let display = &cell.obj;
Some(display.as_packet(display::DisplayHandle::new(idx as u32, cell.generation)))
Some(display.as_packet(wayvr::display::DisplayHandle::new(
idx as u32,
cell.generation,
)))
})
.collect();
@@ -185,8 +196,6 @@ impl Connection {
params: &TickParams,
serial: ipc::Serial,
) -> anyhow::Result<()> {
let input_state = &params.app.input_state;
let to_arr = |vec: &Vec3A| -> [f32; 3] { [vec.x, vec.y, vec.z] };
send_packet(
@@ -194,12 +203,12 @@ impl Connection {
&ipc::data_encode(&PacketServer::WlxInputStateResponse(
serial,
packet_server::WlxInputState {
hmd_pos: to_arr(&input_state.hmd.translation),
hmd_pos: to_arr(&params.input_state.hmd.translation),
left: WlxInputStatePointer {
pos: to_arr(&input_state.pointers[0].raw_pose.translation),
pos: to_arr(&params.input_state.pointers[0].raw_pose.translation),
},
right: WlxInputStatePointer {
pos: to_arr(&input_state.pointers[0].raw_pose.translation),
pos: to_arr(&params.input_state.pointers[0].raw_pose.translation),
},
},
)),
@@ -208,22 +217,24 @@ impl Connection {
Ok(())
}
#[cfg(feature = "wayvr")]
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(
let display_handle = params.wayland_state.create_display(
packet_params.width,
packet_params.height,
&packet_params.name,
false,
)?;
params
.tasks
.push(TickTask::NewDisplay(packet_params, Some(display_handle)));
params.tasks.push(wayvr::TickTask::NewDisplay(
packet_params,
Some(display_handle),
));
send_packet(
&mut self.conn,
@@ -235,6 +246,7 @@ impl Connection {
Ok(())
}
#[cfg(feature = "wayvr")]
fn handle_wvr_display_remove(
&mut self,
params: &mut TickParams,
@@ -242,8 +254,8 @@ impl Connection {
handle: packet_server::WvrDisplayHandle,
) -> anyhow::Result<()> {
let res = params
.state
.destroy_display(display::DisplayHandle::from_packet(handle))
.wayland_state
.destroy_display(wayvr::display::DisplayHandle::from_packet(handle))
.map_err(|e| format!("{e:?}"));
send_packet(
@@ -253,28 +265,31 @@ impl Connection {
Ok(())
}
#[cfg(feature = "wayvr")]
fn handle_wvr_display_set_visible(
params: &mut TickParams,
handle: packet_server::WvrDisplayHandle,
visible: bool,
) {
params.state.signals.send(WayVRSignal::DisplayVisibility(
display::DisplayHandle::from_packet(handle),
params.signals.send(WayVRSignal::DisplayVisibility(
wayvr::display::DisplayHandle::from_packet(handle),
visible,
));
}
#[cfg(feature = "wayvr")]
fn handle_wvr_display_set_window_layout(
params: &mut TickParams,
handle: packet_server::WvrDisplayHandle,
layout: packet_server::WvrDisplayWindowLayout,
) {
params.state.signals.send(WayVRSignal::DisplayWindowLayout(
display::DisplayHandle::from_packet(handle),
params.signals.send(WayVRSignal::DisplayWindowLayout(
wayvr::display::DisplayHandle::from_packet(handle),
layout,
));
}
#[cfg(feature = "wayvr")]
fn handle_wvr_display_window_list(
&mut self,
params: &mut TickParams,
@@ -288,10 +303,13 @@ impl Connection {
)
};
let Some(display) = params
.state
.displays
.get(&display::DisplayHandle::from_packet(display_handle.clone()))
let Some(display) =
params
.wayland_state
.displays
.get(&wayvr::display::DisplayHandle::from_packet(
display_handle.clone(),
))
else {
return send(None);
};
@@ -302,14 +320,14 @@ impl Connection {
.iter()
.filter_map(|disp_win| {
params
.state
.wayland_state
.wm
.borrow_mut()
.windows
.get(&disp_win.window_handle)
.map(|win| packet_server::WvrWindow {
handle: window::WindowHandle::as_packet(&disp_win.window_handle),
process_handle: process::ProcessHandle::as_packet(
handle: wayvr::window::WindowHandle::as_packet(&disp_win.window_handle),
process_handle: wayvr::process::ProcessHandle::as_packet(
&disp_win.process_handle,
),
pos_x: win.pos_x,
@@ -324,17 +342,18 @@ impl Connection {
}))
}
#[cfg(feature = "wayvr")]
fn handle_wvr_window_set_visible(
params: &mut TickParams,
handle: packet_server::WvrWindowHandle,
visible: bool,
) {
let to_resize = if let Some(window) = params
.state
.wayland_state
.wm
.borrow_mut()
.windows
.get_mut(&window::WindowHandle::from_packet(handle))
.get_mut(&wayvr::window::WindowHandle::from_packet(handle))
{
window.visible = visible;
Some(window.display_handle)
@@ -343,13 +362,14 @@ impl Connection {
};
if let Some(to_resize) = to_resize
&& let Some(display) = params.state.displays.get_mut(&to_resize)
&& let Some(display) = params.wayland_state.displays.get_mut(&to_resize)
{
display.reposition_windows();
display.trigger_rerender();
}
}
#[cfg(feature = "wayvr")]
fn handle_wvr_process_launch(
&mut self,
params: &mut TickParams,
@@ -359,8 +379,8 @@ impl Connection {
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),
let res = params.wayland_state.spawn_process(
wayvr::display::DisplayHandle::from_packet(packet_params.target_display),
&packet_params.exec,
&args_vec,
&env_vec,
@@ -378,15 +398,16 @@ impl Connection {
Ok(())
}
#[cfg(feature = "wayvr")]
fn handle_wvr_display_get(
&mut self,
params: &TickParams,
serial: ipc::Serial,
display_handle: packet_server::WvrDisplayHandle,
) -> anyhow::Result<()> {
let native_handle = &display::DisplayHandle::from_packet(display_handle);
let native_handle = &wayvr::display::DisplayHandle::from_packet(display_handle);
let disp = params
.state
.wayland_state
.displays
.get(native_handle)
.map(|disp| disp.as_packet(*native_handle));
@@ -399,13 +420,14 @@ impl Connection {
Ok(())
}
#[cfg(feature = "wayvr")]
fn handle_wvr_process_list(
&mut self,
params: &TickParams,
serial: ipc::Serial,
) -> anyhow::Result<()> {
let list: Vec<packet_server::WvrProcess> = params
.state
.wayland_state
.processes
.vec
.iter()
@@ -415,7 +437,10 @@ impl Connection {
return None;
};
let process = &cell.obj;
Some(process.to_packet(process::ProcessHandle::new(idx as u32, cell.generation)))
Some(process.to_packet(wayvr::process::ProcessHandle::new(
idx as u32,
cell.generation,
)))
})
.collect();
@@ -431,12 +456,13 @@ impl Connection {
}
// This request doesn't return anything to the client
#[cfg(feature = "wayvr")]
fn handle_wvr_process_terminate(
params: &mut TickParams,
process_handle: packet_server::WvrProcessHandle,
) {
let native_handle = &process::ProcessHandle::from_packet(process_handle);
let process = params.state.processes.get_mut(native_handle);
let native_handle = &wayvr::process::ProcessHandle::from_packet(process_handle);
let process = params.wayland_state.processes.get_mut(native_handle);
let Some(process) = process else {
return;
@@ -445,15 +471,16 @@ impl Connection {
process.terminate();
}
#[cfg(feature = "wayvr")]
fn handle_wvr_process_get(
&mut self,
params: &TickParams,
serial: ipc::Serial,
process_handle: packet_server::WvrProcessHandle,
) -> anyhow::Result<()> {
let native_handle = &process::ProcessHandle::from_packet(process_handle);
let native_handle = &wayvr::process::ProcessHandle::from_packet(process_handle);
let process = params
.state
.wayland_state
.processes
.get(native_handle)
.map(|process| process.to_packet(*native_handle));
@@ -466,17 +493,18 @@ impl Connection {
Ok(())
}
#[cfg(feature = "wayvr")]
fn handle_wlx_haptics(
params: &mut TickParams,
haptics_params: packet_client::WlxHapticsParams,
) {
params.state.signals.send(super::WayVRSignal::Haptics(
crate::backend::input::Haptics {
params
.signals
.send(WayVRSignal::Haptics(crate::backend::input::Haptics {
duration: haptics_params.duration,
frequency: haptics_params.frequency,
intensity: haptics_params.intensity,
},
));
}));
}
fn handle_wlx_panel(
@@ -486,9 +514,8 @@ impl Connection {
use crate::backend::task::{ModifyPanelCommand, ModifyPanelTask};
params
.state
.signals
.send(super::WayVRSignal::CustomTask(ModifyPanelTask {
.send(WayVRSignal::CustomTask(ModifyPanelTask {
overlay: custom_params.overlay,
element: custom_params.element,
command: match custom_params.command {
@@ -511,6 +538,9 @@ impl Connection {
}));
}
// FIXME: we should probably respond an error to the client in case if wayland server feature is disabled
// fix this after we're done with the webkit-based wayvr-dashboard
#[allow(unused_variables)]
fn process_payload(&mut self, params: &mut TickParams, payload: Payload) -> anyhow::Result<()> {
let packet: PacketClient = ipc::data_decode(&payload)?;
@@ -525,42 +555,55 @@ impl Connection {
self.handle_wlx_input_state(params, serial)?;
}
PacketClient::WvrDisplayList(serial) => {
#[cfg(feature = "wayvr")]
self.handle_wvr_display_list(params, serial)?;
}
PacketClient::WvrDisplayGet(serial, display_handle) => {
#[cfg(feature = "wayvr")]
self.handle_wvr_display_get(params, serial, display_handle)?;
}
PacketClient::WvrDisplayRemove(serial, display_handle) => {
#[cfg(feature = "wayvr")]
self.handle_wvr_display_remove(params, serial, display_handle)?;
}
PacketClient::WvrDisplaySetVisible(display_handle, visible) => {
#[cfg(feature = "wayvr")]
Self::handle_wvr_display_set_visible(params, display_handle, visible);
}
PacketClient::WvrDisplaySetWindowLayout(display_handle, layout) => {
#[cfg(feature = "wayvr")]
Self::handle_wvr_display_set_window_layout(params, display_handle, layout);
}
PacketClient::WvrDisplayWindowList(serial, display_handle) => {
#[cfg(feature = "wayvr")]
self.handle_wvr_display_window_list(params, serial, display_handle)?;
}
PacketClient::WvrWindowSetVisible(window_handle, visible) => {
#[cfg(feature = "wayvr")]
Self::handle_wvr_window_set_visible(params, window_handle, visible);
}
PacketClient::WvrProcessGet(serial, process_handle) => {
#[cfg(feature = "wayvr")]
self.handle_wvr_process_get(params, serial, process_handle)?;
}
PacketClient::WvrProcessList(serial) => {
#[cfg(feature = "wayvr")]
self.handle_wvr_process_list(params, serial)?;
}
PacketClient::WvrProcessLaunch(serial, packet_params) => {
#[cfg(feature = "wayvr")]
self.handle_wvr_process_launch(params, serial, packet_params)?;
}
PacketClient::WvrDisplayCreate(serial, packet_params) => {
#[cfg(feature = "wayvr")]
self.handle_wvr_display_create(params, serial, packet_params)?;
}
PacketClient::WvrProcessTerminate(process_handle) => {
#[cfg(feature = "wayvr")]
Self::handle_wvr_process_terminate(params, process_handle);
}
PacketClient::WlxHaptics(haptics_params) => {
#[cfg(feature = "wayvr")]
Self::handle_wlx_haptics(params, haptics_params);
}
PacketClient::WlxModifyPanel(custom_params) => {

View File

@@ -0,0 +1,4 @@
pub mod event_queue;
pub mod events;
pub mod ipc_server;
pub mod signal;

View File

@@ -0,0 +1,19 @@
#[cfg(feature = "wayvr")]
use crate::backend::wayvr;
#[derive(Clone)]
pub enum WayVRSignal {
#[cfg(feature = "wayvr")]
DisplayVisibility(wayvr::display::DisplayHandle, bool),
#[cfg(feature = "wayvr")]
DisplayWindowLayout(
wayvr::display::DisplayHandle,
wayvr_ipc::packet_server::WvrDisplayWindowLayout,
),
#[cfg(feature = "wayvr")]
BroadcastStateChanged(wayvr_ipc::packet_server::WvrStateChanged),
#[cfg(feature = "wayvr")]
Haptics(crate::backend::input::Haptics),
DropOverlay(crate::windowing::OverlayID),
CustomTask(crate::backend::task::ModifyPanelTask),
}

View File

@@ -22,6 +22,7 @@ mod config;
mod config_io;
mod graphics;
mod gui;
mod ipc;
mod overlays;
mod shaders;
mod state;

View File

@@ -22,13 +22,11 @@ use crate::{
backend::{
input::{self, HoverResult},
task::{OverlayTask, TaskType},
wayvr::{
self, WayVR, WayVRAction, WayVRDisplayClickAction, display,
server_ipc::{gen_args_vec, gen_env_vec},
},
wayvr::{self, WayVR, WayVRAction, WayVRDisplayClickAction, display},
},
config_wayvr,
graphics::{Vert2Uv, dmabuf::WGfxDmabuf},
ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal},
state::{self, AppState},
subsystem::{hid::WheelDelta, input::KeyboardFocus},
windowing::{
@@ -63,31 +61,34 @@ impl WayVRContext {
}
}
struct OverlayToCreate {
pub struct OverlayToCreate {
pub conf_display: config_wayvr::WayVRDisplay,
pub disp_handle: display::DisplayHandle,
}
pub struct WayVRData {
display_handle_map: HashMap<display::DisplayHandle, OverlayID>,
overlays_to_create: Vec<OverlayToCreate>,
dashboard_executed: bool,
pub display_handle_map: HashMap<display::DisplayHandle, OverlayID>,
pub overlays_to_create: Vec<OverlayToCreate>,
pub dashboard_executed: bool,
pub data: WayVR,
pending_haptics: Option<input::Haptics>,
pub pending_haptics: Option<input::Haptics>,
}
impl WayVRData {
pub fn new(config: wayvr::Config) -> anyhow::Result<Self> {
pub fn new(
config: wayvr::Config,
signals: SyncEventQueue<WayVRSignal>,
) -> anyhow::Result<Self> {
Ok(Self {
display_handle_map: HashMap::default(),
data: WayVR::new(config)?,
data: WayVR::new(config, signals)?,
overlays_to_create: Vec::new(),
dashboard_executed: false,
pending_haptics: None,
})
}
fn get_unique_display_name(&self, mut candidate: String) -> String {
pub fn get_unique_display_name(&self, mut candidate: String) -> String {
let mut num = 0;
while !self
@@ -179,7 +180,7 @@ impl WayVRBackend {
}
}
fn get_or_create_display_by_name(
pub fn get_or_create_display_by_name(
app: &mut AppState,
wayvr: &mut WayVRData,
disp_name: &str,
@@ -290,12 +291,12 @@ where
let args_vec = &conf_dash
.args
.as_ref()
.map_or_else(Vec::new, |args| gen_args_vec(args.as_str()));
.map_or_else(Vec::new, |args| ipc_server::gen_args_vec(args.as_str()));
let env_vec = &conf_dash
.env
.as_ref()
.map_or_else(Vec::new, |env| gen_env_vec(env));
.map_or_else(Vec::new, |env| ipc_server::gen_env_vec(env));
let mut userdata = HashMap::new();
userdata.insert(String::from("type"), String::from("dashboard"));
@@ -322,9 +323,7 @@ where
let cur_visibility = !display.visible;
wayvr
.data
.ipc_server
app.ipc_server
.broadcast(PacketServer::WvrStateChanged(if cur_visibility {
WvrStateChanged::DashboardShown
} else {
@@ -379,7 +378,7 @@ fn create_overlay(
Ok(overlay)
}
fn create_queued_displays<O>(
pub fn create_queued_displays<O>(
app: &mut AppState,
data: &mut WayVRData,
overlays: &mut OverlayWindowManager<O>,
@@ -405,152 +404,6 @@ where
Ok(())
}
#[allow(clippy::too_many_lines)]
pub fn tick_events<O>(
app: &mut AppState,
overlays: &mut OverlayWindowManager<O>,
) -> anyhow::Result<()>
where
O: Default,
{
let Some(r_wayvr) = app.wayvr.clone() else {
return Ok(());
};
let mut wayvr = r_wayvr.borrow_mut();
while let Some(signal) = wayvr.data.state.signals.read() {
match signal {
wayvr::WayVRSignal::DisplayVisibility(display_handle, visible) => {
if let Some(overlay_id) = wayvr.display_handle_map.get(&display_handle) {
let overlay_id = *overlay_id;
wayvr
.data
.state
.set_display_visible(display_handle, visible);
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Id(overlay_id),
Box::new(move |app, o| {
if visible == o.is_active() {
return;
}
if visible {
o.activate(app);
} else {
o.deactivate();
}
}),
)));
}
}
wayvr::WayVRSignal::DisplayWindowLayout(display_handle, layout) => {
wayvr.data.state.set_display_layout(display_handle, layout);
}
wayvr::WayVRSignal::BroadcastStateChanged(packet) => {
wayvr
.data
.ipc_server
.broadcast(packet_server::PacketServer::WvrStateChanged(packet));
}
wayvr::WayVRSignal::DropOverlay(overlay_id) => {
app.tasks
.enqueue(TaskType::Overlay(OverlayTask::Drop(OverlaySelector::Id(
overlay_id,
))));
}
wayvr::WayVRSignal::Haptics(haptics) => {
wayvr.pending_haptics = Some(haptics);
}
wayvr::WayVRSignal::CustomTask(custom_task) => {
app.tasks
.enqueue(TaskType::Overlay(OverlayTask::ModifyPanel(custom_task)));
}
}
}
let res = wayvr.data.tick_events(app)?;
drop(wayvr);
for result in res {
match result {
wayvr::TickTask::NewExternalProcess(request) => {
let config = &app.session.wayvr_config;
let disp_name = request.env.display_name.map_or_else(
|| {
config
.get_default_display()
.map(|(display_name, _)| display_name)
},
|display_name| {
config
.get_display(display_name.as_str())
.map(|_| display_name)
},
);
if let Some(disp_name) = disp_name {
let mut wayvr = r_wayvr.borrow_mut();
log::info!("Registering external process with PID {}", request.pid);
let disp_handle = get_or_create_display_by_name(app, &mut wayvr, &disp_name)?;
wayvr
.data
.state
.add_external_process(disp_handle, request.pid);
wayvr
.data
.state
.manager
.add_client(wayvr::client::WayVRClient {
client: request.client,
display_handle: disp_handle,
pid: request.pid,
});
}
}
wayvr::TickTask::NewDisplay(cpar, disp_handle) => {
log::info!("Creating new display with name \"{}\"", cpar.name);
let mut wayvr = r_wayvr.borrow_mut();
let unique_name = wayvr.get_unique_display_name(cpar.name);
let disp_handle = match disp_handle {
Some(d) => d,
None => wayvr.data.state.create_display(
cpar.width,
cpar.height,
&unique_name,
false,
)?,
};
wayvr.overlays_to_create.push(OverlayToCreate {
disp_handle,
conf_display: config_wayvr::WayVRDisplay {
attach_to: Some(config_wayvr::AttachTo::from_packet(&cpar.attach_to)),
width: cpar.width,
height: cpar.height,
pos: None,
primary: None,
rotation: None,
scale: cpar.scale,
},
});
}
}
}
let mut wayvr = r_wayvr.borrow_mut();
create_queued_displays(app, &mut wayvr, overlays)?;
Ok(())
}
impl WayVRBackend {
fn ensure_software_data(
&mut self,
@@ -805,7 +658,7 @@ pub fn create_wayvr_display_overlay(
display_scale: f32,
name: &str,
) -> anyhow::Result<OverlayWindowConfig> {
let wayvr = app.get_wayvr()?;
let wayvr = app.wayvr.clone();
let backend = Box::new(WayVRBackend::new(
app,
@@ -866,7 +719,7 @@ fn action_app_click<O>(
where
O: Default,
{
let wayvr = app.get_wayvr()?;
let wayvr = app.wayvr.clone();
let catalog = app
.session
@@ -887,12 +740,12 @@ where
let args_vec = &app_entry
.args
.as_ref()
.map_or_else(Vec::new, |args| gen_args_vec(args.as_str()));
.map_or_else(Vec::new, |args| ipc_server::gen_args_vec(args.as_str()));
let env_vec = &app_entry
.env
.as_ref()
.map_or_else(Vec::new, |env| gen_env_vec(env));
.map_or_else(Vec::new, |env| ipc_server::gen_env_vec(env));
// Terminate existing process if required
if let Some(process_handle) =
@@ -930,7 +783,7 @@ pub fn action_display_click<O>(
where
O: Default,
{
let wayvr = app.get_wayvr()?;
let wayvr = app.wayvr.clone();
let mut wayvr = wayvr.borrow_mut();
let Some(handle) = WayVR::get_display_by_name(&wayvr.data.state.displays, display_name) else {
@@ -990,14 +843,7 @@ pub fn wayvr_action<O>(
}
}
WayVRAction::ToggleDashboard => {
let wayvr = match app.get_wayvr() {
Ok(wayvr) => wayvr,
Err(e) => {
log::error!("WayVR Error: {e:?}");
return;
}
};
let wayvr = app.wayvr.clone();
let mut wayvr = wayvr.borrow_mut();
if let Err(e) = toggle_dashboard::<O>(app, overlays, &mut wayvr) {

View File

@@ -12,11 +12,6 @@ version: 1
# "software": Read pixel data to memory via glReadPixels() every time a content has been updated. Minor performance impact on large resolutions
blit_method: "dmabuf"
# Set to true if you want to make Wyland server instantly available.
# By default, WayVR starts only when it's needed.
# (this option is primarily used for remote starting external processes and development purposes)
run_compositor_at_start: false
# Automatically close overlays with zero window count?
auto_hide: true

View File

@@ -27,6 +27,7 @@ use crate::{
config_io::{self, get_config_file_path},
graphics::WGfxExtras,
gui,
ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal},
subsystem::{audio::AudioOutput, dbus::DbusConnector, input::HidWrapper},
};
@@ -53,11 +54,16 @@ pub struct AppState {
pub xr_backend: XrBackend,
pub ipc_server: ipc_server::WayVRServer,
pub wayvr_signals: SyncEventQueue<WayVRSignal>,
#[cfg(feature = "osc")]
pub osc_sender: Option<OscSender>,
// wayland server
// TODO: rename to wayland_server?
#[cfg(feature = "wayvr")]
pub wayvr: Option<Rc<RefCell<WayVRData>>>, // Dynamically created if requested
pub wayvr: Rc<RefCell<WayVRData>>,
}
#[allow(unused_mut)]
@@ -71,18 +77,18 @@ impl AppState {
let mut tasks = TaskContainer::new();
let session = AppSession::load();
let wayvr_signals = SyncEventQueue::new();
#[cfg(feature = "wayvr")]
let wayvr = session
.wayvr_config
.post_load(&session.config, &mut tasks)?;
let wayvr =
session
.wayvr_config
.post_load(&session.config, &mut tasks, wayvr_signals.clone())?;
let mut hid_provider = HidWrapper::new();
#[cfg(feature = "wayvr")]
if let Some(wayvr) = wayvr.as_ref() {
hid_provider.set_wayvr(wayvr.clone());
}
hid_provider.set_wayvr(wayvr.clone());
#[cfg(feature = "osc")]
let osc_sender = crate::subsystem::osc::OscSender::new(session.config.osc_out_port).ok();
@@ -114,6 +120,8 @@ impl AppState {
let dbus = DbusConnector::default();
let ipc_server = ipc_server::WayVRServer::new()?;
Ok(Self {
session,
tasks,
@@ -135,6 +143,8 @@ impl AppState {
)?,
dbus,
xr_backend,
ipc_server,
wayvr_signals,
#[cfg(feature = "osc")]
osc_sender,
@@ -144,21 +154,6 @@ impl AppState {
})
}
#[cfg(feature = "wayvr")]
#[allow(dead_code)]
pub fn get_wayvr(&mut self) -> anyhow::Result<Rc<RefCell<WayVRData>>> {
if let Some(wvr) = &self.wayvr {
Ok(wvr.clone())
} else {
let wayvr = Rc::new(RefCell::new(WayVRData::new(
WayVRConfig::get_wayvr_config(&self.session.config, &self.session.wayvr_config)?,
)?));
self.hid_provider.set_wayvr(wayvr.clone());
self.wayvr = Some(wayvr.clone());
Ok(wayvr)
}
}
pub fn try_load_bytes(path: &str, fallback_data: &'static [u8]) -> &'static [u8] {
if path.is_empty() {
return fallback_data;
@@ -187,7 +182,7 @@ pub struct AppSession {
pub config: GeneralConfig,
#[cfg(feature = "wayvr")]
pub wayvr_config: WayVRConfig,
pub wayvr_config: WayVRConfig, // TODO: rename to "wayland_server_config"
pub toast_topics: IdMap<ToastTopic, ToastDisplayMethod>,
}