webxr_api/
registry.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 base::generic_channel::{self, GenericCallback, GenericReceiver, GenericSender};
6use embedder_traits::EventLoopWaker;
7use ipc_channel::ipc::IpcSender;
8use log::warn;
9use profile_traits::generic_callback::GenericCallback as ProfileGenericCallback;
10use serde::{Deserialize, Serialize};
11
12use crate::{
13    DiscoveryAPI, Error, Frame, GLTypes, LayerGrandManager, MainThreadSession, MockDeviceInit,
14    MockDeviceMsg, MockDiscoveryAPI, Session, SessionBuilder, SessionId, SessionInit, SessionMode,
15};
16
17#[derive(Clone, Serialize, Deserialize)]
18pub struct Registry {
19    sender: GenericSender<RegistryMsg>,
20    waker: MainThreadWakerImpl,
21}
22
23pub struct MainThreadRegistry<GL> {
24    discoveries: Vec<Box<dyn DiscoveryAPI<GL>>>,
25    sessions: Vec<Box<dyn MainThreadSession>>,
26    mocks: Vec<Box<dyn MockDiscoveryAPI<GL>>>,
27    sender: GenericSender<RegistryMsg>,
28    receiver: GenericReceiver<RegistryMsg>,
29    waker: MainThreadWakerImpl,
30    grand_manager: LayerGrandManager<GL>,
31    next_session_id: u32,
32}
33
34#[derive(Clone, Serialize, Deserialize)]
35struct MainThreadWakerImpl {
36    callback: GenericCallback<()>,
37}
38
39impl MainThreadWakerImpl {
40    fn new(waker: Box<dyn EventLoopWaker>) -> Result<MainThreadWakerImpl, Error> {
41        let callback =
42            GenericCallback::new(move |_| waker.wake()).expect("Could not construct callback");
43        Ok(MainThreadWakerImpl { callback })
44    }
45
46    fn wake(&self) {
47        let _ = self.callback.send(());
48    }
49}
50
51impl Registry {
52    pub fn supports_session(
53        &mut self,
54        mode: SessionMode,
55        dest: ProfileGenericCallback<Result<(), Error>>,
56    ) {
57        let _ = self.sender.send(RegistryMsg::SupportsSession(mode, dest));
58        self.waker.wake();
59    }
60
61    pub fn request_session(
62        &mut self,
63        mode: SessionMode,
64        init: SessionInit,
65        dest: IpcSender<Result<Session, Error>>,
66        animation_frame_handler: IpcSender<Frame>,
67    ) {
68        let _ = self.sender.send(RegistryMsg::RequestSession(
69            mode,
70            init,
71            dest,
72            animation_frame_handler,
73        ));
74        self.waker.wake();
75    }
76
77    pub fn simulate_device_connection(
78        &mut self,
79        init: MockDeviceInit,
80        dest: ProfileGenericCallback<Result<GenericSender<MockDeviceMsg>, Error>>,
81    ) {
82        let _ = self
83            .sender
84            .send(RegistryMsg::SimulateDeviceConnection(init, dest));
85        self.waker.wake();
86    }
87}
88
89impl<GL: 'static + GLTypes> MainThreadRegistry<GL> {
90    pub fn new(
91        waker: Box<dyn EventLoopWaker>,
92        grand_manager: LayerGrandManager<GL>,
93    ) -> Result<Self, Error> {
94        let Some((sender, receiver)) = generic_channel::channel() else {
95            return Err(Error::CommunicationError);
96        };
97        let discoveries = Vec::new();
98        let sessions = Vec::new();
99        let mocks = Vec::new();
100        let waker = MainThreadWakerImpl::new(waker)?;
101        Ok(MainThreadRegistry {
102            discoveries,
103            sessions,
104            mocks,
105            sender,
106            receiver,
107            waker,
108            grand_manager,
109            next_session_id: 0,
110        })
111    }
112
113    pub fn registry(&self) -> Registry {
114        Registry {
115            sender: self.sender.clone(),
116            waker: self.waker.clone(),
117        }
118    }
119
120    pub fn register<D>(&mut self, discovery: D)
121    where
122        D: DiscoveryAPI<GL>,
123    {
124        self.discoveries.push(Box::new(discovery));
125    }
126
127    pub fn register_mock<D>(&mut self, discovery: D)
128    where
129        D: MockDiscoveryAPI<GL>,
130    {
131        self.mocks.push(Box::new(discovery));
132    }
133
134    pub fn run_on_main_thread<S>(&mut self, session: S)
135    where
136        S: MainThreadSession,
137    {
138        self.sessions.push(Box::new(session));
139    }
140
141    pub fn run_one_frame(&mut self) {
142        while let Ok(msg) = self.receiver.try_recv() {
143            self.handle_msg(msg);
144        }
145        for session in &mut self.sessions {
146            session.run_one_frame();
147        }
148        self.sessions.retain(|session| session.running());
149    }
150
151    pub fn running(&self) -> bool {
152        self.sessions.iter().any(|session| session.running())
153    }
154
155    fn handle_msg(&mut self, msg: RegistryMsg) {
156        match msg {
157            RegistryMsg::SupportsSession(mode, dest) => {
158                let _ = dest.send(self.supports_session(mode));
159            },
160            RegistryMsg::RequestSession(mode, init, dest, raf_sender) => {
161                let _ = dest.send(self.request_session(mode, init, raf_sender));
162            },
163            RegistryMsg::SimulateDeviceConnection(init, dest) => {
164                let _ = dest.send(self.simulate_device_connection(init));
165            },
166        }
167    }
168
169    fn supports_session(&mut self, mode: SessionMode) -> Result<(), Error> {
170        for discovery in &self.discoveries {
171            if discovery.supports_session(mode) {
172                return Ok(());
173            }
174        }
175        Err(Error::NoMatchingDevice)
176    }
177
178    fn request_session(
179        &mut self,
180        mode: SessionMode,
181        init: SessionInit,
182        raf_sender: IpcSender<Frame>,
183    ) -> Result<Session, Error> {
184        for discovery in &mut self.discoveries {
185            if discovery.supports_session(mode) {
186                let raf_sender = raf_sender.clone();
187                let id = SessionId(self.next_session_id);
188                self.next_session_id += 1;
189                let xr = SessionBuilder::new(
190                    &mut self.sessions,
191                    raf_sender,
192                    self.grand_manager.clone(),
193                    id,
194                );
195                match discovery.request_session(mode, &init, xr) {
196                    Ok(session) => return Ok(session),
197                    Err(err) => warn!("XR device error {:?}", err),
198                }
199            }
200        }
201        warn!("no device could support the session");
202        Err(Error::NoMatchingDevice)
203    }
204
205    fn simulate_device_connection(
206        &mut self,
207        init: MockDeviceInit,
208    ) -> Result<GenericSender<MockDeviceMsg>, Error> {
209        for mock in &mut self.mocks {
210            let Some((sender, receiver)) = generic_channel::channel() else {
211                return Err(Error::CommunicationError);
212            };
213            if let Ok(discovery) = mock.simulate_device_connection(init.clone(), receiver) {
214                self.discoveries.insert(0, discovery);
215                return Ok(sender);
216            }
217        }
218        Err(Error::NoMatchingDevice)
219    }
220}
221
222#[derive(Serialize, Deserialize)]
223#[expect(clippy::large_enum_variant)]
224enum RegistryMsg {
225    RequestSession(
226        SessionMode,
227        SessionInit,
228        IpcSender<Result<Session, Error>>,
229        IpcSender<Frame>,
230    ),
231    SupportsSession(SessionMode, ProfileGenericCallback<Result<(), Error>>),
232    SimulateDeviceConnection(
233        MockDeviceInit,
234        ProfileGenericCallback<Result<GenericSender<MockDeviceMsg>, Error>>,
235    ),
236}