gpui-component/crates/wef/src/wef.rs
2025-06-18 10:46:55 +08:00

146 lines
4.3 KiB
Rust

use std::ffi::{CString, c_char, c_void};
use crate::{ApplicationHandler, Error, app_handler::ApplicationState, ffi::*, settings::Settings};
/// Initialize the CEF browser process.
///
/// This function should be called on the main application thread to
/// initialize the CEF browser process.
pub fn init<T>(settings: Settings<T>) -> Result<(), Error>
where
T: ApplicationHandler,
{
unsafe {
let callbacks = CAppCallbacks {
on_schedule_message_pump_work: crate::app_handler::on_schedule_message_pump_work::<T>,
};
let handler = Box::into_raw(Box::new(ApplicationState {
handler: settings.handler,
}));
extern "C" fn destroy_handler<T>(user_data: *mut c_void) {
unsafe { _ = Box::from_raw(user_data as *mut T) }
}
let c_settings = CSettings {
locale: to_cstr_ptr_opt(settings.locale.as_deref()),
cache_path: to_cstr_ptr_opt(settings.cache_path.as_deref()),
root_cache_path: to_cstr_ptr_opt(settings.root_cache_path.as_deref()),
browser_subprocess_path: to_cstr_ptr_opt(settings.browser_subprocess_path.as_deref()),
callbacks,
userdata: handler as *mut c_void,
destroy_userdata: destroy_handler::<T>,
};
if !wef_init(&c_settings) {
return Err(Error::InitializeBrowserProcess);
}
}
Ok(())
}
/// Executes the CEF subprocess.
///
/// This function should be called from the application entry point function
/// to execute a secondary process. It can be used to run secondary
/// processes from the browser client executable.
///
/// If called for the browser process (identified by no "type" command-line
/// value) it will return immediately with a value of `false`.
///
/// If called for a recognized secondary process it will block until the
/// process should exit and then return `true`.
///
/// # Examples
///
/// ```rust, no_run
/// use wef::Settings;
///
/// fn main() -> Result<(), Box<dyn std::error::Error>> {
/// if wef::exec_process()? {
/// return Ok(());
/// }
///
/// wef::init(Settings::new());
/// // ... event loop
/// wef::shutdown();
/// Ok(())
/// }
/// ```
pub fn exec_process() -> Result<bool, Error> {
let args: Vec<CString> = std::env::args()
.filter_map(|arg| CString::new(arg).ok())
.collect();
let c_args: Vec<*const c_char> = args.iter().map(|arg| arg.as_ptr()).collect();
Ok(unsafe { wef_exec_process(c_args.as_ptr(), args.len() as i32) })
}
/// Perform a single iteration of CEF message loop processing.
///
/// This function is provided for cases where the CEF message loop must be
/// integrated into an existing application message loop.
pub fn do_message_work() {
unsafe { wef_do_message_work() };
}
/// Shuts down the CEF library.
///
/// # Panics
///
/// This function **MUST NOT** be called while any `CefBrowser` instances are
/// still alive. If there are any `CefBrowser` objects that have not been
/// dropped properly at the time of calling this function, it will likely lead
/// to a crash or undefined behavior.
pub fn shutdown() {
unsafe { wef_shutdown() };
}
/// Launch the Wef application.
///
/// This function initializes the CEF library and runs the main process.
/// It is a convenience function that combines the [`init`] and [`shutdown`]
/// functions.
///
/// On macOS, it also loads the CEF framework using the
/// `crate::FrameworkLoader`.
///
/// # Panics
///
/// This function panics if the CEF library fails to initialize or if the
/// CEF framework fails to load on macOS.
///
/// # Examples
///
/// ```rust, no_run
/// use wef::Settings;
///
/// fn main() {
/// let settings = Settings::new();
/// wef::launch(settings, || {
/// // do something in the main process
/// });
/// }
/// ```
pub fn launch<T, F, R>(settings: Settings<T>, f: F) -> R
where
T: ApplicationHandler,
F: FnOnce() -> R,
{
if cfg!(not(target_os = "macos")) {
if exec_process().expect("failed to execute process") {
// Is helper process, exit immediately
std::process::exit(0);
}
}
#[cfg(target_os = "macos")]
let _ = crate::FrameworkLoader::load_in_main().expect("failed to load CEF framework");
// Run the main process
init(settings).expect("failed to initialize CEF");
let res = f();
shutdown();
res
}