From 96db24d02e54e6dceef80d293ee3a22f13755b75 Mon Sep 17 00:00:00 2001 From: Aleksander Date: Sun, 3 Nov 2024 16:26:37 +0100 Subject: [PATCH] WayVR: Modify readme, Various improvements - Add "Quick setup" and images in readme - Implement `click_freeze_time_ms` - Automatically show hidden display if AppClick has been triggered --- contrib/wayvr/README.md | 90 ++++++++++++ contrib/wayvr/watch_wayvr_example.yaml | 195 +++++++++++++++++++++++++ src/backend/openvr/mod.rs | 2 +- src/backend/openxr/mod.rs | 2 +- src/backend/wayvr/README.md | 37 ----- src/backend/wayvr/client.rs | 2 +- src/backend/wayvr/display.rs | 21 ++- src/backend/wayvr/event_queue.rs | 34 ++--- src/backend/wayvr/handle.rs | 2 +- src/backend/wayvr/mod.rs | 23 ++- src/config_wayvr.rs | 17 ++- src/overlays/keyboard.rs | 9 +- src/overlays/wayvr.rs | 184 ++++++++++++++--------- src/state.rs | 24 +-- 14 files changed, 485 insertions(+), 157 deletions(-) create mode 100644 contrib/wayvr/README.md create mode 100644 contrib/wayvr/watch_wayvr_example.yaml delete mode 100644 src/backend/wayvr/README.md diff --git a/contrib/wayvr/README.md b/contrib/wayvr/README.md new file mode 100644 index 0000000..673445d --- /dev/null +++ b/contrib/wayvr/README.md @@ -0,0 +1,90 @@ +

+ +

