From d713302d0d66b523c963c5b8be8a904100f1176e Mon Sep 17 00:00:00 2001 From: Sapphire Date: Wed, 4 Feb 2026 13:03:34 -0600 Subject: [PATCH] add option to block poses when using keyboard (OpenXR only) --- dash-frontend/assets/lang/en.json | 2 ++ dash-frontend/src/tab/settings.rs | 5 ++++ wayvr/src/backend/input.rs | 23 ++++++++++----- wayvr/src/backend/openxr/blocker.rs | 46 ++++++++++++++++++----------- wayvr/src/config.rs | 2 ++ wayvr/src/res/config.yaml | 4 +++ wlx-common/src/config.rs | 3 ++ 7 files changed, 60 insertions(+), 25 deletions(-) diff --git a/dash-frontend/assets/lang/en.json b/dash-frontend/assets/lang/en.json index c3e594c..524d0a5 100644 --- a/dash-frontend/assets/lang/en.json +++ b/dash-frontend/assets/lang/en.json @@ -36,6 +36,8 @@ "BLOCK_GAME_INPUT_HELP": "Blocks all input when an overlay is hovered", "BLOCK_GAME_INPUT_IGNORE_WATCH": "Ignore watch when blocking input", "BLOCK_GAME_INPUT_IGNORE_WATCH_HELP": "Do not block input when watch is hovered", + "BLOCK_POSES_ON_KBD_INTERACTION": "Block poses when interacting with keyboard", + "BLOCK_POSES_ON_KBD_INTERACTION_HELP": "Blocks the game from receiving poses when the keyboard is hovered and 'Block game input' is enabled", "CAPTURE_METHOD": "Wayland screen capture", "CAPTURE_METHOD_HELP": "Try changing this if you are\nexperiencing black or glitchy screens", "CLEAR_PIPEWIRE_TOKENS": "Clear PipeWire tokens", diff --git a/dash-frontend/src/tab/settings.rs b/dash-frontend/src/tab/settings.rs index 5266b4d..c678725 100644 --- a/dash-frontend/src/tab/settings.rs +++ b/dash-frontend/src/tab/settings.rs @@ -227,6 +227,7 @@ enum SettingType { LeftHandedMouse, BlockGameInput, BlockGameInputIgnoreWatch, + BlockPosesOnKbdInteraction, SpaceDragMultiplier, UseSkybox, UsePassthrough, @@ -260,6 +261,7 @@ impl SettingType { Self::LeftHandedMouse => &mut config.left_handed_mouse, Self::BlockGameInput => &mut config.block_game_input, Self::BlockGameInputIgnoreWatch => &mut config.block_game_input_ignore_watch, + Self::BlockPosesOnKbdInteraction => &mut config.block_poses_on_kbd_interaction, Self::UseSkybox => &mut config.use_skybox, Self::UsePassthrough => &mut config.use_passthrough, Self::ScreenRenderDown => &mut config.screen_render_down, @@ -363,6 +365,7 @@ impl SettingType { Self::LeftHandedMouse => Ok("APP_SETTINGS.LEFT_HANDED_MOUSE"), Self::BlockGameInput => Ok("APP_SETTINGS.BLOCK_GAME_INPUT"), Self::BlockGameInputIgnoreWatch => Ok("APP_SETTINGS.BLOCK_GAME_INPUT_IGNORE_WATCH"), + Self::BlockPosesOnKbdInteraction => Ok("APP_SETTINGS.BLOCK_POSES_ON_KBD_INTERACTION"), Self::SpaceDragMultiplier => Ok("APP_SETTINGS.SPACE_DRAG_MULTIPLIER"), Self::UseSkybox => Ok("APP_SETTINGS.USE_SKYBOX"), Self::UsePassthrough => Ok("APP_SETTINGS.USE_PASSTHROUGH"), @@ -390,6 +393,7 @@ impl SettingType { Self::LeftHandedMouse => Some("APP_SETTINGS.LEFT_HANDED_MOUSE_HELP"), Self::BlockGameInput => Some("APP_SETTINGS.BLOCK_GAME_INPUT_HELP"), Self::BlockGameInputIgnoreWatch => Some("APP_SETTINGS.BLOCK_GAME_INPUT_IGNORE_WATCH_HELP"), + Self::BlockPosesOnKbdInteraction => Some("APP_SETTINGS.BLOCK_POSES_ON_KBD_INTERACTION_HELP"), Self::UseSkybox => Some("APP_SETTINGS.USE_SKYBOX_HELP"), Self::UsePassthrough => Some("APP_SETTINGS.USE_PASSTHROUGH_HELP"), Self::ScreenRenderDown => Some("APP_SETTINGS.SCREEN_RENDER_DOWN_HELP"), @@ -726,6 +730,7 @@ impl TabSettings { slider_f32!(mp, c, SettingType::SpaceDragMultiplier, -10.0, 10.0, 0.5); checkbox!(mp, c, SettingType::BlockGameInput); checkbox!(mp, c, SettingType::BlockGameInputIgnoreWatch); + checkbox!(mp, c, SettingType::BlockPosesOnKbdInteraction); } TabNameEnum::Controls => { let c = category!(mp, root, "APP_SETTINGS.CONTROLS", "dashboard/controller.svg")?; diff --git a/wayvr/src/backend/input.rs b/wayvr/src/backend/input.rs index 25061bd..43da696 100644 --- a/wayvr/src/backend/input.rs +++ b/wayvr/src/backend/input.rs @@ -13,6 +13,7 @@ use wlx_common::windowing::{OverlayWindowState, Positioning}; use crate::backend::task::{InputTask, OverlayTask}; use crate::overlays::anchor::{ANCHOR_NAME, GRAB_HELP_NAME}; +use crate::overlays::keyboard::KEYBOARD_NAME; use crate::overlays::watch::WATCH_NAME; use crate::state::{AppSession, AppState}; use crate::subsystem::hid::WheelDelta; @@ -210,6 +211,7 @@ pub struct InteractionState { pub next_push: Instant, pub haptics: Option, pub should_block_input: bool, + pub should_block_poses: bool, } impl Default for InteractionState { @@ -222,6 +224,7 @@ impl Default for InteractionState { next_push: Instant::now(), haptics: None, should_block_input: false, + should_block_poses: false, } } } @@ -473,13 +476,18 @@ where hit.primary = true; } - pointer.interaction.should_block_input = hovered - .config - .active_state - .as_ref() - .map_or(false, |state| state.block_input) - && (hovered.config.name.as_ref() != WATCH_NAME - || !app.session.config.block_game_input_ignore_watch); + if let Some(state) = hovered.config.active_state.as_ref() { + pointer.interaction.should_block_input = state.block_input + && (hovered.config.name.as_ref() != WATCH_NAME + || !app.session.config.block_game_input_ignore_watch); + + pointer.interaction.should_block_poses = state.block_input + && app.session.config.block_poses_on_kbd_interaction + && hovered.config.name.as_ref() == KEYBOARD_NAME; + } else { + pointer.interaction.should_block_input = false; + pointer.interaction.should_block_poses = false; + } #[cfg(debug_assertions)] log::trace!("Hit: {} {:?}", hovered.config.name, hit); @@ -556,6 +564,7 @@ fn handle_no_hit( let pointer = &mut app.input_state.pointers[pointer_idx]; pointer.interaction.should_block_input = false; + pointer.interaction.should_block_poses = false; // in case click released while not aiming at anything // send release event to overlay that was originally clicked diff --git a/wayvr/src/backend/openxr/blocker.rs b/wayvr/src/backend/openxr/blocker.rs index 8470311..981e309 100644 --- a/wayvr/src/backend/openxr/blocker.rs +++ b/wayvr/src/backend/openxr/blocker.rs @@ -5,14 +5,16 @@ use crate::state::AppState; pub(super) struct InputBlocker { use_io_blocks: bool, - blocked_last_frame: bool, + inputs_blocked_last_frame: bool, + poses_blocked_last_frame: bool, } impl InputBlocker { pub fn new(monado: &Monado) -> Self { Self { use_io_blocks: monado.get_api_version() >= Version::new(1, 6, 0), - blocked_last_frame: false, + inputs_blocked_last_frame: false, + poses_blocked_last_frame: false, } } @@ -21,29 +23,36 @@ impl InputBlocker { return; // monado not available }; - let should_block = app + let should_block_inputs = app .input_state .pointers .iter() .any(|p| p.interaction.should_block_input) && app.session.config.block_game_input; - match (should_block, self.blocked_last_frame) { - (true, false) => { + let should_block_poses = app + .input_state + .pointers + .iter() + .any(|p| p.interaction.should_block_poses) + && app.session.config.block_poses_on_kbd_interaction; + + if should_block_inputs != self.inputs_blocked_last_frame + || should_block_poses != self.poses_blocked_last_frame + { + if should_block_inputs { trace!("Blocking input"); - self.block_inputs(monado, true); - } - (false, true) => { + } else { trace!("Unblocking input"); - self.block_inputs(monado, false); } - _ => {} + self.block_inputs(monado, should_block_inputs, should_block_poses); } - self.blocked_last_frame = should_block; + self.inputs_blocked_last_frame = should_block_inputs; + self.poses_blocked_last_frame = should_block_poses; } - fn block_inputs(&self, monado: &mut Monado, block: bool) { + fn block_inputs(&self, monado: &mut Monado, block_inputs: bool, block_poses: bool) { match monado.clients() { Ok(clients) => { for mut client in clients { @@ -69,13 +78,14 @@ impl InputBlocker { if state.contains(ClientState::ClientSessionVisible) { let r = if self.use_io_blocks { - client.set_io_blocks(if block { - BlockFlags::BlockInputs.into() - } else { - BlockFlags::None.into() - }) + let flags = match (block_inputs, block_poses) { + (true, true) => BlockFlags::BlockPoses | BlockFlags::BlockInputs, + (true, false) => BlockFlags::BlockInputs.into(), + (false, _) => BlockFlags::None.into(), + }; + client.set_io_blocks(flags) } else { - client.set_io_active(!block) + client.set_io_active(!block_inputs) }; if let Err(e) = r { warn!("Failed to set io active for client: {e}"); diff --git a/wayvr/src/config.rs b/wayvr/src/config.rs index b2b6dfd..da2a811 100644 --- a/wayvr/src/config.rs +++ b/wayvr/src/config.rs @@ -126,6 +126,7 @@ pub struct AutoSettings { pub left_handed_mouse: bool, pub block_game_input: bool, pub block_game_input_ignore_watch: bool, + pub block_poses_on_kbd_interaction: bool, pub space_drag_multiplier: f32, pub use_skybox: bool, pub use_passthrough: bool, @@ -174,6 +175,7 @@ pub fn save_settings(config: &GeneralConfig) -> anyhow::Result<()> { left_handed_mouse: config.left_handed_mouse, block_game_input: config.block_game_input, block_game_input_ignore_watch: config.block_game_input_ignore_watch, + block_poses_on_kbd_interaction: config.block_poses_on_kbd_interaction, space_drag_multiplier: config.space_drag_multiplier, use_skybox: config.use_skybox, use_passthrough: config.use_passthrough, diff --git a/wayvr/src/res/config.yaml b/wayvr/src/res/config.yaml index bc6561d..76064af 100644 --- a/wayvr/src/res/config.yaml +++ b/wayvr/src/res/config.yaml @@ -102,6 +102,10 @@ ## Do not block input when the watch is hovered. #block_game_input_ignore_watch: true +## Monado/WiVRn only. Do not send hand poses when interacting with the +## keyboard. +#block_poses_on_kbd_interaction: true + ## How fast to drag when the space drag feature is activated #space_drag_multiplier: 1.0 diff --git a/wlx-common/src/config.rs b/wlx-common/src/config.rs index faede03..580cd59 100644 --- a/wlx-common/src/config.rs +++ b/wlx-common/src/config.rs @@ -251,6 +251,9 @@ pub struct GeneralConfig { #[serde(default = "def_true")] pub block_game_input_ignore_watch: bool, + #[serde(default = "def_true")] + pub block_poses_on_kbd_interaction: bool, + #[serde(default = "def_one")] pub space_drag_multiplier: f32,