feat: added hold trigger for right-click

This commit is contained in:
2026-02-14 03:19:36 +08:00
parent 9685576272
commit 4a9fe2019e

View File

@@ -1,7 +1,7 @@
use std::f32::consts::PI; use std::f32::consts::PI;
use std::process::{Child, Command}; use std::process::{Child, Command};
use std::sync::Arc; use std::sync::Arc;
use std::time::Instant; use std::time::{Duration, Instant};
use glam::{Affine3A, Vec2, Vec3A, Vec3Swizzles}; use glam::{Affine3A, Vec2, Vec3A, Vec3Swizzles};
@@ -91,46 +91,57 @@ impl InputState {
if hand.now.click { if hand.now.click {
hand.last_click = Instant::now(); hand.last_click = Instant::now();
if !hand.before.click {
hand.click_press_start = Some(Instant::now());
hand.interaction.long_press_click_sent = false;
}
} else {
hand.click_press_start = None;
hand.interaction.long_press_click_sent = false;
} }
// Prevent the mode from changing during a click let long_press_threshold = Duration::from_secs_f32(session.config.long_press_duration);
if !hand.before.click { let is_long_press = hand
.click_press_start
.is_some_and(|start| start.elapsed() >= long_press_threshold);
// Prevent the mode from changing during a click (except for long press)
if !hand.before.click || is_long_press {
if hand.now.click_modifier_right { if hand.now.click_modifier_right {
hand.interaction.mode = PointerMode::Right; hand.interaction.mode = PointerMode::Right;
continue; } else if is_long_press {
} hand.interaction.mode = PointerMode::Right;
} else if hand.now.click_modifier_middle {
if hand.now.click_modifier_middle {
hand.interaction.mode = PointerMode::Middle; hand.interaction.mode = PointerMode::Middle;
continue; } else if !hand.before.click {
} let hmd_up = self.hmd.transform_vector3a(Vec3A::Y);
let dot = hmd_up.dot(hand.pose.transform_vector3a(Vec3A::X))
* 2.0f32.mul_add(-(hand.idx as f32), 1.0);
let hmd_up = self.hmd.transform_vector3a(Vec3A::Y); hand.interaction.mode = if dot < -0.85 {
let dot = hmd_up.dot(hand.pose.transform_vector3a(Vec3A::X)) PointerMode::Right
* 2.0f32.mul_add(-(hand.idx as f32), 1.0); } else if dot > 0.7 {
PointerMode::Middle
} else {
PointerMode::Left
};
hand.interaction.mode = if dot < -0.85 { let middle_click_orientation = false;
PointerMode::Right let right_click_orientation = false;
} else if dot > 0.7 { match hand.interaction.mode {
PointerMode::Middle PointerMode::Middle => {
} else { if !middle_click_orientation {
PointerMode::Left hand.interaction.mode = PointerMode::Left;
}; }
let middle_click_orientation = false;
let right_click_orientation = false;
match hand.interaction.mode {
PointerMode::Middle => {
if !middle_click_orientation {
hand.interaction.mode = PointerMode::Left;
} }
} PointerMode::Right => {
PointerMode::Right => { if !right_click_orientation {
if !right_click_orientation { hand.interaction.mode = PointerMode::Left;
hand.interaction.mode = PointerMode::Left; }
} }
_ => {}
} }
_ => {}
} }
} }
@@ -212,6 +223,9 @@ pub struct InteractionState {
pub haptics: Option<f32>, pub haptics: Option<f32>,
pub should_block_input: bool, pub should_block_input: bool,
pub should_block_poses: bool, pub should_block_poses: bool,
pub click_sent: bool,
pub click_mode: PointerMode,
pub long_press_click_sent: bool,
} }
impl Default for InteractionState { impl Default for InteractionState {
@@ -225,6 +239,9 @@ impl Default for InteractionState {
haptics: None, haptics: None,
should_block_input: false, should_block_input: false,
should_block_poses: false, should_block_poses: false,
click_sent: false,
click_mode: PointerMode::Left,
long_press_click_sent: false,
} }
} }
} }
@@ -236,6 +253,7 @@ pub struct Pointer {
pub now: PointerState, pub now: PointerState,
pub before: PointerState, pub before: PointerState,
pub last_click: Instant, pub last_click: Instant,
pub click_press_start: Option<Instant>,
pub pending_haptics: Option<Haptics>, pub pending_haptics: Option<Haptics>,
pub(super) interaction: InteractionState, pub(super) interaction: InteractionState,
pub tracked: bool, pub tracked: bool,
@@ -252,6 +270,7 @@ impl Pointer {
now: PointerState::default(), now: PointerState::default(),
before: PointerState::default(), before: PointerState::default(),
last_click: Instant::now(), last_click: Instant::now(),
click_press_start: None,
pending_haptics: None, pending_haptics: None,
interaction: InteractionState::default(), interaction: InteractionState::default(),
tracked: false, tracked: false,
@@ -318,7 +337,7 @@ pub struct GrabData {
} }
#[repr(u8)] #[repr(u8)]
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default, PartialEq)]
pub enum PointerMode { pub enum PointerMode {
#[default] #[default]
Left, Left,
@@ -522,9 +541,23 @@ where
handle_scroll(&hit, hovered, app); handle_scroll(&hit, hovered, app);
// click / release // click / release - normal behavior
let pointer = &mut app.input_state.pointers[hit.pointer]; let pointer = &mut app.input_state.pointers[hit.pointer];
if pointer.now.click && !pointer.before.click {
// Check if we should send a long-press click immediately
let long_press_threshold = Duration::from_secs_f32(app.session.config.long_press_duration);
let is_long_press = pointer
.click_press_start
.is_some_and(|start| start.elapsed() >= long_press_threshold);
if is_long_press && pointer.now.click && !pointer.interaction.long_press_click_sent {
// Immediately send right-click (press + release)
pointer.interaction.long_press_click_sent = true;
let mut hit_right = hit;
hit_right.mode = PointerMode::Right;
hovered.config.backend.on_pointer(app, &hit_right, true);
hovered.config.backend.on_pointer(app, &hit_right, false);
} else if pointer.now.click && !pointer.before.click {
pointer.interaction.clicked_id = Some(hit.overlay); pointer.interaction.clicked_id = Some(hit.overlay);
update_focus( update_focus(
&mut app.hid_provider.keyboard_focus, &mut app.hid_provider.keyboard_focus,
@@ -532,13 +565,15 @@ where
); );
hovered.config.backend.on_pointer(app, &hit, true); hovered.config.backend.on_pointer(app, &hit, true);
} else if !pointer.now.click && pointer.before.click { } else if !pointer.now.click && pointer.before.click {
// send release event to overlay that was originally clicked // Only send release if we haven't already sent a long press click
if let Some(clicked_id) = pointer.interaction.clicked_id.take() { if !pointer.interaction.long_press_click_sent {
if let Some(clicked) = overlays.mut_by_id(clicked_id) { if let Some(clicked_id) = pointer.interaction.clicked_id.take() {
clicked.config.backend.on_pointer(app, &hit, false); if let Some(clicked) = overlays.mut_by_id(clicked_id) {
clicked.config.backend.on_pointer(app, &hit, false);
}
} else {
hovered.config.backend.on_pointer(app, &hit, false);
} }
} else {
hovered.config.backend.on_pointer(app, &hit, false);
} }
} }
@@ -570,8 +605,8 @@ fn handle_no_hit<O>(
// send release event to overlay that was originally clicked // send release event to overlay that was originally clicked
if !pointer.now.click if !pointer.now.click
&& pointer.before.click && pointer.before.click
&& !pointer.interaction.long_press_click_sent
&& let Some(clicked_id) = pointer.interaction.clicked_id.take() && let Some(clicked_id) = pointer.interaction.clicked_id.take()
&& let Some(clicked) = overlays.mut_by_id(clicked_id)
{ {
let hit = PointerHit { let hit = PointerHit {
pointer: pointer.idx, pointer: pointer.idx,
@@ -579,7 +614,9 @@ fn handle_no_hit<O>(
mode: pointer.interaction.mode, mode: pointer.interaction.mode,
..Default::default() ..Default::default()
}; };
clicked.config.backend.on_pointer(app, &hit, false); if let Some(clicked) = overlays.mut_by_id(clicked_id) {
clicked.config.backend.on_pointer(app, &hit, false);
}
} }
} }