Fix message loop handling and improve BrowserProcessHandler (#15)

* add: implement MessagePumpChecker for message loop management

* update: refactor message loop handling and improve MessagePumpChecker

* update: refactor message loop handling to use MessageLoopTimer and improve BrowserProcessHandler

* update: reorganize imports and enhance browser process handler integration

* update: support Bevy 0.18 and improve message loop handling

* update: remove unused FpsOverlayPlugin and clean up dependencies

* update: simplify cef and cef-dll-sys version specifications in Cargo.toml

---------

Co-authored-by: not-elm <elmgameinfo@gmail.com>
This commit is contained in:
elm
2026-02-01 12:00:56 +09:00
committed by GitHub
parent 8f38892009
commit 5be6380474
10 changed files with 160 additions and 79 deletions

View File

@@ -8,83 +8,88 @@ use cef::{Settings, api_hash, execute_process, initialize, shutdown, sys};
///
/// - Windows and Linux: Support [`multi_threaded_message_loop`](https://cef-builds.spotifycdn.com/docs/106.1/structcef__settings__t.html#a518ac90db93ca5133a888faa876c08e0), so it is used.
/// - macOS: Calls [`CefDoMessageLoopWork`](https://cef-builds.spotifycdn.com/docs/106.1/cef__app_8h.html#a830ae43dcdffcf4e719540204cefdb61) every frame.
pub struct MessageLoopPlugin {
_app: Box<cef::App>,
#[cfg(all(target_os = "macos", not(feature = "debug")))]
_loader: Box<cef::library_loader::LibraryLoader>,
#[cfg(all(target_os = "macos", feature = "debug"))]
_loader: Box<DebugLibraryLoader>,
}
pub struct MessageLoopPlugin;
impl Plugin for MessageLoopPlugin {
fn build(&self, app: &mut App) {
#[cfg(target_os = "macos")]
load_cef_library(app);
let _ = api_hash(sys::CEF_API_VERSION_LAST, 0);
let args = Args::new();
let (tx, rx) = std::sync::mpsc::channel();
let mut cef_app = BrowserProcessAppBuilder::build(tx);
let ret = execute_process(
Some(args.as_main_args()),
Some(&mut cef_app),
std::ptr::null_mut(),
);
assert_eq!(ret, -1, "cannot execute browser process");
cef_initialize(&args, &mut cef_app);
app.insert_non_send_resource(cef_app);
app.insert_non_send_resource(MessageLoopWorkingReceiver(rx));
app.insert_non_send_resource(RunOnMainThread)
.add_systems(Main, cef_do_message_loop_work)
.add_systems(Update, cef_shutdown.run_if(on_message::<AppExit>));
}
}
impl Default for MessageLoopPlugin {
fn default() -> Self {
#[cfg(target_os = "macos")]
let _loader = {
macos::install_cef_app_protocol();
#[cfg(all(target_os = "macos", feature = "debug"))]
let loader = DebugLibraryLoader::new();
#[cfg(all(target_os = "macos", not(feature = "debug")))]
let loader =
cef::library_loader::LibraryLoader::new(&std::env::current_exe().unwrap(), false);
assert!(loader.load());
loader
};
let _ = api_hash(sys::CEF_API_VERSION_LAST, 0);
let args = Args::new();
let mut app = BrowserProcessAppBuilder::build();
let ret = execute_process(
Some(args.as_main_args()),
Some(&mut app),
std::ptr::null_mut(),
);
assert_eq!(ret, -1, "cannot execute browser process");
let settings = Settings {
#[cfg(all(target_os = "macos", feature = "debug"))]
framework_dir_path: debug_chromium_embedded_framework_dir_path()
.to_str()
.unwrap()
.into(),
#[cfg(all(target_os = "macos", feature = "debug"))]
browser_subprocess_path: debug_render_process_path().to_str().unwrap().into(),
#[cfg(all(target_os = "macos", feature = "debug"))]
no_sandbox: true as _,
windowless_rendering_enabled: true as _,
// #[cfg(any(target_os = "windows", target_os = "linux"))]
// multi_threaded_message_loop: true as _,
#[cfg(target_os = "macos")]
external_message_pump: true as _,
..Default::default()
};
assert_eq!(
initialize(
Some(args.as_main_args()),
Some(&settings),
Some(&mut app),
std::ptr::null_mut(),
),
1
);
Self {
_app: Box::new(app),
#[cfg(target_os = "macos")]
_loader: Box::new(_loader),
}
}
#[cfg(target_os = "macos")]
fn load_cef_library(app: &mut App) {
macos::install_cef_app_protocol();
#[cfg(all(target_os = "macos", feature = "debug"))]
let loader = DebugLibraryLoader::new();
#[cfg(all(target_os = "macos", not(feature = "debug")))]
let loader = cef::library_loader::LibraryLoader::new(&std::env::current_exe().unwrap(), false);
assert!(loader.load());
app.insert_non_send_resource(loader);
}
fn cef_do_message_loop_work(_: NonSend<RunOnMainThread>) {
cef::do_message_loop_work();
fn cef_initialize(args: &Args, cef_app: &mut cef::App) {
let settings = Settings {
#[cfg(all(target_os = "macos", feature = "debug"))]
framework_dir_path: debug_chromium_embedded_framework_dir_path()
.to_str()
.unwrap()
.into(),
#[cfg(all(target_os = "macos", feature = "debug"))]
browser_subprocess_path: debug_render_process_path().to_str().unwrap().into(),
#[cfg(all(target_os = "macos", feature = "debug"))]
no_sandbox: true as _,
windowless_rendering_enabled: true as _,
// #[cfg(any(target_os = "windows", target_os = "linux"))]
// multi_threaded_message_loop: true as _,
multi_threaded_message_loop: false as _,
external_message_pump: true as _,
..Default::default()
};
assert_eq!(
initialize(
Some(args.as_main_args()),
Some(&settings),
Some(cef_app),
std::ptr::null_mut(),
),
1
);
}
fn cef_do_message_loop_work(
receiver: NonSend<MessageLoopWorkingReceiver>,
mut timer: Local<Option<MessageLoopTimer>>,
mut max_delay_timer: Local<MessageLoopWorkingMaxDelayTimer>,
) {
while let Ok(t) = receiver.try_recv() {
timer.replace(t);
}
if timer.as_ref().map(|t| t.is_finished()).unwrap_or(false) || max_delay_timer.is_finished() {
cef::do_message_loop_work();
*max_delay_timer = MessageLoopWorkingMaxDelayTimer::default();
timer.take();
}
}
fn cef_shutdown(_: NonSend<RunOnMainThread>) {

View File

@@ -30,7 +30,7 @@ impl Plugin for CefPlugin {
fn build(&self, app: &mut App) {
app.add_plugins((
LocalHostPlugin,
MessageLoopPlugin::default(),
MessageLoopPlugin,
WebviewCoreComponentsPlugin,
WebviewPlugin,
IpcPlugin,