per-action thresholds

This commit is contained in:
galister 2026-07-02 15:02:01 +09:00
parent 638af5a608
commit b906bc6656
12 changed files with 247 additions and 220 deletions

View File

@ -181,8 +181,6 @@
"WATCH_VIEW_ANGLE_HELP": "Control how the watch fades away",
"GRID_OPACITY": "Floor grid opacity",
"GRID_OPACITY_HELP": "Opacity of the floor grid when the skybox is enabled",
"XR_CLICK_SENSITIVITY": "XR trigger sensitivity",
"XR_CLICK_SENSITIVITY_HELP": "Press and release values for analog triggers",
"XWAYLAND_BY_DEFAULT": "Run apps in Compatibility mode by default",
"SAVE": "Save",
"CANCEL": "Cancel"

View File

@ -316,8 +316,6 @@ enum SettingType {
UseSkybox,
WatchViewAngleMax,
WatchViewAngleMin,
XrClickSensitivity,
XrClickSensitivityRelease,
XwaylandByDefault,
}
@ -378,8 +376,6 @@ impl SettingType {
Self::UiRoundMultiplier => &mut config.ui_round_multiplier,
Self::WatchViewAngleMax => &mut config.watch_view_angle_max,
Self::WatchViewAngleMin => &mut config.watch_view_angle_min,
Self::XrClickSensitivity => &mut config.xr_click_sensitivity,
Self::XrClickSensitivityRelease => &mut config.xr_click_sensitivity_release,
_ => panic!("Requested f32 for non-f32 SettingType"),
}
}
@ -490,8 +486,6 @@ impl SettingType {
Self::UseSkybox => Ok("APP_SETTINGS.USE_SKYBOX"),
Self::WatchViewAngleMax => Ok("APP_SETTINGS.WATCH_VIEW_ANGLE"),
Self::WatchViewAngleMin => Ok("APP_SETTINGS.WATCH_VIEW_ANGLE"),
Self::XrClickSensitivity => Ok("APP_SETTINGS.XR_CLICK_SENSITIVITY"),
Self::XrClickSensitivityRelease => Ok("APP_SETTINGS.XR_CLICK_SENSITIVITY"),
Self::XwaylandByDefault => Ok("APP_SETTINGS.XWAYLAND_BY_DEFAULT"),
}
}
@ -519,8 +513,6 @@ impl SettingType {
Self::UseSkybox => Some("APP_SETTINGS.USE_SKYBOX_HELP"),
Self::WatchViewAngleMax => Some("APP_SETTINGS.WATCH_VIEW_ANGLE_HELP"),
Self::WatchViewAngleMin => Some("APP_SETTINGS.WATCH_VIEW_ANGLE_HELP"),
Self::XrClickSensitivity => Some("APP_SETTINGS.XR_CLICK_SENSITIVITY_HELP"),
Self::XrClickSensitivityRelease => Some("APP_SETTINGS.XR_CLICK_SENSITIVITY_HELP"),
_ => None,
}
}

View File

