use alloc::boxed::Box;
use alloc::vec::Vec;
use core::future::Future;
use waterui_core::AnyView;
use waterui_core::view_renderer::{CustomViewRenderer, RenderResult, RenderSize, ViewRenderer};
use crate::WuiEnv;
use crate::closure::ForeignCallbackContext;
use crate::components::layout::WuiSize;
#[repr(C)]
#[derive(Debug)]
pub struct ViewRenderCallback {
pub data: *mut (),
pub call: unsafe extern "C" fn(
data: *mut (),
rgba_ptr: *const u8,
rgba_len: usize,
width: u32,
height: u32,
),
}
pub type ViewRenderFn = unsafe extern "C" fn(
context: *mut (),
view: *mut (), size: WuiSize, callback: ViewRenderCallback,
);
struct FFIViewRenderer {
context: ForeignCallbackContext,
render_fn: ViewRenderFn,
}
impl CustomViewRenderer for FFIViewRenderer {
fn render_to_rgba(
&self,
view: AnyView,
size: RenderSize,
) -> impl Future<Output = RenderResult> {
struct CallbackData {
sender: async_channel::Sender<RenderResult>,
}
unsafe extern "C" fn render_trampoline(
data: *mut (),
rgba_ptr: *const u8,
rgba_len: usize,
width: u32,
height: u32,
) {
let CallbackData { sender } = *unsafe { Box::from_raw(data.cast::<CallbackData>()) };
let rgba_data = if rgba_len == 0 {
Vec::new()
} else {
unsafe { core::slice::from_raw_parts(rgba_ptr, rgba_len) }.to_vec()
};
let result = RenderResult {
rgba_data,
width,
height,
};
let _ = sender.try_send(result);
}
let render_fn = self.render_fn;
let view_ptr = Box::into_raw(Box::new(view));
let view_ptr_void = view_ptr.cast::<()>();
let wui_size = WuiSize {
width: size.width,
height: size.height,
};
let (tx, rx) = async_channel::bounded::<RenderResult>(1);
let callback_data = Box::new(CallbackData { sender: tx });
let callback_data = Box::into_raw(callback_data).cast::<()>();
let callback = ViewRenderCallback {
data: callback_data,
call: render_trampoline,
};
unsafe {
(render_fn)(self.context.data(), view_ptr_void, wui_size, callback);
}
async move {
rx.recv()
.await
.expect("Native view renderer dropped its completion callback")
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_env_install_view_renderer(
env: *mut WuiEnv,
context: *mut (),
render_fn: ViewRenderFn,
drop_context: unsafe extern "C" fn(*mut ()),
) {
let env = unsafe { crate::borrow_ffi_mut(env) };
let renderer = ViewRenderer::new(FFIViewRenderer {
context: unsafe { ForeignCallbackContext::new(context, drop_context) },
render_fn,
});
env.insert(renderer);
}