maplibre_winit/
lib.rs

1#![deny(unused_imports)]
2
3use std::{fmt::Debug, marker::PhantomData};
4
5use instant::Instant;
6use maplibre::{
7    environment::{Environment, OffscreenKernelEnvironment},
8    event_loop::{EventLoop, EventLoopProxy, SendEventError},
9    io::{apc::AsyncProcedureCall, scheduler::Scheduler, source_client::HttpClient},
10    map::Map,
11    window::{HeadedMapWindow, MapWindowConfig},
12};
13use winit::{
14    event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
15    event_loop::ControlFlow,
16};
17
18use crate::input::{InputController, UpdateState};
19
20pub mod input;
21
22pub type RawWinitWindow = winit::window::Window;
23pub type RawWinitEventLoop<ET> = winit::event_loop::EventLoop<ET>;
24pub type RawEventLoopProxy<ET> = winit::event_loop::EventLoopProxy<ET>;
25
26#[cfg(target_arch = "wasm32")]
27mod web;
28
29#[cfg(not(target_arch = "wasm32"))]
30mod noweb;
31
32#[cfg(not(target_arch = "wasm32"))]
33pub use noweb::*;
34#[cfg(target_arch = "wasm32")]
35pub use web::*;
36
37pub struct WinitMapWindow<ET: 'static> {
38    window: RawWinitWindow,
39    event_loop: Option<WinitEventLoop<ET>>,
40}
41
42impl<ET> WinitMapWindow<ET> {
43    pub fn take_event_loop(&mut self) -> Option<WinitEventLoop<ET>> {
44        self.event_loop.take()
45    }
46}
47
48impl<ET> HeadedMapWindow for WinitMapWindow<ET> {
49    type RawWindow = RawWinitWindow;
50
51    fn raw(&self) -> &Self::RawWindow {
52        &self.window
53    }
54
55    fn request_redraw(&self) {
56        self.window.request_redraw()
57    }
58
59    fn id(&self) -> u64 {
60        self.window.id().into()
61    }
62}
63
64pub struct WinitEventLoop<ET: 'static> {
65    event_loop: RawWinitEventLoop<ET>,
66}
67
68impl<ET: 'static + PartialEq + Debug> EventLoop<ET> for WinitEventLoop<ET> {
69    type EventLoopProxy = WinitEventLoopProxy<ET>;
70
71    fn run<E>(self, mut map: Map<E>, max_frames: Option<u64>)
72    where
73        E: Environment,
74        <E::MapWindowConfig as MapWindowConfig>::MapWindow: HeadedMapWindow,
75    {
76        let mut last_render_time = Instant::now();
77        let mut current_frame: u64 = 0;
78
79        let mut input_controller = InputController::new(0.2, 100.0, 0.1);
80
81        self.event_loop
82            .run(move |event, _window_target, control_flow| {
83                #[cfg(target_os = "android")]
84                if !map.has_renderer() && event == Event::Resumed {
85                    use tokio::{runtime::Handle, task};
86
87                    task::block_in_place(|| {
88                        Handle::current().block_on(async {
89                            map.initialize_renderer().await.unwrap();
90                        })
91                    });
92                    return;
93                }
94
95                match event {
96                    Event::DeviceEvent {
97                        ref event,
98                        .. // We're not using device_id currently
99                    } => {
100                        input_controller.device_input(event);
101                    }
102
103                    Event::WindowEvent {
104                        ref event,
105                        window_id,
106                    } if window_id == map.window().id().into() => {
107                        if !input_controller.window_input(event) {
108                            match event {
109                                WindowEvent::CloseRequested
110                                | WindowEvent::KeyboardInput {
111                                    input:
112                                    KeyboardInput {
113                                        state: ElementState::Pressed,
114                                        virtual_keycode: Some(VirtualKeyCode::Escape),
115                                        ..
116                                    },
117                                    ..
118                                } => *control_flow = ControlFlow::Exit,
119                                WindowEvent::Resized(physical_size) => {
120                                    if let Ok(map_context) =  map.context_mut() {
121                                        map_context.resize(physical_size.width, physical_size.height);
122                                    }
123                                }
124                                WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
125                                    if let Ok(map_context) =  map.context_mut() {
126                                        map_context.resize(new_inner_size.width, new_inner_size.height);
127                                    }
128                                }
129                                _ => {}
130                            }
131                        }
132                    }
133                    Event::RedrawRequested(_) => {
134                        let now = Instant::now();
135                        let dt = now - last_render_time;
136                        last_render_time = now;
137
138                        if let Ok(map_context) =  map.context_mut() {
139                            input_controller.update_state(map_context, dt);
140                        }
141
142                        // TODO: Maybe handle gracefully
143                        map.run_schedule().expect("Failed to run schedule!");
144
145                        if let Some(max_frames) = max_frames {
146                            if current_frame >= max_frames {
147                                log::info!("Exiting because maximum frames reached.");
148                                *control_flow = ControlFlow::Exit;
149                            }
150
151                            current_frame += 1;
152                        }
153                    }
154                    Event::Suspended => {
155                        // FIXME unimplemented!()
156                    }
157                    Event::Resumed => {
158                        // FIXME unimplemented!()
159                    }
160                    Event::MainEventsCleared => {
161                        // RedrawRequested will only trigger once, unless we manually
162                        // request it.
163                        map.window().request_redraw();
164                    }
165                    _ => {}
166                }
167            });
168    }
169
170    fn create_proxy(&self) -> Self::EventLoopProxy {
171        WinitEventLoopProxy {
172            proxy: self.event_loop.create_proxy(),
173        }
174    }
175}
176pub struct WinitEventLoopProxy<ET: 'static> {
177    proxy: RawEventLoopProxy<ET>,
178}
179
180impl<ET: 'static> EventLoopProxy<ET> for WinitEventLoopProxy<ET> {
181    fn send_event(&self, event: ET) -> Result<(), SendEventError> {
182        self.proxy
183            .send_event(event)
184            .map_err(|_e| SendEventError::Closed)
185    }
186}
187
188pub struct WinitEnvironment<
189    S: Scheduler,
190    HC: HttpClient,
191    K: OffscreenKernelEnvironment,
192    APC: AsyncProcedureCall<K>,
193    ET,
194> {
195    phantom_s: PhantomData<S>,
196    phantom_hc: PhantomData<HC>,
197    phantom_k: PhantomData<K>,
198    phantom_apc: PhantomData<APC>,
199    phantom_et: PhantomData<ET>,
200}
201
202impl<
203        S: Scheduler,
204        HC: HttpClient,
205        K: OffscreenKernelEnvironment,
206        APC: AsyncProcedureCall<K>,
207        ET: 'static,
208    > Environment for WinitEnvironment<S, HC, K, APC, ET>
209{
210    type MapWindowConfig = WinitMapWindowConfig<ET>;
211    type AsyncProcedureCall = APC;
212    type Scheduler = S;
213    type HttpClient = HC;
214    type OffscreenKernelEnvironment = K;
215}