irondash_engine_context 0.5.0

Easy access to FlutterView, FlutterBinaryMessenger and FlutterTextureRegistry for FFI.
Documentation
use std::{
    ffi::{c_char, c_int, c_void},
    mem::transmute,
};

mod sys;

use crate::{
    platform::platform_impl::sys::glib::{
        g_main_context_invoke_full, gboolean, gpointer, G_SOURCE_REMOVE,
    },
    Error, Result,
};

use self::sys::glib::{g_main_context_default, GMainContext};

pub struct PlatformContext {}

const RTLD_LAZY: c_int = 1;

extern "C" {
    fn dlopen(filename: *const c_char, flags: c_int) -> *mut c_void;
    fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
    pub fn pthread_self() -> usize;
}

pub(crate) type FlutterView = FlView;
pub(crate) type FlutterTextureRegistry = FlTextureRegistrar;
pub(crate) type FlutterBinaryMessenger = FlBinaryMessenger;

type FlView = *mut c_void;
type FlTextureRegistrar = *mut c_void;
type FlBinaryMessenger = *mut c_void;
type GetMainThreadIdProc = unsafe extern "C" fn() -> usize;
type GetFlutterViewProc = unsafe extern "C" fn(i64) -> FlView;
type RegisterDestroyNotificationProc = unsafe extern "C" fn(extern "C" fn(i64)) -> ();
type GetFlutterTextureRegistrarProc = unsafe extern "C" fn(i64) -> FlTextureRegistrar;
type GetFlutterBinaryMessengerProc = unsafe extern "C" fn(i64) -> FlBinaryMessenger;

fn context_invoke<F>(context: *mut GMainContext, func: F)
where
    F: FnOnce() + 'static,
{
    unsafe extern "C" fn trampoline<F: FnOnce() + 'static>(func: gpointer) -> gboolean {
        let func: &mut Option<F> = &mut *(func as *mut Option<F>);
        let func = func
            .take()
            .expect("MainContext::invoke() closure called multiple times");
        func();
        G_SOURCE_REMOVE
    }
    unsafe extern "C" fn destroy_closure<F: FnOnce() + 'static>(ptr: gpointer) {
        let _ = Box::<Option<F>>::from_raw(ptr as *mut _);
    }
    let callback = Box::into_raw(Box::new(Some(func)));
    unsafe {
        g_main_context_invoke_full(
            context,
            0,
            Some(trampoline::<F>),
            callback as gpointer,
            Some(destroy_closure::<F>),
        )
    }
}

impl PlatformContext {
    pub fn new() -> Result<Self> {
        let res = Self {};
        res.initialize()?;
        Ok(res)
    }

    pub fn perform_on_main_thread(f: impl FnOnce() + Send + 'static) -> Result<()> {
        let context = unsafe { g_main_context_default() };
        context_invoke(context, f);
        Ok(())
    }

    pub fn is_main_thread() -> Result<bool> {
        let proc = Self::get_proc(b"IrondashEngineContextGetMainThreadId\0")?;
        let proc: GetMainThreadIdProc = unsafe { std::mem::transmute(proc) };
        let main_thread_id = unsafe { proc() };
        let current_thread_id = unsafe { pthread_self() };
        Ok(main_thread_id == current_thread_id)
    }

    fn initialize(&self) -> Result<()> {
        let proc = Self::get_proc(b"IrondashEngineContextRegisterDestroyNotification\0")?;
        let proc: RegisterDestroyNotificationProc = unsafe { std::mem::transmute(proc) };
        unsafe { proc(on_engine_destroyed) };
        Ok(())
    }

    fn get_proc(name: &[u8]) -> Result<*mut c_void> {
        let dl = unsafe { dlopen(std::ptr::null_mut(), RTLD_LAZY) };
        let res = unsafe { dlsym(dl, name.as_ptr() as *const _) };
        if res.is_null() {
            Err(Error::PluginNotLoaded)
        } else {
            Ok(res)
        }
    }

    pub fn get_flutter_view(&self, handle: i64) -> Result<FlView> {
        let proc = Self::get_proc(b"IrondashEngineContextGetFlutterView\0")?;
        let proc: GetFlutterViewProc = unsafe { transmute(proc) };
        let view = unsafe { proc(handle) };
        if view.is_null() {
            Err(Error::InvalidHandle)
        } else {
            Ok(view)
        }
    }

    pub fn get_binary_messenger(&self, handle: i64) -> Result<FlBinaryMessenger> {
        let proc = Self::get_proc(b"IrondashEngineContextGetBinaryMessenger\0")?;
        let proc: GetFlutterBinaryMessengerProc = unsafe { transmute(proc) };
        let messenger = unsafe { proc(handle) };
        if messenger.is_null() {
            Err(Error::InvalidHandle)
        } else {
            Ok(messenger)
        }
    }

    pub fn get_texture_registry(&self, handle: i64) -> Result<FlTextureRegistrar> {
        let proc = Self::get_proc(b"IrondashEngineContextGetTextureRegistrar\0")?;
        let proc: GetFlutterTextureRegistrarProc = unsafe { transmute(proc) };
        let registry = unsafe { proc(handle) };
        if registry.is_null() {
            Err(Error::InvalidHandle)
        } else {
            Ok(registry)
        }
    }
}

extern "C" fn on_engine_destroyed(handle: i64) {
    if let Some(engine_context) = crate::EngineContext::try_get() {
        engine_context.on_engine_destroyed(handle);
    }
}