refactor(objects/mouse_pointer): single task pointer

This commit is contained in:
Nova
2025-10-13 03:39:55 -07:00
parent 05d4670609
commit 0e4f5de529
2 changed files with 231 additions and 111 deletions

2
Cargo.lock generated
View File

@@ -4779,7 +4779,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"itertools 0.14.0", "itertools 0.11.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.104", "syn 2.0.104",

View File

@@ -1,7 +1,7 @@
use super::{CaptureManager, DistanceCalculator, get_sorted_handlers}; use super::{CaptureManager, DistanceCalculator, get_sorted_handlers};
use crate::{ use crate::{
DbusConnection, ObjectRegistryRes, DbusConnection, ObjectRegistryRes,
core::client::INTERNAL_CLIENT, core::{client::INTERNAL_CLIENT, task},
nodes::{ nodes::{
Node, OwnedNode, Node, OwnedNode,
fields::{EXPORTED_FIELDS, Field, FieldTrait, Ray}, fields::{EXPORTED_FIELDS, Field, FieldTrait, Ray},
@@ -9,6 +9,7 @@ use crate::{
items::panel::KEYMAPS, items::panel::KEYMAPS,
spatial::Spatial, spatial::Spatial,
}, },
objects::FieldRef,
}; };
use bevy::{ use bevy::{
input::{ input::{
@@ -27,17 +28,36 @@ use rustc_hash::{FxHashMap, FxHasher};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use slotmap::{DefaultKey, Key as SlotKey}; use slotmap::{DefaultKey, Key as SlotKey};
use stardust_xr::{ 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, values::Datamap,
}; };
use std::sync::{Arc, Weak}; 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 tokio::time::{Duration, timeout};
use xkbcommon_rs::{Context, Keymap, KeymapFormat, xkb_keymap::CompileFlags}; use xkbcommon_rs::{Context, Keymap, KeymapFormat, xkb_keymap::CompileFlags};
use zbus::{Connection, names::OwnedInterfaceName}; use zbus::{Connection, names::OwnedInterfaceName};
pub struct FlatscreenInputPlugin; #[derive(Clone)]
struct HandlerInfo {
handler: ObjectInfo,
field_ref: Arc<Field>,
keyboard_proxy: KeyboardHandlerProxy<'static>,
}
#[derive(Debug, Clone)]
struct InputEvent {
key: u32,
pressed: bool,
}
pub struct FlatscreenInputPlugin;
impl Plugin for FlatscreenInputPlugin { impl Plugin for FlatscreenInputPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(Startup, setup); app.add_systems(Startup, setup);
@@ -50,9 +70,9 @@ impl Plugin for FlatscreenInputPlugin {
#[require(Camera3d)] #[require(Camera3d)]
pub struct FlatscreenCam; pub struct FlatscreenCam;
fn setup(mut cmds: Commands) { fn setup(mut cmds: Commands, object_registry: Res<ObjectRegistryRes>) {
let Ok(pointer) = let Ok(pointer) = MousePointer::new(object_registry.0.clone())
MousePointer::new().inspect_err(|err| error!("unable to create mouse pointer: {err}")) .inspect_err(|err| error!("unable to create mouse pointer: {err}"))
else { else {
return; return;
}; };
@@ -161,6 +181,14 @@ trait KeyboardHandler {
async fn reset(&self) -> zbus::Result<()>; 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)] #[derive(Resource)]
pub struct MousePointer { pub struct MousePointer {
node: OwnedNode, node: OwnedNode,
@@ -169,10 +197,16 @@ pub struct MousePointer {
pointer: Arc<InputMethod>, pointer: Arc<InputMethod>,
capture_manager: CaptureManager, capture_manager: CaptureManager,
mouse_datamap: MouseEvent, mouse_datamap: MouseEvent,
keyboard_cache: Arc<DashMap<ObjectInfo, Weak<Field>>>, // Task management
focus_task_abort_handle: AbortHandle,
input_delivery_task_abort_handle: AbortHandle,
// Channels
input_event_tx: mpsc::UnboundedSender<InputEvent>,
// Notification for focus recalculation
focus_notify: Arc<Notify>,
} }
impl MousePointer { impl MousePointer {
pub fn new() -> Result<Self> { pub fn new(object_registry: Arc<ObjectRegistry>) -> Result<Self> {
let node = Node::generate(&INTERNAL_CLIENT, false).add_to_scenegraph_owned()?; let node = Node::generate(&INTERNAL_CLIENT, false).add_to_scenegraph_owned()?;
let spatial = Spatial::add_to(&node.0, None, Mat4::IDENTITY, false); let spatial = Spatial::add_to(&node.0, None, Mat4::IDENTITY, false);
let pointer = InputMethod::add_to( let pointer = InputMethod::add_to(
@@ -189,6 +223,39 @@ impl MousePointer {
.unwrap(), .unwrap(),
); );
// Create channels and notification
let (focused_handler_tx, focused_handler_rx) = watch::channel::<Option<HandlerInfo>>(None);
let (input_event_tx, input_event_rx) = mpsc::unbounded_channel::<InputEvent>();
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 { Ok(MousePointer {
node, node,
spatial, spatial,
@@ -196,7 +263,10 @@ impl MousePointer {
capture_manager: CaptureManager::default(), capture_manager: CaptureManager::default(),
mouse_datamap: Default::default(), mouse_datamap: Default::default(),
keymap, keymap,
keyboard_cache: Arc::new(DashMap::default()), focus_task_abort_handle,
input_delivery_task_abort_handle,
input_event_tx,
focus_notify,
}) })
} }
pub fn update( pub fn update(
@@ -251,12 +321,28 @@ impl MousePointer {
*self.pointer.datamap.lock() = Datamap::from_typed(&self.mouse_datamap).unwrap(); *self.pointer.datamap.lock() = Datamap::from_typed(&self.mouse_datamap).unwrap();
} }
self.target_pointer_input(); self.target_pointer_input();
self.send_keyboard_input(
dbus_connection, // Send keyboard input events via channel
object_registry, for event in keyboard_input_events.read() {
keyboard_input_events, if let Some(key) = map_key(event.key_code) {
self.keyboard_cache.clone(), 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) { fn target_pointer_input(&mut self) {
let distance_calculator: DistanceCalculator = |space, data, field| { let distance_calculator: DistanceCalculator = |space, data, field| {
@@ -293,112 +379,146 @@ impl MousePointer {
); );
} }
pub fn send_keyboard_input( async fn focus_tracking_task(
&mut self, object_registry: Arc<ObjectRegistry>,
dbus_connection: &Connection, focus_notify: Arc<Notify>,
object_registry: &ObjectRegistry, spatial: Arc<Spatial>,
mut keyboard_input_events: EventReader<KeyboardInput>, pointer: Arc<InputMethod>,
keyboard_handler_cache: Arc<DashMap<ObjectInfo, Weak<Field>>>, focused_handler_tx: watch::Sender<Option<HandlerInfo>>,
) { ) {
let keyboard_handlers = object_registry.get_objects("org.stardustxr.XKBv1"); info!("Focus tracking task started");
let events = keyboard_input_events
.read()
.filter_map(|e| Some((map_key(e.key_code)?, e.state)))
.collect::<Vec<_>>();
// 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();
async move { // Create keyboard handler query inside the task
let mut closest_handler = None; let mut keyboard_query = ObjectQuery::<
let mut closest_distance = f32::MAX; (FieldRefProxy<'static>, KeyboardHandlerProxy<'static>),
_,
let mut join_set = JoinSet::new(); >::new(object_registry.clone(), ());
for handler in &keyboard_handlers { let (keyboard_handlers, mapper) = keyboard_query.to_list_query();
if keyboard_handler_cache.contains_key(handler) { task::new(
continue; || "Focus tracking mapper",
} mapper.init(async |ev| match ev {
let handler = handler.clone(); ListEvent::NewMatch((field_ref, keyboard_proxy)) => {
let dbus_connection = dbus_connection.clone(); info!("New keyboard handler found");
join_set.spawn(async move { let uid = timeout(Duration::from_millis(100), field_ref.uid())
// 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::<FieldRefProxy>(&dbus_connection)
.await
.ok()?;
let uid = field_ref.uid().await.ok()?;
Some((handler, uid))
})
.await .await
.ok() .ok()?
.flatten() .ok()?;
}); let field_node = EXPORTED_FIELDS.lock().get(&uid)?.upgrade()?;
let field = field_node.get_aspect::<Field>();
Some((field, keyboard_proxy))
} }
while let Some(Ok(Some((handler, field_ref_id)))) = join_set.join_next().await { ListEvent::Modified((field_ref, keyboard_proxy)) => {
let exported_fields = EXPORTED_FIELDS.lock(); let uid = timeout(Duration::from_millis(100), field_ref.uid())
let Some(field_ref_node) = .await
exported_fields.get(&field_ref_id).and_then(|f| f.upgrade()) .ok()?
else { .ok()?;
println!("didn't find a thing :("); let field_node = EXPORTED_FIELDS.lock().get(&uid)?.upgrade()?;
continue; let field = field_node.get_aspect::<Field>();
}; Some((field, keyboard_proxy))
// println!("still sendin stuff :)");
let Ok(field_ref) = field_ref_node.get_aspect::<Field>() 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;
} }
_ => None,
}),
);
for entry in &*keyboard_handler_cache { // Main focus calculation loop
let (handler, field_ref) = entry.pair(); loop {
let Some(field_ref) = field_ref.clone().upgrade() else { let mut closest_handler = None;
continue; let mut closest_distance = f32::MAX;
};
let result = field_ref.ray_march(Ray { // Find closest handler
origin: vec3(0.0, 0.0, 0.0), for (handler, (field_ref, keyboard_proxy)) in &*keyboard_handlers.iter().await {
direction: vec3(0.0, 0.0, -1.0), let Ok(field_ref) = field_ref else {
space: spatial.clone(), continue;
});
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::<KeyboardHandlerProxy>(&dbus_connection)
.await
else {
return;
}; };
// Register keymap first let result = field_ref.ray_march(Ray {
let _ = keyboard_handler.keymap(keymap_id).await; origin: vec3(0.0, 0.0, 0.0),
direction: vec3(0.0, 0.0, -1.0),
space: spatial.clone(),
});
// Send key states if result.deepest_point_distance > 0.0
for (key, state) in events.iter() { && result.min_distance < 0.05
let pressed = matches!(state, ButtonState::Pressed); && result.deepest_point_distance < closest_distance
let _ = keyboard_handler.key_state(key + 8, pressed).await; {
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<Option<HandlerInfo>>,
mut input_event_rx: mpsc::UnboundedReceiver<InputEvent>,
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();
} }
} }