input blocking improvements (#399)

* move input blocking hover logic into InteractionState

* add per-window input blocking setting

* OpenVR: per-hand input blocking

* move block_input setting into OverlayWindowState
This commit is contained in:
Orion
2026-01-18 04:09:22 +01:00
committed by GitHub
parent 2e1d07abc2
commit 0181119393
15 changed files with 75 additions and 30 deletions

View File

@@ -72,8 +72,9 @@
<div id="pos_align_to_hmd" >
<CheckBox id="align_box" translation="EDIT_MODE.ALIGN_TO_HMD" tooltip_side="bottom" />
</div>
<div id="pos_global">
<div id="pos_global" gap="16" justify_content="center" align_items="center">
<CheckBox id="global_box" translation="EDIT_MODE.GLOBAL" tooltip_side="bottom" />
<CheckBox id="block_input_box" translation="EDIT_MODE.BLOCK_INPUT" tooltip_side="bottom" />
</div>
</div>
</div>

View File

@@ -39,6 +39,7 @@
"MOVE_PRESS_AND_DRAG": "Verschieben (drücken & ziehen)",
"OPACITY": "Undurchsichtigkeit",
"POSITIONING": "Positionierung",
"BLOCK_GAME_INPUT": "Spieleingabe blockieren",
"RESIZE_PRESS_AND_DRAG": "Größe ändern (gedrückt halten & ziehen)",
"POS_STATIC": "Statisch: Bleibt an Ort und Stelle und wird niemals neu zentriert.",
"POS_ANCHORED": "Verankert: Bewegt sich zusammen mit dem Rest des Sets. Standard.",

View File

@@ -45,6 +45,7 @@
"POS_STATIC": "Static: Not part of any set, no recenter.",
"POSITIONING": "Positioning",
"GLOBAL": "Always visible",
"BLOCK_INPUT": "Block game input",
"RESIZE_PRESS_AND_DRAG": "Resize (press & drag)",
"STEREO_3D_MODE": {
"ADJUST_MOUSE": "Adjust mouse",

View File

@@ -39,6 +39,7 @@
"MOVE_PRESS_AND_DRAG": "Mover (presionar y arrastrar)",
"OPACITY": "Opacidad",
"POSITIONING": "Posicionamiento",
"BLOCK_GAME_INPUT": "Bloquear entrada del juego",
"RESIZE_PRESS_AND_DRAG": "Redimensionar (presionar y arrastrar)",
"POS_STATIC": "Estático: Permanece en su lugar y nunca se recentra.",
"POS_ANCHORED": "Anclado: Se mueve junto con el resto del conjunto. Predeterminado.",

View File

@@ -38,6 +38,7 @@
"POS_HMD": "Segui l'HMD.",
"POS_STATIC": "Statico: non fa parte di alcun set, senza recentratura.",
"POSITIONING": "Posizionamento",
"BLOCK_GAME_INPUT": "Blocca l'input di gioco",
"RESIZE_PRESS_AND_DRAG": "Ridimensiona (premi e trascina)",
"STEREO_3D_MODE": {
"SPLIT_BOTTOM_TOP": "DAL BASSO→IN ALTO",

View File

@@ -39,6 +39,7 @@
"MOVE_PRESS_AND_DRAG": "移動(押してドラッグ)",
"OPACITY": "不透明度",
"POSITIONING": "位置付け",
"BLOCK_GAME_INPUT": "ゲームの入力をブロック",
"RESIZE_PRESS_AND_DRAG": "サイズ変更(押してドラッグ)",
"POS_STATIC": "固定:置かれた場所に留まり、再センタリングされません。",
"POS_ANCHORED": "アンカー:セット内の他のウィンドウと共に移動します。デフォルト。",

View File

@@ -39,6 +39,7 @@
"MOVE_PRESS_AND_DRAG": "Przesuń (naciśnij i przeciągnij)",
"OPACITY": "Przezroczystość",
"POSITIONING": "Pozycjonowanie",
"BLOCK_INPUT": "Blokuj input z gry",
"RESIZE_PRESS_AND_DRAG": "Zmień rozmiar (naciśnij i przeciągnij)",
"POS_STATIC": "Statyczne: Pozostaje w miejscu i nigdy nie jest ponownie wyśrodkowywane.",
"POS_ANCHORED": "Zakotwiczone: Porusza się razem z resztą zestawu. Domyślne.",

View File

@@ -38,6 +38,7 @@
"POS_HMD": "跟随头显 (HMD)。",
"POS_STATIC": "静态:不属于任何集合,不重新居中。",
"POSITIONING": "定位",
"BLOCK_GAME_INPUT": "屏蔽游戏输入",
"RESIZE_PRESS_AND_DRAG": "调整大小 (按住并拖拽)",
"STEREO_3D_MODE": {
"SPLIT_BOTTOM_TOP": "下→上",

View File

@@ -3,7 +3,7 @@ use std::process::{Child, Command};
use std::sync::Arc;
use std::time::Instant;
use glam::{Affine3A, Mat3A, Vec2, Vec3, Vec3A, Vec3Swizzles};
use glam::{Affine3A, Vec2, Vec3A, Vec3Swizzles};
use idmap_derive::IntegerId;
use smallvec::{SmallVec, smallvec};
@@ -209,6 +209,7 @@ pub struct InteractionState {
pub hovered_id: Option<OverlayID>,
pub next_push: Instant,
pub haptics: Option<f32>,
pub should_block_input: bool,
}
impl Default for InteractionState {
@@ -220,6 +221,7 @@ impl Default for InteractionState {
hovered_id: None,
next_push: Instant::now(),
haptics: None,
should_block_input: false,
}
}
}
@@ -393,7 +395,7 @@ where
for (idx, hit) in hits.iter().enumerate() {
populate_lines(
lines,
&mut app.input_state.pointers[idx],
&app.input_state.pointers[idx],
hit.0,
&app.input_state.hmd,
);
@@ -471,6 +473,14 @@ 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);
#[cfg(debug_assertions)]
log::trace!("Hit: {} {:?}", hovered.config.name, hit);
@@ -544,9 +554,11 @@ fn handle_no_hit<O>(
}
}
let pointer = &mut app.input_state.pointers[pointer_idx];
pointer.interaction.should_block_input = false;
// in case click released while not aiming at anything
// send release event to overlay that was originally clicked
let pointer = &mut app.input_state.pointers[pointer_idx];
if !pointer.now.click
&& pointer.before.click
&& let Some(clicked_id) = pointer.interaction.clicked_id.take()

View File

@@ -15,7 +15,6 @@ use wlx_common::config_io;
use crate::{
backend::input::{Haptics, TrackedDevice, TrackedDeviceRole},
state::AppState,
windowing::OverlayID,
};
use super::helpers::{Affine3AConvert, OVRError};
@@ -143,26 +142,41 @@ impl OpenVrInputSource {
input: &mut InputManager,
system: &mut SystemManager,
app: &mut AppState,
watch_id: OverlayID,
) {
let should_block_input = app.input_state.pointers.iter().any(|p| {
p.interaction.hovered_id.is_some_and(|id| {
id != watch_id || !app.session.config.block_game_input_ignore_watch
})
}) && app.session.config.block_game_input;
let should_block_input_left = app.input_state.pointers[0].interaction.should_block_input
&& app.session.config.block_game_input;
let aas = ActiveActionSet(ovr_overlay::sys::VRActiveActionSet_t {
let should_block_input_right = app.input_state.pointers[1].interaction.should_block_input
&& app.session.config.block_game_input;
let aas_left = ActiveActionSet(ovr_overlay::sys::VRActiveActionSet_t {
ulActionSet: self.set_hnd.0,
ulRestrictedToDevice: 0,
ulRestrictedToDevice: self.hands[0].input_hnd.0,
ulSecondaryActionSet: 0,
unPadding: 0,
// the range between 0x01000000 and 0x01FFFFFF overrides game action sets as long as
// global input from overlays is enabled in SteamVR developer settings
// (taken from https://github.com/ValveSoftware/openvr/issues/1236)
nPriority: if should_block_input { 0x01000000 } else { 0x0 },
nPriority: if should_block_input_left {
0x01000000
} else {
0x0
},
});
let _ = input.update_actions(&mut [aas]);
let aas_right = ActiveActionSet(ovr_overlay::sys::VRActiveActionSet_t {
ulActionSet: self.set_hnd.0,
ulRestrictedToDevice: self.hands[1].input_hnd.0,
ulSecondaryActionSet: 0,
unPadding: 0,
nPriority: if should_block_input_right {
0x01000000
} else {
0x0
},
});
let _ = input.update_actions(&mut [aas_left, aas_right]);
let devices = system.get_device_to_absolute_tracking_pose(universe.clone(), 0.005);
app.input_state.hmd = devices[0].mDeviceToAbsoluteTracking.to_affine();

View File

@@ -241,13 +241,7 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
let universe = playspace.get_universe();
app.input_state.pre_update();
input_source.update(
universe.clone(),
&mut input_mgr,
&mut system_mgr,
&mut app,
watch_id,
);
input_source.update(universe.clone(), &mut input_mgr, &mut system_mgr, &mut app);
app.input_state.post_update(&app.session);
if app

View File

@@ -1,7 +1,7 @@
use libmonado::{ClientState, Monado};
use log::{trace, warn};
use crate::{state::AppState, windowing::OverlayID};
use crate::state::AppState;
pub(super) struct InputBlocker {
blocked_last_frame: bool,
@@ -14,16 +14,17 @@ impl InputBlocker {
}
}
pub fn update(&mut self, app: &mut AppState, watch_id: OverlayID) {
pub fn update(&mut self, app: &mut AppState) {
let Some(monado) = &mut app.monado else {
return; // monado not available
};
let should_block = app.input_state.pointers.iter().any(|p| {
p.interaction.hovered_id.is_some_and(|id| {
id != watch_id || !app.session.config.block_game_input_ignore_watch
})
}) && app.session.config.block_game_input;
let should_block = 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) => {

View File

@@ -274,7 +274,7 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
app.input_state.post_update(&app.session);
if let Some(ref mut blocker) = blocker {
blocker.update(&mut app, watch_id);
blocker.update(&mut app);
}
if app

View File

@@ -432,6 +432,7 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
set_up_checkbox(&mut panel, "additive_box", cb_assign_additive)?;
set_up_checkbox(&mut panel, "align_box", cb_assign_align)?;
set_up_checkbox(&mut panel, "global_box", cb_assign_global)?;
set_up_checkbox(&mut panel, "block_input_box", cb_assign_block_input)?;
set_up_checkbox(
&mut panel,
"stereo_full_frame_box",
@@ -498,6 +499,11 @@ fn reset_panel(
.fetch_component_as::<ComponentCheckbox>("global_box")?;
c.set_checked(&mut common, owc.global);
let c = panel
.parser_state
.fetch_component_as::<ComponentCheckbox>("block_input_box")?;
c.set_checked(&mut common, state.block_input);
panel
.state
.pos
@@ -601,6 +607,14 @@ const fn cb_assign_global(_app: &mut AppState, owc: &mut OverlayWindowConfig, gl
owc.global = global;
}
const fn cb_assign_block_input(
_app: &mut AppState,
owc: &mut OverlayWindowConfig,
block_input: bool,
) {
owc.active_state.as_mut().unwrap().block_input = block_input;
}
fn cb_assign_stereo_full_frame(
_app: &mut AppState,
owc: &mut OverlayWindowConfig,

View File

@@ -76,6 +76,7 @@ pub struct OverlayWindowState {
pub curvature: Option<f32>,
pub additive: bool,
pub saved_transform: Option<Affine3A>,
pub block_input: bool,
}
impl Default for OverlayWindowState {
@@ -89,6 +90,7 @@ impl Default for OverlayWindowState {
transform: Affine3A::IDENTITY,
additive: false,
saved_transform: None,
block_input: true,
}
}
}