aframe/
sys.rs

1//! Lower level FFI stuff. Mostly used internally, but exposed in case the abstractions
2//! of this library are too restrictive.
3//! Using this should not be necessary for the usage of this crate, but the 
4//! public APIs have been provided while this crate is still feature-incomplete.
5
6use wasm_bindgen::{JsCast, prelude::*};
7use std::convert::TryFrom;
8use js_sys::{Array, Object};
9use std::sync::LazyLock;
10
11static AFRAME: LazyLock<Option<Aframe>> = LazyLock::new(Aframe::get);
12
13#[wasm_bindgen]
14extern 
15{
16    /// [registering-a-primitive](https://aframe.io/docs/1.6.0/introduction/html-and-primitives.html#registering-a-primitive)
17    #[wasm_bindgen(js_namespace = AFRAME)]
18    pub fn registerPrimitive(name: &str, definition: JsValue);
19
20    /// [register-component-name-definition](https://aframe.io/docs/1.6.0/core/component.html#aframe-registercomponent-name-definition)
21    #[wasm_bindgen(js_namespace = AFRAME)]
22    pub fn registerComponent(name: &str, data: JsValue);
23
24    /// [registering-a-system](https://aframe.io/docs/1.6.0/core/systems.html#registering-a-system)
25    #[wasm_bindgen(js_namespace = AFRAME)]
26    pub fn registerSystem(name: &str, data: JsValue);
27
28    /// [register-a-custom-shader-material](https://aframe.io/docs/1.6.0/components/material.html#register-a-custom-shader-material)
29    #[wasm_bindgen(js_namespace = AFRAME)]
30    pub fn registerShader(name: &str, data: JsValue);
31
32    /// [register-a-custom-geometry](https://aframe.io/docs/1.6.0/components/geometry.html#register-a-custom-geometry)
33    #[wasm_bindgen(js_namespace = AFRAME)]
34    pub fn registerGeometry(name: &str, data: JsValue);
35
36    /// [aframe_properties_registerelement](https://aframe.io/docs/1.6.0/core/globals.html#aframe_properties_registerelement)
37    #[wasm_bindgen(js_namespace = AFRAME)]
38    pub fn registerElement(name: &str, data: JsValue);
39
40    // /// Checks if a VR headset is connected by looking for orientation data.
41    // #[wasm_bindgen(js_namespace = ["AFRAME", "utils", "device"])]
42    // pub fn checkHeadsetConnected() -> bool;
43    // /// Checks if device is Gear VR.
44    // #[wasm_bindgen(js_namespace = ["AFRAME", "utils", "device"])]
45    // pub fn isGearVR() -> bool;
46    // /// Checks if device is Oculus Go.
47    // #[wasm_bindgen(js_namespace = ["AFRAME", "utils", "device"])]
48    // pub fn isOculusGo() -> bool;
49    // /// Checks if device is a smartphone.
50    // #[wasm_bindgen(js_namespace = ["AFRAME", "utils", "device"])]
51    // pub fn isMobile() -> bool;
52}
53
54/// Access a field from an object
55pub(crate) fn access_field(obj: &Object, field_name: &'static str) -> Option<JsValue>
56{
57        Object::entries(obj)
58            .iter()
59            .find(|e| e.dyn_ref::<Array>()
60                .filter(|entry| entry
61                    .iter()
62                    .next()
63                    .and_then(|key| key.as_string())
64                    .filter(|key_str| key_str == field_name)
65                    .is_some())
66                .is_some())
67}
68
69/// Global [three.js](https://threejs.org/) object.
70pub fn three_js() -> Option<JsValue>
71{
72    AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "THREE"))
73}
74
75/// Object of registered components.
76pub fn components() -> Option<JsValue>
77{
78    AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "components"))
79}
80
81/// Object of registered geometries.
82pub fn geometries() -> Option<JsValue>
83{
84    AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "geometries"))
85}
86
87/// Object of registered primitives.
88pub fn primitives() -> Option<JsValue>
89{
90    AFRAME.as_ref()
91        .and_then(|aframe| access_field(&aframe.0, "primitives"))
92        .and_then(|primitives| 
93        {
94            primitives.unchecked_into::<Array>()
95                .iter()
96                .skip(1)
97                .next()
98                .and_then(|primitives| access_field(primitives.unchecked_ref(), "primitives"))
99        })
100}
101
102/// Object of registered shaders.
103pub fn shaders() -> Option<JsValue>
104{
105    AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "shaders"))
106}
107
108/// Object of registered systems.
109pub fn systems() -> Option<JsValue>
110{
111    AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "systems"))
112}
113
114/// Version of A-Frame build.
115pub fn version() -> Option<JsValue>
116{
117    AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "version"))
118}
119
120pub fn utils() -> Option<JsValue>
121{
122    AFRAME.as_ref()
123        .and_then(|aframe| access_field(&aframe.0, "utils"))
124        .and_then(|utils| utils.unchecked_into::<Array>().iter().skip(1).next())
125}
126
127pub fn device() -> Option<JsValue>
128{
129    utils()
130        .and_then(|utils| 
131        {
132            access_field(utils.unchecked_ref(), "device")
133                .and_then(|utils| utils.unchecked_into::<Array>().iter().skip(1).next())
134        })
135}
136
137struct Aframe(Object);
138unsafe impl Send for Aframe {}
139unsafe impl Sync for Aframe {}
140
141impl Aframe
142{
143    fn get() -> Option<Self>
144    {
145        web_sys::window()
146            .ok_or("Failed to access window")
147            .and_then(Aframe::try_from)
148            .ok()
149    }
150}
151
152impl TryFrom<web_sys::Window> for Aframe
153{
154    type Error = &'static str;
155
156    fn try_from(window: web_sys::Window) -> Result<Self, Self::Error> 
157    {
158        window.get("AFRAME")
159            .map(Aframe)
160            .ok_or("Failed to access AFRAME global")
161    }
162}