use std::{any::Any, fmt};
#[cfg(any(feature = "webview-cef", feature = "cef-header"))]
use waterui_browser_cef::CefWebViewHandle;
use waterui_browser_cef::{
CefInputModifiers, CefKeyInput, CefPageHandle, CefPointerButton, CefRuntime,
CefRuntimeConfiguration, CefTextRange, gpu_view,
};
#[cfg(any(feature = "chromium", feature = "cef-header"))]
use waterui_chromium::{ChromiumView, PageMode};
use waterui_core::Environment;
use waterui_graphics::gpu_surface::GpuSurface;
#[cfg(any(feature = "webview-cef", feature = "cef-header"))]
use waterui_webview::WebView;
#[cfg(any(feature = "webview-cef", feature = "cef-header"))]
use crate::WuiAnyView;
use crate::components::visual::gpu_surface::WuiGpuSurface;
use crate::{IntoFFI, IntoRust, WuiStr};
pub(crate) fn configure_environment(env: &mut Environment) {
let runtime = CefRuntime::initialize(CefRuntimeConfiguration::packaged());
#[cfg(any(feature = "webview-cef", feature = "cef-header"))]
env.insert(runtime.webview_controller());
#[cfg(any(feature = "chromium", feature = "cef-header"))]
env.insert(runtime.chromium_controller());
env.insert(runtime.clone());
runtime.start_message_pump();
}
#[repr(C)]
#[derive(Debug)]
pub struct WuiCefSurface {
pub gpu_surface: WuiGpuSurface,
pub state: *mut WuiCefSurfaceState,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct WuiCefInputModifiers {
pub shift: bool,
pub control: bool,
pub alt: bool,
pub command: bool,
pub primary_button: bool,
pub middle_button: bool,
pub secondary_button: bool,
}
impl From<WuiCefInputModifiers> for CefInputModifiers {
fn from(value: WuiCefInputModifiers) -> Self {
Self {
shift: value.shift,
control: value.control,
alt: value.alt,
command: value.command,
primary_button: value.primary_button,
middle_button: value.middle_button,
secondary_button: value.secondary_button,
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum WuiCefPointerButton {
Primary,
Middle,
Secondary,
}
impl From<WuiCefPointerButton> for CefPointerButton {
fn from(value: WuiCefPointerButton) -> Self {
match value {
WuiCefPointerButton::Primary => Self::Primary,
WuiCefPointerButton::Middle => Self::Middle,
WuiCefPointerButton::Secondary => Self::Secondary,
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum WuiCefEditCommand {
Undo,
Redo,
Cut,
Copy,
Paste,
SelectAll,
}
pub struct WuiCefSurfaceState {
page: CefPageHandle,
_source: Box<dyn Any>,
}
impl fmt::Debug for WuiCefSurfaceState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WuiCefSurfaceState").finish_non_exhaustive()
}
}
fn surface(page: CefPageHandle, source: impl Any) -> WuiCefSurface {
let gpu_surface = GpuSurface::new(gpu_view(page.clone())).into_ffi();
WuiCefSurface {
gpu_surface,
state: Box::into_raw(Box::new(WuiCefSurfaceState {
page,
_source: Box::new(source),
})),
}
}
#[cfg(any(feature = "chromium", feature = "cef-header"))]
impl IntoFFI for ChromiumView {
type FFI = WuiCefSurface;
fn into_ffi(self) -> Self::FFI {
assert_eq!(
self.page().mode(),
PageMode::Visible,
"headless Chromium pages cannot be rendered as ChromiumView"
);
let page = self
.page()
.handle()
.downcast_ref::<CefPageHandle>()
.unwrap_or_else(|| {
panic!("Apple ChromiumView handle does not use the selected CEF runtime")
})
.clone();
surface(page, self)
}
}
#[cfg(any(feature = "chromium", feature = "cef-header"))]
ffi_view!(ChromiumView, WuiCefSurface, chromium, any());
#[cfg(any(feature = "webview-cef", feature = "cef-header"))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_force_as_cef_webview(view: *mut WuiAnyView) -> WuiCefSurface {
let any: waterui::AnyView = unsafe { IntoRust::into_rust(view) };
let view = unsafe { *any.downcast_unchecked::<waterui_core::Native<WebView>>() }.into_inner();
let page = view
.handle()
.downcast_ref::<CefWebViewHandle>()
.unwrap_or_else(|| panic!("Apple WebView handle does not use the selected CEF runtime"))
.page()
.clone();
surface(page, view)
}
const unsafe fn borrow_state<'a>(state: *const WuiCefSurfaceState) -> &'a WuiCefSurfaceState {
unsafe { crate::borrow_ffi(state) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_cef_surface_set_focus(
state: *const WuiCefSurfaceState,
focused: bool,
) {
unsafe { borrow_state(state) }.page.set_focus(focused);
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_cef_surface_request_frame(state: *const WuiCefSurfaceState) {
unsafe { borrow_state(state) }.page.request_frame();
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_cef_surface_set_viewport(
state: *const WuiCefSurfaceState,
width: u32,
height: u32,
scale: f64,
) {
let state = unsafe { borrow_state(state) };
assert!(
scale.is_finite() && scale > 0.0 && scale <= f64::from(f32::MAX),
"CEF viewport scale must be a positive, finite device-pixel ratio representable as f32, got {scale}"
);
#[expect(
clippy::cast_possible_truncation,
reason = "narrowing to CEF's own f32 device scale factor, range-checked directly above"
)]
let scale = scale as f32;
state.page.set_viewport(width, height, scale);
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_cef_surface_go_back(state: *const WuiCefSurfaceState) {
unsafe { borrow_state(state) }.page.go_back();
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_cef_surface_go_forward(state: *const WuiCefSurfaceState) {
unsafe { borrow_state(state) }.page.go_forward();
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_cef_surface_pointer_move(
state: *const WuiCefSurfaceState,
x: f64,
y: f64,
modifiers: WuiCefInputModifiers,
) {
unsafe { borrow_state(state) }
.page
.pointer_move(x, y, modifiers.into());
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_cef_surface_pointer_button(
state: *const WuiCefSurfaceState,
pressed: bool,
button: WuiCefPointerButton,
x: f64,
y: f64,
modifiers: WuiCefInputModifiers,
) {
unsafe { borrow_state(state) }.page.pointer_button(
pressed,
button.into(),
x,
y,
modifiers.into(),
);
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_cef_surface_scroll(
state: *const WuiCefSurfaceState,
x: f64,
y: f64,
delta_x: f64,
delta_y: f64,
modifiers: WuiCefInputModifiers,
) {
unsafe { borrow_state(state) }
.page
.scroll(x, y, delta_x, delta_y, modifiers.into());
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_cef_surface_key(
state: *const WuiCefSurfaceState,
pressed: bool,
native_keycode: u32,
keyval: u32,
character: u32,
modifiers: WuiCefInputModifiers,
) {
let character = (character != 0).then(|| {
char::from_u32(character)
.unwrap_or_else(|| panic!("CEF key character is not a Unicode scalar: {character}"))
});
unsafe { borrow_state(state) }.page.key(
pressed,
CefKeyInput {
native_keycode,
keyval,
character,
},
modifiers.into(),
);
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_cef_surface_commit_text(
state: *const WuiCefSurfaceState,
text: WuiStr,
replacement_start: u32,
replacement_end: u32,
) {
let text: waterui::Str = unsafe { text.into_rust() };
unsafe { borrow_state(state) }.page.commit_text(
text.as_str(),
optional_cef_range(replacement_start, replacement_end),
);
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_cef_surface_set_composition(
state: *const WuiCefSurfaceState,
text: WuiStr,
selection_start: u32,
selection_end: u32,
replacement_start: u32,
replacement_end: u32,
) {
let text: waterui::Str = unsafe { text.into_rust() };
unsafe { borrow_state(state) }.page.set_composition(
text.as_str(),
selection_start,
selection_end,
optional_cef_range(replacement_start, replacement_end),
);
}
fn optional_cef_range(start: u32, end: u32) -> Option<CefTextRange> {
if start == u32::MAX || end == u32::MAX {
assert_eq!(
(start, end),
(u32::MAX, u32::MAX),
"CEF replacement range must be entirely specified or entirely absent"
);
return None;
}
Some(CefTextRange::new(start, end))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_cef_surface_finish_composition(
state: *const WuiCefSurfaceState,
keep_selection: bool,
) {
unsafe { borrow_state(state) }
.page
.finish_composition(keep_selection);
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_cef_surface_cancel_composition(state: *const WuiCefSurfaceState) {
unsafe { borrow_state(state) }.page.cancel_composition();
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_cef_surface_edit(
state: *const WuiCefSurfaceState,
command: WuiCefEditCommand,
) {
let page = &unsafe { borrow_state(state) }.page;
match command {
WuiCefEditCommand::Undo => page.undo(),
WuiCefEditCommand::Redo => page.redo(),
WuiCefEditCommand::Cut => page.cut(),
WuiCefEditCommand::Copy => page.copy(),
WuiCefEditCommand::Paste => page.paste(),
WuiCefEditCommand::SelectAll => page.select_all(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_cef_surface_drop(state: *mut WuiCefSurfaceState) {
drop(unsafe { Box::from_raw(state) });
}
#[cfg(target_os = "macos")]
#[unsafe(no_mangle)]
pub extern "C" fn waterui_cef_prepare_macos_application() {
waterui_browser_cef::initialize_macos_application();
}
#[cfg(target_os = "macos")]
#[unsafe(no_mangle)]
pub extern "C" fn waterui_cef_run_packaged_subprocess() -> i32 {
waterui_browser_cef::run_packaged_subprocess()
}