ailloli_ui_openxr/
openxr_runtime.rs

1//! OpenXR runtime scaffolding for Ailloli UI.
2//!
3//! This module focuses on reusable integration points while keeping host-specific
4//! session policy outside the framework:
5//!
6//! - host can build their own OpenXR session loop
7//! - map controller/hand sample input into `ailloli_ui_runtime` events via
8//!   [`crate::input::OpenXrInputMapper`]
9//! - keep a dedicated façade object in the same crate (`VrApp`) without touching
10//!   `ailloli_ui_winit`.
11//!
12//! The production OpenXR path owns XR lifecycle here, while `ailloli_ui_render_vulkan`
13//! owns Vulkan/SPIR-V UI drawing.
14
15#[cfg(feature = "openxr")]
16pub mod composer;
17#[cfg(feature = "openxr")]
18pub mod error;
19#[cfg(feature = "openxr")]
20pub mod external_host;
21#[cfg(feature = "openxr")]
22pub mod input;
23#[cfg(feature = "openxr")]
24pub mod instance;
25#[cfg(feature = "openxr")]
26pub mod panel;
27#[cfg(feature = "openxr")]
28pub mod ray_overlay;
29#[cfg(feature = "openxr")]
30pub mod session_loop;
31#[cfg(all(feature = "openxr", feature = "smoke_ui"))]
32pub mod smoke;
33#[cfg(feature = "openxr")]
34pub mod swapchain;
35#[cfg(feature = "openxr")]
36pub mod ui_layer;
37#[cfg(feature = "openxr")]
38pub mod ui_loop;
39#[cfg(feature = "openxr")]
40pub mod vulkan;
41
42#[cfg(feature = "openxr")]
43pub use composer::{
44    OpenXrCurvedLayerBackend, OpenXrCurvedScreenOptions, OpenXrQuadComposer,
45    OpenXrQuadFrameLoopOptions, OpenXrQuadLayerOptions, OpenXrRenderVulkanFrameLoopOptions,
46    OpenXrResolvedScreenBackend, OpenXrScreenComposer, OpenXrScreenLayer, OpenXrScreenLayerOptions,
47    OpenXrScreenSurfaceMode, OpenXrUiFrameLoopOptions,
48};
49#[cfg(feature = "openxr")]
50pub use error::OpenXrRuntimeError;
51#[cfg(feature = "openxr")]
52pub use external_host::{
53    OpenXrExternalUiHost, OpenXrExternalUiHostFrame, OpenXrExternalUiHostFrameParts,
54    OpenXrExternalUiHostFrameResult, OpenXrExternalUiHostOptions, OpenXrExternalUiHostRayOptions,
55};
56#[cfg(feature = "openxr")]
57pub use input::{
58    OpenXrActionInput, OpenXrActionInputFrame, OpenXrInputCapabilities, OpenXrInputHand,
59    OpenXrInputSourceInfo, OpenXrInputSourceKind, OpenXrPointerSelectionPolicy,
60    OpenXrUiInputOptions,
61};
62#[cfg(feature = "openxr")]
63pub use instance::OpenXrInstance;
64#[cfg(feature = "openxr")]
65pub use panel::{
66    apply_pointer_depth_delta, face_user_yaw_only, face_user_yaw_pitch_clamped,
67    logical_point_to_panel_local, panel_local_to_world, vec3_from_xr, OpenXrPanelFacingMode,
68    OpenXrPanelFacingOptions, OpenXrPanelGrabState, PanelDepthUpdate, DEFAULT_PANEL_PITCH_MAX_RAD,
69    DEFAULT_PANEL_PITCH_MIN_RAD,
70};
71#[cfg(feature = "openxr")]
72pub use ray_overlay::{
73    OpenXrRayHitKind, OpenXrRayOverlay, OpenXrRayOverlayOptions, OpenXrRaySample,
74    OPENXR_RAY_MAX_LENGTH_METERS, OPENXR_RAY_MIN_LENGTH_METERS, OPENXR_RAY_TEXTURE_HEIGHT,
75    OPENXR_RAY_TEXTURE_WIDTH, OPENXR_RAY_WIDTH_METERS,
76};
77#[cfg(feature = "openxr")]
78pub use session_loop::{OpenXrRuntime, OpenXrRuntimeOptions, ReferenceSpacePreference};
79#[cfg(all(feature = "openxr", feature = "smoke_ui"))]
80pub use smoke::{run_openxr_smoke, OpenXrSmokeExitReason, OpenXrSmokeOptions, OpenXrSmokeResult};
81#[cfg(feature = "openxr")]
82pub use swapchain::{OpenXrAcquiredImage, OpenXrQuadSwapchain, OpenXrSwapchainFormat};
83#[cfg(feature = "openxr")]
84pub use ui_layer::{
85    OpenXrExternalHostFrame, OpenXrExternalVulkanContext, OpenXrQuadPointerMapper, OpenXrUiLayer,
86    OpenXrUiLayerOptions,
87};
88#[cfg(feature = "openxr")]
89pub use vulkan::OpenXrVulkanContext;
90
91use ailloli_ui_core::event::{Event, Modifiers};
92use std::marker::PhantomData;
93
94use crate::input::{map_ray_to_openxr_input_events, OpenXrInputMapper, OpenXrPointerSource};
95
96/// VR-friendly facade hook for external loops.
97///
98/// `VrApp` does not own an OpenXR session; it only provides stable stateful
99/// input conversion entry points for any loop that emits `OpenXrPointerSource`s.
100///
101/// # Examples
102///
103/// ```
104/// use ailloli_ui_openxr::openxr_runtime::VrApp;
105///
106/// let app = VrApp::<()>::new();
107/// assert!(format!("{:?}", app.input_mapper).contains("source_state"));
108/// ```
109#[derive(Debug)]
110pub struct VrApp<A> {
111    /// Associates the facade with the host's action type without storing it.
112    phantom: PhantomData<A>,
113    /// Stateful pointer-transition mapper shared across frames.
114    pub input_mapper: OpenXrInputMapper,
115}
116
117impl<A> VrApp<A> {
118    /// Creates a facade with no remembered pointer state.
119    ///
120    /// # Examples
121    ///
122    /// ```
123    /// use ailloli_ui_openxr::openxr_runtime::VrApp;
124    /// let _: VrApp<String> = VrApp::new();
125    /// ```
126    pub fn new() -> Self {
127        Self {
128            phantom: PhantomData,
129            input_mapper: OpenXrInputMapper::new(),
130        }
131    }
132
133    /// Converts one ordered source slice into runtime events.
134    ///
135    /// The mapper retains hover and pressed transitions for subsequent calls.
136    ///
137    /// # Examples
138    ///
139    /// ```
140    /// use ailloli_ui_core::event::Modifiers;
141    /// use ailloli_ui_openxr::{OpenXrPointerHit, OpenXrPointerSample};
142    /// use ailloli_ui_openxr::openxr_runtime::VrApp;
143    /// let mut app = VrApp::<()>::new();
144    /// let events = app.map_pointer_sources(
145    ///     &[OpenXrPointerSample::new(1, OpenXrPointerHit::Miss, false)],
146    ///     Modifiers::default(),
147    /// );
148    /// assert!(events.is_empty());
149    /// ```
150    pub fn map_pointer_sources<T>(&mut self, sources: &[T], modifiers: Modifiers) -> Vec<Event>
151    where
152        T: OpenXrPointerSource,
153    {
154        map_ray_to_openxr_input_events(&mut self.input_mapper, sources, modifiers)
155    }
156
157    /// Resets all pointer state without synthesizing releases.
158    ///
159    /// Call this on app hide or focus loss before accepting a fresh source set.
160    ///
161    /// # Examples
162    ///
163    /// ```
164    /// use ailloli_ui_openxr::openxr_runtime::VrApp;
165    /// let mut app = VrApp::<()>::new();
166    /// app.clear_pointer_state();
167    /// ```
168    pub fn clear_pointer_state(&mut self) {
169        self.input_mapper.clear();
170    }
171}