@ -10,9 +10,7 @@ use crate::{
frontend::FrontendTasks,
tab::settings::{
SettingType, SettingsMountParams, SettingsTab,
macros::{
options_category, options_checkbox, options_dropdown, options_range_f32, options_slider_f32, options_slider_i32,
},
macros::{options_category, options_checkbox, options_dropdown, options_slider_f32, options_slider_i32},
},
views::{ViewUpdateParams, input_profiles},
};
@ -104,15 +102,6 @@ impl State {
if par.feats.openxr {
options_slider_f32(par.mp, c, SettingType::PointerLerpFactor, 0.1, 1.0, 0.1)?;
options_range_f32(
par.mp,
c,
SettingType::XrClickSensitivityRelease,
SettingType::XrClickSensitivity,
0.1,
0.9,
0.1,
)?;
}
options_slider_i32(par.mp, c, SettingType::ClickFreezeTimeMs, 0, 500, 50)?;

View File

@ -1,6 +1,6 @@
use std::rc::Rc;
use anyhow::{bail, Context};
use anyhow::{Context, bail};
use serde::{Deserialize, Serialize};
use strum::{AsRefStr, EnumProperty, EnumString};
use wgui::i18n::Translation;

View File

@ -27,7 +27,7 @@ use crate::{
tab::settings::horiz_cell,
util::{
openxr_bindings_schema::{
BindingsDropdown, ClickType, Component, ControllerProfile, ParsedOpenXrInputPath, Side, SubpathKind,
BindingsDropdown, ClickType, Component, ControllerProfile, ParsedOpenXrInputPath, Side, SubpathKind,
},
popup_manager::{MountPopupOnceParams, MountPopupOnceParamsExtra, PopupHolder, PopupPadding},
wgui_simple,
@ -40,7 +40,7 @@ enum Task {
Save,
Cancel,
OpenContextMenu(glam::Vec2, Vec<context_menu::Cell>),
UpdateThreshold(Rc<str>, f32, f32),
UpdateThreshold(Rc<str>, Side, f32, f32),
}
pub struct Params<'a> {
@ -89,11 +89,15 @@ impl ViewTrait for View {
blueprint: context_menu::Blueprint::Cells(cells),
});
}
Task::UpdateThreshold(action_name, lo, hi) => {
Task::UpdateThreshold(action_name, side, lo, hi) => {
let cur_profile = &mut self.profiles[self.cur_profile_idx];
let action_mut = get_action_mut(cur_profile, &*action_name);
action_mut.threshold = Some([lo, hi]);
if matches!(side, Side::Right) {
action_mut.threshold_right = Some([lo, hi]);
} else {
action_mut.threshold_left = Some([lo, hi]);
}
}
}
}
@ -238,7 +242,13 @@ impl View {
for action in action_names {
let current = get_action_mut(&mut self.profiles[self.cur_profile_idx], action);
input_controls_for_action(&mut mp, self.list_parent, action.into(), &self.controller_profile, current)?;
input_controls_for_action(
&mut mp,
self.list_parent,
action.into(),
&self.controller_profile,
current,
)?;
}
Ok(())
@ -253,27 +263,31 @@ impl View {
let action = cur_profile.pose.get_or_insert_default();
if action.left.is_none() && profile_left.is_some() {
let path = "/user/hand/left/input/aim/pose";
action.left = Some(OneOrMany::One(path.into()));
let path = "/user/hand/left/input/aim/pose";
action.left = Some(OneOrMany::One(path.into()));
}
if action.right.is_none() && profile_right.is_some() {
let path = "/user/hand/right/input/aim/pose";
action.right = Some(OneOrMany::One(path.into()));
let path = "/user/hand/right/input/aim/pose";
action.right = Some(OneOrMany::One(path.into()));
}
let action = cur_profile.haptic.get_or_insert_default();
let has_haptic = profile_left.map(|x| x.find_subpath(SubpathKind::Haptic).is_some()).unwrap_or_default();
let has_haptic = profile_left
.map(|x| x.find_subpath(SubpathKind::Haptic).is_some())
.unwrap_or_default();
if action.left.is_none() && has_haptic {
let path = "/user/hand/left/output/haptic";
action.left = Some(OneOrMany::One(path.into()));
let path = "/user/hand/left/output/haptic";
action.left = Some(OneOrMany::One(path.into()));
}
let has_haptic = profile_right.map(|x| x.find_subpath(SubpathKind::Haptic).is_some()).unwrap_or_default();
let has_haptic = profile_right
.map(|x| x.find_subpath(SubpathKind::Haptic).is_some())
.unwrap_or_default();
if action.right.is_none() && has_haptic {
let path = "/user/hand/right/output/haptic";
action.right = Some(OneOrMany::One(path.into()));
let path = "/user/hand/right/output/haptic";
action.right = Some(OneOrMany::One(path.into()));
}
}
}
@ -381,7 +395,7 @@ fn input_controls_for_action(
action.clone(),
click_type,
profile,
current.threshold,
current.threshold_left,
)?;
let current_right = current.right.as_ref().map(|x| match x {
@ -397,7 +411,7 @@ fn input_controls_for_action(
action,
click_type,
profile,
current.threshold,
current.threshold_right,
)
}
@ -419,13 +433,19 @@ fn input_controls_for_hand(
let parent = horiz_cell(mp.layout, parent)?;
let available_components : Rc<[Component]> = current
let available_components: Rc<[Component]> = current
.as_ref()
.and_then(|par| user_path.find_subpath(par.subpath))
.map(|subp| subp.components)
.unwrap_or_default().into();
.unwrap_or_default()
.into();
let available_subpaths : Rc<[SubpathKind]> = user_path.paths.iter().filter(|x| !x.kind.get_bool("Hidden").unwrap_or_default()).map(|x| x.kind).collect();
let available_subpaths: Rc<[SubpathKind]> = user_path
.paths
.iter()
.filter(|x| !x.kind.get_bool("Hidden").unwrap_or_default())
.map(|x| x.kind)
.collect();
subpath_dropdown(
mp,
@ -452,7 +472,7 @@ fn input_controls_for_hand(
if let Some(component) = current.as_ref().map(|x| x.component)
&& component.is_analog()
{
threshold_slider(mp, parent, action, threshold)?;
threshold_slider(mp, parent, action, side, threshold)?;
}
Ok(())
@ -530,6 +550,7 @@ fn threshold_slider(
mp: &mut MacroParams,
parent: WidgetID,
action: Rc<str>,
side: Side,
current: Option<[f32; 2]>,
) -> anyhow::Result<()> {
let id = mp.idx.to_string();
@ -554,9 +575,9 @@ fn threshold_slider(
let tasks = mp.tasks.clone();
move |_common, e| {
if matches!(e.index, wgui::components::slider::ValueIndex::Primary) {
tasks.push(Task::UpdateThreshold(action.clone(), e.value, current[1]));
tasks.push(Task::UpdateThreshold(action.clone(), side, e.value, current[1]));
} else {
tasks.push(Task::UpdateThreshold(action.clone(), current[0], e.value));
tasks.push(Task::UpdateThreshold(action.clone(), side, current[0], e.value));
}
}
}));
@ -626,18 +647,23 @@ fn create_dropdown<B: 'static + BindingsDropdown>(
Ok(())
}
fn apply_subpath(side_mut: &mut Option<OneOrMany<String>>, side_str: &str, subpath_str: &str, profile: &ControllerProfile,) {
let (Ok(side), Ok(subpath)) = (Side::try_from(side_str), SubpathKind::try_from(subpath_str)) else {
return;
};
let Some(subpath_obj) = profile.find_userpath(side).and_then(|p| p.find_subpath(subpath)) else {
return;
};
fn apply_subpath(
side_mut: &mut Option<OneOrMany<String>>,
side_str: &str,
subpath_str: &str,
profile: &ControllerProfile,
) {
let (Ok(side), Ok(subpath)) = (Side::try_from(side_str), SubpathKind::try_from(subpath_str)) else {
return;
};
let Some(subpath_obj) = profile.find_userpath(side).and_then(|p| p.find_subpath(subpath)) else {
return;
};
let comp : Component = if let Some(first) = side_mut.as_ref().map(|x| match x {
OneOrMany::One(x) => x.as_str(),
OneOrMany::Many(x) => x.first().unwrap().as_str(),
}) {
let comp: Component = if let Some(first) = side_mut.as_ref().map(|x| match x {
OneOrMany::One(x) => x.as_str(),
OneOrMany::Many(x) => x.first().unwrap().as_str(),
}) {
let Ok(parsed) = ParsedOpenXrInputPath::try_from(first) else {
return;
};
@ -647,34 +673,32 @@ fn apply_subpath(side_mut: &mut Option<OneOrMany<String>>, side_str: &str, subpa
parsed_compo = *subpath_obj.components.first().unwrap();
}
parsed_compo
} else {
} else {
*subpath_obj.components.first().unwrap()
};
let comp_str = comp.as_ref().to_lowercase();
*side_mut = Some(OneOrMany::One(format!(
"/user/hand/{side_str}/input/{subpath_str}/{comp_str}"
)));
*side_mut = Some(OneOrMany::One(format!(
"/user/hand/{side_str}/input/{subpath_str}/{comp_str}"
)));
}
fn apply_comp(side_mut: &mut Option<OneOrMany<String>>, side: &str, comp: &str) {
if side_mut.is_none() {
return;
}
if side_mut.is_none() {
return;
}
let first = match side_mut.as_ref().unwrap() {
OneOrMany::One(x) => x.as_str(),
OneOrMany::Many(x) => x.first().unwrap().as_str(),
};
let first = match side_mut.as_ref().unwrap() {
OneOrMany::One(x) => x.as_str(),
OneOrMany::Many(x) => x.first().unwrap().as_str(),
};
let Ok(parsed) = ParsedOpenXrInputPath::try_from(first) else {
return;
};
let Ok(parsed) = ParsedOpenXrInputPath::try_from(first) else {
return;
};
let subpath = parsed.subpath.as_ref().to_lowercase();
let subpath = parsed.subpath.as_ref().to_lowercase();
*side_mut = Some(OneOrMany::One(format!(
"/user/hand/{side}/input/{subpath}/{comp}"
)));
*side_mut = Some(OneOrMany::One(format!("/user/hand/{side}/input/{subpath}/{comp}")));
}

View File

@ -17,7 +17,7 @@ use crate::{
openxr_controller_profiles::OPENXR_INPUT_PROFILES,
popup_manager::{MountPopupOnceParams, PopupHolder},
},
views::{self, bindings, ViewTrait, ViewUpdateParams},
views::{self, ViewTrait, ViewUpdateParams, bindings},
};
#[derive(Clone)]

View File

@ -115,6 +115,7 @@ pub struct CustomClickAction {
single: MultiClickHandler<0>,
double: MultiClickHandler<1>,
triple: MultiClickHandler<2>,
threshold: [f32; 2],
}
impl CustomClickAction {
@ -127,18 +128,19 @@ impl CustomClickAction {
single,
double,
triple,
threshold: [0.5, 0.7],
})
}
pub fn state(
&mut self,
before: bool,
state: &XrState,
session: &AppSession,
) -> anyhow::Result<bool> {
pub fn set_threshold(&mut self, threshold: [f32; 2]) {
self.threshold = threshold;
}
pub fn state(&mut self, before: bool, state: &XrState) -> anyhow::Result<bool> {
let threshold = if before {
session.config.xr_click_sensitivity_release
self.threshold[1]
} else {
session.config.xr_click_sensitivity
self.threshold[0]
};
Ok(self.single.check(&state.session, threshold)?
@ -171,14 +173,13 @@ impl OpenXrInputSource {
.instance()
.create_action_set("wayvr", "WayVR Actions", 0)?;
let left_source = OpenXrHandSource::new(&mut action_set, "left")?;
let right_source = OpenXrHandSource::new(&mut action_set, "right")?;
let fallback_source = OpenXrHandSource::new(&mut action_set, "handsfree")?;
let mut left_source = OpenXrHandSource::new(&mut action_set, "left")?;
let mut right_source = OpenXrHandSource::new(&mut action_set, "right")?;
let mut fallback_source = OpenXrHandSource::new(&mut action_set, "handsfree")?;
suggest_bindings(
&xr.instance,
&[&left_source, &right_source, &fallback_source],
);
let mut hands: [&mut OpenXrHandSource; 3] =
[&mut left_source, &mut right_source, &mut fallback_source];
suggest_bindings(&xr.instance, &mut hands);
xr.session.attach_action_sets(&[&action_set])?;
@ -381,7 +382,7 @@ impl OpenXrPointer {
return Ok(());
}
self.pointer_load_actions(pointer, xr, session)?;
self.pointer_load_actions(pointer, xr)?;
Ok(())
}
@ -394,7 +395,7 @@ impl OpenXrPointer {
) -> anyhow::Result<()> {
pointer.handsfree = false;
self.pointer_load_pose(pointer, xr, session.config.pointer_lerp_factor)?;
self.pointer_load_actions(pointer, xr, session)?;
self.pointer_load_actions(pointer, xr)?;
Ok(())
}
@ -431,15 +432,10 @@ impl OpenXrPointer {
Ok(())
}
fn pointer_load_actions(
&mut self,
pointer: &mut Pointer,
xr: &XrState,
session: &AppSession,
) -> anyhow::Result<()> {
pointer.now.click = self.source.click.state(pointer.before.click, xr, session)?;
fn pointer_load_actions(&mut self, pointer: &mut Pointer, xr: &XrState) -> anyhow::Result<()> {
pointer.now.click = self.source.click.state(pointer.before.click, xr)?;
pointer.now.grab = self.source.grab.state(pointer.before.grab, xr, session)?;
pointer.now.grab = self.source.grab.state(pointer.before.grab, xr)?;
let scroll = self
.source
@ -450,50 +446,44 @@ impl OpenXrPointer {
pointer.now.scroll_x = scroll.x;
pointer.now.scroll_y = scroll.y;
pointer.now.alt_click =
self.source
.alt_click
.state(pointer.before.alt_click, xr, session)?;
pointer.now.alt_click = self.source.alt_click.state(pointer.before.alt_click, xr)?;
pointer.now.show_hide =
self.source
.show_hide
.state(pointer.before.show_hide, xr, session)?;
pointer.now.show_hide = self.source.show_hide.state(pointer.before.show_hide, xr)?;
pointer.now.click_modifier_right =
self.source
.modifier_right
.state(pointer.before.click_modifier_right, xr, session)?;
pointer.now.click_modifier_right = self
.source
.modifier_right
.state(pointer.before.click_modifier_right, xr)?;
pointer.now.toggle_dashboard =
self.source
.toggle_dashboard
.state(pointer.before.toggle_dashboard, xr, session)?;
pointer.now.toggle_dashboard = self
.source
.toggle_dashboard
.state(pointer.before.toggle_dashboard, xr)?;
pointer.now.click_modifier_middle =
self.source
.modifier_middle
.state(pointer.before.click_modifier_middle, xr, session)?;
pointer.now.click_modifier_middle = self
.source
.modifier_middle
.state(pointer.before.click_modifier_middle, xr)?;
pointer.now.move_mouse =
self.source
.move_mouse
.state(pointer.before.move_mouse, xr, session)?;
pointer.now.move_mouse = self
.source
.move_mouse
.state(pointer.before.move_mouse, xr)?;
pointer.now.space_drag =
self.source
.space_drag
.state(pointer.before.space_drag, xr, session)?;
pointer.now.space_drag = self
.source
.space_drag
.state(pointer.before.space_drag, xr)?;
pointer.now.space_rotate =
self.source
.space_rotate
.state(pointer.before.space_rotate, xr, session)?;
pointer.now.space_rotate = self
.source
.space_rotate
.state(pointer.before.space_rotate, xr)?;
pointer.now.space_reset =
self.source
.space_reset
.state(pointer.before.space_reset, xr, session)?;
pointer.now.space_reset = self
.source
.space_reset
.state(pointer.before.space_reset, xr)?;
Ok(())
}
@ -648,7 +638,20 @@ macro_rules! add_custom_lr {
}
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
fn suggest_bindings(instance: &xr::Instance, hands: &[&OpenXrHandSource; 3]) {
macro_rules! set_threshold_for {
($hands:expr, $profile_action:expr, $field:ident) => {
if let Some(ref profile_action) = $profile_action {
if let Some(threshold) = profile_action.threshold_left {
$hands[0].$field.set_threshold(threshold);
}
if let Some(threshold) = profile_action.threshold_right {
$hands[1].$field.set_threshold(threshold);
}
}
};
}
fn suggest_bindings(instance: &xr::Instance, hands: &mut [&mut OpenXrHandSource; 3]) {
let profiles = load_xr_input_profiles();
for profile in profiles {
@ -659,69 +662,83 @@ fn suggest_bindings(instance: &xr::Instance, hands: &[&OpenXrHandSource; 3]) {
continue;
};
let mut bindings: Vec<xr::Binding> = vec![];
add_custom_lr!(profile.pose, pose, hands, bindings, instance);
add_custom_lr!(profile.haptic, haptics, hands, bindings, instance);
add_custom_lr!(profile.scroll, scroll, hands, bindings, instance);
add_custom!(profile.click, click, hands, bindings, instance);
add_custom!(profile.alt_click, alt_click, hands, bindings, instance);
add_custom!(profile.grab, grab, hands, bindings, instance);
add_custom!(profile.show_hide, show_hide, hands, bindings, instance);
add_custom!(
profile.toggle_dashboard,
toggle_dashboard,
hands,
bindings,
instance
);
add_custom!(profile.space_drag, space_drag, hands, bindings, instance);
add_custom!(
profile.space_rotate,
space_rotate,
hands,
bindings,
instance
);
add_custom!(profile.space_reset, space_reset, hands, bindings, instance);
add_custom!(
profile.click_modifier_right,
modifier_right,
hands,
bindings,
instance
);
add_custom!(
profile.click_modifier_middle,
modifier_middle,
hands,
bindings,
instance
);
add_custom!(profile.move_mouse, move_mouse, hands, bindings, instance);
if instance
.suggest_interaction_profile_bindings(profile_path, &bindings)
.is_err()
{
log::error!("Bad bindings for {}", &profile.profile[22..]);
log::error!("Verify config: ~/.config/wayvr/openxr_actions.json5");
} else {
log::debug!(
"Bindings for {} bound successfully.",
&profile.profile[22..]
let mut bindings: Vec<xr::Binding> = vec![];
add_custom_lr!(profile.pose, pose, hands, bindings, instance);
add_custom_lr!(profile.haptic, haptics, hands, bindings, instance);
add_custom_lr!(profile.scroll, scroll, hands, bindings, instance);
add_custom!(profile.click, click, hands, bindings, instance);
add_custom!(profile.alt_click, alt_click, hands, bindings, instance);
add_custom!(profile.grab, grab, hands, bindings, instance);
add_custom!(profile.show_hide, show_hide, hands, bindings, instance);
add_custom!(
profile.toggle_dashboard,
toggle_dashboard,
hands,
bindings,
instance
);
add_custom!(profile.space_drag, space_drag, hands, bindings, instance);
add_custom!(
profile.space_rotate,
space_rotate,
hands,
bindings,
instance
);
add_custom!(profile.space_reset, space_reset, hands, bindings, instance);
add_custom!(
profile.click_modifier_right,
modifier_right,
hands,
bindings,
instance
);
add_custom!(
profile.click_modifier_middle,
modifier_middle,
hands,
bindings,
instance
);
add_custom!(profile.move_mouse, move_mouse, hands, bindings, instance);
if instance
.suggest_interaction_profile_bindings(profile_path, &bindings)
.is_err()
{
log::error!("Bad bindings for {}", &profile.profile[22..]);
log::error!("Verify config: ~/.config/wayvr/openxr_actions.json5");
} else {
log::debug!(
"Bindings for {} bound successfully.",
&profile.profile[22..]
);
}
}
set_threshold_for!(hands, profile.click, click);
set_threshold_for!(hands, profile.alt_click, alt_click);
set_threshold_for!(hands, profile.grab, grab);
set_threshold_for!(hands, profile.show_hide, show_hide);
set_threshold_for!(hands, profile.toggle_dashboard, toggle_dashboard);
set_threshold_for!(hands, profile.space_drag, space_drag);
set_threshold_for!(hands, profile.space_rotate, space_rotate);
set_threshold_for!(hands, profile.space_reset, space_reset);
set_threshold_for!(hands, profile.click_modifier_right, modifier_right);
set_threshold_for!(hands, profile.click_modifier_middle, modifier_middle);
set_threshold_for!(hands, profile.move_mouse, move_mouse);
}
}

View File

@ -148,8 +148,6 @@ pub struct AutoSettings {
pub enable_watch: bool,
pub sets_on_watch: bool,
pub hide_grab_help: bool,
pub xr_click_sensitivity: f32,
pub xr_click_sensitivity_release: f32,
pub allow_sliding: bool,
pub focus_follows_mouse_mode: bool,
pub left_handed_mouse: bool,
@ -210,8 +208,6 @@ pub fn save_settings(config: &GeneralConfig) -> anyhow::Result<()> {
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,
xr_click_sensitivity_release: config.xr_click_sensitivity_release,
allow_sliding: config.allow_sliding,
focus_follows_mouse_mode: config.focus_follows_mouse_mode,
left_handed_mouse: config.left_handed_mouse,

View File

@ -194,11 +194,6 @@
#invert_scroll_direction_x: false
#invert_scroll_direction_y: false
## Press and release thresholds for grip & trigger
## when using OpenXR (WiVRn, Monado)
#xr_click_sensitivity: 0.7
#xr_click_sensitivity_release: 0.5
## How many seconds to buttons need to be held
## before it's considered a long press
#long_press_duration: 1.0

View File

@ -230,7 +230,11 @@
move_mouse: {
left: "/user/hand/left/input/trigger/value",
right: "/user/hand/right/input/trigger/value",
threshold: [
threshold_left: [
0.1,
0.2
],
threshold_right: [
0.1,
0.2
]
@ -282,7 +286,11 @@
move_mouse: {
left: "/user/hand/left/input/trigger/value",
right: "/user/hand/right/input/trigger/value",
threshold: [
threshold_left: [
0.1,
0.2
],
threshold_right: [
0.1,
0.2
]
@ -326,7 +334,11 @@
move_mouse: {
left: "/user/hand/left/input/trigger/value",
right: "/user/hand/right/input/trigger/value",
threshold: [
threshold_left: [
0.1,
0.2
],
threshold_right: [
0.1,
0.2
]
@ -361,7 +373,11 @@
move_mouse: {
left: "/user/hand/left/input/trigger/value",
right: "/user/hand/right/input/trigger/value",
threshold: [
threshold_left: [
0.1,
0.2
],
threshold_right: [
0.1,
0.2
]
@ -408,7 +424,11 @@
move_mouse: {
left: "/user/hand/left/input/trigger/value",
right: "/user/hand/right/input/trigger/value",
threshold: [
threshold_left: [
0.1,
0.2
],
threshold_right: [
0.1,
0.2
]

View File

@ -292,12 +292,6 @@ pub struct GeneralConfig {
#[serde(default)]
pub capture_method: CaptureMethod,
#[serde(default = "def_point7")]
pub xr_click_sensitivity: f32,
#[serde(default = "def_half")]
pub xr_click_sensitivity_release: f32,
#[serde(default = "def_true")]
pub allow_sliding: bool,

View File

@ -20,7 +20,9 @@ pub struct OpenXrInputAction {
#[serde(skip_serializing_if = "Option::is_none")]
pub handsfree: Option<OneOrMany<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub threshold: Option<[f32; 2]>,
pub threshold_left: Option<[f32; 2]>,
#[serde(skip_serializing_if = "Option::is_none")]
pub threshold_right: Option<[f32; 2]>,
#[serde(skip_serializing_if = "Option::is_none")]
pub double_click: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]