use bevy::prelude::*;
use cef::rc::{Rc, RcImpl};
use cef::*;
use cef_dll_sys::cef_paint_element_type_t;
use std::cell::Cell;
use std::os::raw::c_int;
pub type SharedTexture = std::rc::Rc<Cell<Option<RenderTextureMessage>>>;
#[cfg(target_os = "windows")]
pub type TextureSender = async_channel::Sender<RenderTextureMessage>;
#[derive(Debug, Clone, PartialEq, Message)]
pub struct RenderTextureMessage {
pub webview: Entity,
pub ty: RenderPaintElementType,
pub width: u32,
pub height: u32,
pub buffer: Vec<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RenderPaintElementType {
View,
Popup,
}
#[cfg(not(target_os = "windows"))]
pub type SharedViewSize = std::rc::Rc<std::cell::Cell<Vec2>>;
#[cfg(target_os = "windows")]
pub type SharedViewSize = std::sync::Arc<std::sync::Mutex<Vec2>>;
pub struct RenderHandlerBuilder {
object: *mut RcImpl<sys::cef_render_handler_t, Self>,
webview: Entity,
#[cfg(not(target_os = "windows"))]
view_slot: SharedTexture,
#[cfg(not(target_os = "windows"))]
popup_slot: SharedTexture,
#[cfg(target_os = "windows")]
texture_sender: TextureSender,
size: SharedViewSize,
}
impl RenderHandlerBuilder {
#[cfg(not(target_os = "windows"))]
pub fn build(
webview: Entity,
view_slot: SharedTexture,
popup_slot: SharedTexture,
size: SharedViewSize,
) -> RenderHandler {
RenderHandler::new(Self {
object: std::ptr::null_mut(),
webview,
view_slot,
popup_slot,
size,
})
}
#[cfg(target_os = "windows")]
pub fn build(
webview: Entity,
texture_sender: TextureSender,
size: SharedViewSize,
) -> RenderHandler {
RenderHandler::new(Self {
object: std::ptr::null_mut(),
webview,
texture_sender,
size,
})
}
}
impl Rc for RenderHandlerBuilder {
fn as_base(&self) -> &sys::cef_base_ref_counted_t {
unsafe {
let base = &*self.object;
std::mem::transmute(&base.cef_object)
}
}
}
impl WrapRenderHandler for RenderHandlerBuilder {
fn wrap_rc(&mut self, object: *mut RcImpl<sys::_cef_render_handler_t, Self>) {
self.object = object;
}
}
impl Clone for RenderHandlerBuilder {
fn clone(&self) -> Self {
let object = unsafe {
let rc_impl = &mut *self.object;
rc_impl.interface.add_ref();
rc_impl
};
Self {
object,
webview: self.webview,
#[cfg(not(target_os = "windows"))]
view_slot: self.view_slot.clone(),
#[cfg(not(target_os = "windows"))]
popup_slot: self.popup_slot.clone(),
#[cfg(target_os = "windows")]
texture_sender: self.texture_sender.clone(),
size: self.size.clone(),
}
}
}
impl ImplRenderHandler for RenderHandlerBuilder {
fn view_rect(&self, _browser: Option<&mut Browser>, rect: Option<&mut cef::Rect>) {
if let Some(rect) = rect {
#[cfg(not(target_os = "windows"))]
let size = self.size.get();
#[cfg(target_os = "windows")]
let size = *self.size.lock().unwrap();
rect.width = size.x as _;
rect.height = size.y as _;
}
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn on_paint(
&self,
_browser: Option<&mut Browser>,
type_: PaintElementType,
_dirty_rects: Option<&[cef::Rect]>,
buffer: *const u8,
width: c_int,
height: c_int,
) {
let ty = match type_.as_ref() {
cef_paint_element_type_t::PET_POPUP => RenderPaintElementType::Popup,
_ => RenderPaintElementType::View,
};
let texture = RenderTextureMessage {
webview: self.webview,
ty,
width: width as u32,
height: height as u32,
buffer: unsafe {
std::slice::from_raw_parts(buffer, (width * height * 4) as usize).to_vec()
},
};
#[cfg(not(target_os = "windows"))]
{
let slot = match ty {
RenderPaintElementType::Popup => &self.popup_slot,
RenderPaintElementType::View => &self.view_slot,
};
slot.set(Some(texture));
}
#[cfg(target_os = "windows")]
{
let _ = self.texture_sender.send_blocking(texture);
}
}
#[inline]
fn get_raw(&self) -> *mut sys::_cef_render_handler_t {
self.object.cast()
}
}