#![allow(clippy::new_without_default)]
#![allow(clippy::type_complexity)]
use std::{
cell::{Cell, RefCell},
rc::Rc,
};
use irondash_run_loop::RunLoop;
use once_cell::sync::OnceCell;
mod platform;
mod error;
pub use error::Error;
pub type Result<T> = std::result::Result<T, Error>;
pub type FlutterView = platform::FlutterView;
pub type FlutterTextureRegistry = platform::FlutterTextureRegistry;
pub type FlutterBinaryMessenger = platform::FlutterBinaryMessenger;
#[cfg(target_os = "android")]
pub type Activity = platform::Activity;
pub struct EngineContext {
platform_context: platform::PlatformContext,
destroy_notifications: RefCell<Vec<(i64, Rc<dyn Fn(i64)>)>>,
next_notification_handle: Cell<i64>,
}
unsafe impl Sync for EngineContext {}
unsafe impl Send for EngineContext {}
static ENGINE_CONTEXT: OnceCell<EngineContext> = OnceCell::new();
impl EngineContext {
pub fn get() -> Result<&'static Self> {
if !RunLoop::is_main_thread() {
return Err(Error::InvalidThread);
}
if ENGINE_CONTEXT.get().is_none() {
let context = Self::new();
match context {
Ok(context) => ENGINE_CONTEXT.set(context).ok(),
Err(err) => return Err(err),
};
}
Ok(ENGINE_CONTEXT.get().unwrap())
}
pub fn register_destroy_notification<F>(&self, callback: F) -> i64
where
F: Fn(i64) + 'static,
{
let notification_handle = self.next_notification_handle.get();
self.next_notification_handle.set(notification_handle + 1);
self.destroy_notifications
.borrow_mut()
.push((notification_handle, Rc::new(callback)));
notification_handle
}
pub fn unregister_destroy_notification(&self, notification_handle: i64) {
let mut notifications = self.destroy_notifications.borrow_mut();
notifications.retain(|(handle, _)| *handle != notification_handle);
}
pub fn get_flutter_view(&self, handle: i64) -> Result<platform::FlutterView> {
let handle = Self::strip_version(handle)?;
self.platform_context.get_flutter_view(handle)
}
pub fn get_texture_registry(&self, handle: i64) -> Result<FlutterTextureRegistry> {
let handle = Self::strip_version(handle)?;
self.platform_context.get_texture_registry(handle)
}
pub fn get_binary_messenger(&self, handle: i64) -> Result<FlutterBinaryMessenger> {
let handle = Self::strip_version(handle)?;
self.platform_context.get_binary_messenger(handle)
}
#[cfg(target_os = "android")]
pub fn get_activity(&self, handle: i64) -> Result<Activity> {
let handle = Self::strip_version(handle)?;
self.platform_context.get_activity(handle)
}
fn new() -> Result<Self> {
Ok(Self {
platform_context: platform::PlatformContext::new()?,
destroy_notifications: RefCell::new(Vec::new()),
next_notification_handle: Cell::new(1),
})
}
pub(crate) fn try_get() -> Option<&'static Self> {
assert!(RunLoop::is_main_thread());
ENGINE_CONTEXT.get()
}
fn strip_version(handle: i64) -> Result<i64> {
let expected_version = 3i64;
let version_shift = 48;
let version_mask = 0xFFi64 << version_shift;
let handle_version = (handle & version_mask) >> version_shift;
if handle_version != expected_version {
return Err(Error::InvalidVersion);
}
let handle = handle & !version_mask;
Ok(handle)
}
pub(crate) fn on_engine_destroyed(&self, handle: i64) {
let callbacks: Vec<_> = self
.destroy_notifications
.borrow()
.iter()
.map(|(_, callback)| callback.clone())
.collect();
for callback in callbacks {
callback(handle);
}
}
}