Skip to main content

script/dom/webxr/
xrinputsourcearray.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use dom_struct::dom_struct;
6use js::context::JSContext;
7use script_bindings::cell::DomRefCell;
8use script_bindings::reflector::{Reflector, reflect_dom_object};
9use webxr_api::{InputId, InputSource};
10
11use crate::dom::bindings::codegen::Bindings::XRInputSourceArrayBinding::XRInputSourceArrayMethods;
12use crate::dom::bindings::inheritance::Castable;
13use crate::dom::bindings::reflector::DomGlobal;
14use crate::dom::bindings::root::{Dom, DomRoot};
15use crate::dom::event::Event;
16use crate::dom::window::Window;
17use crate::dom::xrinputsource::XRInputSource;
18use crate::dom::xrinputsourceschangeevent::XRInputSourcesChangeEvent;
19use crate::dom::xrsession::XRSession;
20use crate::script_runtime::CanGc;
21
22#[dom_struct]
23pub(crate) struct XRInputSourceArray {
24    reflector_: Reflector,
25    input_sources: DomRefCell<Vec<Dom<XRInputSource>>>,
26}
27
28impl XRInputSourceArray {
29    fn new_inherited() -> XRInputSourceArray {
30        XRInputSourceArray {
31            reflector_: Reflector::new(),
32            input_sources: DomRefCell::new(vec![]),
33        }
34    }
35
36    pub(crate) fn new(window: &Window, can_gc: CanGc) -> DomRoot<XRInputSourceArray> {
37        reflect_dom_object(
38            Box::new(XRInputSourceArray::new_inherited()),
39            window,
40            can_gc,
41        )
42    }
43
44    pub(crate) fn add_input_sources(
45        &self,
46        cx: &mut JSContext,
47        session: &XRSession,
48        inputs: &[InputSource],
49    ) {
50        let global = self.global();
51        let window = global.as_window();
52
53        let mut added = vec![];
54        for info in inputs {
55            // This is quadratic, but won't be a problem for the only case
56            // where we add multiple input sources (the initial input sources case)
57            debug_assert!(
58                !self
59                    .input_sources
60                    .borrow()
61                    .iter()
62                    .any(|i| i.id() == info.id),
63                "Should never add a duplicate input id!"
64            );
65            let input = XRInputSource::new(cx, window, session, info.clone());
66            self.input_sources.borrow_mut().push(Dom::from_ref(&input));
67            added.push(input);
68        }
69
70        let event = XRInputSourcesChangeEvent::new(
71            window,
72            atom!("inputsourceschange"),
73            false,
74            true,
75            session,
76            &added,
77            &[],
78            CanGc::from_cx(cx),
79        );
80        event.upcast::<Event>().fire(cx, session.upcast());
81    }
82
83    pub(crate) fn remove_input_source(&self, cx: &mut JSContext, session: &XRSession, id: InputId) {
84        let global = self.global();
85        let window = global.as_window();
86        let removed = if let Some(i) = self.input_sources.borrow().iter().find(|i| i.id() == id) {
87            i.gamepad().update_connected(false);
88            [DomRoot::from_ref(&**i)]
89        } else {
90            return;
91        };
92
93        let event = XRInputSourcesChangeEvent::new(
94            window,
95            atom!("inputsourceschange"),
96            false,
97            true,
98            session,
99            &[],
100            &removed,
101            CanGc::from_cx(cx),
102        );
103        self.input_sources.borrow_mut().retain(|i| i.id() != id);
104        event.upcast::<Event>().fire(cx, session.upcast());
105    }
106
107    pub(crate) fn add_remove_input_source(
108        &self,
109        cx: &mut JSContext,
110        session: &XRSession,
111        id: InputId,
112        info: InputSource,
113    ) {
114        let global = self.global();
115        let window = global.as_window();
116        let root;
117        let removed = if let Some(i) = self.input_sources.borrow().iter().find(|i| i.id() == id) {
118            i.gamepad().update_connected(false);
119            root = [DomRoot::from_ref(&**i)];
120            &root as &[_]
121        } else {
122            warn!("Could not find removed input source with id {:?}", id);
123            &[]
124        };
125        self.input_sources.borrow_mut().retain(|i| i.id() != id);
126        let input = XRInputSource::new(cx, window, session, info);
127        self.input_sources.borrow_mut().push(Dom::from_ref(&input));
128
129        let added = [input];
130
131        let event = XRInputSourcesChangeEvent::new(
132            window,
133            atom!("inputsourceschange"),
134            false,
135            true,
136            session,
137            &added,
138            removed,
139            CanGc::from_cx(cx),
140        );
141        event.upcast::<Event>().fire(cx, session.upcast());
142    }
143
144    pub(crate) fn find(&self, id: InputId) -> Option<DomRoot<XRInputSource>> {
145        self.input_sources
146            .borrow()
147            .iter()
148            .find(|x| x.id() == id)
149            .map(|x| DomRoot::from_ref(&**x))
150    }
151}
152
153impl XRInputSourceArrayMethods<crate::DomTypeHolder> for XRInputSourceArray {
154    /// <https://immersive-web.github.io/webxr/#dom-xrinputsourcearray-length>
155    fn Length(&self) -> u32 {
156        self.input_sources.borrow().len() as u32
157    }
158
159    /// <https://immersive-web.github.io/webxr/#xrinputsourcearray>
160    fn IndexedGetter(&self, n: u32) -> Option<DomRoot<XRInputSource>> {
161        self.input_sources
162            .borrow()
163            .get(n as usize)
164            .map(|x| DomRoot::from_ref(&**x))
165    }
166}