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