mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
* Switch HTTP cache crate * Set compiler on linux * Start work on offscreen kernel sharing * Finish basic config sharing mechanism on web and native; not really used on web so far though
235 lines
7.9 KiB
Rust
235 lines
7.9 KiB
Rust
#![deny(unused_imports)]
|
|
|
|
use std::{fmt::Debug, marker::PhantomData};
|
|
|
|
use instant::Instant;
|
|
use maplibre::{
|
|
environment::{Environment, OffscreenKernel},
|
|
event_loop::{EventLoop, EventLoopProxy, SendEventError},
|
|
io::{apc::AsyncProcedureCall, scheduler::Scheduler, source_client::HttpClient},
|
|
map::Map,
|
|
window::{HeadedMapWindow, MapWindowConfig, PhysicalSize},
|
|
};
|
|
use winit::{
|
|
event::{ElementState, Event, KeyEvent, WindowEvent},
|
|
keyboard::{Key, NamedKey},
|
|
};
|
|
|
|
use crate::input::{InputController, UpdateState};
|
|
|
|
pub mod input;
|
|
|
|
#[cfg(target_os = "android")]
|
|
pub use winit::platform::android::activity as android_activity;
|
|
|
|
pub type RawWinitWindow = winit::window::Window;
|
|
pub type RawWinitEventLoop<ET> = winit::event_loop::EventLoop<ET>;
|
|
pub type RawEventLoopProxy<ET> = winit::event_loop::EventLoopProxy<ET>;
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
mod web;
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
mod noweb;
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
pub use noweb::*;
|
|
#[cfg(target_arch = "wasm32")]
|
|
pub use web::*;
|
|
|
|
pub struct WinitMapWindow<ET: 'static> {
|
|
window: RawWinitWindow,
|
|
event_loop: Option<WinitEventLoop<ET>>,
|
|
}
|
|
|
|
impl<ET> WinitMapWindow<ET> {
|
|
pub fn take_event_loop(&mut self) -> Option<WinitEventLoop<ET>> {
|
|
self.event_loop.take()
|
|
}
|
|
}
|
|
|
|
impl<ET> HeadedMapWindow for WinitMapWindow<ET> {
|
|
type WindowHandle = RawWinitWindow;
|
|
|
|
fn handle(&self) -> &Self::WindowHandle {
|
|
&self.window
|
|
}
|
|
|
|
fn request_redraw(&self) {
|
|
self.window.request_redraw()
|
|
}
|
|
|
|
fn scale_factor(&self) -> f64 {
|
|
self.window.scale_factor()
|
|
}
|
|
|
|
fn id(&self) -> u64 {
|
|
self.window.id().into()
|
|
}
|
|
}
|
|
|
|
pub struct WinitEventLoop<ET: 'static> {
|
|
event_loop: RawWinitEventLoop<ET>,
|
|
}
|
|
|
|
impl<ET: 'static + PartialEq + Debug> EventLoop<ET> for WinitEventLoop<ET> {
|
|
type EventLoopProxy = WinitEventLoopProxy<ET>;
|
|
|
|
fn run<E>(self, mut map: Map<E>, max_frames: Option<u64>)
|
|
where
|
|
E: Environment,
|
|
<E::MapWindowConfig as MapWindowConfig>::MapWindow: HeadedMapWindow,
|
|
{
|
|
let mut last_render_time = Instant::now();
|
|
let mut current_frame: u64 = 0;
|
|
|
|
let mut input_controller = InputController::new(0.2, 100.0, 0.1);
|
|
let mut scale_factor = map.window().scale_factor();
|
|
|
|
self.event_loop
|
|
.run(move |event, window_target| {
|
|
#[cfg(target_os = "android")]
|
|
if !map.is_initialized() && event == Event::Resumed {
|
|
use tokio::{runtime::Handle, task};
|
|
|
|
task::block_in_place(|| {
|
|
Handle::current().block_on(async {
|
|
map.initialize_renderer().await.unwrap();
|
|
})
|
|
});
|
|
return;
|
|
}
|
|
|
|
match event {
|
|
Event::DeviceEvent {
|
|
ref event,
|
|
.. // We're not using device_id currently
|
|
} => {
|
|
input_controller.device_input(event);
|
|
}
|
|
|
|
Event::WindowEvent {
|
|
ref event,
|
|
window_id,
|
|
} if window_id == map.window().id().into() => {
|
|
match event {
|
|
WindowEvent::RedrawRequested => {
|
|
if !map.is_initialized() {
|
|
return;
|
|
}
|
|
|
|
let now = Instant::now();
|
|
let dt = now - last_render_time;
|
|
last_render_time = now;
|
|
|
|
if let Ok(map_context) = map.context_mut() {
|
|
input_controller.update_state(map_context, dt);
|
|
}
|
|
|
|
// TODO: Handle gracefully
|
|
map.run_schedule().expect("Failed to run schedule!");
|
|
|
|
if let Some(max_frames) = max_frames {
|
|
if current_frame >= max_frames {
|
|
log::info!("Exiting because maximum frames reached.");
|
|
window_target.exit()
|
|
}
|
|
|
|
current_frame += 1;
|
|
}
|
|
|
|
map.window().request_redraw();
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
if !input_controller.window_input(event, scale_factor) {
|
|
match event {
|
|
WindowEvent::CloseRequested
|
|
| WindowEvent::KeyboardInput {
|
|
event: KeyEvent {
|
|
state: ElementState::Pressed,
|
|
logical_key: Key::Named(NamedKey::Exit),
|
|
..
|
|
},
|
|
..
|
|
} => window_target.exit(),
|
|
WindowEvent::Resized(winit::dpi::PhysicalSize { width, height}) => {
|
|
if let Ok(map_context) = map.context_mut() {
|
|
let size = PhysicalSize::new(*width, *height).expect("window values should not be zero");
|
|
map_context.resize(size, scale_factor);
|
|
map.window().request_redraw();
|
|
}
|
|
}
|
|
WindowEvent::ScaleFactorChanged { inner_size_writer, scale_factor: new_scale_factor } => {
|
|
if let Ok(map_context) = map.context_mut() {
|
|
log::info!("New scaling factor: {}", new_scale_factor);
|
|
scale_factor = *new_scale_factor;
|
|
map_context.resize(map_context.renderer.resources.surface.size(), scale_factor);
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
Event::Suspended => {
|
|
log::info!("Suspending and dropping render state.");
|
|
map.reset() // TODO: Instead of resetting the whole map (incl. the renderer) only reset the renderer
|
|
}
|
|
Event::Resumed => {
|
|
// FIXME unimplemented!()
|
|
}
|
|
_ => {}
|
|
}
|
|
});
|
|
}
|
|
|
|
fn create_proxy(&self) -> Self::EventLoopProxy {
|
|
WinitEventLoopProxy {
|
|
proxy: self.event_loop.create_proxy(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct WinitEventLoopProxy<ET: 'static> {
|
|
proxy: RawEventLoopProxy<ET>,
|
|
}
|
|
|
|
impl<ET: 'static> EventLoopProxy<ET> for WinitEventLoopProxy<ET> {
|
|
fn send_event(&self, event: ET) -> Result<(), SendEventError> {
|
|
self.proxy
|
|
.send_event(event)
|
|
.map_err(|_e| SendEventError::Closed)
|
|
}
|
|
}
|
|
|
|
pub struct WinitEnvironment<
|
|
S: Scheduler,
|
|
HC: HttpClient,
|
|
K: OffscreenKernel,
|
|
APC: AsyncProcedureCall<K>,
|
|
ET,
|
|
> {
|
|
phantom_s: PhantomData<S>,
|
|
phantom_hc: PhantomData<HC>,
|
|
phantom_k: PhantomData<K>,
|
|
phantom_apc: PhantomData<APC>,
|
|
phantom_et: PhantomData<ET>,
|
|
}
|
|
|
|
impl<
|
|
S: Scheduler,
|
|
HC: HttpClient,
|
|
K: OffscreenKernel,
|
|
APC: AsyncProcedureCall<K>,
|
|
ET: 'static + Clone,
|
|
> Environment for WinitEnvironment<S, HC, K, APC, ET>
|
|
{
|
|
type MapWindowConfig = WinitMapWindowConfig<ET>;
|
|
type AsyncProcedureCall = APC;
|
|
type Scheduler = S;
|
|
type HttpClient = HC;
|
|
type OffscreenKernelEnvironment = K;
|
|
}
|