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:
118
Cargo.lock
generated
118
Cargo.lock
generated
@@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "ab_glyph"
|
||||
@@ -432,6 +432,15 @@ dependencies = [
|
||||
"syn 2.0.89",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-polyfill"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.1.2"
|
||||
@@ -707,9 +716,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.8.0"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
||||
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
||||
|
||||
[[package]]
|
||||
name = "calloop"
|
||||
@@ -903,6 +912,12 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
@@ -1099,6 +1114,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
@@ -1362,6 +1383,12 @@ dependencies = [
|
||||
"const-random",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doctest-file"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562"
|
||||
|
||||
[[package]]
|
||||
name = "downcast-rs"
|
||||
version = "1.2.1"
|
||||
@@ -1805,6 +1832,15 @@ dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
@@ -1836,6 +1872,20 @@ dependencies = [
|
||||
"hashbrown 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.7.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
|
||||
dependencies = [
|
||||
"atomic-polyfill",
|
||||
"hash32",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"spin",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
@@ -2139,6 +2189,19 @@ dependencies = [
|
||||
"nix 0.29.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "interprocess"
|
||||
version = "2.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "894148491d817cb36b6f778017b8ac46b17408d522dd90f539d677ea938362eb"
|
||||
dependencies = [
|
||||
"doctest-file",
|
||||
"libc",
|
||||
"recvmsg",
|
||||
"widestring",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "2.0.3"
|
||||
@@ -3221,6 +3284,17 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "postcard"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "170a2601f67cc9dba8edd8c4870b15f71a6a2dc196daec8c83f72b59dff628a8"
|
||||
dependencies = [
|
||||
"cobs",
|
||||
"heapless",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
@@ -3393,6 +3467,12 @@ dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "recvmsg"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
@@ -3808,6 +3888,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
@@ -4512,6 +4601,19 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayvr_ipc"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/olekolek1000/wayvr-ipc.git?rev=c2a6438ffdcc78ff9c0637d914df1bc673723824#c2a6438ffdcc78ff9c0637d914df1bc673723824"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"log",
|
||||
"postcard",
|
||||
"serde",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.72"
|
||||
@@ -4544,6 +4646,12 @@ dependencies = [
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@@ -4957,6 +5065,7 @@ version = "0.6.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ash",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"clap",
|
||||
@@ -4973,6 +5082,7 @@ dependencies = [
|
||||
"idmap-derive",
|
||||
"image_dds",
|
||||
"input-linux",
|
||||
"interprocess",
|
||||
"json",
|
||||
"json5",
|
||||
"khronos-egl",
|
||||
@@ -4984,6 +5094,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"openxr",
|
||||
"ovr_overlay",
|
||||
"postcard",
|
||||
"regex",
|
||||
"rodio",
|
||||
"rosc",
|
||||
@@ -5001,6 +5112,7 @@ dependencies = [
|
||||
"vulkano-shaders",
|
||||
"wayland-client",
|
||||
"wayland-egl",
|
||||
"wayvr_ipc",
|
||||
"winit",
|
||||
"wlx-capture",
|
||||
"xcb",
|
||||
|
||||
15
Cargo.toml
15
Cargo.toml
@@ -70,7 +70,9 @@ image_dds = { version = "0.6.0", default-features = false, features = [
|
||||
] }
|
||||
mint = "0.5.9"
|
||||
|
||||
# WayVR-only deps
|
||||
################################
|
||||
#WayVR-only deps
|
||||
################################
|
||||
khronos-egl = { version = "6.0.0", features = ["static"], optional = true }
|
||||
smithay = { git = "https://github.com/Smithay/smithay.git", default-features = false, features = [
|
||||
"renderer_gl",
|
||||
@@ -81,12 +83,17 @@ smithay = { git = "https://github.com/Smithay/smithay.git", default-features = f
|
||||
uuid = { version = "1.10.0", features = ["v4", "fast-rng"], optional = true }
|
||||
wayland-client = { version = "0.31.6", optional = true }
|
||||
wayland-egl = { version = "0.32.4", optional = true }
|
||||
interprocess = { version = "2.2.2", optional = true }
|
||||
postcard = { version = "1.1.1", optional = true }
|
||||
bytes = { version = "1.9.0", optional = true }
|
||||
wayvr_ipc = { git = "https://github.com/olekolek1000/wayvr-ipc.git", rev = "c2a6438ffdcc78ff9c0637d914df1bc673723824", default-features = false, optional = true }
|
||||
################################
|
||||
|
||||
[build-dependencies]
|
||||
regex = { version = "*" }
|
||||
|
||||
[features]
|
||||
default = ["openxr", "openvr", "osc", "x11", "wayland", "wayvr"]
|
||||
default = ["openvr", "openxr", "osc", "x11", "wayland", "wayvr"]
|
||||
openvr = ["dep:ovr_overlay", "dep:json"]
|
||||
openxr = ["dep:openxr", "dep:libmonado-rs"]
|
||||
osc = ["dep:rosc"]
|
||||
@@ -101,5 +108,9 @@ wayvr = [
|
||||
"dep:uuid",
|
||||
"dep:wayland-client",
|
||||
"dep:wayland-egl",
|
||||
"dep:interprocess",
|
||||
"dep:postcard",
|
||||
"dep:bytes",
|
||||
"dep:wayvr_ipc",
|
||||
]
|
||||
as-raw-xcb-connection = []
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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()?;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
src/backend/wayvr/server_ipc.rs
Normal file
418
src/backend/wayvr/server_ipc.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ use crate::{
|
||||
wayvr,
|
||||
},
|
||||
config::{load_known_yaml, ConfigType},
|
||||
overlays::wayvr::{WayVRAction, WayVRState},
|
||||
overlays::wayvr::{WayVRAction, WayVRData},
|
||||
};
|
||||
|
||||
// Flat version of RelativeTo
|
||||
@@ -40,6 +40,16 @@ impl AttachTo {
|
||||
AttachTo::Head => RelativeTo::Head,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_packet(input: &wayvr_ipc::packet_client::AttachTo) -> AttachTo {
|
||||
match input {
|
||||
wayvr_ipc::packet_client::AttachTo::None => AttachTo::None,
|
||||
wayvr_ipc::packet_client::AttachTo::HandLeft => AttachTo::HandLeft,
|
||||
wayvr_ipc::packet_client::AttachTo::HandRight => AttachTo::HandRight,
|
||||
wayvr_ipc::packet_client::AttachTo::Head => AttachTo::Head,
|
||||
wayvr_ipc::packet_client::AttachTo::Stage => AttachTo::Stage,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
@@ -60,8 +70,8 @@ pub struct WayVRAppEntry {
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct WayVRDisplay {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub width: u16,
|
||||
pub height: u16,
|
||||
pub scale: Option<f32>,
|
||||
pub rotation: Option<Rotation>,
|
||||
pub pos: Option<[f32; 3]>,
|
||||
@@ -96,12 +106,20 @@ fn def_keyboard_repeat_rate() -> u32 {
|
||||
50
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct WayVRDashboard {
|
||||
pub exec: String,
|
||||
pub args: Option<String>,
|
||||
pub env: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct WayVRConfig {
|
||||
pub version: u32,
|
||||
pub run_compositor_at_start: bool,
|
||||
pub catalogs: HashMap<String, WayVRCatalog>,
|
||||
pub displays: BTreeMap<String, WayVRDisplay>, // sorted alphabetically
|
||||
pub dashboard: WayVRDashboard,
|
||||
|
||||
#[serde(default = "def_true")]
|
||||
pub auto_hide: bool,
|
||||
@@ -154,7 +172,7 @@ impl WayVRConfig {
|
||||
&self,
|
||||
config: &crate::config::GeneralConfig,
|
||||
tasks: &mut TaskContainer,
|
||||
) -> anyhow::Result<Option<Rc<RefCell<WayVRState>>>> {
|
||||
) -> anyhow::Result<Option<Rc<RefCell<WayVRData>>>> {
|
||||
let primary_count = self
|
||||
.displays
|
||||
.iter()
|
||||
@@ -182,8 +200,8 @@ impl WayVRConfig {
|
||||
|
||||
if self.run_compositor_at_start {
|
||||
// Start Wayland server instantly
|
||||
Ok(Some(Rc::new(RefCell::new(WayVRState::new(
|
||||
Self::get_wayvr_config(config, &self),
|
||||
Ok(Some(Rc::new(RefCell::new(WayVRData::new(
|
||||
Self::get_wayvr_config(config, self),
|
||||
)?))))
|
||||
} else {
|
||||
// Lazy-init WayVR later if the user requested
|
||||
|
||||
@@ -2,7 +2,7 @@ use glam::Vec3A;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::backend::overlay::{OverlayData, OverlayState, RelativeTo};
|
||||
use crate::backend::overlay::{OverlayData, OverlayState, RelativeTo, Z_ORDER_ANCHOR};
|
||||
use crate::config::{load_known_yaml, ConfigType};
|
||||
use crate::gui::modular::{modular_canvas, ModularUiConfig};
|
||||
use crate::state::AppState;
|
||||
@@ -21,7 +21,7 @@ where
|
||||
want_visible: false,
|
||||
interactable: false,
|
||||
grabbable: false,
|
||||
z_order: 67,
|
||||
z_order: Z_ORDER_ANCHOR,
|
||||
spawn_scale: config.width,
|
||||
spawn_point: Vec3A::NEG_Z * 0.5,
|
||||
relative_to: RelativeTo::Stage,
|
||||
|
||||
@@ -40,7 +40,7 @@ fn send_key(app: &mut AppState, key: VirtualKey, down: bool) {
|
||||
{
|
||||
#[cfg(feature = "wayvr")]
|
||||
if let Some(wayvr) = &app.wayvr {
|
||||
wayvr.borrow_mut().state.send_key(key as u32, down);
|
||||
wayvr.borrow_mut().data.state.send_key(key as u32, down);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::{
|
||||
backend::{
|
||||
common::OverlaySelector,
|
||||
overlay::{OverlayBackend, OverlayState, RelativeTo},
|
||||
overlay::{OverlayBackend, OverlayState, RelativeTo, Z_ORDER_TOAST},
|
||||
task::TaskType,
|
||||
},
|
||||
gui::{canvas::builder::CanvasBuilder, color_parse},
|
||||
@@ -199,7 +199,7 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<(OverlayState, Box<dyn
|
||||
spawn_scale: size.0 * PIXELS_TO_METERS,
|
||||
spawn_rotation,
|
||||
spawn_point,
|
||||
z_order: 70,
|
||||
z_order: Z_ORDER_TOAST,
|
||||
relative_to,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use glam::Vec3A;
|
||||
|
||||
use crate::{
|
||||
backend::overlay::{ui_transform, OverlayData, OverlayState, RelativeTo},
|
||||
backend::overlay::{ui_transform, OverlayData, OverlayState, RelativeTo, Z_ORDER_WATCH},
|
||||
config::{load_known_yaml, ConfigType},
|
||||
gui::{
|
||||
canvas::Canvas,
|
||||
@@ -25,7 +25,7 @@ where
|
||||
name: WATCH_NAME.into(),
|
||||
want_visible: true,
|
||||
interactable: true,
|
||||
z_order: 68,
|
||||
z_order: Z_ORDER_WATCH,
|
||||
spawn_scale: config.width,
|
||||
spawn_point: state.session.config.watch_pos,
|
||||
spawn_rotation: state.session.config.watch_rot,
|
||||
|
||||
@@ -10,23 +10,33 @@ use crate::{
|
||||
input::{self, InteractionHandler},
|
||||
overlay::{
|
||||
ui_transform, FrameTransform, OverlayData, OverlayID, OverlayRenderer, OverlayState,
|
||||
SplitOverlayBackend,
|
||||
SplitOverlayBackend, Z_ORDER_DASHBOARD,
|
||||
},
|
||||
task::TaskType,
|
||||
wayvr::{self, display, WayVR},
|
||||
wayvr::{
|
||||
self, display,
|
||||
server_ipc::{gen_args_vec, gen_env_vec},
|
||||
WayVR,
|
||||
},
|
||||
},
|
||||
config_wayvr,
|
||||
graphics::WlxGraphics,
|
||||
state::{self, AppState, KeyboardFocus},
|
||||
};
|
||||
|
||||
// Hard-coded for now
|
||||
const DASHBOARD_WIDTH: u16 = 960;
|
||||
const DASHBOARD_HEIGHT: u16 = 540;
|
||||
const DASHBOARD_DISPLAY_NAME: &str = "_DASHBOARD";
|
||||
|
||||
pub struct WayVRContext {
|
||||
wayvr: Rc<RefCell<WayVRState>>,
|
||||
wayvr: Rc<RefCell<WayVRData>>,
|
||||
display: wayvr::display::DisplayHandle,
|
||||
}
|
||||
|
||||
impl WayVRContext {
|
||||
pub fn new(
|
||||
wvr: Rc<RefCell<WayVRState>>,
|
||||
wvr: Rc<RefCell<WayVRData>>,
|
||||
display: wayvr::display::DisplayHandle,
|
||||
) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
@@ -36,18 +46,46 @@ impl WayVRContext {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WayVRState {
|
||||
pub display_handle_map: HashMap<display::DisplayHandle, OverlayID>,
|
||||
pub state: WayVR,
|
||||
struct OverlayToCreate {
|
||||
pub conf_display: config_wayvr::WayVRDisplay,
|
||||
pub disp_handle: display::DisplayHandle,
|
||||
}
|
||||
|
||||
impl WayVRState {
|
||||
pub struct WayVRData {
|
||||
display_handle_map: HashMap<display::DisplayHandle, OverlayID>,
|
||||
overlays_to_create: Vec<OverlayToCreate>,
|
||||
pub data: WayVR,
|
||||
}
|
||||
|
||||
impl WayVRData {
|
||||
pub fn new(config: wayvr::Config) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
display_handle_map: Default::default(),
|
||||
state: WayVR::new(config)?,
|
||||
data: WayVR::new(config)?,
|
||||
overlays_to_create: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_unique_display_name(&self, mut candidate: String) -> String {
|
||||
let mut num = 0;
|
||||
|
||||
while !self
|
||||
.data
|
||||
.state
|
||||
.displays
|
||||
.vec
|
||||
.iter()
|
||||
.flatten()
|
||||
.any(|d| d.obj.name == candidate)
|
||||
{
|
||||
if num > 0 {
|
||||
candidate = format!("{} ({})", candidate, num);
|
||||
}
|
||||
num += 1;
|
||||
}
|
||||
|
||||
candidate
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WayVRInteractionHandler {
|
||||
@@ -72,14 +110,15 @@ impl InteractionHandler for WayVRInteractionHandler {
|
||||
) -> Option<input::Haptics> {
|
||||
let ctx = self.context.borrow();
|
||||
|
||||
let wayvr = &mut ctx.wayvr.borrow_mut().state;
|
||||
if let Some(disp) = wayvr.displays.get(&ctx.display) {
|
||||
let wayvr = &mut ctx.wayvr.borrow_mut().data;
|
||||
|
||||
if let Some(disp) = wayvr.state.displays.get(&ctx.display) {
|
||||
let pos = self.mouse_transform.transform_point2(hit.uv);
|
||||
let x = ((pos.x * disp.width as f32) as i32).max(0);
|
||||
let y = ((pos.y * disp.height as f32) as i32).max(0);
|
||||
|
||||
let ctx = self.context.borrow();
|
||||
wayvr.send_mouse_move(ctx.display, x as u32, y as u32);
|
||||
wayvr.state.send_mouse_move(ctx.display, x as u32, y as u32);
|
||||
}
|
||||
|
||||
None
|
||||
@@ -100,11 +139,11 @@ impl InteractionHandler for WayVRInteractionHandler {
|
||||
}
|
||||
} {
|
||||
let ctx = self.context.borrow();
|
||||
let wayvr = &mut ctx.wayvr.borrow_mut().state;
|
||||
let wayvr = &mut ctx.wayvr.borrow_mut().data;
|
||||
if pressed {
|
||||
wayvr.send_mouse_down(ctx.display, index);
|
||||
wayvr.state.send_mouse_down(ctx.display, index);
|
||||
} else {
|
||||
wayvr.send_mouse_up(ctx.display, index);
|
||||
wayvr.state.send_mouse_up(ctx.display, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,6 +152,7 @@ impl InteractionHandler for WayVRInteractionHandler {
|
||||
let ctx = self.context.borrow();
|
||||
ctx.wayvr
|
||||
.borrow_mut()
|
||||
.data
|
||||
.state
|
||||
.send_mouse_scroll(ctx.display, delta);
|
||||
}
|
||||
@@ -127,8 +167,8 @@ pub struct WayVRRenderer {
|
||||
|
||||
impl WayVRRenderer {
|
||||
pub fn new(
|
||||
app: &mut state::AppState,
|
||||
wvr: Rc<RefCell<WayVRState>>,
|
||||
app: &state::AppState,
|
||||
wvr: Rc<RefCell<WayVRData>>,
|
||||
display: wayvr::display::DisplayHandle,
|
||||
) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
@@ -140,19 +180,13 @@ impl WayVRRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_or_create_display<O>(
|
||||
fn get_or_create_display_by_name(
|
||||
app: &mut AppState,
|
||||
wayvr: &mut WayVRState,
|
||||
wayvr: &mut WayVRData,
|
||||
disp_name: &str,
|
||||
) -> anyhow::Result<(display::DisplayHandle, Option<OverlayData<O>>)>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
let created_overlay: Option<OverlayData<O>>;
|
||||
|
||||
) -> anyhow::Result<display::DisplayHandle> {
|
||||
let disp_handle =
|
||||
if let Some(disp) = WayVR::get_display_by_name(&wayvr.state.displays, disp_name) {
|
||||
created_overlay = None;
|
||||
if let Some(disp) = WayVR::get_display_by_name(&wayvr.data.state.displays, disp_name) {
|
||||
disp
|
||||
} else {
|
||||
let conf_display = app
|
||||
@@ -165,117 +199,280 @@ where
|
||||
))?
|
||||
.clone();
|
||||
|
||||
let disp_handle = wayvr.state.create_display(
|
||||
let disp_handle = wayvr.data.state.create_display(
|
||||
conf_display.width,
|
||||
conf_display.height,
|
||||
disp_name,
|
||||
conf_display.primary.unwrap_or(false),
|
||||
)?;
|
||||
|
||||
let mut overlay = create_wayvr_display_overlay::<O>(
|
||||
app,
|
||||
conf_display.width,
|
||||
conf_display.height,
|
||||
wayvr.overlays_to_create.push(OverlayToCreate {
|
||||
conf_display,
|
||||
disp_handle,
|
||||
conf_display.scale.unwrap_or(1.0),
|
||||
)?;
|
||||
|
||||
wayvr
|
||||
.display_handle_map
|
||||
.insert(disp_handle, overlay.state.id);
|
||||
|
||||
if let Some(attach_to) = &conf_display.attach_to {
|
||||
overlay.state.relative_to = attach_to.get_relative_to();
|
||||
}
|
||||
|
||||
if let Some(rot) = &conf_display.rotation {
|
||||
overlay.state.spawn_rotation = glam::Quat::from_axis_angle(
|
||||
Vec3::from_slice(&rot.axis),
|
||||
f32::to_radians(rot.angle),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(pos) = &conf_display.pos {
|
||||
overlay.state.spawn_point = Vec3A::from_slice(pos);
|
||||
}
|
||||
|
||||
let display = wayvr.state.displays.get_mut(&disp_handle).unwrap(); // Never fails
|
||||
display.overlay_id = Some(overlay.state.id);
|
||||
|
||||
created_overlay = Some(overlay);
|
||||
});
|
||||
|
||||
disp_handle
|
||||
};
|
||||
|
||||
Ok((disp_handle, created_overlay))
|
||||
Ok(disp_handle)
|
||||
}
|
||||
|
||||
fn toggle_dashboard<O>(
|
||||
app: &mut AppState,
|
||||
overlays: &mut OverlayContainer<O>,
|
||||
wayvr: &mut WayVRData,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
let (newly_created, disp_handle) = wayvr.data.state.get_or_create_dashboard_display(
|
||||
DASHBOARD_WIDTH,
|
||||
DASHBOARD_HEIGHT,
|
||||
DASHBOARD_DISPLAY_NAME,
|
||||
)?;
|
||||
|
||||
if newly_created {
|
||||
log::info!("Creating dashboard overlay");
|
||||
|
||||
let mut overlay = create_overlay::<O>(
|
||||
app,
|
||||
wayvr,
|
||||
DASHBOARD_DISPLAY_NAME,
|
||||
OverlayToCreate {
|
||||
disp_handle,
|
||||
conf_display: config_wayvr::WayVRDisplay {
|
||||
attach_to: None,
|
||||
width: DASHBOARD_WIDTH,
|
||||
height: DASHBOARD_HEIGHT,
|
||||
scale: None,
|
||||
rotation: None,
|
||||
pos: None,
|
||||
primary: None,
|
||||
},
|
||||
},
|
||||
)?;
|
||||
|
||||
overlay.state.want_visible = true;
|
||||
overlay.state.spawn_scale = 1.25;
|
||||
overlay.state.z_order = Z_ORDER_DASHBOARD;
|
||||
overlay.state.reset(app, true);
|
||||
|
||||
// FIXME: overlay curvature needs to be dispatched for some unknown reason, this value is not set otherwise
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Id(overlay.state.id),
|
||||
Box::new(move |_app, o| {
|
||||
o.curvature = Some(0.15);
|
||||
}),
|
||||
));
|
||||
|
||||
overlays.add(overlay);
|
||||
|
||||
let conf_dash = &app.session.wayvr_config.dashboard;
|
||||
|
||||
let args_vec = match &conf_dash.args {
|
||||
Some(args) => gen_args_vec(args),
|
||||
None => vec![],
|
||||
};
|
||||
|
||||
let env_vec = match &conf_dash.env {
|
||||
Some(env) => gen_env_vec(env),
|
||||
None => vec![],
|
||||
};
|
||||
|
||||
// Start dashboard specified in the WayVR config
|
||||
let _process_handle_unused =
|
||||
wayvr
|
||||
.data
|
||||
.state
|
||||
.spawn_process(disp_handle, &conf_dash.exec, &args_vec, &env_vec)?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let display = wayvr.data.state.displays.get(&disp_handle).unwrap(); // safe
|
||||
let Some(overlay_id) = display.overlay_id else {
|
||||
anyhow::bail!("Overlay ID not set for dashboard display");
|
||||
};
|
||||
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Id(overlay_id),
|
||||
Box::new(move |app, o| {
|
||||
// Toggle visibility
|
||||
o.want_visible = !o.want_visible;
|
||||
if o.want_visible {
|
||||
o.reset(app, true);
|
||||
}
|
||||
}),
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_overlay<O>(
|
||||
app: &mut AppState,
|
||||
data: &mut WayVRData,
|
||||
name: &str,
|
||||
cell: OverlayToCreate,
|
||||
) -> anyhow::Result<OverlayData<O>>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
let conf_display = &cell.conf_display;
|
||||
let disp_handle = cell.disp_handle;
|
||||
|
||||
let mut overlay = create_wayvr_display_overlay::<O>(
|
||||
app,
|
||||
conf_display.width,
|
||||
conf_display.height,
|
||||
disp_handle,
|
||||
conf_display.scale.unwrap_or(1.0),
|
||||
name,
|
||||
)?;
|
||||
|
||||
data.display_handle_map
|
||||
.insert(disp_handle, overlay.state.id);
|
||||
|
||||
if let Some(attach_to) = &conf_display.attach_to {
|
||||
overlay.state.relative_to = attach_to.get_relative_to();
|
||||
}
|
||||
|
||||
if let Some(rot) = &conf_display.rotation {
|
||||
overlay.state.spawn_rotation =
|
||||
glam::Quat::from_axis_angle(Vec3::from_slice(&rot.axis), f32::to_radians(rot.angle));
|
||||
}
|
||||
|
||||
if let Some(pos) = &conf_display.pos {
|
||||
overlay.state.spawn_point = Vec3A::from_slice(pos);
|
||||
}
|
||||
|
||||
let display = data.data.state.displays.get_mut(&disp_handle).unwrap(); // Never fails
|
||||
display.overlay_id = Some(overlay.state.id);
|
||||
|
||||
Ok(overlay)
|
||||
}
|
||||
|
||||
fn create_queued_displays<O>(
|
||||
app: &mut AppState,
|
||||
data: &mut WayVRData,
|
||||
overlays: &mut OverlayContainer<O>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
let overlays_to_create = std::mem::take(&mut data.overlays_to_create);
|
||||
|
||||
for cell in overlays_to_create {
|
||||
let Some(disp) = data.data.state.displays.get(&cell.disp_handle) else {
|
||||
continue; // this shouldn't happen
|
||||
};
|
||||
|
||||
let name = disp.name.clone();
|
||||
|
||||
let overlay = create_overlay::<O>(app, data, name.as_str(), cell)?;
|
||||
overlays.add(overlay); // Insert freshly created WayVR overlay into wlx stack
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn tick_events<O>(app: &mut AppState, overlays: &mut OverlayContainer<O>) -> anyhow::Result<()>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
if let Some(r_wayvr) = app.wayvr.clone() {
|
||||
let mut wayvr = r_wayvr.borrow_mut();
|
||||
while let Some(signal) = wayvr.state.signals.read() {
|
||||
match signal {
|
||||
wayvr::WayVRSignal::DisplayHideRequest(display_handle) => {
|
||||
if let Some(overlay_id) = wayvr.display_handle_map.get(&display_handle) {
|
||||
let overlay_id = *overlay_id;
|
||||
wayvr.state.set_display_visible(display_handle, false);
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Id(overlay_id),
|
||||
Box::new(move |_app, o| {
|
||||
o.want_visible = false;
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let Some(r_wayvr) = app.wayvr.clone() else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let res = wayvr.state.tick_events()?;
|
||||
drop(wayvr);
|
||||
let mut wayvr = r_wayvr.borrow_mut();
|
||||
|
||||
for result in res {
|
||||
match result {
|
||||
wayvr::TickResult::NewExternalProcess(req) => {
|
||||
let config = &app.session.wayvr_config;
|
||||
|
||||
let disp_name = if let Some(display_name) = req.env.display_name {
|
||||
config
|
||||
.get_display(display_name.as_str())
|
||||
.map(|_| display_name)
|
||||
} else {
|
||||
config
|
||||
.get_default_display()
|
||||
.map(|(display_name, _)| display_name)
|
||||
};
|
||||
|
||||
if let Some(disp_name) = disp_name {
|
||||
let mut wayvr = r_wayvr.borrow_mut();
|
||||
|
||||
log::info!("Registering external process with PID {}", req.pid);
|
||||
|
||||
let (disp_handle, created_overlay) =
|
||||
get_or_create_display::<O>(app, &mut wayvr, &disp_name)?;
|
||||
|
||||
wayvr.state.add_external_process(disp_handle, req.pid);
|
||||
|
||||
wayvr.state.manager.add_client(wayvr::client::WayVRClient {
|
||||
client: req.client,
|
||||
display_handle: disp_handle,
|
||||
pid: req.pid,
|
||||
});
|
||||
|
||||
if let Some(created_overlay) = created_overlay {
|
||||
overlays.add(created_overlay);
|
||||
}
|
||||
}
|
||||
while let Some(signal) = wayvr.data.signals.read() {
|
||||
match signal {
|
||||
wayvr::WayVRSignal::DisplayHideRequest(display_handle) => {
|
||||
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, false);
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Id(overlay_id),
|
||||
Box::new(move |_app, o| {
|
||||
o.want_visible = false;
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let res = wayvr.data.tick_events()?;
|
||||
drop(wayvr);
|
||||
|
||||
for result in res {
|
||||
match result {
|
||||
wayvr::TickTask::NewExternalProcess(req) => {
|
||||
let config = &app.session.wayvr_config;
|
||||
|
||||
let disp_name = if let Some(display_name) = req.env.display_name {
|
||||
config
|
||||
.get_display(display_name.as_str())
|
||||
.map(|_| display_name)
|
||||
} else {
|
||||
config
|
||||
.get_default_display()
|
||||
.map(|(display_name, _)| display_name)
|
||||
};
|
||||
|
||||
if let Some(disp_name) = disp_name {
|
||||
let mut wayvr = r_wayvr.borrow_mut();
|
||||
|
||||
log::info!("Registering external process with PID {}", req.pid);
|
||||
|
||||
let disp_handle = get_or_create_display_by_name(app, &mut wayvr, &disp_name)?;
|
||||
|
||||
wayvr.data.state.add_external_process(disp_handle, req.pid);
|
||||
|
||||
wayvr
|
||||
.data
|
||||
.state
|
||||
.manager
|
||||
.add_client(wayvr::client::WayVRClient {
|
||||
client: req.client,
|
||||
display_handle: disp_handle,
|
||||
pid: req.pid,
|
||||
});
|
||||
}
|
||||
}
|
||||
wayvr::TickTask::NewDisplay(cpar) => {
|
||||
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 = 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(())
|
||||
}
|
||||
|
||||
@@ -290,11 +487,11 @@ impl WayVRRenderer {
|
||||
|
||||
let ctx = self.context.borrow_mut();
|
||||
let wayvr = ctx.wayvr.borrow_mut();
|
||||
if let Some(disp) = wayvr.state.displays.get(&ctx.display) {
|
||||
if let Some(disp) = wayvr.data.state.displays.get(&ctx.display) {
|
||||
let frame = DmabufFrame {
|
||||
format: FrameFormat {
|
||||
width: disp.width,
|
||||
height: disp.height,
|
||||
width: disp.width as u32,
|
||||
height: disp.height as u32,
|
||||
fourcc: FourCC {
|
||||
value: data.mod_info.fourcc,
|
||||
},
|
||||
@@ -339,15 +536,15 @@ impl OverlayRenderer for WayVRRenderer {
|
||||
|
||||
fn pause(&mut self, _app: &mut state::AppState) -> anyhow::Result<()> {
|
||||
let ctx = self.context.borrow_mut();
|
||||
let wayvr = &mut ctx.wayvr.borrow_mut().state;
|
||||
wayvr.set_display_visible(ctx.display, false);
|
||||
let wayvr = &mut ctx.wayvr.borrow_mut().data;
|
||||
wayvr.state.set_display_visible(ctx.display, false);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resume(&mut self, _app: &mut state::AppState) -> anyhow::Result<()> {
|
||||
let ctx = self.context.borrow_mut();
|
||||
let wayvr = &mut ctx.wayvr.borrow_mut().state;
|
||||
wayvr.set_display_visible(ctx.display, true);
|
||||
let wayvr = &mut ctx.wayvr.borrow_mut().data;
|
||||
wayvr.state.set_display_visible(ctx.display, true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -355,9 +552,10 @@ impl OverlayRenderer for WayVRRenderer {
|
||||
let ctx = self.context.borrow();
|
||||
let mut wayvr = ctx.wayvr.borrow_mut();
|
||||
|
||||
wayvr.state.tick_display(ctx.display)?;
|
||||
wayvr.data.tick_display(ctx.display)?;
|
||||
|
||||
let dmabuf_data = wayvr
|
||||
.data
|
||||
.state
|
||||
.get_dmabuf_data(ctx.display)
|
||||
.ok_or(anyhow::anyhow!("Failed to fetch dmabuf data"))?
|
||||
@@ -385,18 +583,19 @@ impl OverlayRenderer for WayVRRenderer {
|
||||
#[allow(dead_code)]
|
||||
pub fn create_wayvr_display_overlay<O>(
|
||||
app: &mut state::AppState,
|
||||
display_width: u32,
|
||||
display_height: u32,
|
||||
display_width: u16,
|
||||
display_height: u16,
|
||||
display_handle: wayvr::display::DisplayHandle,
|
||||
display_scale: f32,
|
||||
name: &str,
|
||||
) -> anyhow::Result<OverlayData<O>>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
let transform = ui_transform(&[display_width, display_height]);
|
||||
let transform = ui_transform(&[display_width as u32, display_height as u32]);
|
||||
|
||||
let state = OverlayState {
|
||||
name: format!("WayVR Screen ({}x{})", display_width, display_height).into(),
|
||||
name: format!("WayVR - {}", name).into(),
|
||||
keyboard_focus: Some(KeyboardFocus::WayVR),
|
||||
want_visible: true,
|
||||
interactable: true,
|
||||
@@ -440,20 +639,21 @@ pub enum WayVRAction {
|
||||
display_name: Arc<str>,
|
||||
action: WayVRDisplayClickAction,
|
||||
},
|
||||
ToggleDashboard,
|
||||
}
|
||||
|
||||
fn show_display<O>(wayvr: &mut WayVRState, overlays: &mut OverlayContainer<O>, display_name: &str)
|
||||
fn show_display<O>(wayvr: &mut WayVRData, overlays: &mut OverlayContainer<O>, display_name: &str)
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
if let Some(display) = WayVR::get_display_by_name(&wayvr.state.displays, display_name) {
|
||||
if let Some(display) = WayVR::get_display_by_name(&wayvr.data.state.displays, display_name) {
|
||||
if let Some(overlay_id) = wayvr.display_handle_map.get(&display) {
|
||||
if let Some(overlay) = overlays.mut_by_id(*overlay_id) {
|
||||
overlay.state.want_visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
wayvr.state.set_display_visible(display, true);
|
||||
wayvr.data.state.set_display_visible(display, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,7 +662,7 @@ fn action_app_click<O>(
|
||||
overlays: &mut OverlayContainer<O>,
|
||||
catalog_name: &Arc<str>,
|
||||
app_name: &Arc<str>,
|
||||
) -> anyhow::Result<Option<OverlayData<O>>>
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
@@ -481,46 +681,40 @@ where
|
||||
if let Some(app_entry) = catalog.get_app(app_name) {
|
||||
let mut wayvr = wayvr.borrow_mut();
|
||||
|
||||
let (disp_handle, created_overlay) =
|
||||
get_or_create_display::<O>(app, &mut wayvr, &app_entry.target_display)?;
|
||||
let disp_handle =
|
||||
get_or_create_display_by_name(app, &mut wayvr, &app_entry.target_display)?;
|
||||
|
||||
// Parse additional args
|
||||
let args_vec: Vec<&str> = if let Some(args) = &app_entry.args {
|
||||
args.as_str().split_whitespace().collect()
|
||||
} else {
|
||||
vec![]
|
||||
let args_vec = match &app_entry.args {
|
||||
Some(args) => gen_args_vec(args),
|
||||
None => vec![],
|
||||
};
|
||||
|
||||
// Parse additional env
|
||||
let env_vec: Vec<(&str, &str)> = if let Some(env) = &app_entry.env {
|
||||
// splits "FOO=BAR=TEST,123" into (&"foo", &"bar=test,123")
|
||||
env.iter()
|
||||
.filter_map(|e| e.as_str().split_once('='))
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
let env_vec = match &app_entry.env {
|
||||
Some(env) => gen_env_vec(env),
|
||||
None => vec![],
|
||||
};
|
||||
|
||||
// Terminate existing process if required
|
||||
if let Some(process_handle) =
|
||||
wayvr
|
||||
.data
|
||||
.state
|
||||
.process_query(disp_handle, &app_entry.exec, &args_vec, &env_vec)
|
||||
{
|
||||
// Terminate process
|
||||
wayvr.state.terminate_process(process_handle);
|
||||
wayvr.data.terminate_process(process_handle);
|
||||
} else {
|
||||
// Spawn process
|
||||
wayvr
|
||||
.data
|
||||
.state
|
||||
.spawn_process(disp_handle, &app_entry.exec, &args_vec, &env_vec)?;
|
||||
|
||||
show_display(&mut wayvr, overlays, &app_entry.target_display.as_str());
|
||||
show_display::<O>(&mut wayvr, overlays, app_entry.target_display.as_str());
|
||||
}
|
||||
Ok(created_overlay)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn action_display_click<O>(
|
||||
@@ -535,23 +729,31 @@ where
|
||||
let wayvr = app.get_wayvr()?;
|
||||
let mut wayvr = wayvr.borrow_mut();
|
||||
|
||||
if let Some(handle) = WayVR::get_display_by_name(&wayvr.state.displays, display_name) {
|
||||
if let Some(display) = wayvr.state.displays.get_mut(&handle) {
|
||||
if let Some(overlay_id) = display.overlay_id {
|
||||
if let Some(overlay) = overlays.mut_by_id(overlay_id) {
|
||||
match action {
|
||||
WayVRDisplayClickAction::ToggleVisibility => {
|
||||
// Toggle visibility
|
||||
overlay.state.want_visible = !overlay.state.want_visible;
|
||||
}
|
||||
WayVRDisplayClickAction::Reset => {
|
||||
// Show it at the front
|
||||
overlay.state.want_visible = true;
|
||||
overlay.state.reset(app, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let Some(handle) = WayVR::get_display_by_name(&wayvr.data.state.displays, display_name) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let Some(display) = wayvr.data.state.displays.get_mut(&handle) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let Some(overlay_id) = display.overlay_id else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let Some(overlay) = overlays.mut_by_id(overlay_id) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
match action {
|
||||
WayVRDisplayClickAction::ToggleVisibility => {
|
||||
// Toggle visibility
|
||||
overlay.state.want_visible = !overlay.state.want_visible;
|
||||
}
|
||||
WayVRDisplayClickAction::Reset => {
|
||||
// Show it at the front
|
||||
overlay.state.want_visible = true;
|
||||
overlay.state.reset(app, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,17 +769,10 @@ where
|
||||
catalog_name,
|
||||
app_name,
|
||||
} => {
|
||||
match action_app_click(app, overlays, catalog_name, app_name) {
|
||||
Ok(res) => {
|
||||
if let Some(created_overlay) = res {
|
||||
overlays.add(created_overlay);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
// Happens if something went wrong with initialization
|
||||
// or input exec path is invalid. Do nothing, just print an error
|
||||
log::error!("action_app_click failed: {}", e);
|
||||
}
|
||||
if let Err(e) = action_app_click(app, overlays, catalog_name, app_name) {
|
||||
// Happens if something went wrong with initialization
|
||||
// or input exec path is invalid. Do nothing, just print an error
|
||||
log::error!("action_app_click failed: {}", e);
|
||||
}
|
||||
}
|
||||
WayVRAction::DisplayClick {
|
||||
@@ -588,5 +783,13 @@ where
|
||||
log::error!("action_display_click failed: {}", e);
|
||||
}
|
||||
}
|
||||
WayVRAction::ToggleDashboard => {
|
||||
let wayvr = app.get_wayvr().unwrap(); /* safe */
|
||||
let mut wayvr = wayvr.borrow_mut();
|
||||
|
||||
if let Err(e) = toggle_dashboard::<O>(app, overlays, &mut wayvr) {
|
||||
log::error!("toggle_dashboard failed: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
|
||||
version: 1
|
||||
|
||||
# Set to true if you want to make Wyland server instantly available
|
||||
# (used for remote starting external processes)
|
||||
# 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?
|
||||
@@ -22,6 +23,17 @@ keyboard_repeat_delay: 200
|
||||
# Chars per second
|
||||
keyboard_repeat_rate: 50
|
||||
|
||||
# WayVR-compatible dashboard.
|
||||
# For now, there is only one kind of dashboard with WayVR IPC support (WayVR Dashboard).
|
||||
# Build instructions: https://github.com/olekolek1000/wayvr-dashboard
|
||||
# exec: Executable path, for example /home/USER/wayvr_dashboard/src-tauri/target/release/wayvr_dashboard
|
||||
# GDK_BACKEND=wayland: Force-use Wayland for GTK apps
|
||||
# LIBGL_ALWAYS_SOFTWARE: Mesa crash mitigation for Tauri apps on AMD GPUs
|
||||
dashboard:
|
||||
exec: "wayvr_dashboard"
|
||||
args: ""
|
||||
env: ["GDK_BACKEND=wayland", "LIBGL_ALWAYS_SOFTWARE=1"]
|
||||
|
||||
displays:
|
||||
Watch:
|
||||
width: 400
|
||||
|
||||
@@ -10,7 +10,7 @@ use vulkano::image::view::ImageView;
|
||||
#[cfg(feature = "wayvr")]
|
||||
use {
|
||||
crate::config_wayvr::{self, WayVRConfig},
|
||||
crate::overlays::wayvr::WayVRState,
|
||||
crate::overlays::wayvr::WayVRData,
|
||||
std::{cell::RefCell, rc::Rc},
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ pub struct AppState {
|
||||
pub keyboard_focus: KeyboardFocus,
|
||||
|
||||
#[cfg(feature = "wayvr")]
|
||||
pub wayvr: Option<Rc<RefCell<WayVRState>>>, // Dynamically created if requested
|
||||
pub wayvr: Option<Rc<RefCell<WayVRData>>>, // Dynamically created if requested
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
@@ -122,11 +122,11 @@ impl AppState {
|
||||
|
||||
#[cfg(feature = "wayvr")]
|
||||
#[allow(dead_code)]
|
||||
pub fn get_wayvr(&mut self) -> anyhow::Result<Rc<RefCell<WayVRState>>> {
|
||||
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(WayVRState::new(
|
||||
let wayvr = Rc::new(RefCell::new(WayVRData::new(
|
||||
WayVRConfig::get_wayvr_config(&self.session.config, &self.session.wayvr_config),
|
||||
)?));
|
||||
self.wayvr = Some(wayvr.clone());
|
||||
|
||||
Reference in New Issue
Block a user