use super::{ INPUT_HANDLER_REGISTRY, INPUT_METHOD_REF_ASPECT_ALIAS_INFO, INPUT_METHOD_REGISTRY, InputData, InputDataTrait, InputDataType, InputHandler, InputMethodAspect, InputMethodRefAspect, input_method_client, }; use crate::{ core::{ client::Client, error::{Result, ServerError}, registry::Registry, }, nodes::{ Node, alias::{Alias, AliasList}, fields::{FIELD_ALIAS_INFO, Field}, spatial::Spatial, }, }; use color_eyre::eyre::eyre; use parking_lot::Mutex; use stardust_xr::values::Datamap; use std::sync::{Arc, Weak}; pub struct InputMethod { pub spatial: Arc, pub data: Mutex, pub datamap: Mutex, handler_aliases: AliasList, handler_field_aliases: AliasList, pub(super) handler_order: Mutex>>, pub capture_attempts: Registry, pub captures: Registry, } impl InputMethod { pub fn add_to( node: &Arc, data: InputDataType, datamap: Datamap, ) -> Result> { let method = InputMethod { spatial: node.get_aspect::().unwrap().clone(), data: Mutex::new(data), datamap: Mutex::new(datamap), handler_aliases: AliasList::default(), handler_field_aliases: AliasList::default(), handler_order: Mutex::new(Vec::new()), capture_attempts: Registry::new(), captures: Registry::new(), }; for handler in INPUT_HANDLER_REGISTRY.get_valid_contents() { method.handle_new_handler(&handler); } let method = INPUT_METHOD_REGISTRY.add(method); node.add_aspect_raw(method.clone()); node.add_aspect(InputMethodRef); Ok(method) } pub fn distance(&self, to: &Field) -> f32 { self.data.lock().distance(&self.spatial, to) } pub fn set_handler_order<'a>(&self, handlers: impl Iterator>) { *self.handler_order.lock() = handlers.map(Arc::downgrade).collect(); } pub(super) fn make_alias(&self, handler: &InputHandler) { let Some(method_node) = self.spatial.node() else { return; }; let Some(handler_node) = handler.spatial.node() else { return; }; let Some(client) = handler_node.get_client() else { return; }; let Ok(method_alias) = Alias::create( &method_node, &client, INPUT_METHOD_REF_ASPECT_ALIAS_INFO.clone(), Some(&handler.method_aliases), ) else { return; }; method_alias.set_enabled(false); } pub(super) fn handle_new_handler(&self, handler: &InputHandler) { self.make_alias(handler); let Some(method_node) = self.spatial.node() else { return; }; let Some(method_client) = method_node.get_client() else { return; }; let Some(handler_node) = handler.spatial.node() else { return; }; // Receiver itself let Ok(handler_alias) = Alias::create( &handler_node, &method_client, INPUT_METHOD_REF_ASPECT_ALIAS_INFO.clone(), Some(&self.handler_aliases), ) else { return; }; let Some(handler_field_node) = handler.field.spatial.node() else { return; }; // Handler's field let Ok(rx_field_alias) = Alias::create( &handler_field_node, &method_client, FIELD_ALIAS_INFO.clone(), Some(&self.handler_field_aliases), ) else { return; }; let _ = input_method_client::create_handler(&method_node, &handler_alias, &rx_field_alias); } pub(super) fn handle_drop_handler(&self, handler: &InputHandler) { let Some(tx_node) = self.spatial.node() else { return; }; let Some(handler_alias) = self.handler_aliases.get_from_aspect(handler) else { return; }; let _ = input_method_client::destroy_handler(&tx_node, handler_alias.id); self.handler_aliases.remove_aspect(handler); self.handler_field_aliases .remove_aspect(handler.field.as_ref()); self.capture_attempts.remove(handler); } pub(super) fn serialize(&self, alias_id: u64, handler: &Arc) -> InputData { let mut input = self.data.lock().clone(); input.transform(self, handler); InputData { id: alias_id, input, distance: self.distance(&handler.field), datamap: self.datamap.lock().clone(), order: self .handler_order .lock() .iter() .enumerate() .find(|(_, h)| h.ptr_eq(&Arc::downgrade(handler))) .unwrap() .0 as u32, captured: self.captures.get_valid_contents().contains(handler), } } pub(super) fn cull_capture_attempts(&self) { let sent = self .handler_order .lock() .iter() .filter_map(Weak::upgrade) .collect::>(); self.capture_attempts.retain(|handler| { !handler .spatial .node() .and_then(|n| n.get_client()) .map(|c| c.unresponsive()) .unwrap_or(false) && sent.contains(handler) }); } } impl InputMethodAspect for InputMethod { #[doc = "Set the spatial input component of this input method. You must keep the same input data type throughout the entire thing."] fn set_input( node: Arc, _calling_client: Arc, input: InputDataType, ) -> Result<()> { let input_method = node.get_aspect::()?; *input_method.data.lock() = input; Ok(()) } #[doc = "Set the datmap of this input method"] fn set_datamap(node: Arc, _calling_client: Arc, datamap: Datamap) -> Result<()> { let input_method = node.get_aspect::()?; *input_method.datamap.lock() = datamap; Ok(()) } #[doc = "Manually set the order of handlers to propagate input to, or else let the server decide."] fn set_handler_order( node: Arc, _calling_client: Arc, handlers: Vec>, ) -> Result<()> { let input_method = node.get_aspect::()?; let handlers = handlers .into_iter() .filter_map(|p| p.get_aspect::().ok()) .map(|i| Arc::downgrade(&i)) .collect::>(); *input_method.handler_order.lock() = handlers; Ok(()) } #[doc = "Set which handlers are captured."] fn set_captures( node: Arc, _calling_client: Arc, handlers: Vec>, ) -> Result<()> { let input_method = node.get_aspect::()?; input_method.captures.clear(); for handler in handlers { let Ok(handler) = handler.get_aspect::() else { continue; }; input_method.captures.add_raw(&handler); } Ok(()) } } impl Drop for InputMethod { fn drop(&mut self) { INPUT_METHOD_REGISTRY.remove(self); } } pub struct InputMethodRef; impl InputMethodRefAspect for InputMethodRef { #[doc = "Try to capture the input method with the given handler. When the handler does not get input from the method, it will be released."] fn try_capture( node: Arc, _calling_client: Arc, handler: Arc, ) -> Result<()> { let input_method = node.get_aspect::()?; let input_handler = handler.get_aspect::()?; input_method.capture_attempts.add_raw(&input_handler); let Some(handler_alias) = input_method .handler_aliases .get_from_aspect(&*input_handler) else { return Err(ServerError::Report(eyre!( "Internal: Couldn't get handler alias somehow?" ))); }; input_method_client::request_capture_handler(&node, handler_alias.get_id()) } #[doc = "If captured by this handler, release it (e.g. the object is let go of after grabbing)."] fn release(node: Arc, _calling_client: Arc, handler: Arc) -> Result<()> { let input_method = node.get_aspect::()?; let input_handler = handler.get_aspect::()?; input_method.capture_attempts.remove(&input_handler); let Some(handler_alias) = input_method .handler_aliases .get_from_aspect(&*input_handler) else { return Err(ServerError::Report(eyre!( "Internal: Couldn't get handler alias somehow?" ))); }; input_method_client::release_handler(&node, handler_alias.get_id()) } }