From d31b3ca6c011c34d0250f037bab9378397eb3d02 Mon Sep 17 00:00:00 2001 From: galister <22305755+galister@users.noreply.github.com> Date: Tue, 20 Feb 2024 20:54:06 +0100 Subject: [PATCH] omg big commit --- Cargo.lock | 110 +++++++++++++-------------- src/backend/common.rs | 27 ++++++- src/backend/openvr/lines.rs | 4 - src/backend/openvr/mod.rs | 26 ++++++- src/backend/openvr/overlay.rs | 9 +++ src/backend/openxr/mod.rs | 19 +++++ src/backend/overlay.rs | 28 ++++--- src/gui/mod.rs | 4 - src/main.rs | 18 ++++- src/overlays/keyboard.rs | 1 - src/overlays/mirror.rs | 135 ++++++++++++++++++++++++++++++++++ src/overlays/mod.rs | 2 + src/overlays/screen.rs | 58 +++++++-------- src/overlays/watch.rs | 91 +++++++++++++++++++++-- src/res/watch.yaml | 32 ++++++-- 15 files changed, 441 insertions(+), 123 deletions(-) create mode 100644 src/overlays/mirror.rs diff --git a/Cargo.lock b/Cargo.lock index ef94f79..702f506 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,9 +26,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" +checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" dependencies = [ "cfg-if", "getrandom", @@ -350,7 +350,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -385,7 +385,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -443,7 +443,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.49", + "syn 2.0.50", "which", ] @@ -456,7 +456,7 @@ dependencies = [ "autocxx-engine", "env_logger", "indexmap 1.9.3", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -483,7 +483,7 @@ dependencies = [ "rustversion", "serde_json", "strum_macros 0.24.3", - "syn 2.0.49", + "syn 2.0.50", "tempfile", "thiserror", "version_check", @@ -499,7 +499,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -516,7 +516,7 @@ dependencies = [ "quote", "serde", "serde_json", - "syn 2.0.49", + "syn 2.0.50", "thiserror", ] @@ -537,7 +537,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -557,7 +557,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -645,7 +645,7 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -1051,9 +1051,9 @@ checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" [[package]] name = "cxx" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aff472b83efd22bfc0176aa8ba34617dd5c17364670eb201a5f06d339b8abf7" +checksum = "0c15f3b597018782655a05d417f28bac009f6eb60f4b6703eb818998c1aaa16a" dependencies = [ "cc", "cxxbridge-flags", @@ -1063,31 +1063,31 @@ dependencies = [ [[package]] name = "cxx-gen" -version = "0.7.116" +version = "0.7.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2a39f7064dacffa9bf2d33d8dcc7777b60c789a95e1cd62d5fbeb1161428f2" +checksum = "54b629c0d006c7e44c1444dd17d18a458c9390d32276b758ac7abd21a75c99b0" dependencies = [ "codespan-reporting", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] name = "cxxbridge-flags" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589e83d02fc1d4fb78f5ad56ca08835341e23499d086d2821315869426d618dc" +checksum = "7a7eb4c4fd18505f5a935f9c2ee77780350dcdb56da7cd037634e806141c5c43" [[package]] name = "cxxbridge-macro" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2cb1fd8ffae4230c7cfbbaf3698dbeaf750fa8c5dadf7ed897df581b9b572a5" +checksum = "5d914fcc6452d133236ee067a9538be25ba6a644a450e1a6c617da84bf029854" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -1110,7 +1110,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -1121,7 +1121,7 @@ checksum = "c5a91391accf613803c2a9bf9abccdbaa07c54b4244a5b64883f9c3c137c86be" dependencies = [ "darling_core", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -1215,7 +1215,7 @@ checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -1236,7 +1236,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -1419,7 +1419,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -1542,7 +1542,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -2159,7 +2159,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -2375,7 +2375,7 @@ dependencies = [ "proc-macro-crate 2.0.2", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -2482,7 +2482,7 @@ dependencies = [ [[package]] name = "ovr_overlay" version = "0.0.0" -source = "git+https://github.com/galister/ovr_overlay_oyasumi#e719339c017f36f090cb2ba87240580e9dff3b15" +source = "git+https://github.com/galister/ovr_overlay_oyasumi#c9794ab4439242fbaa908a2c625ed47fcf173508" dependencies = [ "byteorder", "derive_more", @@ -2497,7 +2497,7 @@ dependencies = [ [[package]] name = "ovr_overlay_sys" version = "0.0.0" -source = "git+https://github.com/galister/ovr_overlay_oyasumi#e719339c017f36f090cb2ba87240580e9dff3b15" +source = "git+https://github.com/galister/ovr_overlay_oyasumi#c9794ab4439242fbaa908a2c625ed47fcf173508" dependencies = [ "autocxx", "autocxx-build", @@ -2715,7 +2715,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -3042,29 +3042,29 @@ checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] name = "serde_json" -version = "1.0.113" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -3079,7 +3079,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -3305,7 +3305,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -3321,9 +3321,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.49" +version = "2.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" dependencies = [ "proc-macro2", "quote", @@ -3387,7 +3387,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -3504,7 +3504,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -3553,9 +3553,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -3654,7 +3654,7 @@ dependencies = [ "proc-macro-crate 2.0.2", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -3667,7 +3667,7 @@ dependencies = [ "proc-macro2", "quote", "shaderc", - "syn 2.0.49", + "syn 2.0.50", "vulkano", ] @@ -3714,7 +3714,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", "wasm-bindgen-shared", ] @@ -3748,7 +3748,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4292,8 +4292,8 @@ dependencies = [ [[package]] name = "wlx-capture" -version = "0.1.1" -source = "git+https://github.com/galister/wlx-capture#06a6ff48464fdda0f563a3b5858fcb69ea0f5980" +version = "0.2.0" +source = "git+https://github.com/galister/wlx-capture#b715303245197ea3bb7b8b5e348f5c26299e3c8a" dependencies = [ "ashpd", "drm-fourcc", @@ -4554,7 +4554,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] diff --git a/src/backend/common.rs b/src/backend/common.rs index 7f4dbd2..0d7091e 100644 --- a/src/backend/common.rs +++ b/src/backend/common.rs @@ -20,7 +20,7 @@ use crate::{ state::AppState, }; -use super::overlay::{OverlayData, OverlayState}; +use super::overlay::{OverlayBackend, OverlayData, OverlayState}; #[derive(Error, Debug)] pub enum BackendError { @@ -91,6 +91,22 @@ where } } + pub fn drop_by_selector(&mut self, selector: &OverlaySelector) { + match selector { + OverlaySelector::Id(id) => { + self.overlays.remove(id); + } + OverlaySelector::Name(name) => { + let id = self + .overlays + .iter() + .find(|(_, o)| *o.state.name == **name) + .map(|(id, _)| *id); + id.and_then(|id| self.overlays.remove(&id)); + } + }; + } + pub fn get_by_id<'a>(&'a mut self, id: usize) -> Option<&'a OverlayData> { self.overlays.get(&id) } @@ -115,6 +131,10 @@ where self.overlays.values_mut() } + pub fn add(&mut self, overlay: OverlayData) { + self.overlays.insert(overlay.state.id, overlay); + } + pub fn show_hide(&mut self, app: &mut AppState) { let any_shown = self .overlays @@ -170,6 +190,11 @@ pub enum TaskType { OverlaySelector, Box, ), + CreateOverlay( + OverlaySelector, + Box Option<(OverlayState, Box)> + Send>, + ), + DropOverlay(OverlaySelector), Toast(Toast), } diff --git a/src/backend/openvr/lines.rs b/src/backend/openvr/lines.rs index 2158cc1..a6e3193 100644 --- a/src/backend/openvr/lines.rs +++ b/src/backend/openvr/lines.rs @@ -64,7 +64,6 @@ impl LinePool { state: OverlayState { name: Arc::from(format!("wlx-line{}", id)), show_hide: true, - size: (0, 0), ..Default::default() }, backend: Box::new(SplitOverlayBackend { @@ -181,7 +180,4 @@ impl OverlayRenderer for StaticRenderer { fn view(&mut self) -> Option> { Some(self.view.clone()) } - fn extent(&self) -> [u32; 3] { - self.view.image().extent().clone() - } } diff --git a/src/backend/openvr/mod.rs b/src/backend/openvr/mod.rs index 912e304..be33882 100644 --- a/src/backend/openvr/mod.rs +++ b/src/backend/openvr/mod.rs @@ -27,6 +27,7 @@ use crate::{ manifest::{install_manifest, uninstall_manifest}, overlay::OpenVrOverlayData, }, + overlay::OverlayData, }, graphics::WlxGraphics, overlays::watch::{watch_fade, WATCH_NAME}, @@ -157,6 +158,27 @@ pub fn openvr_run(running: Arc) -> Result<(), BackendError> { f(&mut state, &mut o.state); } } + TaskType::CreateOverlay(sel, f) => { + let None = overlays.mut_by_selector(&sel) else { + continue; + }; + + let Some((state, backend)) = f(&mut state) else { + continue; + }; + + overlays.add(OverlayData { + state, + backend, + ..Default::default() + }); + } + TaskType::DropOverlay(sel) => { + if let Some(o) = overlays.mut_by_selector(&sel) { + o.destroy(&mut overlay_mngr); + overlays.drop_by_selector(&sel); + } + } TaskType::Toast(t) => { // TODO toasts log::info!("Toast: {} {}", t.title, t.body); @@ -209,7 +231,7 @@ pub fn openvr_run(running: Arc) -> Result<(), BackendError> { let _ = sender.send_params(&overlays); }; - log::debug!("Rendering frame"); + log::trace!("Rendering frame"); for o in overlays.iter_mut() { if o.state.want_visible { @@ -217,7 +239,7 @@ pub fn openvr_run(running: Arc) -> Result<(), BackendError> { } } - log::debug!("Rendering overlays"); + log::trace!("Rendering overlays"); overlays .iter_mut() diff --git a/src/backend/openvr/overlay.rs b/src/backend/openvr/overlay.rs index d4577a4..0d812e3 100644 --- a/src/backend/openvr/overlay.rs +++ b/src/backend/openvr/overlay.rs @@ -254,4 +254,13 @@ impl OverlayData { log::error!("{}: Failed to set overlay texture: {}", self.state.name, e); } } + + pub(super) fn destroy(&mut self, overlay: &mut OverlayManager) { + if let Some(handle) = self.data.handle { + log::debug!("{}: destroy", self.state.name); + if let Err(e) = overlay.destroy_overlay(handle) { + log::error!("{}: Failed to destroy overlay: {}", self.state.name, e); + } + } + } } diff --git a/src/backend/openxr/mod.rs b/src/backend/openxr/mod.rs index a615c33..a4418e2 100644 --- a/src/backend/openxr/mod.rs +++ b/src/backend/openxr/mod.rs @@ -16,6 +16,7 @@ use crate::{ common::{OverlayContainer, TaskType}, input::interact, openxr::{input::DoubleClickCounter, lines::LinePool, overlay::OpenXrOverlayData}, + overlay::OverlayData, }, graphics::WlxGraphics, overlays::watch::{watch_fade, WATCH_NAME}, @@ -187,6 +188,24 @@ pub fn openxr_run(running: Arc) -> Result<(), BackendError> { f(&mut app_state, &mut o.state); } } + TaskType::CreateOverlay(sel, f) => { + let None = overlays.mut_by_selector(&sel) else { + continue; + }; + + let Some((state, backend)) = f(&mut app_state) else { + continue; + }; + + overlays.add(OverlayData { + state, + backend, + ..Default::default() + }); + } + TaskType::DropOverlay(sel) => { + overlays.drop_by_selector(&sel); + } TaskType::Toast(t) => { // TODO toasts log::info!("Toast: {} {}", t.title, t.body); diff --git a/src/backend/overlay.rs b/src/backend/overlay.rs index be74b68..d431045 100644 --- a/src/backend/overlay.rs +++ b/src/backend/overlay.rs @@ -7,7 +7,7 @@ use std::{ }; use anyhow::Ok; -use glam::{Affine2, Affine3A, Mat3A, Quat, Vec3, Vec3A}; +use glam::{Affine2, Affine3A, Mat3A, Quat, Vec2, Vec3, Vec3A}; use vulkano::image::view::ImageView; use crate::state::AppState; @@ -21,7 +21,6 @@ pub trait OverlayBackend: OverlayRenderer + InteractionHandler {} pub struct OverlayState { pub id: usize, pub name: Arc, - pub size: (i32, i32), pub want_visible: bool, pub show_hide: bool, pub grabbable: bool, @@ -44,7 +43,6 @@ impl Default for OverlayState { OverlayState { id: AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed), name: Arc::from(""), - size: (0, 0), want_visible: false, show_hide: false, grabbable: false, @@ -208,7 +206,6 @@ pub trait OverlayRenderer { fn resume(&mut self, app: &mut AppState) -> anyhow::Result<()>; fn render(&mut self, app: &mut AppState) -> anyhow::Result<()>; fn view(&mut self) -> Option>; - fn extent(&self) -> [u32; 3]; } pub struct FallbackRenderer; @@ -229,9 +226,6 @@ impl OverlayRenderer for FallbackRenderer { fn view(&mut self) -> Option> { None } - fn extent(&self) -> [u32; 3] { - [0, 0, 0] - } } // Boilerplate and dummies @@ -274,9 +268,6 @@ impl OverlayRenderer for SplitOverlayBackend { fn view(&mut self) -> Option> { self.renderer.view() } - fn extent(&self) -> [u32; 3] { - self.renderer.extent() - } } impl InteractionHandler for SplitOverlayBackend { fn on_left(&mut self, app: &mut AppState, pointer: usize) { @@ -292,3 +283,20 @@ impl InteractionHandler for SplitOverlayBackend { self.interaction.on_pointer(app, hit, pressed); } } + +pub fn ui_transform(extent: &[u32; 2]) -> Affine2 { + let center = Vec2 { x: 0.5, y: 0.5 }; + if extent[1] > extent[0] { + Affine2::from_cols( + Vec2::X * (extent[1] as f32 / extent[0] as f32), + Vec2::NEG_Y, + center, + ) + } else { + Affine2::from_cols( + Vec2::X, + Vec2::NEG_Y * (extent[0] as f32 / extent[1] as f32), + center, + ) + } +} diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 4a0ae65..162a508 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -506,10 +506,6 @@ impl OverlayRenderer for Canvas { fn view(&mut self) -> Option> { Some(self.view_final.clone()) } - - fn extent(&self) -> [u32; 3] { - self.view_final.image().extent().clone() - } } impl OverlayBackend for Canvas {} diff --git a/src/main.rs b/src/main.rs index d056c3a..15c7b17 100644 --- a/src/main.rs +++ b/src/main.rs @@ -59,16 +59,26 @@ fn auto_run(running: Arc) { #[cfg(feature = "openxr")] { use crate::backend::openxr::openxr_run; - let Err(BackendError::NotSupported) = openxr_run(running.clone()) else { - return; + match openxr_run(running.clone()) { + Ok(()) => return, + Err(BackendError::NotSupported) => (), + Err(e) => { + log::error!("{}", e); + return; + } }; } #[cfg(feature = "openvr")] { use crate::backend::openvr::openvr_run; - let Err(BackendError::NotSupported) = openvr_run(running) else { - return; + match openvr_run(running.clone()) { + Ok(()) => return, + Err(BackendError::NotSupported) => (), + Err(e) => { + log::error!("{}", e); + return; + } }; } diff --git a/src/overlays/keyboard.rs b/src/overlays/keyboard.rs index 488e000..79ae6ee 100644 --- a/src/overlays/keyboard.rs +++ b/src/overlays/keyboard.rs @@ -121,7 +121,6 @@ where Ok(OverlayData { state: OverlayState { name: KEYBOARD_NAME.into(), - size: (size.x as _, size.y as _), grabbable: true, recenter: true, interactable: true, diff --git a/src/overlays/mirror.rs b/src/overlays/mirror.rs new file mode 100644 index 0000000..a3f50ef --- /dev/null +++ b/src/overlays/mirror.rs @@ -0,0 +1,135 @@ +use std::{sync::Arc, thread::JoinHandle}; + +use futures::executor; +use glam::vec3a; +use wlx_capture::pipewire::{pipewire_select_screen, PipewireCapture, PipewireSelectScreenResult}; + +use crate::{ + backend::{ + common::{OverlaySelector, TaskType}, + overlay::{ + ui_transform, OverlayBackend, OverlayRenderer, OverlayState, SplitOverlayBackend, + }, + }, + state::{AppSession, AppState}, +}; + +use super::screen::ScreenRenderer; + +pub struct MirrorRenderer { + name: Arc, + renderer: Option, + selector: Option>>, + last_extent: [u32; 3], +} +impl MirrorRenderer { + pub fn new(name: Arc) -> Self { + Self { + name, + renderer: None, + selector: Some(std::thread::spawn(|| { + executor::block_on(pipewire_select_screen(None, false, false, false)).ok() + })), + last_extent: [0; 3], + } + } +} + +impl OverlayRenderer for MirrorRenderer { + fn init(&mut self, _app: &mut AppState) -> anyhow::Result<()> { + Ok(()) + } + fn render(&mut self, app: &mut AppState) -> anyhow::Result<()> { + if let Some(selector) = self.selector.take() { + if !selector.is_finished() { + self.selector = Some(selector); + return Ok(()); + } + + // safe unwrap because we know it's finished + if let Some(pw_result) = selector.join().unwrap() { + log::info!( + "{}: PipeWire node selected: {}", + self.name.clone(), + pw_result.node_id + ); + let capture = PipewireCapture::new(self.name.clone(), pw_result.node_id, 60); + self.renderer = Some(ScreenRenderer::new_raw( + self.name.clone(), + Box::new(capture), + )); + app.tasks.enqueue(TaskType::Overlay( + OverlaySelector::Name(self.name.clone()), + Box::new(|app, o| { + o.grabbable = true; + o.interactable = true; + o.reset(app, false); + }), + )); + } else { + log::warn!("Failed to create pipewire mirror"); + self.renderer = None; + // drop self + app.tasks + .enqueue(TaskType::DropOverlay(OverlaySelector::Name( + self.name.clone(), + ))); + } + } + + if let Some(renderer) = self.renderer.as_mut() { + renderer.render(app)?; + if let Some(view) = renderer.view() { + let extent = view.image().extent(); + if self.last_extent != extent { + self.last_extent = extent.clone(); + // resized + app.tasks.enqueue(TaskType::Overlay( + OverlaySelector::Name(self.name.clone()), + Box::new(move |_app, o| { + o.interaction_transform = ui_transform(&[extent[0], extent[1]]); + }), + )); + } + } + } + + Ok(()) + } + fn pause(&mut self, app: &mut AppState) -> anyhow::Result<()> { + if let Some(renderer) = self.renderer.as_mut() { + renderer.pause(app)?; + } + Ok(()) + } + fn resume(&mut self, app: &mut AppState) -> anyhow::Result<()> { + if let Some(renderer) = self.renderer.as_mut() { + renderer.resume(app)?; + } + Ok(()) + } + fn view(&mut self) -> Option> { + self.renderer.as_mut().and_then(|r| r.view()) + } +} + +pub fn new_mirror( + name: Arc, + show_hide: bool, + session: &AppSession, +) -> Option<(OverlayState, Box)> { + let state = OverlayState { + name: name.clone(), + show_hide, + want_visible: true, + spawn_scale: 0.5 * session.config.desktop_view_scale, + spawn_point: vec3a(0., 0.5, -0.5), + ..Default::default() + }; + let backend = Box::new(SplitOverlayBackend { + renderer: Box::new(MirrorRenderer::new(name)), + ..Default::default() + }); + + Some((state, backend)) +} diff --git a/src/overlays/mod.rs b/src/overlays/mod.rs index e246d7b..4d89225 100644 --- a/src/overlays/mod.rs +++ b/src/overlays/mod.rs @@ -1,4 +1,6 @@ pub mod keyboard; +#[cfg(feature = "wayland")] +pub mod mirror; pub mod screen; pub mod toast; pub mod watch; diff --git a/src/overlays/screen.rs b/src/overlays/screen.rs index 27e2744..d9821b9 100644 --- a/src/overlays/screen.rs +++ b/src/overlays/screen.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use std::{ ops::Add, ptr, - sync::{mpsc::Receiver, Arc}, + sync::Arc, time::{Duration, Instant}, }; use vulkano::{ @@ -237,12 +237,22 @@ pub struct ScreenRenderer { name: Arc, capture: Box, pipeline: Option, - receiver: Option>, last_view: Option>, extent: [u32; 3], } impl ScreenRenderer { + #[cfg(feature = "wayland")] + pub fn new_raw(name: Arc, capture: Box) -> ScreenRenderer { + ScreenRenderer { + name, + capture, + pipeline: None, + last_view: None, + extent: [0; 3], + } + } + #[cfg(feature = "wayland")] pub fn new_wlr(output: &WlxOutput) -> Option { let client = WlxClient::new()?; @@ -252,7 +262,6 @@ impl ScreenRenderer { name: output.name.clone(), capture: Box::new(capture), pipeline: None, - receiver: None, last_view: None, extent: extent_from_res(output.size), }) @@ -268,7 +277,7 @@ impl ScreenRenderer { )> { let name = output.name.clone(); let select_screen_result = - futures::executor::block_on(pipewire_select_screen(token)).ok()?; + futures::executor::block_on(pipewire_select_screen(token, true, true, true)).ok()?; let capture = PipewireCapture::new(name, select_screen_result.node_id, 60); @@ -277,7 +286,6 @@ impl ScreenRenderer { name: output.name.clone(), capture: Box::new(capture), pipeline: None, - receiver: None, last_view: None, extent: extent_from_res(output.size), }, @@ -293,7 +301,6 @@ impl ScreenRenderer { name: screen.name.clone(), capture: Box::new(capture), pipeline: None, - receiver: None, last_view: None, extent: extent_from_res((screen.monitor.width(), screen.monitor.height())), } @@ -305,7 +312,7 @@ impl OverlayRenderer for ScreenRenderer { Ok(()) } fn render(&mut self, app: &mut AppState) -> anyhow::Result<()> { - let receiver = self.receiver.get_or_insert_with(|| { + if !self.capture.ready() { let allow_dmabuf = &*app.session.config.capture_method != "pw_fallback"; let drm_formats = DRM_FORMATS.get_or_init({ @@ -349,14 +356,11 @@ impl OverlayRenderer for ScreenRenderer { } }); - let rx = self.capture.init(&drm_formats); + self.capture.init(&drm_formats); self.capture.request_new_frame(); - rx - }); + }; - let mut mouse = None; - - for frame in receiver.try_iter() { + for frame in self.capture.receive().into_iter() { match frame { WlxFrame::Dmabuf(frame) => { if !frame.is_valid() { @@ -425,29 +429,29 @@ impl OverlayRenderer for ScreenRenderer { upload.texture2d(frame.format.width, frame.format.height, format, &data)?; let mut pipeline = None; - if mouse.is_some() { - let new_pipeline = self.pipeline.get_or_insert_with(|| { - let mut pipeline = ScreenPipeline::new(&self.extent, app).unwrap(); // TODO - self.last_view = Some(pipeline.view.clone()); - pipeline.ensure_mouse_initialized(&mut upload).unwrap(); // TODO - pipeline + if frame.mouse.is_some() { + pipeline = Some(match self.pipeline { + Some(ref mut p) => p, + _ => { + let mut pipeline = ScreenPipeline::new(&self.extent, app)?; + self.last_view = Some(pipeline.view.clone()); + pipeline.ensure_mouse_initialized(&mut upload)?; + self.pipeline = Some(pipeline); + self.pipeline.as_mut().unwrap() // safe + } }); - pipeline = Some(new_pipeline); } upload.build_and_execute_now()?; if let Some(pipeline) = pipeline { - pipeline.render(image, mouse.as_ref(), app)?; + pipeline.render(image, frame.mouse.as_ref(), app)?; } else { let view = ImageView::new_default(image)?; self.last_view = Some(view); } self.capture.request_new_frame(); } - WlxFrame::Mouse(m) => { - mouse = Some(m); - } }; } Ok(()) @@ -463,9 +467,6 @@ impl OverlayRenderer for ScreenRenderer { fn view(&mut self) -> Option> { self.last_view.clone() } - fn extent(&self) -> [u32; 3] { - self.extent.clone() - } } #[cfg(feature = "wayland")] @@ -489,7 +490,6 @@ where output.logical_pos, ); - let size = (output.size.0, output.size.1); let mut capture: Option = None; if &*session.config.capture_method == "auto" && wl.maybe_wlr_dmabuf_mgr.is_some() { @@ -576,7 +576,6 @@ where Some(OverlayData { state: OverlayState { name: output.name.clone(), - size, show_hide: session .config .show_screens @@ -741,7 +740,6 @@ where OverlayData { state: OverlayState { name: s.name.clone(), - size, show_hide: session .config .show_screens diff --git a/src/overlays/watch.rs b/src/overlays/watch.rs index a02d48a..31f6470 100644 --- a/src/overlays/watch.rs +++ b/src/overlays/watch.rs @@ -8,14 +8,14 @@ use std::{ use chrono::Local; use chrono_tz::Tz; -use glam::{vec2, Affine2, Quat, Vec3, Vec3A}; +use glam::{Quat, Vec3, Vec3A}; use serde::Deserialize; use crate::{ backend::{ common::{OverlaySelector, TaskType}, input::PointerMode, - overlay::{OverlayData, OverlayState, RelativeTo}, + overlay::{ui_transform, OverlayData, OverlayState, RelativeTo}, }, config::load_watch, gui::{color_parse, CanvasBuilder, Control}, @@ -270,24 +270,37 @@ where }; } } + #[cfg(feature = "wayland")] + WatchElement::MirrorButton { + rect: [x, y, w, h], + font_size, + bg_color, + fg_color, + text, + name, + show_hide, + } => { + canvas.bg_color = color_parse(&bg_color).unwrap_or(FALLBACK_COLOR); + canvas.fg_color = color_parse(&fg_color).unwrap_or(FALLBACK_COLOR); + canvas.font_size = font_size; + let button = canvas.button(x, y, w, h, text.clone()); + button.state = Some(ElemState::Mirror { name, show_hide }); + button.on_press = Some(btn_mirror_dn::); + } } } - let interaction_transform = - Affine2::from_translation(vec2(0.5, 0.5)) * Affine2::from_scale(vec2(1., -2.0)); - let relative_to = RelativeTo::Hand(state.session.watch_hand); Ok(OverlayData { state: OverlayState { name: WATCH_NAME.into(), - size: (400, 200), want_visible: true, interactable: true, spawn_scale: WATCH_SCALE * state.session.config.watch_scale, spawn_point: state.session.watch_pos.into(), spawn_rotation: state.session.watch_rot, - interaction_transform, + interaction_transform: ui_transform(&config.watch_size), relative_to, ..Default::default() }, @@ -328,6 +341,60 @@ enum ElemState { func_right: Option, func_middle: Option, }, + #[cfg(feature = "wayland")] + Mirror { name: Arc, show_hide: bool }, +} + +#[cfg(feature = "wayland")] +fn btn_mirror_dn( + control: &mut Control<(), ElemState>, + _: &mut (), + app: &mut AppState, + mode: PointerMode, +) where + O: Default, +{ + let ElemState::Mirror { name, show_hide } = control.state.as_ref().unwrap() + // want panic + else { + log::error!("Mirror state not found"); + return; + }; + + let selector = OverlaySelector::Name(name.clone()); + + match mode { + PointerMode::Left => { + app.tasks.enqueue(TaskType::Overlay( + selector.clone(), + Box::new(|_app, o| { + o.want_visible = !o.want_visible; + }), + )); + + app.tasks.enqueue(TaskType::CreateOverlay( + selector, + Box::new({ + let name = name.clone(); + let show_hide = *show_hide; + move |app| super::mirror::new_mirror(name.clone(), show_hide, &app.session) + }), + )); + } + PointerMode::Right => { + app.tasks.enqueue(TaskType::Overlay( + selector, + Box::new(|_app, o| { + o.grabbable = !o.grabbable; + o.interactable = o.grabbable; + }), + )); + } + PointerMode::Middle => { + app.tasks.enqueue(TaskType::DropOverlay(selector)); + } + _ => {} + } } fn btn_func_dn( @@ -773,6 +840,16 @@ enum WatchElement { func_middle: Option, text: Arc, }, + #[cfg(feature = "wayland")] + MirrorButton { + rect: [f32; 4], + font_size: isize, + bg_color: Arc, + fg_color: Arc, + name: Arc, + text: Arc, + show_hide: bool, + }, } #[derive(Deserialize)] diff --git a/src/res/watch.yaml b/src/res/watch.yaml index e33dad8..2e3bd53 100644 --- a/src/res/watch.yaml +++ b/src/res/watch.yaml @@ -132,14 +132,36 @@ watch_elements: # exec: ["echo", "customize me! see watch.yaml"] # interval: 0 # seconds - # volume buttons - - type: ExecButton + ### MirrorButton + # Bring an additional PipeWire screen, window, region or virtual screen share into VR. + # These are view-only, and will not respond to pointers by moving your mouse. + # You may have as many as you like, but the `name` must be unique for each. + # Controls: + # - Blue Click: Show/hide. Shows pipewire prompt on first show. + # - Orange Click: Toggle lock in place + # - Purple Click: Stop capture. After doing this, you may Blue-click again to select a different source. + # Warning: + # - Window shares may stop updating if the window goes off-screen or is on an inactive workspace + # - Resizing, minimizing, maximizing windows may break stuff. Complain to your xdg-desktop-portal implementation. + # - Selections are not saved across sessions + + - type: MirrorButton rect: [327, 52, 46, 32] font_size: 14 fg_color: "#FFFFFF" - bg_color: "#505050" - text: "+" - exec: [ "pactl", "set-sink-volume", "@DEFAULT_SINK@", "+5%" ] + bg_color: "#B05050" + name: "M1" + text: "M" + show_hide: false # should it respond to show/hide binding? + + # volume buttons + # - type: ExecButton + #rect: [327, 52, 46, 32] + #font_size: 14 + #fg_color: "#FFFFFF" + #bg_color: "#505050" + # text: "+" + # exec: [ "pactl", "set-sink-volume", "@DEFAULT_SINK@", "+5%" ] - type: ExecButton rect: [327, 116, 46, 32] font_size: 14