+ +**WayVR acts as a bridge between Wayland applications and wlx-overlay-s panels, allowing you to display your applications within a VR environment. Internally, WayVR utilizes Smithay to run a Wayland compositor.** + +# >> Quick setup << + +#### Configure your applications list + +Go to `src/res/wayvr.yaml` to configure your desired application list. This configuration file represents all currently available WayVR options. Feel free to adjust it to your liking. + +#### Add WayVR Launcher to your watch + +Copy `watch_wayvr_example.yaml` to `~/.config/wlxoverlay/watch.yaml`. This file contains pre-configured **WayVRLauncher** and **WayVRDisplayList** widget types. By default, the _default_catalog_ is used. + +That's it; you're all set! + +###### _Make sure you have `wayvr` feature enabled in Cargo.toml (enabled by default)_ + +![alt text](https://raw.githubusercontent.com/galister/wlx-overlay-s/refs/heads/guide/wayvr/watch.jpg) + +# Overview + +### Features + +- Display Wayland applications without GPU overhead (zero-copy via dma-buf) +- Mouse and keyboard input, with precision scrolling support +- Tested on AMD and Nvidia + +### Supported software + +- Basically all Qt applications (they work out of the box) +- Most XWayland applications via `cage` + +### XWayland + +WayVR does not have native XWayland support. You can run X11 applications (or these who require DISPLAY set) by wrapping them in a `cage` program, like so: + +```yaml +- name: "Xeyes" + target_display: "Disp1" + exec: "cage" + args: "xeyes -- -fg blue" +``` + +instead of: + +```yaml +- name: "Xeyes" + target_display: "Disp1" + exec: "xeyes" + args: "-fg blue" +``` + +in `wayvr.yaml` configuration file, in your desired catalog. + +### Launching external apps inside WayVR + +To launch your app externally: + +```sh +DISPLAY= WAYLAND_DISPLAY=wayland-$(cat $XDG_RUNTIME_DIR/wayvr.disp) yourapp +``` + +or (in the most cases): + +``` +DISPLAY= WAYLAND_DISPLAY=wayland-20 yourapp +``` + +Setting `DISPLAY` to an empty string forces various apps to use Wayland instead of X11. + +# Troubleshooting + +### My application doesn't launch but others do! + +Even though some applications support Wayland, some still check for the `DISPLAY` environment variable and an available X11 server, throwing an error. This can also be fixed by running `cage` on top of them. + +### Image corruption + +dma-buf textures may display various graphical glitches due to unsupported dma-buf tiling modifiers between GLES<->Vulkan on Radeon RDNA3 graphics cards. Current situation: https://gitlab.freedesktop.org/mesa/mesa/-/issues/11629). Nvidia should work out of the box, without any isues. Alternatively, you can run wlx-overlay-s with `LIBGL_ALWAYS_SOFTWARE=1` to mitigate that (only the Smithay compositor will run in software renderer mode, wlx will still be accelerated). + +### Floating windows + +Context menus are not functional in most cases yet, including drag & drop support. + +### Forced window shadows in GTK + +GNOME still insists on rendering client-side decorations instead of server-side ones. This results in all GTK applications looking odd due to additional window shadows. [Fix here, "Client-side decorations"](https://wiki.archlinux.org/title/GTK) diff --git a/contrib/wayvr/watch_wayvr_example.yaml b/contrib/wayvr/watch_wayvr_example.yaml new file mode 100644 index 0000000..10f56ef --- /dev/null +++ b/contrib/wayvr/watch_wayvr_example.yaml @@ -0,0 +1,195 @@ +# looking to make changes? +# drop me in ~/.config/wlxoverlay/watch.yaml +# + +width: 0.115 + +size: [400, 272] + +elements: + # background panel + - type: Panel + rect: [0, 30, 400, 130] + corner_radius: 20 + bg_color: "#24273a" + + - type: Button + rect: [2, 162, 26, 36] + corner_radius: 4 + font_size: 15 + bg_color: "#c6a0f6" + fg_color: "#24273a" + text: "C" + click_up: # destroy if exists, otherwise create + - type: Window + target: settings + action: ShowUi # only triggers if not exists + - type: Window + target: settings + action: Destroy # only triggers if exists since before current frame + + # Keyboard button + - type: Button + rect: [32, 162, 60, 36] + corner_radius: 4 + font_size: 15 + fg_color: "#24273a" + bg_color: "#a6da95" + text: Kbd + click_up: + - type: Overlay + target: "kbd" + action: ToggleVisible + long_click_up: + - type: Overlay + target: "kbd" + action: Reset + right_up: + - type: Overlay + target: "kbd" + action: ToggleImmovable + middle_up: + - type: Overlay + target: "kbd" + action: ToggleInteraction + scroll_up: + - type: Overlay + target: "kbd" + action: + Opacity: { delta: 0.025 } + scroll_down: + - type: Overlay + target: "kbd" + action: + Opacity: { delta: -0.025 } + + # bottom row, of keyboard + overlays + - type: OverlayList + rect: [94, 160, 306, 40] + corner_radius: 4 + font_size: 15 + fg_color: "#cad3f5" + bg_color: "#1e2030" + layout: Horizontal + click_up: ToggleVisible + long_click_up: Reset + right_up: ToggleImmovable + middle_up: ToggleInteraction + scroll_up: + Opacity: { delta: 0.025 } + scroll_down: + Opacity: { delta: -0.025 } + + - type: WayVRLauncher + rect: [0, 200, 400, 36] + corner_radius: 4 + font_size: 15 + fg_color: "#24273a" + bg_color: "#e590c4" + catalog_name: "default_catalog" + + - type: WayVRDisplayList + rect: [0, 236, 400, 36] + corner_radius: 4 + font_size: 15 + fg_color: "#24273a" + bg_color: "#ca68a4" + + # local clock + - type: Label + rect: [19, 90, 200, 50] + corner_radius: 4 + font_size: 46 # Use 32 for 12-hour time + fg_color: "#cad3f5" + source: Clock + format: "%H:%M" # 23:59 + #format: "%I:%M %p" # 11:59 PM + + # local date + - type: Label + rect: [20, 117, 200, 20] + corner_radius: 4 + font_size: 14 + fg_color: "#cad3f5" + source: Clock + format: "%x" # local date representation + + # local day-of-week + - type: Label + rect: [20, 137, 200, 50] + corner_radius: 4 + font_size: 14 + fg_color: "#cad3f5" + source: Clock + format: "%A" # Tuesday + #format: "%a" # Tue + + # alt clock 1 + - type: Label + rect: [210, 90, 200, 50] + corner_radius: 4 + font_size: 24 # Use 18 for 12-hour time + fg_color: "#8bd5ca" + source: Clock + timezone: "Asia/Tokyo" # change TZ1 here + format: "%H:%M" # 23:59 + #format: "%I:%M %p" # 11:59 PM + - type: Label + rect: [210, 60, 200, 50] + corner_radius: 4 + font_size: 14 + fg_color: "#8bd5ca" + source: Static + text: "Tokyo" # change TZ1 label here + + # alt clock 2 + - type: Label + rect: [210, 150, 200, 50] + corner_radius: 4 + font_size: 24 # Use 18 for 12-hour time + fg_color: "#b7bdf8" + source: Clock + timezone: "America/Chicago" # change TZ2 here + format: "%H:%M" # 23:59 + #format: "%I:%M %p" # 11:59 PM + - type: Label + rect: [210, 120, 200, 50] + corner_radius: 4 + font_size: 14 + fg_color: "#b7bdf8" + source: Static + text: "Chicago" # change TZ2 label here + + # batteries + - type: BatteryList + rect: [0, 5, 400, 30] + corner_radius: 4 + font_size: 16 + fg_color: "#8bd5ca" + fg_color_low: "#B06060" + fg_color_charging: "#6080A0" + num_devices: 9 + layout: Horizontal + low_threshold: 33 + + # volume buttons + - type: Button + rect: [315, 52, 70, 32] + corner_radius: 4 + font_size: 13 + fg_color: "#cad3f5" + bg_color: "#5b6078" + text: "Vol +" + click_down: + - type: Exec + command: ["pactl", "set-sink-volume", "@DEFAULT_SINK@", "+5%"] + - type: Button + rect: [315, 116, 70, 32] + corner_radius: 4 + font_size: 13 + fg_color: "#cad3f5" + bg_color: "#5b6078" + text: "Vol -" + click_down: + - type: Exec + command: ["pactl", "set-sink-volume", "@DEFAULT_SINK@", "-5%"] diff --git a/src/backend/openvr/mod.rs b/src/backend/openvr/mod.rs index 2c1b868..95e01a5 100644 --- a/src/backend/openvr/mod.rs +++ b/src/backend/openvr/mod.rs @@ -346,7 +346,7 @@ pub fn openvr_run(running: Arc, show_by_default: bool) -> Result<(), #[cfg(feature = "wayvr")] if let Some(wayvr) = &state.wayvr { - wayvr.borrow_mut().tick_finish()?; + wayvr.borrow_mut().state.tick_finish()?; } // chaperone diff --git a/src/backend/openxr/mod.rs b/src/backend/openxr/mod.rs index f2cd591..d6e04cd 100644 --- a/src/backend/openxr/mod.rs +++ b/src/backend/openxr/mod.rs @@ -402,7 +402,7 @@ pub fn openxr_run(running: Arc, show_by_default: bool) -> Result<(), #[cfg(feature = "wayvr")] if let Some(wayvr) = &app_state.wayvr { - wayvr.borrow_mut().tick_finish()?; + wayvr.borrow_mut().state.tick_finish()?; } command_buffer.build_and_execute_now()?; diff --git a/src/backend/wayvr/README.md b/src/backend/wayvr/README.md deleted file mode 100644 index fd72057..0000000 --- a/src/backend/wayvr/README.md +++ /dev/null @@ -1,37 +0,0 @@ -**WayVR acts as a bridge between Wayland applications and wlx-overlay-s panels, allowing you to display your applications within a VR environment. Internally, WayVR utilizes Smithay to run a Wayland compositor.** - -# Features - -- Display Wayland applications without GPU overhead (zero-copy via dma-buf) -- Mouse input -- Precision scrolling support -- XWayland "support" via `cage` - -# Supported hardware - -### Confirmed working GPUs - -- Navi 32 family: AMD Radeon RX 7800 XT **\*** -- Navi 23 family: AMD Radeon RX 6600 XT -- Navi 21 family: AMD Radeon Pro W6800, AMD Radeon RX 6800 XT -- Nvidia GTX 16 Series -- _Your GPU here? (Let us know!)_ - -**\*** - With dmabuf modifier mitigation (probably Mesa bug) - -# Supported software - -- Basically all Qt applications (they work out of the box) -- Most XWayland applications via `cage` - -# Known issues - -- Context menus are not functional in most cases yet - -- Due to unknown circumstances, dma-buf textures may display various graphical glitches due to invalid dma-buf tiling modifier. Please report your GPU model when filing an issue. Alternatively, you can run wlx-overlay-s with `LIBGL_ALWAYS_SOFTWARE=1` to mitigate that (only the Smithay compositor will run in software renderer mode, wlx will still be accelerated). - -- ~~Potential data race in the rendering pipeline - A texture could be displayed during the clear-and-blit process in the compositor, causing minor artifacts (no fence sync support yet).~~ happens on all overlays on a simulated Monado driver - -- Even though some applications support Wayland, some still check for the `DISPLAY` environment variable and an available X11 server, throwing an error. This can be fixed by running `cage`. - -- GNOME still insists on rendering client-side decorations instead of server-side ones. This results in all GTK applications looking odd due to additional window shadows. [Fix here, "Client-side decorations"](https://wiki.archlinux.org/title/GTK) diff --git a/src/backend/wayvr/client.rs b/src/backend/wayvr/client.rs index 6bb5888..3493e99 100644 --- a/src/backend/wayvr/client.rs +++ b/src/backend/wayvr/client.rs @@ -201,7 +201,7 @@ fn export_display_number(display_num: u32) -> anyhow::Result<()> { .map(PathBuf::from) .unwrap_or_else(|_| PathBuf::from("/tmp")); path.push("wayvr.disp"); - std::fs::write(path, format!("{}\n", display_num)).unwrap(); + std::fs::write(path, format!("{}\n", display_num))?; Ok(()) } diff --git a/src/backend/wayvr/display.rs b/src/backend/wayvr/display.rs index 986cd0d..912f97e 100644 --- a/src/backend/wayvr/display.rs +++ b/src/backend/wayvr/display.rs @@ -19,7 +19,7 @@ use crate::{backend::overlay::OverlayID, gen_id}; use super::{ client::WayVRManager, comp::send_frames_surface_tree, egl_data, event_queue::SyncEventQueue, - process, smithay_wrapper, window, + process, smithay_wrapper, time, window, }; fn generate_auth_key() -> String { @@ -54,6 +54,7 @@ pub struct Display { wm: Rc>, pub displayed_windows: Vec, wayland_env: super::WaylandEnv, + last_pressed_time_ms: u64, // Render data stuff gles_texture: GlesTexture, // TODO: drop texture @@ -121,6 +122,7 @@ impl Display { primary, overlay_id: None, tasks: SyncEventQueue::new(), + last_pressed_time_ms: 0, }) } @@ -250,7 +252,18 @@ impl Display { } } - pub fn send_mouse_move(&self, manager: &mut WayVRManager, x: u32, y: u32) { + pub fn send_mouse_move( + &self, + config: &super::Config, + manager: &mut WayVRManager, + x: u32, + y: u32, + ) { + let current_ms = time::get_millis(); + if self.last_pressed_time_ms + config.click_freeze_time_ms as u64 > current_ms { + return; + } + if let Some(window_handle) = self.get_hovered_window(x, y) { let wm = self.wm.borrow(); if let Some(window) = wm.windows.get(&window_handle) { @@ -283,10 +296,12 @@ impl Display { } } - pub fn send_mouse_down(&self, manager: &mut WayVRManager, index: super::MouseIndex) { + pub fn send_mouse_down(&mut self, manager: &mut WayVRManager, index: super::MouseIndex) { // Change keyboard focus to pressed window let loc = manager.seat_pointer.current_location(); + self.last_pressed_time_ms = time::get_millis(); + if let Some(window_handle) = self.get_hovered_window(loc.x.max(0.0) as u32, loc.y.max(0.0) as u32) { diff --git a/src/backend/wayvr/event_queue.rs b/src/backend/wayvr/event_queue.rs index 40a940b..6f6df55 100644 --- a/src/backend/wayvr/event_queue.rs +++ b/src/backend/wayvr/event_queue.rs @@ -3,30 +3,30 @@ use std::{cell::RefCell, collections::VecDeque, rc::Rc}; struct Data { - queue: VecDeque, + queue: VecDeque, } #[derive(Clone)] pub struct SyncEventQueue { - data: Rc>>, + data: Rc>>, } impl SyncEventQueue { - pub fn new() -> Self { - Self { - data: Rc::new(RefCell::new(Data { - queue: Default::default(), - })), - } - } + pub fn new() -> Self { + Self { + data: Rc::new(RefCell::new(Data { + queue: Default::default(), + })), + } + } - pub fn send(&self, message: DataType) { - let mut data = self.data.borrow_mut(); - data.queue.push_back(message); - } + pub fn send(&self, message: DataType) { + let mut data = self.data.borrow_mut(); + data.queue.push_back(message); + } - pub fn read(&self) -> Option { - let mut data = self.data.borrow_mut(); - data.queue.pop_front() - } + pub fn read(&self) -> Option { + let mut data = self.data.borrow_mut(); + data.queue.pop_front() + } } diff --git a/src/backend/wayvr/handle.rs b/src/backend/wayvr/handle.rs index 2ca7989..2947e3e 100644 --- a/src/backend/wayvr/handle.rs +++ b/src/backend/wayvr/handle.rs @@ -20,7 +20,7 @@ macro_rules! gen_id { } //ThingHandle - #[derive(Default, Clone, Copy, PartialEq)] + #[derive(Default, Clone, Copy, PartialEq, Hash, Eq)] pub struct $handle_name { idx: u32, generation: u64, diff --git a/src/backend/wayvr/mod.rs b/src/backend/wayvr/mod.rs index 485343a..c2dcf90 100644 --- a/src/backend/wayvr/mod.rs +++ b/src/backend/wayvr/mod.rs @@ -65,6 +65,12 @@ pub enum WayVRTask { ProcessTerminationRequest(process::ProcessHandle), } +pub struct Config { + pub click_freeze_time_ms: u32, + pub keyboard_repeat_delay_ms: u32, + pub keyboard_repeat_rate: u32, +} + #[allow(dead_code)] pub struct WayVR { time_start: u64, @@ -74,6 +80,7 @@ pub struct WayVR { wm: Rc>, egl_data: Rc, pub processes: process::ProcessVec, + config: Config, tasks: SyncEventQueue, } @@ -89,7 +96,7 @@ pub enum TickResult { } impl WayVR { - pub fn new() -> anyhow::Result { + pub fn new(config: Config) -> anyhow::Result { log::info!("Initializing WayVR"); let display: wayland_server::Display = wayland_server::Display::new()?; let dh = display.handle(); @@ -100,8 +107,11 @@ impl WayVR { let data_device = DataDeviceState::new::(&dh); let mut seat = seat_state.new_wl_seat(&dh, "wayvr"); - // TODO: Keyboard repeat delay and rate? - let seat_keyboard = seat.add_keyboard(Default::default(), 100, 100)?; + let seat_keyboard = seat.add_keyboard( + Default::default(), + config.keyboard_repeat_delay_ms as i32, + config.keyboard_repeat_rate as i32, + )?; let seat_pointer = seat.add_pointer(); let tasks = SyncEventQueue::new(); @@ -131,6 +141,7 @@ impl WayVR { egl_data: Rc::new(egl_data), wm: Rc::new(RefCell::new(window::WindowManager::new())), tasks, + config, }) } @@ -222,7 +233,7 @@ impl WayVR { } } else { log::error!( - "WayVR window creation failed: Unexpected process PID {}. It wasn't registered before.", + "WayVR window creation failed: Unexpected process ID {}. It wasn't registered before.", client.pid ); } @@ -255,12 +266,12 @@ impl WayVR { 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(&mut self.manager, x, y); + display.send_mouse_move(&self.config, &mut self.manager, x, y); } } pub fn send_mouse_down(&mut self, display: display::DisplayHandle, index: MouseIndex) { - if let Some(display) = self.displays.get(&display) { + if let Some(display) = self.displays.get_mut(&display) { display.send_mouse_down(&mut self.manager, index); } } diff --git a/src/config_wayvr.rs b/src/config_wayvr.rs index 9847619..ef11843 100644 --- a/src/config_wayvr.rs +++ b/src/config_wayvr.rs @@ -17,7 +17,7 @@ use crate::{ wayvr, }, config::{load_known_yaml, ConfigType}, - overlays::wayvr::WayVRAction, + overlays::wayvr::{WayVRAction, WayVRState}, }; // Flat version of RelativeTo @@ -106,10 +106,19 @@ impl WayVRConfig { None } + pub fn get_wayvr_config(config: &crate::config::GeneralConfig) -> wayvr::Config { + wayvr::Config { + click_freeze_time_ms: config.click_freeze_time_ms, + keyboard_repeat_delay_ms: 200, + keyboard_repeat_rate: 50, + } + } + pub fn post_load( &self, + config: &crate::config::GeneralConfig, tasks: &mut TaskContainer, - ) -> anyhow::Result>>> { + ) -> anyhow::Result>>> { let primary_count = self .displays .iter() @@ -137,7 +146,9 @@ impl WayVRConfig { if self.run_compositor_at_start { // Start Wayland server instantly - Ok(Some(Rc::new(RefCell::new(wayvr::WayVR::new()?)))) + Ok(Some(Rc::new(RefCell::new(WayVRState::new( + Self::get_wayvr_config(config), + )?)))) } else { // Lazy-init WayVR later if the user requested Ok(None) diff --git a/src/overlays/keyboard.rs b/src/overlays/keyboard.rs index e1eb769..61a6e08 100644 --- a/src/overlays/keyboard.rs +++ b/src/overlays/keyboard.rs @@ -32,12 +32,6 @@ const AUTO_RELEASE_MODS: [KeyModifier; 5] = [SHIFT, CTRL, ALT, SUPER, META]; pub const KEYBOARD_NAME: &str = "kbd"; fn send_key(app: &mut AppState, key: VirtualKey, down: bool) { - log::info!( - "Sending key {:?} to {:?} (down: {})", - key, - app.keyboard_focus, - down - ); match app.keyboard_focus { KeyboardFocus::PhysicalScreen => { app.hid_provider.send_key(key, down); @@ -46,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().send_key(key as u32, down); + wayvr.borrow_mut().state.send_key(key as u32, down); } } } @@ -241,7 +235,6 @@ fn key_press( app.hid_provider.set_modifiers(data.modifiers); - send_key(app, *vk, true); *pressed = true; } diff --git a/src/overlays/wayvr.rs b/src/overlays/wayvr.rs index 7b25a04..d757f55 100644 --- a/src/overlays/wayvr.rs +++ b/src/overlays/wayvr.rs @@ -1,6 +1,6 @@ use glam::{vec3a, Affine2, Vec3, Vec3A}; use serde::Deserialize; -use std::{cell::RefCell, rc::Rc, sync::Arc}; +use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc}; use vulkano::image::SubresourceLayout; use wlx_capture::frame::{DmabufFrame, FourCC, FrameFormat, FramePlane}; @@ -8,7 +8,10 @@ use crate::{ backend::{ common::OverlayContainer, input::{self, InteractionHandler}, - overlay::{ui_transform, OverlayData, OverlayRenderer, OverlayState, SplitOverlayBackend}, + overlay::{ + ui_transform, OverlayData, OverlayID, OverlayRenderer, OverlayState, + SplitOverlayBackend, + }, wayvr::{self, display, WayVR}, }, graphics::WlxGraphics, @@ -16,13 +19,13 @@ use crate::{ }; pub struct WayVRContext { - wayvr: Rc>, + wayvr: Rc>, display: wayvr::display::DisplayHandle, } impl WayVRContext { pub fn new( - wvr: Rc>, + wvr: Rc>, display: wayvr::display::DisplayHandle, ) -> anyhow::Result { Ok(Self { @@ -32,6 +35,20 @@ impl WayVRContext { } } +pub struct WayVRState { + pub display_handle_map: HashMap, + pub state: WayVR, +} + +impl WayVRState { + pub fn new(config: wayvr::Config) -> anyhow::Result { + Ok(Self { + display_handle_map: Default::default(), + state: WayVR::new(config)?, + }) + } +} + pub struct WayVRInteractionHandler { context: Rc>, mouse_transform: Affine2, @@ -54,7 +71,7 @@ impl InteractionHandler for WayVRInteractionHandler { ) -> Option { let ctx = self.context.borrow(); - let mut wayvr = ctx.wayvr.borrow_mut(); + let wayvr = &mut ctx.wayvr.borrow_mut().state; if let Some(disp) = wayvr.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); @@ -82,7 +99,7 @@ impl InteractionHandler for WayVRInteractionHandler { } } { let ctx = self.context.borrow(); - let mut wayvr = ctx.wayvr.borrow_mut(); + let wayvr = &mut ctx.wayvr.borrow_mut().state; if pressed { wayvr.send_mouse_down(ctx.display, index); } else { @@ -93,7 +110,10 @@ impl InteractionHandler for WayVRInteractionHandler { fn on_scroll(&mut self, _app: &mut state::AppState, _hit: &input::PointerHit, delta: f32) { let ctx = self.context.borrow(); - ctx.wayvr.borrow_mut().send_mouse_scroll(ctx.display, delta); + ctx.wayvr + .borrow_mut() + .state + .send_mouse_scroll(ctx.display, delta); } } @@ -107,7 +127,7 @@ pub struct WayVRRenderer { impl WayVRRenderer { pub fn new( app: &mut state::AppState, - wvr: Rc>, + wvr: Rc>, display: wayvr::display::DisplayHandle, ) -> anyhow::Result { Ok(Self { @@ -121,7 +141,7 @@ impl WayVRRenderer { fn get_or_create_display( app: &mut AppState, - wayvr: &mut WayVR, + wayvr: &mut WayVRState, disp_name: &str, ) -> anyhow::Result<(display::DisplayHandle, Option>)> where @@ -129,57 +149,62 @@ where { let created_overlay: Option>; - let disp_handle = if let Some(disp) = WayVR::get_display_by_name(&wayvr.displays, disp_name) { - created_overlay = None; - disp - } else { - let conf_display = app - .session - .wayvr_config - .get_display(disp_name) - .ok_or(anyhow::anyhow!( - "Cannot find display named \"{}\"", - disp_name - ))? - .clone(); - - let disp_handle = wayvr.create_display( - conf_display.width, - conf_display.height, - disp_name, - conf_display.primary.unwrap_or(false), - )?; - - let mut overlay = create_wayvr_display_overlay::( - app, - conf_display.width, - conf_display.height, - disp_handle, - conf_display.scale.unwrap_or(1.0), - )?; - - if let Some(attach_to) = &conf_display.attach_to { - overlay.state.relative_to = attach_to.get_relative_to(); - } + let disp_handle = + if let Some(disp) = WayVR::get_display_by_name(&wayvr.state.displays, disp_name) { + created_overlay = None; + disp + } else { + let conf_display = app + .session + .wayvr_config + .get_display(disp_name) + .ok_or(anyhow::anyhow!( + "Cannot find display named \"{}\"", + disp_name + ))? + .clone(); + + let disp_handle = wayvr.state.create_display( + conf_display.width, + conf_display.height, + disp_name, + conf_display.primary.unwrap_or(false), + )?; + + let mut overlay = create_wayvr_display_overlay::( + app, + conf_display.width, + conf_display.height, + 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(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); - } + if let Some(pos) = &conf_display.pos { + overlay.state.spawn_point = Vec3A::from_slice(pos); + } - let display = wayvr.displays.get_mut(&disp_handle).unwrap(); // Never fails - display.overlay_id = Some(overlay.state.id); + let display = wayvr.state.displays.get_mut(&disp_handle).unwrap(); // Never fails + display.overlay_id = Some(overlay.state.id); - created_overlay = Some(overlay); + created_overlay = Some(overlay); - disp_handle - }; + disp_handle + }; Ok((disp_handle, created_overlay)) } @@ -189,7 +214,7 @@ where O: Default, { if let Some(wayvr) = app.wayvr.clone() { - let res = wayvr.borrow_mut().tick_events()?; + let res = wayvr.borrow_mut().state.tick_events()?; for result in res { match result { @@ -214,9 +239,9 @@ where let (disp_handle, created_overlay) = get_or_create_display::(app, &mut wayvr, &disp_name)?; - wayvr.add_external_process(disp_handle, req.pid); + wayvr.state.add_external_process(disp_handle, req.pid); - wayvr.manager.add_client(wayvr::client::WayVRClient { + wayvr.state.manager.add_client(wayvr::client::WayVRClient { client: req.client, display_handle: disp_handle, pid: req.pid, @@ -245,7 +270,7 @@ impl WayVRRenderer { let ctx = self.context.borrow_mut(); let wayvr = ctx.wayvr.borrow_mut(); - if let Some(disp) = wayvr.displays.get(&ctx.display) { + if let Some(disp) = wayvr.state.displays.get(&ctx.display) { let frame = DmabufFrame { format: FrameFormat { width: disp.width, @@ -293,14 +318,14 @@ impl OverlayRenderer for WayVRRenderer { fn pause(&mut self, _app: &mut state::AppState) -> anyhow::Result<()> { let ctx = self.context.borrow_mut(); - let mut wayvr = ctx.wayvr.borrow_mut(); + let wayvr = &mut ctx.wayvr.borrow_mut().state; wayvr.set_display_visible(ctx.display, false); Ok(()) } fn resume(&mut self, _app: &mut state::AppState) -> anyhow::Result<()> { let ctx = self.context.borrow_mut(); - let mut wayvr = ctx.wayvr.borrow_mut(); + let wayvr = &mut ctx.wayvr.borrow_mut().state; wayvr.set_display_visible(ctx.display, true); Ok(()) } @@ -309,9 +334,10 @@ impl OverlayRenderer for WayVRRenderer { let ctx = self.context.borrow(); let mut wayvr = ctx.wayvr.borrow_mut(); - wayvr.tick_display(ctx.display)?; + wayvr.state.tick_display(ctx.display)?; let dmabuf_data = wayvr + .state .get_dmabuf_data(ctx.display) .ok_or(anyhow::anyhow!("Failed to fetch dmabuf data"))? .clone(); @@ -392,8 +418,24 @@ pub enum WayVRAction { }, } +fn show_display(wayvr: &mut WayVRState, overlays: &mut OverlayContainer, display_name: &str) +where + O: Default, +{ + if let Some(display) = WayVR::get_display_by_name(&wayvr.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); + } +} + fn action_app_click( app: &mut AppState, + overlays: &mut OverlayContainer, catalog_name: &Arc, app_name: &Arc, ) -> anyhow::Result>> @@ -437,13 +479,19 @@ where // Terminate existing process if required if let Some(process_handle) = - wayvr.process_query(disp_handle, &app_entry.exec, &args_vec, &env_vec) + wayvr + .state + .process_query(disp_handle, &app_entry.exec, &args_vec, &env_vec) { // Terminate process - wayvr.terminate_process(process_handle); + wayvr.state.terminate_process(process_handle); } else { // Spawn process - wayvr.spawn_process(disp_handle, &app_entry.exec, &args_vec, &env_vec)?; + wayvr + .state + .spawn_process(disp_handle, &app_entry.exec, &args_vec, &env_vec)?; + + show_display(&mut wayvr, overlays, &app_entry.target_display.as_str()); } Ok(created_overlay) } else { @@ -463,8 +511,8 @@ where let wayvr = app.get_wayvr()?; let mut wayvr = wayvr.borrow_mut(); - if let Some(handle) = WayVR::get_display_by_name(&wayvr.displays, display_name) { - if let Some(display) = wayvr.displays.get_mut(&handle) { + 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 { @@ -495,7 +543,7 @@ where catalog_name, app_name, } => { - match action_app_click(app, 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); diff --git a/src/state.rs b/src/state.rs index e789590..de8698f 100644 --- a/src/state.rs +++ b/src/state.rs @@ -8,13 +8,11 @@ use std::{io::Cursor, sync::Arc}; use vulkano::image::view::ImageView; #[cfg(feature = "wayvr")] -use std::{cell::RefCell, rc::Rc}; - -#[cfg(feature = "wayvr")] -use crate::backend::wayvr::WayVR; - -#[cfg(feature = "wayvr")] -use crate::config_wayvr::{self, WayVRConfig}; +use { + crate::config_wayvr::{self, WayVRConfig}, + crate::overlays::wayvr::WayVRState, + std::{cell::RefCell, rc::Rc}, +}; use crate::{ backend::{input::InputState, overlay::OverlayID, task::TaskContainer}, @@ -52,7 +50,7 @@ pub struct AppState { pub keyboard_focus: KeyboardFocus, #[cfg(feature = "wayvr")] - pub wayvr: Option>>, // Dynamically created if requested + pub wayvr: Option>>, // Dynamically created if requested } impl AppState { @@ -100,7 +98,9 @@ impl AppState { let session = AppSession::load(); #[cfg(feature = "wayvr")] - let wayvr = session.wayvr_config.post_load(&mut tasks)?; + let wayvr = session + .wayvr_config + .post_load(&session.config, &mut tasks)?; Ok(AppState { fc: FontCache::new(session.config.primary_font.clone())?, @@ -122,11 +122,13 @@ impl AppState { #[cfg(feature = "wayvr")] #[allow(dead_code)] - pub fn get_wayvr(&mut self) -> anyhow::Result>> { + pub fn get_wayvr(&mut self) -> anyhow::Result>> { if let Some(wvr) = &self.wayvr { Ok(wvr.clone()) } else { - let wayvr = Rc::new(RefCell::new(WayVR::new()?)); + let wayvr = Rc::new(RefCell::new(WayVRState::new( + WayVRConfig::get_wayvr_config(&self.session.config), + )?)); self.wayvr = Some(wayvr.clone()); Ok(wayvr) }