From ae698bf111722184981a587e4704beec22757abc Mon Sep 17 00:00:00 2001 From: art0007i Date: Sun, 31 May 2026 23:10:01 +0200 Subject: [PATCH 1/6] add watch toggle setting --- dash-frontend/assets/lang/en.json | 1 + dash-frontend/assets/lang/pl.json | 1 + dash-frontend/src/tab/settings/mod.rs | 3 + .../src/tab/settings/tab_look_and_feel.rs | 1 + wayvr/src/backend/openxr/mod.rs | 31 +++++---- wayvr/src/config.rs | 2 + wayvr/src/res/config.yaml | 3 + wayvr/src/windowing/manager.rs | 67 ++++++++++++------- wlx-common/src/config.rs | 3 + 9 files changed, 73 insertions(+), 39 deletions(-) diff --git a/dash-frontend/assets/lang/en.json b/dash-frontend/assets/lang/en.json index 14b8d023..e8091cb7 100644 --- a/dash-frontend/assets/lang/en.json +++ b/dash-frontend/assets/lang/en.json @@ -100,6 +100,7 @@ "SCREEN_RENDER_DOWN_HELP": "Helps with aliasing on high-res screens", "SCROLL_SPEED": "Scroll speed", "SELECT_VARIANT": "Select variant", + "ENABLE_WATCH": "Enable watch", "SETS_ON_WATCH": "Sets on watch", "SKYBOX": "Skybox", "SKYMAP_ALREADY_DOWNLOADED": "This skymap is already downloaded. Select desired action.", diff --git a/dash-frontend/assets/lang/pl.json b/dash-frontend/assets/lang/pl.json index 7e4ceef4..c45a2cdc 100644 --- a/dash-frontend/assets/lang/pl.json +++ b/dash-frontend/assets/lang/pl.json @@ -44,6 +44,7 @@ "USE_SKYBOX_HELP": "Wyświetlaj niebo, jeśli nie ma aplikacji sceny lub passthrough", "USE_PASSTHROUGH_HELP": "Pozwól na passthrough, jeśli runtime XR to obsługuje", "SCREEN_RENDER_DOWN_HELP": "Pomaga redukować aliasing na ekranach o wysokiej rozdzielczości", + "ENABLE_WATCH": "Włącz zegarek", "SETS_ON_WATCH": "Lista zestawów na zegarku", "TROUBLESHOOTING": "Rozwiązywanie problemów", "CLEAR_SAVED_STATE": "Wyczyść zapisany stan", diff --git a/dash-frontend/src/tab/settings/mod.rs b/dash-frontend/src/tab/settings/mod.rs index ab32bdf1..eb66a6dc 100644 --- a/dash-frontend/src/tab/settings/mod.rs +++ b/dash-frontend/src/tab/settings/mod.rs @@ -274,6 +274,7 @@ enum SettingType { PointerLerpFactor, ScreenRenderDown, ScrollSpeed, + EnableWatch, SetsOnWatch, SpaceDragMultiplier, SpaceDragUnlocked, @@ -309,6 +310,7 @@ impl SettingType { Self::KeyboardSoundEnabled => &mut config.keyboard_sound_enabled, Self::UprightScreenFix => &mut config.upright_screen_fix, Self::DoubleCursorFix => &mut config.double_cursor_fix, + Self::EnableWatch => &mut config.enable_watch, Self::SetsOnWatch => &mut config.sets_on_watch, Self::HideGrabHelp => &mut config.hide_grab_help, Self::AllowSliding => &mut config.allow_sliding, @@ -435,6 +437,7 @@ impl SettingType { Self::PointerLerpFactor => Ok("APP_SETTINGS.POINTER_LERP_FACTOR"), Self::ScreenRenderDown => Ok("APP_SETTINGS.SCREEN_RENDER_DOWN"), Self::ScrollSpeed => Ok("APP_SETTINGS.SCROLL_SPEED"), + Self::EnableWatch => Ok("APP_SETTINGS.ENABLE_WATCH"), Self::SetsOnWatch => Ok("APP_SETTINGS.SETS_ON_WATCH"), Self::SpaceDragMultiplier => Ok("APP_SETTINGS.SPACE_DRAG_MULTIPLIER"), Self::SpaceDragUnlocked => Ok("APP_SETTINGS.SPACE_DRAG_UNLOCKED"), diff --git a/dash-frontend/src/tab/settings/tab_look_and_feel.rs b/dash-frontend/src/tab/settings/tab_look_and_feel.rs index 768536db..216e6688 100644 --- a/dash-frontend/src/tab/settings/tab_look_and_feel.rs +++ b/dash-frontend/src/tab/settings/tab_look_and_feel.rs @@ -22,6 +22,7 @@ impl State { options_slider_f32(par.mp, c, SettingType::UiAnimationSpeed, 0.5, 5.0, 0.1)?; // min, max, step options_slider_f32(par.mp, c, SettingType::UiGradientIntensity, 0.0, 1.0, 0.05)?; // min, max, step options_slider_f32(par.mp, c, SettingType::UiRoundMultiplier, 0.1, 5.0, 0.1)?; + options_checkbox(par.mp, c, SettingType::EnableWatch)?; options_checkbox(par.mp, c, SettingType::SetsOnWatch)?; options_checkbox(par.mp, c, SettingType::Clock12h)?; Ok(State {}) diff --git a/wayvr/src/backend/openxr/mod.rs b/wayvr/src/backend/openxr/mod.rs index f6ec7a44..11cc457d 100644 --- a/wayvr/src/backend/openxr/mod.rs +++ b/wayvr/src/backend/openxr/mod.rs @@ -141,7 +141,7 @@ pub fn openxr_run( lines.allocate(&xr_state, &app)?, ]; - let watch_id = overlays.lookup(WATCH_NAME).unwrap(); // want panic + let watch_id = overlays.lookup(WATCH_NAME); let mut input_source = input::OpenXrInputSource::new(&xr_state)?; @@ -345,17 +345,19 @@ pub fn openxr_run( app.hid_provider.inner.commit(); - let watch = overlays.mut_by_id(watch_id).unwrap(); // want panic - let watch_state = watch.config.active_state.as_mut().unwrap(); - let watch_transform = watch_state.transform; - if watch_state.alpha < 0.05 { - //FIXME: Temporary workaround for Monado bug - watch_state.transform = Affine3A::from_scale(Vec3 { - x: 0.001, - y: 0.001, - z: 0.001, - }); - watch_state.alpha = 0.02; // visible but not really. Monado freaks out if no layers are submitted. + let mut watch_transform = Affine3A::IDENTITY; + if let Some(watch) = watch_id.and_then(|id| overlays.mut_by_id(id)) { + let watch_state = watch.config.active_state.as_mut().unwrap(); + watch_transform = watch_state.transform; + if watch_state.alpha < 0.05 { + //FIXME: Temporary workaround for Monado bug + watch_state.transform = Affine3A::from_scale(Vec3 { + x: 0.001, + y: 0.001, + z: 0.001, + }); + watch_state.alpha = 0.02; // visible but not really. Monado freaks out if no layers are submitted. + } } if let Err(e) = @@ -509,8 +511,9 @@ pub fn openxr_run( delete_queue.retain(|(_, frame)| *frame > cur_frame); //FIXME: Temporary workaround for Monado bug - let watch = overlays.mut_by_id(watch_id).unwrap(); // want panic - watch.config.active_state.as_mut().unwrap().transform = watch_transform; + if let Some(watch) = watch_id.and_then(|id| overlays.mut_by_id(id)) { + watch.config.active_state.as_mut().unwrap().transform = watch_transform; + } } // main_loop if let (Some(blocker), Some(monado)) = (blocker, app.monado_state.as_mut()) { diff --git a/wayvr/src/config.rs b/wayvr/src/config.rs index a42526db..c440fd26 100644 --- a/wayvr/src/config.rs +++ b/wayvr/src/config.rs @@ -145,6 +145,7 @@ pub struct AutoSettings { pub keyboard_sound_enabled: bool, pub upright_screen_fix: bool, pub double_cursor_fix: bool, + pub enable_watch: bool, pub sets_on_watch: bool, pub hide_grab_help: bool, pub xr_click_sensitivity: f32, @@ -200,6 +201,7 @@ pub fn save_settings(config: &GeneralConfig) -> anyhow::Result<()> { keyboard_sound_enabled: config.keyboard_sound_enabled, upright_screen_fix: config.upright_screen_fix, double_cursor_fix: config.double_cursor_fix, + enable_watch: config.enable_watch, sets_on_watch: config.sets_on_watch, hide_grab_help: config.hide_grab_help, xr_click_sensitivity: config.xr_click_sensitivity, diff --git a/wayvr/src/res/config.yaml b/wayvr/src/res/config.yaml index 55747a09..b9fd51af 100644 --- a/wayvr/src/res/config.yaml +++ b/wayvr/src/res/config.yaml @@ -85,6 +85,9 @@ # The settings are here for reference only. # Probably don't include them in your config file. +## The watch will be enabled +#enable_watch: true + ## The bottom of the watch will list sets instead of overlays. #sets_on_watch: false diff --git a/wayvr/src/windowing/manager.rs b/wayvr/src/windowing/manager.rs index 5d8a168c..de14379a 100644 --- a/wayvr/src/windowing/manager.rs +++ b/wayvr/src/windowing/manager.rs @@ -51,7 +51,7 @@ pub struct OverlayWindowManager { /// Usually the same as current_set, except it keeps its value when current_set is hidden. restore_set: usize, anchor_local: Affine3A, - watch_id: OverlayID, + watch_id: Option, keyboard_id: OverlayID, edit_mode: bool, dropped_overlays: VecDeque>, @@ -71,7 +71,7 @@ where sets: vec![OverlayWindowSet::default()], global_set: OverlayWindowSet::default(), anchor_local: Affine3A::from_translation(Vec3::NEG_Z), - watch_id: OverlayID::null(), // set down below + watch_id: None, // set down below keyboard_id: OverlayID::null(), // set down below edit_mode: false, dropped_overlays: VecDeque::with_capacity(8), @@ -127,8 +127,10 @@ where let anchor = OverlayWindowData::from_config(create_anchor(app)?); me.add(anchor, app); - let watch = OverlayWindowData::from_config(create_watch(app)?); - me.watch_id = me.add(watch, app); + if app.session.config.enable_watch { + let watch = OverlayWindowData::from_config(create_watch(app)?); + me.watch_id = Some(me.add(watch, app)); + } let dash_frontend = OverlayWindowData::from_config(create_dash_frontend(app)?); me.add(dash_frontend, app); @@ -149,7 +151,7 @@ where me.restore_layout(app); me.overlays_changed(app)?; - for id in [me.watch_id, me.keyboard_id] { + for id in [me.watch_id, Some(me.keyboard_id)].iter().filter_map(|x| *x) { for ev in [ OverlayEventData::NumSetsChanged(me.sets.len()), OverlayEventData::EditModeChanged(false), @@ -288,6 +290,15 @@ where self.sets_changed(app); } OverlayTask::SettingsChanged => { + if app.session.config.enable_watch != self.watch_id.is_some() { + if let Some(watch_id) = self.watch_id.take() { + self.overlays.remove(watch_id); + } else { + let watch = OverlayWindowData::from_config(create_watch(app)?); + self.watch_id = Some(self.add(watch, app)); + } + } + for o in self.overlays.values_mut() { let _ = o .config @@ -440,7 +451,8 @@ impl OverlayWindowManager { } // global overlays; watch, toast - for oid in &[self.watch_id] { + if let Some(watch_id) = self.watch_id { + for oid in &[watch_id] { let Some(o) = self.get_by_id(*oid) else { break; }; @@ -451,6 +463,7 @@ impl OverlayWindowManager { .config .global_set .insert(o.config.name.clone(), state.clone()); + } } // BackendAttrib @@ -587,16 +600,18 @@ impl OverlayWindowManager { } } } - if changed && let Some(watch) = self.mut_by_id(self.watch_id) { - watch - .config - .active_state - .iter_mut() - .for_each(|f| f.grabbable = enabled); - watch - .config - .backend - .notify(app, OverlayEventData::EditModeChanged(enabled))?; + if let Some(watch_id) = self.watch_id { + if changed && let Some(watch) = self.mut_by_id(watch_id) { + watch + .config + .active_state + .iter_mut() + .for_each(|f| f.grabbable = enabled); + watch + .config + .backend + .notify(app, OverlayEventData::EditModeChanged(enabled))?; + } } Ok(()) } @@ -824,7 +839,7 @@ impl OverlayWindowManager { } self.current_set = new_set; - for id in [self.watch_id, self.keyboard_id] { + for id in [self.watch_id, Some(self.keyboard_id)].iter().filter_map(|x| *x) { let _ = self.mut_by_id(id).context("Missing overlay").and_then(|o| { o.config .backend @@ -877,7 +892,7 @@ impl OverlayWindowManager { } let meta: Rc<[OverlayMeta]> = meta.into(); - for id in [self.watch_id, self.keyboard_id] { + for id in [self.watch_id, Some(self.keyboard_id)].iter().filter_map(|x| *x) { let _ = self.mut_by_id(id).context("Missing overlay").and_then(|o| { o.config .backend @@ -902,7 +917,7 @@ impl OverlayWindowManager { } let vis: Rc<[OverlayID]> = vis.into(); - for id in [self.watch_id, self.keyboard_id] { + for id in [self.watch_id, Some(self.keyboard_id)].iter().filter_map(|x| *x) { let _ = self.mut_by_id(id).context("Missing overlay").and_then(|o| { o.config .backend @@ -915,7 +930,7 @@ impl OverlayWindowManager { fn sets_changed(&mut self, app: &mut AppState) { let len = self.sets.len(); - for id in [self.watch_id, self.keyboard_id] { + for id in [self.watch_id, Some(self.keyboard_id)].iter().filter_map(|x| *x) { if let Some(o) = self.mut_by_id(id) { let _ = o .config @@ -928,11 +943,13 @@ impl OverlayWindowManager { #[allow(clippy::unnecessary_wraps)] pub fn devices_changed(&mut self, app: &mut AppState) -> anyhow::Result<()> { - if let Some(watch) = self.mut_by_id(self.watch_id) { - let _ = watch - .config - .backend - .notify(app, OverlayEventData::DevicesChanged); + if let Some(watch_id) = self.watch_id { + if let Some(watch) = self.mut_by_id(watch_id) { + let _ = watch + .config + .backend + .notify(app, OverlayEventData::DevicesChanged); + } } Ok(()) diff --git a/wlx-common/src/config.rs b/wlx-common/src/config.rs index e673bda9..1770356f 100644 --- a/wlx-common/src/config.rs +++ b/wlx-common/src/config.rs @@ -273,6 +273,9 @@ pub struct GeneralConfig { #[serde(default = "def_false")] pub double_cursor_fix: bool, + #[serde(default = "def_true")] + pub enable_watch: bool, + #[serde(default = "def_false")] pub sets_on_watch: bool, From 98a4f5eeb8b6915a82eb27dd4f8a180e829f26f0 Mon Sep 17 00:00:00 2001 From: art0007i Date: Mon, 1 Jun 2026 00:31:16 +0200 Subject: [PATCH 2/6] previous commit broke monado workaround, this fixes it --- wayvr/src/assets/gui/dummy.xml | 10 ++++++++ wayvr/src/backend/openxr/mod.rs | 25 +------------------- wayvr/src/windowing/manager.rs | 41 ++++++++++++++++++++++++--------- 3 files changed, 41 insertions(+), 35 deletions(-) create mode 100644 wayvr/src/assets/gui/dummy.xml diff --git a/wayvr/src/assets/gui/dummy.xml b/wayvr/src/assets/gui/dummy.xml new file mode 100644 index 00000000..a16ff579 --- /dev/null +++ b/wayvr/src/assets/gui/dummy.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/wayvr/src/backend/openxr/mod.rs b/wayvr/src/backend/openxr/mod.rs index 11cc457d..ba462b35 100644 --- a/wayvr/src/backend/openxr/mod.rs +++ b/wayvr/src/backend/openxr/mod.rs @@ -5,7 +5,6 @@ use std::{ time::{Duration, Instant}, }; -use glam::{Affine3A, Vec3}; use input::OpenXrInputSource; use openxr as xr; use skybox::{Skybox, create_skybox}; @@ -22,7 +21,7 @@ use crate::{ }, config::{save_settings, save_state}, graphics::{GpuFutures, init_openxr_graphics}, - overlays::{toast::Toast, watch::WATCH_NAME}, + overlays::toast::Toast, state::AppState, subsystem::notifications::NotificationManager, windowing::{ @@ -141,8 +140,6 @@ pub fn openxr_run( lines.allocate(&xr_state, &app)?, ]; - let watch_id = overlays.lookup(WATCH_NAME); - let mut input_source = input::OpenXrInputSource::new(&xr_state)?; let mut session_running = false; @@ -345,21 +342,6 @@ pub fn openxr_run( app.hid_provider.inner.commit(); - let mut watch_transform = Affine3A::IDENTITY; - if let Some(watch) = watch_id.and_then(|id| overlays.mut_by_id(id)) { - let watch_state = watch.config.active_state.as_mut().unwrap(); - watch_transform = watch_state.transform; - if watch_state.alpha < 0.05 { - //FIXME: Temporary workaround for Monado bug - watch_state.transform = Affine3A::from_scale(Vec3 { - x: 0.001, - y: 0.001, - z: 0.001, - }); - watch_state.alpha = 0.02; // visible but not really. Monado freaks out if no layers are submitted. - } - } - if let Err(e) = crate::ipc::events::tick_events::(&mut app, &mut overlays) { @@ -509,11 +491,6 @@ pub fn openxr_run( } delete_queue.retain(|(_, frame)| *frame > cur_frame); - - //FIXME: Temporary workaround for Monado bug - if let Some(watch) = watch_id.and_then(|id| overlays.mut_by_id(id)) { - watch.config.active_state.as_mut().unwrap().transform = watch_transform; - } } // main_loop if let (Some(blocker), Some(monado)) = (blocker, app.monado_state.as_mut()) { diff --git a/wayvr/src/windowing/manager.rs b/wayvr/src/windowing/manager.rs index de14379a..642451e4 100644 --- a/wayvr/src/windowing/manager.rs +++ b/wayvr/src/windowing/manager.rs @@ -5,20 +5,17 @@ use std::{ }; use anyhow::Context; -use glam::{Affine3A, Vec3, Vec3A}; +use glam::{Affine3A, Quat, Vec3, Vec3A}; use slotmap::{Key, SecondaryMap, SlotMap}; use wgui::log::LogErr; use wlx_common::{ astr_containers::{AStrMap, AStrMapExt}, config::SerializedWindowSet, - overlays::{BackendAttrib, BackendAttribValue, ToastTopic}, + overlays::{BackendAttrib, BackendAttribValue, ToastTopic}, windowing::{OverlayWindowState, Positioning}, }; use crate::{ - FRAME_COUNTER, - backend::task::{OverlayTask, ToggleMode}, - config::save_state, - overlays::{ + FRAME_COUNTER, backend::task::{OverlayTask, ToggleMode}, config::save_state, gui::panel::{GuiPanel, NewGuiPanelParams}, overlays::{ anchor::{create_anchor, create_grab_help}, custom::create_custom, dashboard::{DASH_NAME, create_dash_frontend}, @@ -27,17 +24,16 @@ use crate::{ screen::create_screens, toast::Toast, watch::{WATCH_NAME, create_watch}, - }, - state::AppState, - windowing::{ + }, state::AppState, windowing::{ OverlayID, OverlaySelector, backend::{OverlayEventData, OverlayMeta}, set::OverlayWindowSet, snap_upright, - window::{OverlayCategory, OverlayWindowData}, - }, + window::{OverlayCategory, OverlayWindowConfig, OverlayWindowData}, + } }; +pub const DUMMY_NAME: &str = "FIXME: Temporary workaround for Monado bug"; pub const MAX_OVERLAY_SETS: usize = 6; pub struct OverlayWindowManager { @@ -126,6 +122,29 @@ where let anchor = OverlayWindowData::from_config(create_anchor(app)?); me.add(anchor, app); + + // FIXME: Temporary workaround for Monado bug + // Monado freaks out if no layers are submitted. + // This creates a panel which is behind your head and is very tiny so you should never be able to see it. + let mut panel = + GuiPanel::new_from_template(app, "gui/dummy.xml", (), NewGuiPanelParams::default()).unwrap(); + panel.update_layout(app)?; + let dummy = OverlayWindowData::from_config(OverlayWindowConfig { + name: DUMMY_NAME.into(), + z_order: u32::MIN, + category: OverlayCategory::Internal, + default_state: OverlayWindowState { + grabbable: false, + interactable: false, + positioning: Positioning::FollowHead { lerp: 1.0 }, + transform: Affine3A::from_scale_rotation_translation(Vec3::ONE * 0.001, Quat::IDENTITY, Vec3::Z), + ..OverlayWindowState::default() + }, + show_on_spawn: true, + global: true, + ..OverlayWindowConfig::from_backend(Box::new(panel)) + }); + me.add(dummy, app); if app.session.config.enable_watch { let watch = OverlayWindowData::from_config(create_watch(app)?); From bdaf063fcc94599e93b3f6253609ce0ff2f446d0 Mon Sep 17 00:00:00 2001 From: art0007i Date: Mon, 1 Jun 2026 09:40:33 +0200 Subject: [PATCH 3/6] use active_state to toggle watch --- wayvr/src/overlays/watch.rs | 2 +- wayvr/src/windowing/manager.rs | 71 +++++++++++++++------------------- 2 files changed, 33 insertions(+), 40 deletions(-) diff --git a/wayvr/src/overlays/watch.rs b/wayvr/src/overlays/watch.rs index 596d2a4d..fa4b85c2 100644 --- a/wayvr/src/overlays/watch.rs +++ b/wayvr/src/overlays/watch.rs @@ -156,7 +156,7 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result { angle_fade: true, ..OverlayWindowState::default() }, - show_on_spawn: true, + show_on_spawn: app.session.config.enable_watch, global: true, ..OverlayWindowConfig::from_backend(Box::new(panel)) }) diff --git a/wayvr/src/windowing/manager.rs b/wayvr/src/windowing/manager.rs index 642451e4..de8b5ecd 100644 --- a/wayvr/src/windowing/manager.rs +++ b/wayvr/src/windowing/manager.rs @@ -47,7 +47,7 @@ pub struct OverlayWindowManager { /// Usually the same as current_set, except it keeps its value when current_set is hidden. restore_set: usize, anchor_local: Affine3A, - watch_id: Option, + watch_id: OverlayID, keyboard_id: OverlayID, edit_mode: bool, dropped_overlays: VecDeque>, @@ -67,7 +67,7 @@ where sets: vec![OverlayWindowSet::default()], global_set: OverlayWindowSet::default(), anchor_local: Affine3A::from_translation(Vec3::NEG_Z), - watch_id: None, // set down below + watch_id: OverlayID::null(), // set down below keyboard_id: OverlayID::null(), // set down below edit_mode: false, dropped_overlays: VecDeque::with_capacity(8), @@ -146,10 +146,8 @@ where }); me.add(dummy, app); - if app.session.config.enable_watch { - let watch = OverlayWindowData::from_config(create_watch(app)?); - me.watch_id = Some(me.add(watch, app)); - } + let watch = OverlayWindowData::from_config(create_watch(app)?); + me.watch_id = me.add(watch, app); let dash_frontend = OverlayWindowData::from_config(create_dash_frontend(app)?); me.add(dash_frontend, app); @@ -170,7 +168,7 @@ where me.restore_layout(app); me.overlays_changed(app)?; - for id in [me.watch_id, Some(me.keyboard_id)].iter().filter_map(|x| *x) { + for id in [me.watch_id, me.keyboard_id] { for ev in [ OverlayEventData::NumSetsChanged(me.sets.len()), OverlayEventData::EditModeChanged(false), @@ -309,12 +307,13 @@ where self.sets_changed(app); } OverlayTask::SettingsChanged => { - if app.session.config.enable_watch != self.watch_id.is_some() { - if let Some(watch_id) = self.watch_id.take() { - self.overlays.remove(watch_id); - } else { - let watch = OverlayWindowData::from_config(create_watch(app)?); - self.watch_id = Some(self.add(watch, app)); + if let Some(watch) = self.mut_by_id(self.watch_id) { + if app.session.config.enable_watch != watch.config.active_state.is_some() { + if watch.config.active_state.is_some() { + watch.config.deactivate(); + } else { + watch.config.activate(app); + } } } @@ -470,8 +469,7 @@ impl OverlayWindowManager { } // global overlays; watch, toast - if let Some(watch_id) = self.watch_id { - for oid in &[watch_id] { + for oid in &[self.watch_id] { let Some(o) = self.get_by_id(*oid) else { break; }; @@ -482,7 +480,6 @@ impl OverlayWindowManager { .config .global_set .insert(o.config.name.clone(), state.clone()); - } } // BackendAttrib @@ -619,18 +616,16 @@ impl OverlayWindowManager { } } } - if let Some(watch_id) = self.watch_id { - if changed && let Some(watch) = self.mut_by_id(watch_id) { - watch - .config - .active_state - .iter_mut() - .for_each(|f| f.grabbable = enabled); - watch - .config - .backend - .notify(app, OverlayEventData::EditModeChanged(enabled))?; - } + if changed && let Some(watch) = self.mut_by_id(self.watch_id) { + watch + .config + .active_state + .iter_mut() + .for_each(|f| f.grabbable = enabled); + watch + .config + .backend + .notify(app, OverlayEventData::EditModeChanged(enabled))?; } Ok(()) } @@ -858,7 +853,7 @@ impl OverlayWindowManager { } self.current_set = new_set; - for id in [self.watch_id, Some(self.keyboard_id)].iter().filter_map(|x| *x) { + for id in [self.watch_id, self.keyboard_id] { let _ = self.mut_by_id(id).context("Missing overlay").and_then(|o| { o.config .backend @@ -911,7 +906,7 @@ impl OverlayWindowManager { } let meta: Rc<[OverlayMeta]> = meta.into(); - for id in [self.watch_id, Some(self.keyboard_id)].iter().filter_map(|x| *x) { + for id in [self.watch_id, self.keyboard_id] { let _ = self.mut_by_id(id).context("Missing overlay").and_then(|o| { o.config .backend @@ -936,7 +931,7 @@ impl OverlayWindowManager { } let vis: Rc<[OverlayID]> = vis.into(); - for id in [self.watch_id, Some(self.keyboard_id)].iter().filter_map(|x| *x) { + for id in [self.watch_id, self.keyboard_id] { let _ = self.mut_by_id(id).context("Missing overlay").and_then(|o| { o.config .backend @@ -949,7 +944,7 @@ impl OverlayWindowManager { fn sets_changed(&mut self, app: &mut AppState) { let len = self.sets.len(); - for id in [self.watch_id, Some(self.keyboard_id)].iter().filter_map(|x| *x) { + for id in [self.watch_id, self.keyboard_id] { if let Some(o) = self.mut_by_id(id) { let _ = o .config @@ -962,13 +957,11 @@ impl OverlayWindowManager { #[allow(clippy::unnecessary_wraps)] pub fn devices_changed(&mut self, app: &mut AppState) -> anyhow::Result<()> { - if let Some(watch_id) = self.watch_id { - if let Some(watch) = self.mut_by_id(watch_id) { - let _ = watch - .config - .backend - .notify(app, OverlayEventData::DevicesChanged); - } + if let Some(watch) = self.mut_by_id(self.watch_id) { + let _ = watch + .config + .backend + .notify(app, OverlayEventData::DevicesChanged); } Ok(()) From 1b4cabac4d860fee7b6977753714f2b9c03ad9fe Mon Sep 17 00:00:00 2001 From: art0007i Date: Mon, 1 Jun 2026 17:25:41 +0200 Subject: [PATCH 4/6] use the other monado workaround --- wayvr/src/assets/gui/dummy.xml | 10 ---------- wayvr/src/backend/openxr/mod.rs | 31 ++++++++++++++++++++++++++++++- wayvr/src/windowing/manager.rs | 32 ++++---------------------------- 3 files changed, 34 insertions(+), 39 deletions(-) delete mode 100644 wayvr/src/assets/gui/dummy.xml diff --git a/wayvr/src/assets/gui/dummy.xml b/wayvr/src/assets/gui/dummy.xml deleted file mode 100644 index a16ff579..00000000 --- a/wayvr/src/assets/gui/dummy.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/wayvr/src/backend/openxr/mod.rs b/wayvr/src/backend/openxr/mod.rs index ba462b35..9ebab6a4 100644 --- a/wayvr/src/backend/openxr/mod.rs +++ b/wayvr/src/backend/openxr/mod.rs @@ -5,6 +5,7 @@ use std::{ time::{Duration, Instant}, }; +use glam::{Affine3A, Vec3}; use input::OpenXrInputSource; use openxr as xr; use skybox::{Skybox, create_skybox}; @@ -21,7 +22,7 @@ use crate::{ }, config::{save_settings, save_state}, graphics::{GpuFutures, init_openxr_graphics}, - overlays::toast::Toast, + overlays::{toast::Toast, watch::WATCH_NAME}, state::AppState, subsystem::notifications::NotificationManager, windowing::{ @@ -140,6 +141,8 @@ pub fn openxr_run( lines.allocate(&xr_state, &app)?, ]; + let watch_id = overlays.lookup(WATCH_NAME).unwrap(); // want panic + let mut input_source = input::OpenXrInputSource::new(&xr_state)?; let mut session_running = false; @@ -342,6 +345,22 @@ pub fn openxr_run( app.hid_provider.inner.commit(); + let watch = overlays.mut_by_id(watch_id).unwrap(); // want panic + if watch.config.active_state.is_none() { + watch.config.activate(&mut app); + } + let watch_state = watch.config.active_state.as_mut().unwrap(); + let watch_transform = watch_state.transform; + if watch_state.alpha < 0.05 || !app.session.config.enable_watch { + //FIXME: Temporary workaround for Monado bug + watch_state.transform = Affine3A::from_scale(Vec3 { + x: 0.001, + y: 0.001, + z: 0.001, + }); + watch_state.alpha = 0.02; // visible but not really. Monado freaks out if no layers are submitted. + } + if let Err(e) = crate::ipc::events::tick_events::(&mut app, &mut overlays) { @@ -491,6 +510,16 @@ pub fn openxr_run( } delete_queue.retain(|(_, frame)| *frame > cur_frame); + + //FIXME: Temporary workaround for Monado bug + let watch = overlays.mut_by_id(watch_id).unwrap(); // want panic + + if let Some(state) = watch.config.active_state.as_mut() { + state.transform = watch_transform + } + if !app.session.config.enable_watch { + watch.config.deactivate(); + } } // main_loop if let (Some(blocker), Some(monado)) = (blocker, app.monado_state.as_mut()) { diff --git a/wayvr/src/windowing/manager.rs b/wayvr/src/windowing/manager.rs index de8b5ecd..e558618d 100644 --- a/wayvr/src/windowing/manager.rs +++ b/wayvr/src/windowing/manager.rs @@ -5,17 +5,17 @@ use std::{ }; use anyhow::Context; -use glam::{Affine3A, Quat, Vec3, Vec3A}; +use glam::{Affine3A, Vec3, Vec3A}; use slotmap::{Key, SecondaryMap, SlotMap}; use wgui::log::LogErr; use wlx_common::{ astr_containers::{AStrMap, AStrMapExt}, config::SerializedWindowSet, - overlays::{BackendAttrib, BackendAttribValue, ToastTopic}, windowing::{OverlayWindowState, Positioning}, + overlays::{BackendAttrib, BackendAttribValue, ToastTopic}, }; use crate::{ - FRAME_COUNTER, backend::task::{OverlayTask, ToggleMode}, config::save_state, gui::panel::{GuiPanel, NewGuiPanelParams}, overlays::{ + FRAME_COUNTER, backend::task::{OverlayTask, ToggleMode}, config::save_state, overlays::{ anchor::{create_anchor, create_grab_help}, custom::create_custom, dashboard::{DASH_NAME, create_dash_frontend}, @@ -29,11 +29,10 @@ use crate::{ backend::{OverlayEventData, OverlayMeta}, set::OverlayWindowSet, snap_upright, - window::{OverlayCategory, OverlayWindowConfig, OverlayWindowData}, + window::{OverlayCategory, OverlayWindowData}, } }; -pub const DUMMY_NAME: &str = "FIXME: Temporary workaround for Monado bug"; pub const MAX_OVERLAY_SETS: usize = 6; pub struct OverlayWindowManager { @@ -123,29 +122,6 @@ where let anchor = OverlayWindowData::from_config(create_anchor(app)?); me.add(anchor, app); - // FIXME: Temporary workaround for Monado bug - // Monado freaks out if no layers are submitted. - // This creates a panel which is behind your head and is very tiny so you should never be able to see it. - let mut panel = - GuiPanel::new_from_template(app, "gui/dummy.xml", (), NewGuiPanelParams::default()).unwrap(); - panel.update_layout(app)?; - let dummy = OverlayWindowData::from_config(OverlayWindowConfig { - name: DUMMY_NAME.into(), - z_order: u32::MIN, - category: OverlayCategory::Internal, - default_state: OverlayWindowState { - grabbable: false, - interactable: false, - positioning: Positioning::FollowHead { lerp: 1.0 }, - transform: Affine3A::from_scale_rotation_translation(Vec3::ONE * 0.001, Quat::IDENTITY, Vec3::Z), - ..OverlayWindowState::default() - }, - show_on_spawn: true, - global: true, - ..OverlayWindowConfig::from_backend(Box::new(panel)) - }); - me.add(dummy, app); - let watch = OverlayWindowData::from_config(create_watch(app)?); me.watch_id = me.add(watch, app); From 48a316e90bf363eaf1093db87adf6d23d54729e0 Mon Sep 17 00:00:00 2001 From: art0007i Date: Mon, 1 Jun 2026 17:28:22 +0200 Subject: [PATCH 5/6] fix whitespace --- wayvr/src/windowing/manager.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wayvr/src/windowing/manager.rs b/wayvr/src/windowing/manager.rs index e558618d..471bc92d 100644 --- a/wayvr/src/windowing/manager.rs +++ b/wayvr/src/windowing/manager.rs @@ -121,7 +121,7 @@ where let anchor = OverlayWindowData::from_config(create_anchor(app)?); me.add(anchor, app); - + let watch = OverlayWindowData::from_config(create_watch(app)?); me.watch_id = me.add(watch, app); From ac09483bfb5f43e54832df625f04e2d667793d28 Mon Sep 17 00:00:00 2001 From: art0007i Date: Mon, 1 Jun 2026 20:57:17 +0200 Subject: [PATCH 6/6] cargo fmt --- dash-frontend/src/tab/settings/tab_features.rs | 2 +- wayvr/src/backend/openxr/mod.rs | 2 +- wayvr/src/windowing/manager.rs | 11 ++++++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/dash-frontend/src/tab/settings/tab_features.rs b/dash-frontend/src/tab/settings/tab_features.rs index e84d89b1..266a7334 100644 --- a/dash-frontend/src/tab/settings/tab_features.rs +++ b/dash-frontend/src/tab/settings/tab_features.rs @@ -1,6 +1,6 @@ use crate::tab::settings::{ - macros::{options_category, options_checkbox, options_range_f32, options_slider_f32}, SettingType, SettingsMountParams, SettingsTab, + macros::{options_category, options_checkbox, options_range_f32, options_slider_f32}, }; pub struct State {} diff --git a/wayvr/src/backend/openxr/mod.rs b/wayvr/src/backend/openxr/mod.rs index 9ebab6a4..75294aea 100644 --- a/wayvr/src/backend/openxr/mod.rs +++ b/wayvr/src/backend/openxr/mod.rs @@ -513,7 +513,7 @@ pub fn openxr_run( //FIXME: Temporary workaround for Monado bug let watch = overlays.mut_by_id(watch_id).unwrap(); // want panic - + if let Some(state) = watch.config.active_state.as_mut() { state.transform = watch_transform } diff --git a/wayvr/src/windowing/manager.rs b/wayvr/src/windowing/manager.rs index 471bc92d..8b859ff0 100644 --- a/wayvr/src/windowing/manager.rs +++ b/wayvr/src/windowing/manager.rs @@ -15,7 +15,10 @@ use wlx_common::{ }; use crate::{ - FRAME_COUNTER, backend::task::{OverlayTask, ToggleMode}, config::save_state, overlays::{ + FRAME_COUNTER, + backend::task::{OverlayTask, ToggleMode}, + config::save_state, + overlays::{ anchor::{create_anchor, create_grab_help}, custom::create_custom, dashboard::{DASH_NAME, create_dash_frontend}, @@ -24,13 +27,15 @@ use crate::{ screen::create_screens, toast::Toast, watch::{WATCH_NAME, create_watch}, - }, state::AppState, windowing::{ + }, + state::AppState, + windowing::{ OverlayID, OverlaySelector, backend::{OverlayEventData, OverlayMeta}, set::OverlayWindowSet, snap_upright, window::{OverlayCategory, OverlayWindowData}, - } + }, }; pub const MAX_OVERLAY_SETS: usize = 6;