#![allow(unused)] use crate::{ core::client::INTERNAL_CLIENT, nodes::{ Node, OwnedNode, fields::{EXPORTED_FIELDS, Field, Shape}, spatial::{EXPORTED_SPATIALS, Spatial}, }, }; use glam::{Mat4, vec3}; use input::{ eye_pointer::EyePointer, mouse_pointer::MousePointer, oxr_controller::OxrControllerInput, oxr_hand::OxrHandInput, }; use parking_lot::RwLock; use play_space::PlaySpaceBounds; use stardust_xr::schemas::dbus::object_registry::ObjectRegistry; use std::{ marker::PhantomData, sync::{Arc, atomic::Ordering}, }; use tokio::{sync::mpsc, task::AbortHandle}; use zbus::{Connection, interface, object_server::Interface, zvariant::OwnedObjectPath}; pub mod hmd; pub mod input; pub mod play_space; pub struct ObjectHandle(Connection, OwnedObjectPath, PhantomData); impl Clone for ObjectHandle { fn clone(&self) -> Self { Self(self.0.clone(), self.1.clone(), PhantomData) } } impl Drop for ObjectHandle { fn drop(&mut self) { let connection = self.0.clone(); let object_path = self.1.clone(); tokio::task::spawn(async move { connection.object_server().remove::(object_path); }); } } /// A wrapper around ObjectHandle that batches async updates /// instead of spawning a tokio task for each state change pub struct AsyncTracked { pub sender: mpsc::UnboundedSender, pub _handle: ObjectHandle, pub _abort_handle: AbortHandle, } impl AsyncTracked { pub fn new(connection: &Connection, path: &str) -> Self { let handle = Tracked::new(connection, path); let (sender, mut receiver) = mpsc::unbounded_channel::(); // Spawn a single long-running task that processes state updates let task = tokio::task::spawn({ let handle = handle.clone(); async move { while let Some(is_tracked) = receiver.recv().await { let _ = handle.set_tracked(is_tracked).await; } } }); Self { sender, _handle: handle, _abort_handle: task.abort_handle(), } } pub fn set_tracked(&self, is_tracked: bool) { // Just send over channel instead of spawning a task let _ = self.sender.send(is_tracked); } } impl Drop for AsyncTracked { fn drop(&mut self) { self._abort_handle.abort(); } } pub struct SpatialRef(u64, OwnedNode); impl SpatialRef { pub fn create(connection: &Connection, path: &str) -> (Arc, ObjectHandle) { let node = OwnedNode(Arc::new(Node::generate(&INTERNAL_CLIENT, false))); let spatial = Spatial::add_to(&node.0, None, Mat4::IDENTITY, false); let uid: u64 = rand::random(); EXPORTED_SPATIALS .lock() .insert(uid, Arc::downgrade(&node.0)); tokio::task::spawn({ let connection = connection.clone(); let path = path.to_string(); async move { connection .object_server() .at(path, Self(uid, node)) .await .unwrap(); } }); ( spatial, ObjectHandle( connection.clone(), OwnedObjectPath::try_from(path.to_string()).unwrap(), PhantomData, ), ) } } #[interface(name = "org.stardustxr.SpatialRef")] impl SpatialRef { #[zbus(property)] fn uid(&self) -> u64 { self.0 } } pub struct Tracked(bool); impl Tracked { pub fn new(connection: &Connection, path: &str) -> ObjectHandle { tokio::task::spawn({ let connection = connection.clone(); let path = path.to_string(); async move { connection .object_server() .at(path, Self(false)) .await .unwrap(); } }); ObjectHandle( connection.clone(), OwnedObjectPath::try_from(path.to_string()).unwrap(), PhantomData, ) } } impl ObjectHandle { pub async fn set_tracked(&self, is_tracked: bool) -> zbus::Result<()> { let tracked_ref = self .0 .object_server() .interface::<_, Tracked>(self.1.as_ref()) .await?; let mut tracked = tracked_ref.get_mut().await; if tracked.0 != is_tracked { tracked.0 = is_tracked; tracked .is_tracked_changed(tracked_ref.signal_emitter()) .await; } Ok(()) } } #[interface(name = "org.stardustxr.Tracked")] impl Tracked { #[zbus(property)] fn is_tracked(&self) -> bool { self.0 } } pub struct FieldRef(u64, OwnedNode); impl FieldRef { pub fn create( connection: &Connection, path: &str, shape: Shape, ) -> (Arc, ObjectHandle) { let node = OwnedNode(Arc::new(Node::generate(&INTERNAL_CLIENT, false))); Spatial::add_to(&node.0, None, Mat4::IDENTITY, false); let field = Field::add_to(&node.0, shape).unwrap(); let uid: u64 = rand::random(); EXPORTED_FIELDS.lock().insert(uid, Arc::downgrade(&node.0)); tokio::task::spawn({ let connection = connection.clone(); let path = path.to_string(); async move { connection .object_server() .at(path, Self(uid, node)) .await .unwrap(); } }); ( field, ObjectHandle( connection.clone(), OwnedObjectPath::try_from(path.to_string()).unwrap(), PhantomData, ), ) } } #[interface(name = "org.stardustxr.FieldRef")] impl FieldRef { #[zbus(property)] fn uid(&self) -> u64 { self.0 } }