diff --git a/wayvr/src/backend/input.rs b/wayvr/src/backend/input.rs index 6cb97a44..213c444b 100644 --- a/wayvr/src/backend/input.rs +++ b/wayvr/src/backend/input.rs @@ -7,7 +7,7 @@ use glam::{Affine3A, Vec2, Vec3A, Vec3Swizzles}; use idmap_derive::IntegerId; use smallvec::{SmallVec, smallvec}; -use strum::AsRefStr; +use strum::{AsRefStr, EnumIs}; use wayvr_ipc::packet_client::{HandsfreeAction, HandsfreeParams}; use wlx_common::common::LeftRight; use wlx_common::windowing::{OverlayWindowState, Positioning}; @@ -58,13 +58,22 @@ pub enum TrackedDeviceRole { Tracker, } +#[derive(Debug, Clone, Copy, EnumIs)] +pub enum FocusPickState { + None, + /// aiming to pick the focus + Aiming, + /// picking the focus. this will be consumed on valid on_hover + Picking, +} + pub struct InputState { pub hmd: Affine3A, pub ipd: f32, pub pointers: [Pointer; 2], pub devices: Vec, pub handsfree_state: PointerState, - pub picking_focus: bool, + pub picking_focus: FocusPickState, processes: Vec, } @@ -77,7 +86,7 @@ impl InputState { devices: Vec::new(), processes: Vec::new(), handsfree_state: PointerState::default(), - picking_focus: false, + picking_focus: FocusPickState::None, } } @@ -210,6 +219,9 @@ fn debug_print_hand(hand: &Pointer) { if hand.now.grab != hand.before.grab { log::debug!("Hand {}: grab {}", hand.idx, hand.now.grab); } + if hand.now.grab_float != hand.before.grab_float { + log::debug!("Hand {}: grab {}", hand.idx, hand.now.grab); + } if hand.now.alt_click != hand.before.alt_click { log::debug!("Hand {}: alt_click {}", hand.idx, hand.now.alt_click); } @@ -316,6 +328,7 @@ pub struct PointerState { pub scroll_y: f32, pub click: bool, pub grab: bool, + pub grab_float: bool, pub alt_click: bool, pub show_hide: bool, pub toggle_dashboard: bool, @@ -411,6 +424,7 @@ fn populate_lines( fn update_focus(app: &mut AppState, overlay_input_focus: Option) { if let Some(f) = &overlay_input_focus + && app.input_state.picking_focus.is_none() && app .hid_provider .set_input_focus(app.wvr_server.as_mut(), *f) @@ -538,8 +552,11 @@ where let hovered_state = hovered.config.active_state.as_mut().unwrap(); + let grab_float = pointer.now.grab_float; + let grab_start = pointer.now.grab && !pointer.before.grab; + // grab - if pointer.now.grab && !pointer.before.grab && hovered_state.grabbable { + if grab_start && hovered_state.grabbable { update_focus(app, hovered.config.input_focus); start_grab( idx, @@ -549,6 +566,7 @@ where hovered_state, app, edit_mode, + grab_float, ); log::debug!("Hand {}: grabbed {}", hit.pointer, hovered.config.name); return ( @@ -762,15 +780,19 @@ fn start_grab( state: &mut OverlayWindowState, app: &mut AppState, edit_mode: bool, + grab_float: bool, ) { let pointer = &mut app.input_state.pointers[idx]; // Grab anchor if: // - grabbed overlay is Anchored // - not in editmode + // - not using grab_float // - grabbing with one hand. (grabbing with the 2nd hand will grab the individual overlay instead) - let grab_anchor = - !edit_mode && !app.anchor_grabbed && matches!(state.positioning, Positioning::Anchored); + let grab_anchor = !edit_mode + && !grab_float + && !app.anchor_grabbed + && matches!(state.positioning, Positioning::Anchored); let relative_grab_transform = if grab_anchor { app.anchor diff --git a/wayvr/src/backend/openvr/mod.rs b/wayvr/src/backend/openvr/mod.rs index d13850d6..c1772578 100644 --- a/wayvr/src/backend/openvr/mod.rs +++ b/wayvr/src/backend/openvr/mod.rs @@ -219,6 +219,9 @@ pub fn openvr_run(args: &Args) -> Result<(), BackendError> { while let Some(task) = due_tasks.pop_front() { match task { + TaskType::Global(task) => { + task(&mut app); + } TaskType::Input(task) => { app.input_state.handle_task(task); } diff --git a/wayvr/src/backend/openxr/input.rs b/wayvr/src/backend/openxr/input.rs index b0a4392d..7860de5c 100644 --- a/wayvr/src/backend/openxr/input.rs +++ b/wayvr/src/backend/openxr/input.rs @@ -4,7 +4,7 @@ use std::{ time::{Duration, Instant}, }; -use glam::{Affine3A, Quat, Vec3, bool}; +use glam::{Affine3A, FloatExt, Quat, Vec3, bool}; use libmonado::{self as mnd, DeviceLogic}; use openxr::{self as xr, Quaternionf, Vector2f, Vector3f}; use wlx_common::{ @@ -238,13 +238,18 @@ impl OpenXrInputSource { let mut any_tracked = false; let old_handsfree = app.session.config.handsfree_pointer; - if app.input_state.picking_focus { + if !app.input_state.picking_focus.is_none() { app.session.config.handsfree_pointer = app.session.config.handsfree_alt_tab.into(); + app.input_state.handsfree_state.scroll_x = + app.input_state.handsfree_state.scroll_x.lerp(0.0, 0.7); + app.input_state.handsfree_state.scroll_y = + app.input_state.handsfree_state.scroll_y.lerp(0.0, 0.7); + let ptr1 = &mut app.input_state.pointers[1]; ptr1.before = ptr1.now; ptr1.now = PointerState::default(); - ptr1.tracked = false + ptr1.tracked = false; } else { for i in 0..2 { let pointer = &mut app.input_state.pointers[i]; @@ -399,6 +404,7 @@ impl OpenXrPointer { // input from wayvrctl pointer.now.click = handsfree_state.click; pointer.now.grab = handsfree_state.grab; + pointer.now.grab_float = handsfree_state.grab_float; pointer.now.click_modifier_right = handsfree_state.click_modifier_right; pointer.now.click_modifier_middle = handsfree_state.click_modifier_middle; pointer.now.scroll_y = handsfree_state.scroll_y; diff --git a/wayvr/src/backend/openxr/mod.rs b/wayvr/src/backend/openxr/mod.rs index af703abc..2af7e2b2 100644 --- a/wayvr/src/backend/openxr/mod.rs +++ b/wayvr/src/backend/openxr/mod.rs @@ -473,6 +473,9 @@ pub fn openxr_run(args: &Args) -> Result<(), BackendError> { app.tasks.retrieve_due(&mut due_tasks); while let Some(task) = due_tasks.pop_front() { match task { + TaskType::Global(task) => { + task(&mut app); + } TaskType::Input(task) => { app.input_state.handle_task(task); } diff --git a/wayvr/src/backend/task.rs b/wayvr/src/backend/task.rs index 14821be3..aacf3826 100644 --- a/wayvr/src/backend/task.rs +++ b/wayvr/src/backend/task.rs @@ -128,8 +128,11 @@ pub enum OverlayTask { Drop(OverlaySelector), } +pub type GlobalTask = dyn FnOnce(&mut AppState) + Send; + #[allow(dead_code)] pub enum TaskType { + Global(Box), Input(InputTask), Overlay(OverlayTask), Playspace(PlayspaceTask), diff --git a/wayvr/src/backend/wayvr/input_capture.rs b/wayvr/src/backend/wayvr/input_capture.rs index c0d7b1db..974d92bd 100644 --- a/wayvr/src/backend/wayvr/input_capture.rs +++ b/wayvr/src/backend/wayvr/input_capture.rs @@ -34,9 +34,8 @@ pub enum KeyCombo { Super2, Super3, Super4, + SuperTab, AltF4, - AltTab, - AltShiftTab, /// Always consumed by InputCapture internally CtrlAltDel, } @@ -61,7 +60,8 @@ pub enum CapturedEvent { horizontal_v120: i32, vertical_v120: i32, }, - UngrabbedAll, + Grabbed, + Ungrabbed, KeyCombo { combo: KeyCombo, pressed: bool, @@ -326,6 +326,7 @@ fn worker_main( } let mut grabbed = false; + let mut pending_grab = false; let mut watchdog = Instant::now() + WATCHDOG_TIMEOUT; let mut emergency_ungrab = false; let mut pointer_accel = PointerAccelConfig::default(); @@ -336,6 +337,8 @@ fn worker_main( loop { match command_rx.try_recv() { Ok(Command::SetGrabbed(requested)) => { + pending_grab = false; + let _ = if requested { match restricted_state.borrow_mut().set_grabbed(true) { Ok(()) => { @@ -409,9 +412,10 @@ fn worker_main( &event_tx, ); grabbed = false; + pending_grab = false; emergency_ungrab = false; - if event_tx.send(CapturedEvent::UngrabbedAll).is_err() { + if event_tx.send(CapturedEvent::Ungrabbed).is_err() { break 'worker; } continue; @@ -459,6 +463,8 @@ fn worker_main( match process_libinput_event( event, grabbed, + true, + &mut pending_grab, &event_tx, &mut runtime_devices, &mut pointer_devices, @@ -473,6 +479,27 @@ fn worker_main( } } + if !grabbed && pending_grab && all_keyboard_keys_released(&runtime_devices) { + pending_grab = false; + + match restricted_state.borrow_mut().set_grabbed(true) { + Ok(()) => { + grabbed = true; + watchdog = Instant::now() + WATCHDOG_TIMEOUT; + + // should not be needed but sanity + clear_transient_state(&mut runtime_devices); + + if event_tx.send(CapturedEvent::Grabbed).is_err() { + break 'worker; + } + } + Err(error) => { + log::warn!("Could not grab input devices after Super+Tab: {error}"); + } + } + } + if emergency_ungrab { force_emergency_ungrab( &mut libinput, @@ -485,7 +512,7 @@ fn worker_main( grabbed = false; emergency_ungrab = false; - if event_tx.send(CapturedEvent::UngrabbedAll).is_err() { + if event_tx.send(CapturedEvent::Ungrabbed).is_err() { break 'worker; } } @@ -494,7 +521,9 @@ fn worker_main( fn process_libinput_event( event: Event, - emit: bool, + grabbed: bool, + allow_deferred_grab: bool, + pending_grab: &mut bool, event_tx: &Sender, runtime_devices: &mut HashMap, pointer_devices: &mut HashSet, @@ -504,7 +533,7 @@ fn process_libinput_event( Event::Device(event) => { handle_device_event(event, runtime_devices, pointer_devices, pointer_accel); } - Event::Keyboard(KeyboardEvent::Key(event)) if emit => { + Event::Keyboard(KeyboardEvent::Key(event)) => { let code_u32 = event.key(); let Ok(code) = u16::try_from(code_u32) else { log::warn!("Ignoring out-of-range Linux key code {code_u32}"); @@ -521,16 +550,26 @@ fn process_libinput_event( state.pressed_keys.remove(&code); } + if !grabbed { + // while ungrabbed, SuperTab will arm an automatic grab + if allow_deferred_grab + && key_combo_is_pressed(KeyCombo::SuperTab, &state.pressed_keys) + { + *pending_grab = true; + } + + return ProcessResult::Continue; + } + if pressed && key_combo_is_pressed(KeyCombo::CtrlAltDel, &state.pressed_keys) { log::info!("Ctrl+Alt+Del pressed, ungrabbing all input devices"); return ProcessResult::EmergencyUngrab; } - // emit press/release whenever a combo's complete state changes. + // while grabbed, these combos get emitted as normal for combo in [ + KeyCombo::SuperTab, KeyCombo::AltF4, - KeyCombo::AltShiftTab, - KeyCombo::AltTab, KeyCombo::Super1, KeyCombo::Super2, KeyCombo::Super3, @@ -564,7 +603,7 @@ fn process_libinput_event( return ProcessResult::ReceiverGone; } } - Event::Pointer(PointerEvent::Motion(event)) if emit => { + Event::Pointer(PointerEvent::Motion(event)) if grabbed => { let dx = event.dx(); let dy = event.dy(); let dx_raw = event.dx_unaccelerated(); @@ -582,7 +621,7 @@ fn process_libinput_event( return ProcessResult::ReceiverGone; } } - Event::Pointer(PointerEvent::Button(event)) if emit => { + Event::Pointer(PointerEvent::Button(event)) if grabbed => { let pressed = event.button_state() == ButtonState::Pressed; if event_tx .send(CapturedEvent::PointerButton { @@ -594,7 +633,7 @@ fn process_libinput_event( return ProcessResult::ReceiverGone; } } - Event::Pointer(PointerEvent::ScrollWheel(event)) if emit => { + Event::Pointer(PointerEvent::ScrollWheel(event)) if grabbed => { let horizontal = if event.has_axis(Axis::Horizontal) { event.scroll_value_v120(Axis::Horizontal) } else { @@ -680,10 +719,14 @@ fn drain_pending_libinput_events( event_tx: &Sender, ) { let _ = libinput.dispatch(); + + let mut ignored = false; for event in libinput { let _ = process_libinput_event( event, false, + false, + &mut ignored, event_tx, runtime_devices, pointer_devices, @@ -836,14 +879,10 @@ fn key_combo_is_pressed(combo: KeyCombo, pressed: &HashSet) -> bool { let meta = pressed.contains(&KeyCode::KEY_LEFTMETA.0) || pressed.contains(&KeyCode::KEY_RIGHTMETA.0); - let shift = - pressed.contains(&KeyCode::KEY_LEFTSHIFT.0) || pressed.contains(&KeyCode::KEY_RIGHTSHIFT.0); - match combo { KeyCombo::AltF4 => alt && pressed.contains(&KeyCode::KEY_F4.0), - KeyCombo::AltShiftTab => alt && shift && pressed.contains(&KeyCode::KEY_TAB.0), - KeyCombo::AltTab => alt && pressed.contains(&KeyCode::KEY_TAB.0), KeyCombo::CtrlAltDel => ctrl && alt && pressed.contains(&KeyCode::KEY_DELETE.0), + KeyCombo::SuperTab => meta && pressed.contains(&KeyCode::KEY_TAB.0), KeyCombo::Super1 => meta && pressed.contains(&KeyCode::KEY_1.0), KeyCombo::Super2 => meta && pressed.contains(&KeyCode::KEY_2.0), KeyCombo::Super3 => meta && pressed.contains(&KeyCode::KEY_3.0), @@ -861,3 +900,11 @@ fn with_device_context(action: &str, path: &Path, error: io::Error) -> io::Error format!("failed to {action} {}: {error}", path.display()), ) } + +fn all_keyboard_keys_released( + runtime_devices: &HashMap, +) -> bool { + runtime_devices + .values() + .all(|state| state.pressed_keys.is_empty()) +} diff --git a/wayvr/src/backend/wayvr/mod.rs b/wayvr/src/backend/wayvr/mod.rs index cbc67833..8afce482 100644 --- a/wayvr/src/backend/wayvr/mod.rs +++ b/wayvr/src/backend/wayvr/mod.rs @@ -77,12 +77,13 @@ use crate::{ state::AppState, subsystem::{ dbus::DbusConnector, - hid::{MODS_TO_KEYS, WheelDelta}, + hid::{self, MODS_TO_KEYS, WheelDelta}, + input::{HidWrapper, InputFocus}, }, windowing::{OverlayID, OverlaySelector, backend::OverlayEventData}, }; -pub use hit_test::{WvrHitContext, WvrHitTarget, build_hit_context}; +pub use hit_test::{WvrHitTarget, build_hit_context}; #[derive(Debug, Clone)] pub struct WaylandEnv { @@ -322,6 +323,7 @@ impl WvrServerState { input_state: &app.input_state, tasks: &mut tasks, signals: &app.wayvr_signals, + hid_wrapper: &mut app.hid_provider, }); // Tick all child processes @@ -637,7 +639,11 @@ impl WvrServerState { wvr_server.manager.cleanup_handles(); } - wvr_server.process_input_capture(&mut app.input_state, &mut app.tasks); + wvr_server.process_input_capture( + &mut app.input_state, + &mut app.tasks, + &mut app.hid_provider, + ); wvr_server.ticks += 1; @@ -752,10 +758,8 @@ impl WvrServerState { &mut self, input_state: &mut InputState, tasks: &mut TaskContainer, + hid_wrapper: &mut HidWrapper, ) { - if !self.has_input_focus { - return; - } let Some(input_capture) = self.input_capture.as_mut() else { return; }; @@ -766,13 +770,47 @@ impl WvrServerState { for ev in input_capture.drain_events() { match ev { input_capture::CapturedEvent::Key { code, pressed } => { - self.manager.send_key((code as u32) + 8, pressed); + let vk = (code as u32) + 8; + if input_state.picking_focus.is_aiming() { + if (hid::VirtualKey::Q as u32) == vk { + input_state.handsfree_state.grab = pressed; + input_state.handsfree_state.grab_float = pressed; + } + if (hid::VirtualKey::A as u32) == vk { + input_state.handsfree_state.grab = pressed; + } + if (hid::VirtualKey::W as u32) == vk { + input_state.handsfree_state.click = pressed; + } + if (hid::VirtualKey::E as u32) == vk && pressed { + tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleEditMode)); + } + if (hid::VirtualKey::D as u32) == vk && pressed { + tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleDashboard)); + } + } else { + self.manager.send_key(vk, pressed); + } } input_capture::CapturedEvent::PointerButton { button, pressed } => { let Some(mouse_index) = Self::button_to_mouse_index(button) else { continue; }; - self.manager.send_pointer_button(mouse_index, pressed); + if input_state.picking_focus.is_aiming() { + match mouse_index { + MouseIndex::Left => input_state.handsfree_state.click = pressed, + MouseIndex::Right => { + input_state.handsfree_state.click = pressed; + input_state.handsfree_state.click_modifier_right = pressed; + } + MouseIndex::Center if pressed => { + tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleEditMode)); + } + _ => {} + } + } else { + self.manager.send_pointer_button(mouse_index, pressed); + } } input_capture::CapturedEvent::PointerMotion { dx, @@ -794,9 +832,16 @@ impl WvrServerState { x: horizontal_v120 as f32, y: vertical_v120 as f32, }; - self.manager.send_pointer_axis_wheel_raw(delta); + if input_state.picking_focus.is_aiming() { + input_state.handsfree_state.scroll_y = (vertical_v120 as f32) * -0.12; + } else { + self.manager.send_pointer_axis_wheel_raw(delta); + } } - input_capture::CapturedEvent::UngrabbedAll => { + input_capture::CapturedEvent::Grabbed => { + hid_wrapper.set_input_focus(Some(self), InputFocus::WayVR); + } + input_capture::CapturedEvent::Ungrabbed => { self.manager.release_all_keys(); self.has_input_focus = false; self.wm.keyboard_focus = None; @@ -808,20 +853,33 @@ impl WvrServerState { self.close_window(window_handle); } } - input_capture::KeyCombo::AltTab => input_state.picking_focus = pressed, - input_capture::KeyCombo::AltShiftTab => { - input_state.handsfree_state.grab = pressed + input_capture::KeyCombo::SuperTab => { + if pressed { + input_state.picking_focus = super::input::FocusPickState::Aiming; + } else { + input_state.picking_focus = super::input::FocusPickState::Picking; + + tasks.enqueue_at( + TaskType::Global(Box::new(|app| { + app.input_state.picking_focus = + super::input::FocusPickState::None; + + let _ = std::mem::take(&mut app.input_state.handsfree_state); + })), + Instant::now() + Duration::from_millis(33), + ); + } } - input_capture::KeyCombo::Super1 => { + input_capture::KeyCombo::Super1 if pressed => { tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleSet(0))) } - input_capture::KeyCombo::Super2 => { + input_capture::KeyCombo::Super2 if pressed => { tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleSet(1))) } - input_capture::KeyCombo::Super3 => { + input_capture::KeyCombo::Super3 if pressed => { tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleSet(2))) } - input_capture::KeyCombo::Super4 => { + input_capture::KeyCombo::Super4 if pressed => { tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleSet(3))) } _ => {} @@ -881,6 +939,7 @@ impl WvrServerState { } } + /// Use HidWrapper::set_input_focus instead!!! pub fn set_input_focus(&mut self, has_focus: bool) { let Some(input_capture) = self.input_capture.as_mut() else { return; @@ -982,7 +1041,7 @@ impl WvrServerState { target: PointerFocusTarget, global_pos: Vec2, hover_window: window::WindowHandle, - force_focus: bool, + picking_focus: bool, ) { if self.mouse_freeze > Instant::now() { return; @@ -990,7 +1049,7 @@ impl WvrServerState { let global_pos = DVec2::from(global_pos); - let (focus, focus_keyboard) = self.get_mouse_focus(target, hover_window, force_focus); + let (focus, focus_keyboard) = self.get_mouse_focus(target, hover_window, picking_focus); if focus_keyboard.is_some() { self.manager.seat_keyboard.set_focus( &mut self.manager.state, diff --git a/wayvr/src/ipc/ipc_server.rs b/wayvr/src/ipc/ipc_server.rs index dffe43f0..d949ffe0 100644 --- a/wayvr/src/ipc/ipc_server.rs +++ b/wayvr/src/ipc/ipc_server.rs @@ -1,5 +1,6 @@ use crate::backend::wayvr::{self, WvrServerState}; +use crate::subsystem::input::{HidWrapper, InputFocus}; use crate::{ backend::input::InputState, ipc::{event_queue::SyncEventQueue, signal::WayVRSignal}, @@ -86,6 +87,7 @@ pub struct TickParams<'a> { pub tasks: &'a mut Vec, pub signals: &'a SyncEventQueue, pub input_state: &'a InputState, + pub hid_wrapper: &'a mut HidWrapper, } pub fn gen_args_vec(input: &str) -> Vec<&str> { @@ -335,7 +337,14 @@ impl Connection { } fn handle_wvr_input_capture(params: &mut TickParams, grab: bool) { - params.wvr_server.set_input_focus(grab); + let val = if grab { + InputFocus::WayVR + } else { + InputFocus::PhysicalScreen + }; + params + .hid_wrapper + .set_input_focus(Some(params.wvr_server), val); } fn handle_wlx_device_haptics( diff --git a/wayvr/src/overlays/screen/backend.rs b/wayvr/src/overlays/screen/backend.rs index e73c11d0..0fa1484f 100644 --- a/wayvr/src/overlays/screen/backend.rs +++ b/wayvr/src/overlays/screen/backend.rs @@ -9,7 +9,7 @@ use wlx_capture::{WlxCapture, frame::Transform}; use crate::{ backend::{ XrBackend, - input::{HoverResult, PointerHit, PointerMode}, + input::{self, HoverResult, PointerHit, PointerMode}, }, overlays::screen::capture::MyFirstDmaExporter, state::AppState, @@ -302,7 +302,9 @@ impl OverlayBackend for ScreenBackend { #[cfg(debug_assertions)] log::trace!("Hover: {:?}", hit.uv); - if app.input_state.picking_focus { + let pick_now = app.input_state.picking_focus.is_picking(); + if pick_now { + app.input_state.picking_focus = input::FocusPickState::None; app.hid_provider .set_input_focus(app.wvr_server.as_mut(), InputFocus::PhysicalScreen); } diff --git a/wayvr/src/overlays/wayvr.rs b/wayvr/src/overlays/wayvr.rs index 8a2cc1af..fe35df75 100644 --- a/wayvr/src/overlays/wayvr.rs +++ b/wayvr/src/overlays/wayvr.rs @@ -845,7 +845,9 @@ impl OverlayBackend for WvrWindowBackend { self.panel_hovered = false; } - if app.input_state.picking_focus { + let pick_now = app.input_state.picking_focus.is_picking(); + if pick_now { + app.input_state.picking_focus = input::FocusPickState::None; app.hid_provider .set_input_focus(app.wvr_server.as_mut(), InputFocus::WayVR); } @@ -855,7 +857,7 @@ impl OverlayBackend for WvrWindowBackend { PointerFocusTarget::Surface { surface, origin }, global_pos, self.window, - app.input_state.picking_focus, + pick_now, ); HoverResult::consume() @@ -866,7 +868,9 @@ impl OverlayBackend for WvrWindowBackend { self.panel_hovered = false; } - if app.input_state.picking_focus { + let pick_now = app.input_state.picking_focus.is_picking(); + if pick_now { + app.input_state.picking_focus = input::FocusPickState::None; app.hid_provider .set_input_focus(app.wvr_server.as_mut(), InputFocus::WayVR); } @@ -876,7 +880,7 @@ impl OverlayBackend for WvrWindowBackend { PointerFocusTarget::Toplevel, pos, self.window, - app.input_state.picking_focus, + pick_now, ); HoverResult::consume()