diff --git a/Cargo.lock b/Cargo.lock index 71373c2..572618d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4779,7 +4779,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.11.0", "proc-macro2", "quote", "syn 2.0.104", diff --git a/src/objects/input/mouse_pointer.rs b/src/objects/input/mouse_pointer.rs index 3da3c95..10524c8 100644 --- a/src/objects/input/mouse_pointer.rs +++ b/src/objects/input/mouse_pointer.rs @@ -1,7 +1,7 @@ use super::{CaptureManager, DistanceCalculator, get_sorted_handlers}; use crate::{ DbusConnection, ObjectRegistryRes, - core::client::INTERNAL_CLIENT, + core::{client::INTERNAL_CLIENT, task}, nodes::{ Node, OwnedNode, fields::{EXPORTED_FIELDS, Field, FieldTrait, Ray}, @@ -9,6 +9,7 @@ use crate::{ items::panel::KEYMAPS, spatial::Spatial, }, + objects::FieldRef, }; use bevy::{ input::{ @@ -27,17 +28,36 @@ use rustc_hash::{FxHashMap, FxHasher}; use serde::{Deserialize, Serialize}; use slotmap::{DefaultKey, Key as SlotKey}; use stardust_xr::{ - schemas::dbus::{ObjectInfo, interfaces::FieldRefProxy, object_registry::ObjectRegistry}, + schemas::dbus::{ + ObjectInfo, + interfaces::FieldRefProxy, + list_query::{ListEvent, ObjectListQuery}, + object_registry::ObjectRegistry, + query::{ObjectQuery, QueryContext, QueryEvent}, + }, values::Datamap, }; use std::sync::{Arc, Weak}; -use tokio::task::JoinSet; +use tokio::sync::{Notify, mpsc, watch}; +use tokio::task::{AbortHandle, JoinSet}; use tokio::time::{Duration, timeout}; use xkbcommon_rs::{Context, Keymap, KeymapFormat, xkb_keymap::CompileFlags}; use zbus::{Connection, names::OwnedInterfaceName}; -pub struct FlatscreenInputPlugin; +#[derive(Clone)] +struct HandlerInfo { + handler: ObjectInfo, + field_ref: Arc, + keyboard_proxy: KeyboardHandlerProxy<'static>, +} +#[derive(Debug, Clone)] +struct InputEvent { + key: u32, + pressed: bool, +} + +pub struct FlatscreenInputPlugin; impl Plugin for FlatscreenInputPlugin { fn build(&self, app: &mut App) { app.add_systems(Startup, setup); @@ -50,9 +70,9 @@ impl Plugin for FlatscreenInputPlugin { #[require(Camera3d)] pub struct FlatscreenCam; -fn setup(mut cmds: Commands) { - let Ok(pointer) = - MousePointer::new().inspect_err(|err| error!("unable to create mouse pointer: {err}")) +fn setup(mut cmds: Commands, object_registry: Res) { + let Ok(pointer) = MousePointer::new(object_registry.0.clone()) + .inspect_err(|err| error!("unable to create mouse pointer: {err}")) else { return; }; @@ -161,6 +181,14 @@ trait KeyboardHandler { async fn reset(&self) -> zbus::Result<()>; } +// Make KeyboardHandlerProxy queryable +stardust_xr::schemas::impl_queryable_for_proxy!(KeyboardHandlerProxy); + +// Query context for keyboard handlers +#[derive(Debug, Clone)] +struct KeyboardQueryContext; +impl QueryContext for KeyboardQueryContext {} + #[derive(Resource)] pub struct MousePointer { node: OwnedNode, @@ -169,10 +197,16 @@ pub struct MousePointer { pointer: Arc, capture_manager: CaptureManager, mouse_datamap: MouseEvent, - keyboard_cache: Arc>>, + // Task management + focus_task_abort_handle: AbortHandle, + input_delivery_task_abort_handle: AbortHandle, + // Channels + input_event_tx: mpsc::UnboundedSender, + // Notification for focus recalculation + focus_notify: Arc, } impl MousePointer { - pub fn new() -> Result { + pub fn new(object_registry: Arc) -> Result { let node = Node::generate(&INTERNAL_CLIENT, false).add_to_scenegraph_owned()?; let spatial = Spatial::add_to(&node.0, None, Mat4::IDENTITY, false); let pointer = InputMethod::add_to( @@ -189,6 +223,39 @@ impl MousePointer { .unwrap(), ); + // Create channels and notification + let (focused_handler_tx, focused_handler_rx) = watch::channel::>(None); + let (input_event_tx, input_event_rx) = mpsc::unbounded_channel::(); + let focus_notify = Arc::new(Notify::new()); + // Spawn input delivery task + info!("Creating input delivery task"); + let input_delivery_task_abort_handle = task::new( + || "Mouse pointer input delivery task", + Self::input_delivery_task( + object_registry.get_connection().clone(), + focused_handler_rx, + input_event_rx, + keymap.data().as_ffi(), + ), + )? + .abort_handle(); + info!("Input delivery task created successfully"); + + // Spawn focus tracking task + info!("Creating focus tracking task"); + let focus_task_abort_handle = task::new( + || "Mouse pointer focus task", + Self::focus_tracking_task( + object_registry, + focus_notify.clone(), + spatial.clone(), + pointer.clone(), + focused_handler_tx, + ), + )? + .abort_handle(); + info!("Focus tracking task created successfully"); + Ok(MousePointer { node, spatial, @@ -196,7 +263,10 @@ impl MousePointer { capture_manager: CaptureManager::default(), mouse_datamap: Default::default(), keymap, - keyboard_cache: Arc::new(DashMap::default()), + focus_task_abort_handle, + input_delivery_task_abort_handle, + input_event_tx, + focus_notify, }) } pub fn update( @@ -251,12 +321,28 @@ impl MousePointer { *self.pointer.datamap.lock() = Datamap::from_typed(&self.mouse_datamap).unwrap(); } self.target_pointer_input(); - self.send_keyboard_input( - dbus_connection, - object_registry, - keyboard_input_events, - self.keyboard_cache.clone(), - ); + + // Send keyboard input events via channel + for event in keyboard_input_events.read() { + if let Some(key) = map_key(event.key_code) { + let input_event = InputEvent { + key, + pressed: matches!(event.state, ButtonState::Pressed), + }; + info!( + "Sending keyboard input event: key={}, pressed={}", + key, input_event.pressed + ); + if let Err(e) = self.input_event_tx.send(input_event) { + error!("Failed to send keyboard input event: {}", e); + } + } else { + warn!("Unable to map key code: {:?}", event.key_code); + } + } + + // Notify focus tracking task to recalculate focus + self.focus_notify.notify_waiters(); } fn target_pointer_input(&mut self) { let distance_calculator: DistanceCalculator = |space, data, field| { @@ -293,112 +379,146 @@ impl MousePointer { ); } - pub fn send_keyboard_input( - &mut self, - dbus_connection: &Connection, - object_registry: &ObjectRegistry, - mut keyboard_input_events: EventReader, - keyboard_handler_cache: Arc>>, + async fn focus_tracking_task( + object_registry: Arc, + focus_notify: Arc, + spatial: Arc, + pointer: Arc, + focused_handler_tx: watch::Sender>, ) { - let keyboard_handlers = object_registry.get_objects("org.stardustxr.XKBv1"); - let events = keyboard_input_events - .read() - .filter_map(|e| Some((map_key(e.key_code)?, e.state))) - .collect::>(); - // Spawn async task to handle keyboard input - tokio::spawn({ - let keyboard_handlers = keyboard_handlers.clone(); - let spatial = self.spatial.clone(); - let keymap_id = self.keymap.data().as_ffi(); - let dbus_connection = dbus_connection.clone(); + info!("Focus tracking task started"); - async move { - let mut closest_handler = None; - let mut closest_distance = f32::MAX; - - let mut join_set = JoinSet::new(); - for handler in &keyboard_handlers { - if keyboard_handler_cache.contains_key(handler) { - continue; - } - let handler = handler.clone(); - let dbus_connection = dbus_connection.clone(); - join_set.spawn(async move { - // TODO: refactor the whole thing so picking the keyboardhandler to send input to is separate from sending - timeout(Duration::from_millis(10), async { - let field_ref = handler - .to_typed_proxy::(&dbus_connection) - .await - .ok()?; - let uid = field_ref.uid().await.ok()?; - Some((handler, uid)) - }) + // Create keyboard handler query inside the task + let mut keyboard_query = ObjectQuery::< + (FieldRefProxy<'static>, KeyboardHandlerProxy<'static>), + _, + >::new(object_registry.clone(), ()); + let (keyboard_handlers, mapper) = keyboard_query.to_list_query(); + task::new( + || "Focus tracking mapper", + mapper.init(async |ev| match ev { + ListEvent::NewMatch((field_ref, keyboard_proxy)) => { + info!("New keyboard handler found"); + let uid = timeout(Duration::from_millis(100), field_ref.uid()) .await - .ok() - .flatten() - }); + .ok()? + .ok()?; + let field_node = EXPORTED_FIELDS.lock().get(&uid)?.upgrade()?; + let field = field_node.get_aspect::(); + Some((field, keyboard_proxy)) } - while let Some(Ok(Some((handler, field_ref_id)))) = join_set.join_next().await { - let exported_fields = EXPORTED_FIELDS.lock(); - let Some(field_ref_node) = - exported_fields.get(&field_ref_id).and_then(|f| f.upgrade()) - else { - println!("didn't find a thing :("); - continue; - }; - // println!("still sendin stuff :)"); - let Ok(field_ref) = field_ref_node.get_aspect::() else { - continue; - }; - drop(exported_fields); - keyboard_handler_cache.insert(handler.clone(), Arc::downgrade(&field_ref)); - } - keyboard_handler_cache.retain(|k, v| v.upgrade().is_some()); - if events.is_empty() { - return; + ListEvent::Modified((field_ref, keyboard_proxy)) => { + let uid = timeout(Duration::from_millis(100), field_ref.uid()) + .await + .ok()? + .ok()?; + let field_node = EXPORTED_FIELDS.lock().get(&uid)?.upgrade()?; + let field = field_node.get_aspect::(); + Some((field, keyboard_proxy)) } + _ => None, + }), + ); - for entry in &*keyboard_handler_cache { - let (handler, field_ref) = entry.pair(); - let Some(field_ref) = field_ref.clone().upgrade() else { - continue; - }; + // Main focus calculation loop + loop { + let mut closest_handler = None; + let mut closest_distance = f32::MAX; - let result = field_ref.ray_march(Ray { - origin: vec3(0.0, 0.0, 0.0), - direction: vec3(0.0, 0.0, -1.0), - space: spatial.clone(), - }); - - if result.deepest_point_distance > 0.0 - && result.min_distance < 0.05 - && result.deepest_point_distance < closest_distance - { - closest_distance = result.deepest_point_distance; - closest_handler = Some(handler.clone()); - } - } - - let Some(handler) = closest_handler else { - return; - }; - let Ok(keyboard_handler) = handler - .to_typed_proxy::(&dbus_connection) - .await - else { - return; + // Find closest handler + for (handler, (field_ref, keyboard_proxy)) in &*keyboard_handlers.iter().await { + let Ok(field_ref) = field_ref else { + continue; }; - // Register keymap first - let _ = keyboard_handler.keymap(keymap_id).await; + let result = field_ref.ray_march(Ray { + origin: vec3(0.0, 0.0, 0.0), + direction: vec3(0.0, 0.0, -1.0), + space: spatial.clone(), + }); - // Send key states - for (key, state) in events.iter() { - let pressed = matches!(state, ButtonState::Pressed); - let _ = keyboard_handler.key_state(key + 8, pressed).await; + if result.deepest_point_distance > 0.0 + && result.min_distance < 0.05 + && result.deepest_point_distance < closest_distance + { + closest_distance = result.deepest_point_distance; + closest_handler = Some(HandlerInfo { + handler: handler.clone(), + field_ref: field_ref.clone(), + keyboard_proxy: keyboard_proxy.clone(), + }); } } - }); + + // Update focused handler + if let Some(ref handler_info) = closest_handler { + info!( + "Focus tracking task: Focused on handler at distance {}", + closest_distance + ); + } else { + debug!("Focus tracking task: No handler in focus"); + } + let _ = focused_handler_tx.send(closest_handler); + + // Wait for next frame signal + focus_notify.notified().await; + } + } + + async fn input_delivery_task( + dbus_connection: Connection, + mut focused_handler_rx: watch::Receiver>, + mut input_event_rx: mpsc::UnboundedReceiver, + keymap_id: u64, + ) { + info!("Input delivery task started"); + loop { + // Handle input events + while let Some(input_event) = input_event_rx.recv().await { + info!( + "Input delivery task: Received input event key={}, pressed={}", + input_event.key, input_event.pressed + ); + // Get current focused handler + let current_handler = focused_handler_rx.borrow().clone(); + let Some(handler_info) = current_handler else { + warn!("Input delivery task: No focused handler, dropping input event"); + continue; + }; + + // Send input to handler using cached proxy + info!("Input delivery task: Sending to handler"); + let keyboard_handler = &handler_info.keyboard_proxy; + + // Register keymap first + if let Err(e) = keyboard_handler.keymap(keymap_id).await { + warn!("Input delivery task: Failed to register keymap: {}", e); + } + + // Send key state + if let Err(e) = keyboard_handler + .key_state(input_event.key + 8, input_event.pressed) + .await + { + error!("Input delivery task: Failed to send key state: {}", e); + } else { + info!( + "Input delivery task: Successfully sent key {} (pressed={})", + input_event.key + 8, + input_event.pressed + ); + } + } + } + } +} + +impl Drop for MousePointer { + fn drop(&mut self) { + // Abort the persistent tasks when MousePointer is dropped + self.focus_task_abort_handle.abort(); + self.input_delivery_task_abort_handle.abort(); } }