#![warn(missing_docs)]
pub use crate::software_renderer;
#[cfg(all(not(feature = "std"), feature = "unsafe-single-threaded"))]
use crate::unsafe_single_threaded::{thread_local, OnceCell};
pub use crate::window::WindowAdapter;
use alloc::boxed::Box;
use alloc::rc::Rc;
use alloc::string::String;
#[cfg(feature = "std")]
use once_cell::sync::OnceCell;
pub trait Platform {
fn create_window_adapter(&self) -> Rc<dyn WindowAdapter>;
fn run_event_loop(&self) {
unimplemented!("The backend does not implement running an eventloop")
}
#[doc(hidden)]
fn set_event_loop_quit_on_last_window_closed(&self, _quit_on_last_window_closed: bool) {
unimplemented!("The backend does not implement event loop quit behaviors")
}
fn new_event_loop_proxy(&self) -> Option<Box<dyn EventLoopProxy>> {
None
}
fn duration_since_start(&self) -> core::time::Duration {
#[cfg(feature = "std")]
{
let the_beginning = *INITIAL_INSTANT.get_or_init(instant::Instant::now);
instant::Instant::now() - the_beginning
}
#[cfg(not(feature = "std"))]
unimplemented!("The platform abstraction must implement `duration_since_start`")
}
fn set_clipboard_text(&self, _text: &str) {}
fn clipboard_text(&self) -> Option<String> {
None
}
fn debug_log(&self, _arguments: core::fmt::Arguments) {
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
pub fn log(s: &str);
}
log(&_arguments.to_string());
} else if #[cfg(feature = "std")] {
eprintln!("{}", _arguments);
}
}
}
}
pub trait EventLoopProxy: Send + Sync {
fn quit_event_loop(&self) -> Result<(), crate::api::EventLoopError>;
fn invoke_from_event_loop(
&self,
event: Box<dyn FnOnce() + Send>,
) -> Result<(), crate::api::EventLoopError>;
}
#[cfg(feature = "std")]
static INITIAL_INSTANT: once_cell::sync::OnceCell<instant::Instant> =
once_cell::sync::OnceCell::new();
#[cfg(feature = "std")]
impl std::convert::From<crate::animations::Instant> for instant::Instant {
fn from(our_instant: crate::animations::Instant) -> Self {
let the_beginning = *INITIAL_INSTANT.get_or_init(instant::Instant::now);
the_beginning + core::time::Duration::from_millis(our_instant.0)
}
}
thread_local! {
pub(crate) static PLATFORM_INSTANCE : once_cell::unsync::OnceCell<Box<dyn Platform>>
= once_cell::unsync::OnceCell::new()
}
static EVENTLOOP_PROXY: OnceCell<Box<dyn EventLoopProxy + 'static>> = OnceCell::new();
pub(crate) fn event_loop_proxy() -> Option<&'static dyn EventLoopProxy> {
EVENTLOOP_PROXY.get().map(core::ops::Deref::deref)
}
#[derive(Debug, Clone)]
#[repr(C)]
#[non_exhaustive]
pub enum SetPlatformError {
AlreadySet,
}
pub fn set_platform(platform: Box<dyn Platform + 'static>) -> Result<(), SetPlatformError> {
PLATFORM_INSTANCE.with(|instance| {
if instance.get().is_some() {
return Err(SetPlatformError::AlreadySet);
}
if let Some(proxy) = platform.new_event_loop_proxy() {
EVENTLOOP_PROXY.set(proxy).map_err(|_| SetPlatformError::AlreadySet)?
}
instance.set(platform.into()).map_err(|_| SetPlatformError::AlreadySet).unwrap();
Ok(())
})
}
pub fn update_timers_and_animations() {
crate::timers::TimerList::maybe_activate_timers();
crate::animations::update_animations();
}
pub fn duration_until_next_timer_update() -> Option<core::time::Duration> {
crate::timers::TimerList::next_timeout().map(|timeout| {
let duration_since_start = crate::platform::PLATFORM_INSTANCE
.with(|p| p.get().map(|p| p.duration_since_start()))
.unwrap_or_default();
core::time::Duration::from_millis(
timeout.0.saturating_sub(duration_since_start.as_millis() as u64),
)
})
}