Desktop: Use multithreaded CEF event loop on Windows and Linux (#3076)
* Prototype multi threaded event loop * Fix input event dispatch * Remove dead code * Reenable do_message_loop_work for macos targets * Cleanup * Review cleanup * Remove outdated comment * Attempt to fix texture import errors --------- Co-authored-by: Timon Schelling <me@timon.zip>
This commit is contained in:
parent
0e467907e2
commit
e4dd3ce806
|
|
@ -24,7 +24,7 @@ use winit::window::WindowId;
|
||||||
use crate::cef;
|
use crate::cef;
|
||||||
|
|
||||||
pub(crate) struct WinitApp {
|
pub(crate) struct WinitApp {
|
||||||
cef_context: cef::Context<cef::Initialized>,
|
cef_context: Box<dyn cef::CefContext>,
|
||||||
window: Option<Arc<Window>>,
|
window: Option<Arc<Window>>,
|
||||||
cef_schedule: Option<Instant>,
|
cef_schedule: Option<Instant>,
|
||||||
window_size_sender: Sender<WindowSize>,
|
window_size_sender: Sender<WindowSize>,
|
||||||
|
|
@ -38,7 +38,7 @@ pub(crate) struct WinitApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WinitApp {
|
impl WinitApp {
|
||||||
pub(crate) fn new(cef_context: cef::Context<cef::Initialized>, window_size_sender: Sender<WindowSize>, wgpu_context: WgpuContext, event_loop_proxy: EventLoopProxy<CustomEvent>) -> Self {
|
pub(crate) fn new(cef_context: Box<dyn cef::CefContext>, window_size_sender: Sender<WindowSize>, wgpu_context: WgpuContext, event_loop_proxy: EventLoopProxy<CustomEvent>) -> Self {
|
||||||
let rendering_loop_proxy = event_loop_proxy.clone();
|
let rendering_loop_proxy = event_loop_proxy.clone();
|
||||||
let (start_render_sender, start_render_receiver) = std::sync::mpsc::sync_channel(1);
|
let (start_render_sender, start_render_receiver) = std::sync::mpsc::sync_channel(1);
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
|
|
@ -71,7 +71,7 @@ impl WinitApp {
|
||||||
tracing::error!("Failed to serialize frontend messages");
|
tracing::error!("Failed to serialize frontend messages");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
self.cef_context.send_web_message(bytes.as_slice());
|
self.cef_context.send_web_message(bytes);
|
||||||
}
|
}
|
||||||
DesktopFrontendMessage::OpenFileDialog { title, filters, context } => {
|
DesktopFrontendMessage::OpenFileDialog { title, filters, context } => {
|
||||||
let event_loop_proxy = self.event_loop_proxy.clone();
|
let event_loop_proxy = self.event_loop_proxy.clone();
|
||||||
|
|
@ -254,7 +254,7 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_event(&mut self, event_loop: &ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {
|
fn window_event(&mut self, event_loop: &ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {
|
||||||
let Some(event) = self.cef_context.handle_window_event(event) else { return };
|
self.cef_context.handle_window_event(&event);
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
WindowEvent::CloseRequested => {
|
WindowEvent::CloseRequested => {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ use std::sync::mpsc::Receiver;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
|
mod consts;
|
||||||
mod context;
|
mod context;
|
||||||
mod dirs;
|
mod dirs;
|
||||||
mod input;
|
mod input;
|
||||||
|
|
@ -34,7 +35,7 @@ mod texture_import;
|
||||||
#[cfg(feature = "accelerated_paint")]
|
#[cfg(feature = "accelerated_paint")]
|
||||||
use texture_import::SharedTextureHandle;
|
use texture_import::SharedTextureHandle;
|
||||||
|
|
||||||
pub(crate) use context::{Context, InitError, Initialized, Setup, SetupError};
|
pub(crate) use context::{CefContext, CefContextBuilder, InitError};
|
||||||
use winit::event_loop::EventLoopProxy;
|
use winit::event_loop::EventLoopProxy;
|
||||||
|
|
||||||
pub(crate) trait CefEventHandler: Clone {
|
pub(crate) trait CefEventHandler: Clone {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub(crate) const GRAPHITE_SCHEME: &str = "graphite-static";
|
||||||
|
pub(crate) const FRONTEND_DOMAIN: &str = "frontend";
|
||||||
|
|
@ -1,164 +1,15 @@
|
||||||
use cef::sys::{CEF_API_VERSION_LAST, cef_resultcode_t};
|
mod multithreaded;
|
||||||
use cef::{App, BrowserSettings, Client, DictionaryValue, ImplBrowser, ImplBrowserHost, ImplCommandLine, RenderHandler, RequestContext, WindowInfo, browser_host_create_browser_sync, initialize};
|
mod singlethreaded;
|
||||||
use cef::{Browser, CefString, Settings, api_hash, args::Args, execute_process};
|
|
||||||
use thiserror::Error;
|
|
||||||
use winit::event::WindowEvent;
|
|
||||||
|
|
||||||
use crate::cef::dirs::{cef_cache_dir, cef_data_dir};
|
mod builder;
|
||||||
|
pub(crate) use builder::{CefContextBuilder, InitError};
|
||||||
|
|
||||||
use super::input::InputState;
|
pub(crate) trait CefContext {
|
||||||
use super::ipc::{MessageType, SendMessage};
|
fn work(&mut self);
|
||||||
use super::scheme_handler::{FRONTEND_DOMAIN, GRAPHITE_SCHEME};
|
|
||||||
use super::{CefEventHandler, input};
|
|
||||||
|
|
||||||
use super::internal::{BrowserProcessAppImpl, BrowserProcessClientImpl, RenderHandlerImpl, RenderProcessAppImpl};
|
fn handle_window_event(&mut self, event: &winit::event::WindowEvent);
|
||||||
|
|
||||||
pub(crate) struct Setup {}
|
fn notify_of_resize(&self);
|
||||||
pub(crate) struct Initialized {}
|
|
||||||
pub(crate) trait ContextState {}
|
|
||||||
impl ContextState for Setup {}
|
|
||||||
impl ContextState for Initialized {}
|
|
||||||
|
|
||||||
pub(crate) struct Context<S: ContextState> {
|
fn send_web_message(&self, message: Vec<u8>);
|
||||||
args: Args,
|
|
||||||
pub(crate) browser: Option<Browser>,
|
|
||||||
pub(crate) input_state: InputState,
|
|
||||||
marker: std::marker::PhantomData<S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context<Setup> {
|
|
||||||
pub(crate) fn new() -> Result<Context<Setup>, SetupError> {
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
let _loader = {
|
|
||||||
let loader = library_loader::LibraryLoader::new(&std::env::current_exe().unwrap(), false);
|
|
||||||
assert!(loader.load());
|
|
||||||
loader
|
|
||||||
};
|
|
||||||
let _ = api_hash(CEF_API_VERSION_LAST, 0);
|
|
||||||
|
|
||||||
let args = Args::new();
|
|
||||||
let cmd = args.as_cmd_line().unwrap();
|
|
||||||
let switch = CefString::from("type");
|
|
||||||
let is_browser_process = cmd.has_switch(Some(&switch)) != 1;
|
|
||||||
|
|
||||||
if !is_browser_process {
|
|
||||||
let process_type = CefString::from(&cmd.switch_value(Some(&switch)));
|
|
||||||
let mut app = RenderProcessAppImpl::app();
|
|
||||||
let ret = execute_process(Some(args.as_main_args()), Some(&mut app), std::ptr::null_mut());
|
|
||||||
if ret >= 0 {
|
|
||||||
return Err(SetupError::SubprocessFailed(process_type.to_string()));
|
|
||||||
} else {
|
|
||||||
return Err(SetupError::Subprocess);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Context {
|
|
||||||
args,
|
|
||||||
browser: None,
|
|
||||||
input_state: InputState::default(),
|
|
||||||
marker: std::marker::PhantomData::<Setup>,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn init(self, event_handler: impl CefEventHandler) -> Result<Context<Initialized>, InitError> {
|
|
||||||
let settings = Settings {
|
|
||||||
windowless_rendering_enabled: 1,
|
|
||||||
multi_threaded_message_loop: 0,
|
|
||||||
external_message_pump: 1,
|
|
||||||
root_cache_path: cef_data_dir().to_str().map(CefString::from).unwrap(),
|
|
||||||
cache_path: cef_cache_dir().to_str().map(CefString::from).unwrap(),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Attention! Wrapping this in an extra App is necessary, otherwise the program still compiles but segfaults
|
|
||||||
let mut cef_app = App::new(BrowserProcessAppImpl::new(event_handler.clone()));
|
|
||||||
|
|
||||||
let result = initialize(Some(self.args.as_main_args()), Some(&settings), Some(&mut cef_app), std::ptr::null_mut());
|
|
||||||
if result != 1 {
|
|
||||||
let cef_exit_code = cef::get_exit_code() as u32;
|
|
||||||
if cef_exit_code == cef_resultcode_t::CEF_RESULT_CODE_NORMAL_EXIT_PROCESS_NOTIFIED as u32 {
|
|
||||||
return Err(InitError::AlreadyRunning);
|
|
||||||
}
|
|
||||||
return Err(InitError::InitializationFailed(cef_exit_code));
|
|
||||||
}
|
|
||||||
|
|
||||||
let render_handler = RenderHandler::new(RenderHandlerImpl::new(event_handler.clone()));
|
|
||||||
let mut client = Client::new(BrowserProcessClientImpl::new(render_handler, event_handler.clone()));
|
|
||||||
|
|
||||||
let url = CefString::from(format!("{GRAPHITE_SCHEME}://{FRONTEND_DOMAIN}/").as_str());
|
|
||||||
// let url = CefString::from("chrome://gpu");
|
|
||||||
|
|
||||||
let window_info = WindowInfo {
|
|
||||||
windowless_rendering_enabled: 1,
|
|
||||||
#[cfg(feature = "accelerated_paint")]
|
|
||||||
shared_texture_enabled: if crate::cef::platform::should_enable_hardware_acceleration() { 1 } else { 0 },
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let settings = BrowserSettings {
|
|
||||||
windowless_frame_rate: crate::consts::CEF_WINDOWLESS_FRAME_RATE,
|
|
||||||
background_color: 0x0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let browser = browser_host_create_browser_sync(
|
|
||||||
Some(&window_info),
|
|
||||||
Some(&mut client),
|
|
||||||
Some(&url),
|
|
||||||
Some(&settings),
|
|
||||||
Option::<&mut DictionaryValue>::None,
|
|
||||||
Option::<&mut RequestContext>::None,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(Context {
|
|
||||||
args: self.args.clone(),
|
|
||||||
browser,
|
|
||||||
input_state: self.input_state.clone(),
|
|
||||||
marker: std::marker::PhantomData::<Initialized>,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context<Initialized> {
|
|
||||||
pub(crate) fn work(&mut self) {
|
|
||||||
cef::do_message_loop_work();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn handle_window_event(&mut self, event: WindowEvent) -> Option<WindowEvent> {
|
|
||||||
input::handle_window_event(self, event)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn notify_of_resize(&self) {
|
|
||||||
if let Some(browser) = &self.browser {
|
|
||||||
browser.host().unwrap().was_resized();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn send_web_message(&self, message: &[u8]) {
|
|
||||||
self.send_message(MessageType::SendToJS, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: ContextState> Drop for Context<S> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if self.browser.is_some() {
|
|
||||||
cef::shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub(crate) enum SetupError {
|
|
||||||
#[error("this is the sub process should exit immediately")]
|
|
||||||
Subprocess,
|
|
||||||
#[error("subprocess returned non zero exit code")]
|
|
||||||
SubprocessFailed(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub(crate) enum InitError {
|
|
||||||
#[error("initialization failed")]
|
|
||||||
InitializationFailed(u32),
|
|
||||||
#[error("Another instance is already running")]
|
|
||||||
AlreadyRunning,
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
use cef::args::Args;
|
||||||
|
use cef::sys::{CEF_API_VERSION_LAST, cef_resultcode_t};
|
||||||
|
use cef::{
|
||||||
|
App, BrowserSettings, CefString, Client, DictionaryValue, ImplCommandLine, RenderHandler, RequestContext, Settings, WindowInfo, api_hash, browser_host_create_browser_sync, execute_process,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::CefContext;
|
||||||
|
use super::singlethreaded::SingleThreadedCefContext;
|
||||||
|
use crate::cef::CefHandler;
|
||||||
|
use crate::cef::consts::{FRONTEND_DOMAIN, GRAPHITE_SCHEME};
|
||||||
|
use crate::cef::dirs::{cef_cache_dir, cef_data_dir};
|
||||||
|
use crate::cef::input::InputState;
|
||||||
|
use crate::cef::internal::{BrowserProcessAppImpl, BrowserProcessClientImpl, RenderHandlerImpl, RenderProcessAppImpl};
|
||||||
|
|
||||||
|
pub(crate) struct CefContextBuilder {
|
||||||
|
pub(crate) args: Args,
|
||||||
|
pub(crate) is_sub_process: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for CefContextBuilder {}
|
||||||
|
|
||||||
|
impl CefContextBuilder {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
let _loader = {
|
||||||
|
let loader = library_loader::LibraryLoader::new(&std::env::current_exe().unwrap(), false);
|
||||||
|
assert!(loader.load());
|
||||||
|
loader
|
||||||
|
};
|
||||||
|
let _ = api_hash(CEF_API_VERSION_LAST, 0);
|
||||||
|
|
||||||
|
let args = Args::new();
|
||||||
|
let cmd = args.as_cmd_line().unwrap();
|
||||||
|
let switch = CefString::from("type");
|
||||||
|
let is_sub_process = cmd.has_switch(Some(&switch)) == 1;
|
||||||
|
|
||||||
|
Self { args, is_sub_process }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_sub_process(&self) -> bool {
|
||||||
|
self.is_sub_process
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn execute_sub_process(&self) -> SetupError {
|
||||||
|
let cmd = self.args.as_cmd_line().unwrap();
|
||||||
|
let switch = CefString::from("type");
|
||||||
|
let process_type = CefString::from(&cmd.switch_value(Some(&switch)));
|
||||||
|
let mut app = RenderProcessAppImpl::app();
|
||||||
|
let ret = execute_process(Some(self.args.as_main_args()), Some(&mut app), std::ptr::null_mut());
|
||||||
|
if ret >= 0 {
|
||||||
|
SetupError::SubprocessFailed(process_type.to_string())
|
||||||
|
} else {
|
||||||
|
SetupError::Subprocess
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub(crate) fn initialize(self, event_handler: CefHandler) -> Result<impl CefContext, InitError> {
|
||||||
|
let settings = Settings {
|
||||||
|
windowless_rendering_enabled: 1,
|
||||||
|
multi_threaded_message_loop: 0,
|
||||||
|
external_message_pump: 1,
|
||||||
|
root_cache_path: cef_data_dir().to_str().map(CefString::from).unwrap(),
|
||||||
|
cache_path: cef_cache_dir().to_str().map(CefString::from).unwrap(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
self.initialize_inner(&event_handler, settings)?;
|
||||||
|
|
||||||
|
create_browser(event_handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
pub(crate) fn initialize(self, event_handler: CefHandler) -> Result<impl CefContext, InitError> {
|
||||||
|
let settings = Settings {
|
||||||
|
windowless_rendering_enabled: 1,
|
||||||
|
multi_threaded_message_loop: 1,
|
||||||
|
root_cache_path: cef_data_dir().to_str().map(CefString::from).unwrap(),
|
||||||
|
cache_path: cef_cache_dir().to_str().map(CefString::from).unwrap(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
self.initialize_inner(&event_handler, settings)?;
|
||||||
|
|
||||||
|
super::multithreaded::run_on_ui_thread(move || match create_browser(event_handler) {
|
||||||
|
Ok(context) => {
|
||||||
|
super::multithreaded::CONTEXT.with(|b| {
|
||||||
|
*b.borrow_mut() = Some(context);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to initialize CEF context: {:?}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(super::multithreaded::MultiThreadedCefContextProxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initialize_inner(self, event_handler: &CefHandler, settings: Settings) -> Result<(), InitError> {
|
||||||
|
let mut cef_app = App::new(BrowserProcessAppImpl::new(event_handler.clone()));
|
||||||
|
let result = cef::initialize(Some(self.args.as_main_args()), Some(&settings), Some(&mut cef_app), std::ptr::null_mut());
|
||||||
|
// Attention! Wrapping this in an extra App is necessary, otherwise the program still compiles but segfaults
|
||||||
|
|
||||||
|
if result != 1 {
|
||||||
|
let cef_exit_code = cef::get_exit_code() as u32;
|
||||||
|
if cef_exit_code == cef_resultcode_t::CEF_RESULT_CODE_NORMAL_EXIT_PROCESS_NOTIFIED as u32 {
|
||||||
|
return Err(InitError::AlreadyRunning);
|
||||||
|
}
|
||||||
|
return Err(InitError::InitializationFailed(cef_exit_code));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_browser(event_handler: CefHandler) -> Result<SingleThreadedCefContext, InitError> {
|
||||||
|
let render_handler = RenderHandler::new(RenderHandlerImpl::new(event_handler.clone()));
|
||||||
|
let mut client = Client::new(BrowserProcessClientImpl::new(render_handler, event_handler.clone()));
|
||||||
|
|
||||||
|
let url = CefString::from(format!("{GRAPHITE_SCHEME}://{FRONTEND_DOMAIN}/").as_str());
|
||||||
|
|
||||||
|
let window_info = WindowInfo {
|
||||||
|
windowless_rendering_enabled: 1,
|
||||||
|
#[cfg(feature = "accelerated_paint")]
|
||||||
|
shared_texture_enabled: if crate::cef::platform::should_enable_hardware_acceleration() { 1 } else { 0 },
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let settings = BrowserSettings {
|
||||||
|
windowless_frame_rate: crate::consts::CEF_WINDOWLESS_FRAME_RATE,
|
||||||
|
background_color: 0x0,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let browser = browser_host_create_browser_sync(
|
||||||
|
Some(&window_info),
|
||||||
|
Some(&mut client),
|
||||||
|
Some(&url),
|
||||||
|
Some(&settings),
|
||||||
|
Option::<&mut DictionaryValue>::None,
|
||||||
|
Option::<&mut RequestContext>::None,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(browser) = browser {
|
||||||
|
Ok(SingleThreadedCefContext {
|
||||||
|
browser,
|
||||||
|
input_state: InputState::default(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
tracing::error!("Failed to create browser");
|
||||||
|
Err(InitError::BrowserCreationFailed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub(crate) enum SetupError {
|
||||||
|
#[error("This is the sub process should exit immediately")]
|
||||||
|
Subprocess,
|
||||||
|
#[error("Subprocess returned non zero exit code")]
|
||||||
|
SubprocessFailed(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub(crate) enum InitError {
|
||||||
|
#[error("Initialization failed")]
|
||||||
|
InitializationFailed(u32),
|
||||||
|
#[error("Browser creation failed")]
|
||||||
|
BrowserCreationFailed,
|
||||||
|
#[error("Another instance is already running")]
|
||||||
|
AlreadyRunning,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
use cef::sys::cef_thread_id_t;
|
||||||
|
use cef::{Task, ThreadId, post_task};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use winit::event::WindowEvent;
|
||||||
|
|
||||||
|
use crate::cef::internal::task::ClosureTask;
|
||||||
|
|
||||||
|
use super::CefContext;
|
||||||
|
use super::singlethreaded::SingleThreadedCefContext;
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
pub(super) static CONTEXT: RefCell<Option<SingleThreadedCefContext>> = const { RefCell::new(None) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct MultiThreadedCefContextProxy;
|
||||||
|
|
||||||
|
impl CefContext for MultiThreadedCefContextProxy {
|
||||||
|
fn work(&mut self) {
|
||||||
|
// CEF handles its own message loop in multi-threaded mode
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_window_event(&mut self, event: &WindowEvent) {
|
||||||
|
let event_clone = event.clone();
|
||||||
|
run_on_ui_thread(move || {
|
||||||
|
CONTEXT.with(|b| {
|
||||||
|
if let Some(context) = b.borrow_mut().as_mut() {
|
||||||
|
context.handle_window_event(&event_clone);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify_of_resize(&self) {
|
||||||
|
run_on_ui_thread(move || {
|
||||||
|
CONTEXT.with(|b| {
|
||||||
|
if let Some(context) = b.borrow_mut().as_mut() {
|
||||||
|
context.notify_of_resize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_web_message(&self, message: Vec<u8>) {
|
||||||
|
run_on_ui_thread(move || {
|
||||||
|
CONTEXT.with(|b| {
|
||||||
|
if let Some(context) = b.borrow_mut().as_mut() {
|
||||||
|
context.send_web_message(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for MultiThreadedCefContextProxy {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
cef::shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn run_on_ui_thread<F>(closure: F)
|
||||||
|
where
|
||||||
|
F: FnOnce() + Send + 'static,
|
||||||
|
{
|
||||||
|
let closure_task = ClosureTask::new(closure);
|
||||||
|
let mut task = Task::new(closure_task);
|
||||||
|
post_task(ThreadId::from(cef_thread_id_t::TID_UI), Some(&mut task));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
use cef::{Browser, ImplBrowser, ImplBrowserHost};
|
||||||
|
use winit::event::WindowEvent;
|
||||||
|
|
||||||
|
use crate::cef::input;
|
||||||
|
use crate::cef::input::InputState;
|
||||||
|
use crate::cef::ipc::{MessageType, SendMessage};
|
||||||
|
|
||||||
|
use super::CefContext;
|
||||||
|
|
||||||
|
pub(super) struct SingleThreadedCefContext {
|
||||||
|
pub(super) browser: Browser,
|
||||||
|
pub(super) input_state: InputState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CefContext for SingleThreadedCefContext {
|
||||||
|
fn work(&mut self) {
|
||||||
|
cef::do_message_loop_work();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_window_event(&mut self, event: &WindowEvent) {
|
||||||
|
input::handle_window_event(&self.browser, &mut self.input_state, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify_of_resize(&self) {
|
||||||
|
self.browser.host().unwrap().was_resized();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_web_message(&self, message: Vec<u8>) {
|
||||||
|
self.send_message(MessageType::SendToJS, &message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for SingleThreadedCefContext {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
cef::shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SendMessage for SingleThreadedCefContext {
|
||||||
|
fn send_message(&self, message_type: MessageType, message: &[u8]) {
|
||||||
|
let Some(frame) = self.browser.main_frame() else {
|
||||||
|
tracing::error!("Main frame is not available, cannot send message");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
frame.send_message(message_type, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,164 +1,153 @@
|
||||||
use cef::sys::{cef_event_flags_t, cef_key_event_type_t, cef_mouse_button_type_t};
|
use cef::sys::{cef_event_flags_t, cef_key_event_type_t, cef_mouse_button_type_t};
|
||||||
use cef::{ImplBrowser, ImplBrowserHost, KeyEvent, KeyEventType, MouseEvent};
|
use cef::{Browser, ImplBrowser, ImplBrowserHost, KeyEvent, KeyEventType, MouseEvent};
|
||||||
use winit::dpi::PhysicalPosition;
|
use winit::dpi::PhysicalPosition;
|
||||||
use winit::event::{ElementState, MouseButton, MouseScrollDelta, WindowEvent};
|
use winit::event::{ElementState, MouseButton, MouseScrollDelta, WindowEvent};
|
||||||
|
|
||||||
use super::context::{Context, Initialized};
|
|
||||||
|
|
||||||
mod keymap;
|
mod keymap;
|
||||||
use keymap::{ToDomBits, ToVKBits};
|
use keymap::{ToDomBits, ToVKBits};
|
||||||
|
|
||||||
pub(crate) fn handle_window_event(context: &mut Context<Initialized>, event: WindowEvent) -> Option<WindowEvent> {
|
pub(crate) fn handle_window_event(browser: &Browser, input_state: &mut InputState, event: &WindowEvent) {
|
||||||
match event {
|
match event {
|
||||||
WindowEvent::CursorMoved { position, .. } => {
|
WindowEvent::CursorMoved { position, .. } => {
|
||||||
if let Some(browser) = &context.browser {
|
if let Some(host) = browser.host() {
|
||||||
if let Some(host) = browser.host() {
|
host.set_focus(1);
|
||||||
host.set_focus(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.input_state.update_mouse_position(&position);
|
|
||||||
let mouse_event: MouseEvent = (&context.input_state).into();
|
|
||||||
browser.host().unwrap().send_mouse_move_event(Some(&mouse_event), 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input_state.update_mouse_position(position);
|
||||||
|
let mouse_event: MouseEvent = (input_state).into();
|
||||||
|
browser.host().unwrap().send_mouse_move_event(Some(&mouse_event), 0);
|
||||||
}
|
}
|
||||||
WindowEvent::MouseInput { state, button, .. } => {
|
WindowEvent::MouseInput { state, button, .. } => {
|
||||||
if let Some(browser) = &context.browser {
|
if let Some(host) = browser.host() {
|
||||||
if let Some(host) = browser.host() {
|
host.set_focus(1);
|
||||||
host.set_focus(1);
|
|
||||||
|
|
||||||
let mouse_up = match state {
|
let mouse_up = match state {
|
||||||
ElementState::Pressed => 0,
|
ElementState::Pressed => 0,
|
||||||
ElementState::Released => 1,
|
ElementState::Released => 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let cef_button = match button {
|
let cef_button = match button {
|
||||||
MouseButton::Left => Some(cef::MouseButtonType::from(cef_mouse_button_type_t::MBT_LEFT)),
|
MouseButton::Left => Some(cef::MouseButtonType::from(cef_mouse_button_type_t::MBT_LEFT)),
|
||||||
MouseButton::Right => Some(cef::MouseButtonType::from(cef_mouse_button_type_t::MBT_RIGHT)),
|
MouseButton::Right => Some(cef::MouseButtonType::from(cef_mouse_button_type_t::MBT_RIGHT)),
|
||||||
MouseButton::Middle => Some(cef::MouseButtonType::from(cef_mouse_button_type_t::MBT_MIDDLE)),
|
MouseButton::Middle => Some(cef::MouseButtonType::from(cef_mouse_button_type_t::MBT_MIDDLE)),
|
||||||
MouseButton::Forward => None, //TODO: Handle Forward button
|
MouseButton::Forward => None, //TODO: Handle Forward button
|
||||||
MouseButton::Back => None, //TODO: Handle Back button
|
MouseButton::Back => None, //TODO: Handle Back button
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut mouse_state = context.input_state.mouse_state.clone();
|
let mut mouse_state = input_state.mouse_state.clone();
|
||||||
match button {
|
match button {
|
||||||
MouseButton::Left => {
|
MouseButton::Left => {
|
||||||
mouse_state.left = match state {
|
mouse_state.left = match state {
|
||||||
ElementState::Pressed => true,
|
ElementState::Pressed => true,
|
||||||
ElementState::Released => false,
|
ElementState::Released => false,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
MouseButton::Right => {
|
|
||||||
mouse_state.right = match state {
|
|
||||||
ElementState::Pressed => true,
|
|
||||||
ElementState::Released => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MouseButton::Middle => {
|
|
||||||
mouse_state.middle = match state {
|
|
||||||
ElementState::Pressed => true,
|
|
||||||
ElementState::Released => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
context.input_state.update_mouse_state(mouse_state);
|
|
||||||
|
|
||||||
let mouse_event: MouseEvent = (&context.input_state).into();
|
|
||||||
|
|
||||||
if let Some(button) = cef_button {
|
|
||||||
host.send_mouse_click_event(
|
|
||||||
Some(&mouse_event),
|
|
||||||
button,
|
|
||||||
mouse_up,
|
|
||||||
1, // click count
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
MouseButton::Right => {
|
||||||
|
mouse_state.right = match state {
|
||||||
|
ElementState::Pressed => true,
|
||||||
|
ElementState::Released => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MouseButton::Middle => {
|
||||||
|
mouse_state.middle = match state {
|
||||||
|
ElementState::Pressed => true,
|
||||||
|
ElementState::Released => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
input_state.update_mouse_state(mouse_state);
|
||||||
|
|
||||||
|
let mouse_event: MouseEvent = input_state.into();
|
||||||
|
|
||||||
|
if let Some(button) = cef_button {
|
||||||
|
host.send_mouse_click_event(
|
||||||
|
Some(&mouse_event),
|
||||||
|
button,
|
||||||
|
mouse_up,
|
||||||
|
1, // click count
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::MouseWheel { delta, phase: _, device_id: _, .. } => {
|
WindowEvent::MouseWheel { delta, phase: _, device_id: _, .. } => {
|
||||||
if let Some(browser) = &context.browser {
|
if let Some(host) = browser.host() {
|
||||||
if let Some(host) = browser.host() {
|
let mouse_event = input_state.into();
|
||||||
let mouse_event = (&context.input_state).into();
|
let line_width = 40; //feels about right, TODO: replace with correct value
|
||||||
let line_width = 40; //feels about right, TODO: replace with correct value
|
let line_height = 30; //feels about right, TODO: replace with correct value
|
||||||
let line_height = 30; //feels about right, TODO: replace with correct value
|
let (delta_x, delta_y) = match delta {
|
||||||
let (delta_x, delta_y) = match delta {
|
MouseScrollDelta::LineDelta(x, y) => (x * line_width as f32, y * line_height as f32),
|
||||||
MouseScrollDelta::LineDelta(x, y) => (x * line_width as f32, y * line_height as f32),
|
MouseScrollDelta::PixelDelta(physical_position) => (physical_position.x as f32, physical_position.y as f32),
|
||||||
MouseScrollDelta::PixelDelta(physical_position) => (physical_position.x as f32, physical_position.y as f32),
|
};
|
||||||
};
|
host.send_mouse_wheel_event(Some(&mouse_event), delta_x as i32, delta_y as i32);
|
||||||
host.send_mouse_wheel_event(Some(&mouse_event), delta_x as i32, delta_y as i32);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::ModifiersChanged(modifiers) => {
|
WindowEvent::ModifiersChanged(modifiers) => {
|
||||||
context.input_state.update_modifiers(&modifiers.state());
|
input_state.update_modifiers(&modifiers.state());
|
||||||
}
|
}
|
||||||
WindowEvent::KeyboardInput { device_id: _, event, is_synthetic: _ } => {
|
WindowEvent::KeyboardInput { device_id: _, event, is_synthetic: _ } => {
|
||||||
if let Some(browser) = &context.browser {
|
if let Some(host) = browser.host() {
|
||||||
if let Some(host) = browser.host() {
|
host.set_focus(1);
|
||||||
host.set_focus(1);
|
|
||||||
|
|
||||||
let (named_key, character) = match &event.logical_key {
|
let (named_key, character) = match &event.logical_key {
|
||||||
winit::keyboard::Key::Named(named_key) => (
|
winit::keyboard::Key::Named(named_key) => (
|
||||||
Some(named_key),
|
Some(named_key),
|
||||||
match named_key {
|
match named_key {
|
||||||
winit::keyboard::NamedKey::Space => Some(' '),
|
winit::keyboard::NamedKey::Space => Some(' '),
|
||||||
winit::keyboard::NamedKey::Enter => Some('\u{000d}'),
|
winit::keyboard::NamedKey::Enter => Some('\u{000d}'),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
winit::keyboard::Key::Character(str) => {
|
winit::keyboard::Key::Character(str) => {
|
||||||
let char = str.chars().next().unwrap_or('\0');
|
let char = str.chars().next().unwrap_or('\0');
|
||||||
(None, Some(char))
|
(None, Some(char))
|
||||||
}
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut key_event = KeyEvent {
|
|
||||||
size: size_of::<KeyEvent>(),
|
|
||||||
focus_on_editable_field: 1,
|
|
||||||
modifiers: context.input_state.cef_modifiers(&event.location, event.repeat).raw(),
|
|
||||||
is_system_key: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(named_key) = named_key {
|
|
||||||
key_event.native_key_code = named_key.to_dom_bits();
|
|
||||||
key_event.windows_key_code = named_key.to_vk_bits();
|
|
||||||
} else if let Some(char) = character {
|
|
||||||
key_event.native_key_code = char.to_dom_bits();
|
|
||||||
key_event.windows_key_code = char.to_vk_bits();
|
|
||||||
}
|
}
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
match event.state {
|
let mut key_event = KeyEvent {
|
||||||
ElementState::Pressed => {
|
size: size_of::<KeyEvent>(),
|
||||||
key_event.type_ = KeyEventType::from(cef_key_event_type_t::KEYEVENT_RAWKEYDOWN);
|
focus_on_editable_field: 1,
|
||||||
host.send_key_event(Some(&key_event));
|
modifiers: input_state.cef_modifiers(&event.location, event.repeat).raw(),
|
||||||
|
is_system_key: 0,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(char) = character {
|
if let Some(named_key) = named_key {
|
||||||
let mut buf = [0; 2];
|
key_event.native_key_code = named_key.to_dom_bits();
|
||||||
char.encode_utf16(&mut buf);
|
key_event.windows_key_code = named_key.to_vk_bits();
|
||||||
key_event.character = buf[0];
|
} else if let Some(char) = character {
|
||||||
let mut buf = [0; 2];
|
key_event.native_key_code = char.to_dom_bits();
|
||||||
char.to_lowercase().next().unwrap().encode_utf16(&mut buf);
|
key_event.windows_key_code = char.to_vk_bits();
|
||||||
key_event.unmodified_character = buf[0];
|
|
||||||
|
|
||||||
key_event.type_ = KeyEventType::from(cef_key_event_type_t::KEYEVENT_CHAR);
|
|
||||||
host.send_key_event(Some(&key_event));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ElementState::Released => {
|
|
||||||
key_event.type_ = KeyEventType::from(cef_key_event_type_t::KEYEVENT_KEYUP);
|
|
||||||
host.send_key_event(Some(&key_event));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match event.state {
|
||||||
|
ElementState::Pressed => {
|
||||||
|
key_event.type_ = KeyEventType::from(cef_key_event_type_t::KEYEVENT_RAWKEYDOWN);
|
||||||
|
host.send_key_event(Some(&key_event));
|
||||||
|
|
||||||
|
if let Some(char) = character {
|
||||||
|
let mut buf = [0; 2];
|
||||||
|
char.encode_utf16(&mut buf);
|
||||||
|
key_event.character = buf[0];
|
||||||
|
let mut buf = [0; 2];
|
||||||
|
char.to_lowercase().next().unwrap().encode_utf16(&mut buf);
|
||||||
|
key_event.unmodified_character = buf[0];
|
||||||
|
|
||||||
|
key_event.type_ = KeyEventType::from(cef_key_event_type_t::KEYEVENT_CHAR);
|
||||||
|
host.send_key_event(Some(&key_event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ElementState::Released => {
|
||||||
|
key_event.type_ = KeyEventType::from(cef_key_event_type_t::KEYEVENT_KEYUP);
|
||||||
|
host.send_key_event(Some(&key_event));
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e => return Some(e),
|
_ => {}
|
||||||
}
|
}
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
|
|
@ -227,6 +216,15 @@ impl From<&InputState> for MouseEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl From<&mut InputState> for MouseEvent {
|
||||||
|
fn from(val: &mut InputState) -> Self {
|
||||||
|
MouseEvent {
|
||||||
|
x: val.mouse_position.x as i32,
|
||||||
|
y: val.mouse_position.y as i32,
|
||||||
|
modifiers: val.cef_modifiers_mouse_event().raw(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct CefModifiers(u32);
|
struct CefModifiers(u32);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,13 @@ mod browser_process_app;
|
||||||
mod browser_process_client;
|
mod browser_process_client;
|
||||||
mod browser_process_handler;
|
mod browser_process_handler;
|
||||||
mod browser_process_life_span_handler;
|
mod browser_process_life_span_handler;
|
||||||
pub mod render_handler;
|
|
||||||
mod render_process_app;
|
mod render_process_app;
|
||||||
mod render_process_handler;
|
mod render_process_handler;
|
||||||
mod render_process_v8_handler;
|
mod render_process_v8_handler;
|
||||||
|
|
||||||
|
pub(super) mod render_handler;
|
||||||
|
pub(super) mod task;
|
||||||
|
|
||||||
pub(super) use browser_process_app::BrowserProcessAppImpl;
|
pub(super) use browser_process_app::BrowserProcessAppImpl;
|
||||||
pub(super) use browser_process_client::BrowserProcessClientImpl;
|
pub(super) use browser_process_client::BrowserProcessClientImpl;
|
||||||
pub(super) use render_handler::RenderHandlerImpl;
|
pub(super) use render_handler::RenderHandlerImpl;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use cef::rc::{Rc, RcImpl};
|
use cef::rc::{Rc, RcImpl};
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ use cef::sys::{_cef_browser_process_handler_t, cef_base_ref_counted_t, cef_brows
|
||||||
use cef::{CefString, ImplBrowserProcessHandler, SchemeHandlerFactory, WrapBrowserProcessHandler};
|
use cef::{CefString, ImplBrowserProcessHandler, SchemeHandlerFactory, WrapBrowserProcessHandler};
|
||||||
|
|
||||||
use crate::cef::CefEventHandler;
|
use crate::cef::CefEventHandler;
|
||||||
use crate::cef::scheme_handler::{GRAPHITE_SCHEME, GraphiteSchemeHandlerFactory};
|
use crate::cef::consts::GRAPHITE_SCHEME;
|
||||||
|
use crate::cef::scheme_handler::GraphiteSchemeHandlerFactory;
|
||||||
|
|
||||||
pub(crate) struct BrowserProcessHandlerImpl<H: CefEventHandler> {
|
pub(crate) struct BrowserProcessHandlerImpl<H: CefEventHandler> {
|
||||||
object: *mut RcImpl<cef_browser_process_handler_t, Self>,
|
object: *mut RcImpl<cef_browser_process_handler_t, Self>,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
use cef::rc::{Rc, RcImpl};
|
||||||
|
use cef::sys::{_cef_task_t, cef_base_ref_counted_t};
|
||||||
|
use cef::{ImplTask, WrapTask};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
// Closure-based task wrapper following CEF patterns
|
||||||
|
pub struct ClosureTask<F> {
|
||||||
|
pub(crate) object: *mut RcImpl<_cef_task_t, Self>,
|
||||||
|
pub(crate) closure: RefCell<Option<F>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnOnce() + Send + 'static> ClosureTask<F> {
|
||||||
|
pub fn new(closure: F) -> Self {
|
||||||
|
Self {
|
||||||
|
object: std::ptr::null_mut(),
|
||||||
|
closure: RefCell::new(Some(closure)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnOnce() + Send + 'static> ImplTask for ClosureTask<F> {
|
||||||
|
fn execute(&self) {
|
||||||
|
if let Some(closure) = self.closure.borrow_mut().take() {
|
||||||
|
closure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_raw(&self) -> *mut _cef_task_t {
|
||||||
|
self.object.cast()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnOnce() + Send + 'static> Clone for ClosureTask<F> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
unsafe {
|
||||||
|
if !self.object.is_null() {
|
||||||
|
let rc_impl = &mut *self.object;
|
||||||
|
rc_impl.interface.add_ref();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
object: self.object,
|
||||||
|
closure: RefCell::new(None), // Closure can only be executed once
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnOnce() + Send + 'static> Rc for ClosureTask<F> {
|
||||||
|
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||||
|
unsafe {
|
||||||
|
let base = &*self.object;
|
||||||
|
std::mem::transmute(&base.cef_object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnOnce() + Send + 'static> WrapTask for ClosureTask<F> {
|
||||||
|
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_task_t, Self>) {
|
||||||
|
self.object = object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
use cef::{CefString, Frame, ImplBinaryValue, ImplBrowser, ImplFrame, ImplListValue, ImplProcessMessage, ImplV8Context, ProcessId, V8Context, sys::cef_process_id_t};
|
use cef::{CefString, Frame, ImplBinaryValue, ImplFrame, ImplListValue, ImplProcessMessage, ImplV8Context, ProcessId, V8Context, sys::cef_process_id_t};
|
||||||
|
|
||||||
use super::{Context, Initialized};
|
|
||||||
|
|
||||||
pub(crate) enum MessageType {
|
pub(crate) enum MessageType {
|
||||||
SendToJS,
|
SendToJS,
|
||||||
|
|
@ -39,21 +37,6 @@ pub(crate) struct MessageInfo {
|
||||||
pub(crate) trait SendMessage {
|
pub(crate) trait SendMessage {
|
||||||
fn send_message(&self, message_type: MessageType, message: &[u8]);
|
fn send_message(&self, message_type: MessageType, message: &[u8]);
|
||||||
}
|
}
|
||||||
impl SendMessage for Context<Initialized> {
|
|
||||||
fn send_message(&self, message_type: MessageType, message: &[u8]) {
|
|
||||||
let Some(browser) = &self.browser else {
|
|
||||||
tracing::error!("Browser is not initialized, cannot send message");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(frame) = browser.main_frame() else {
|
|
||||||
tracing::error!("Main frame is not available, cannot send message");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
frame.send_message(message_type, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl SendMessage for Option<V8Context> {
|
impl SendMessage for Option<V8Context> {
|
||||||
fn send_message(&self, message_type: MessageType, message: &[u8]) {
|
fn send_message(&self, message_type: MessageType, message: &[u8]) {
|
||||||
let Some(context) = self else {
|
let Some(context) = self else {
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,7 @@ use cef::{
|
||||||
};
|
};
|
||||||
use include_dir::{Dir, include_dir};
|
use include_dir::{Dir, include_dir};
|
||||||
|
|
||||||
pub(crate) const GRAPHITE_SCHEME: &str = "graphite-static";
|
use super::consts::{FRONTEND_DOMAIN, GRAPHITE_SCHEME};
|
||||||
pub(crate) const FRONTEND_DOMAIN: &str = "frontend";
|
|
||||||
|
|
||||||
pub(crate) struct GraphiteSchemeHandlerFactory {
|
pub(crate) struct GraphiteSchemeHandlerFactory {
|
||||||
object: *mut RcImpl<_cef_scheme_handler_factory_t, Self>,
|
object: *mut RcImpl<_cef_scheme_handler_factory_t, Self>,
|
||||||
|
|
|
||||||
|
|
@ -87,22 +87,15 @@ impl D3D11Importer {
|
||||||
// Wrap D3D12 resource in wgpu-hal texture
|
// Wrap D3D12 resource in wgpu-hal texture
|
||||||
let hal_texture = <api::Dx12 as wgpu::hal::Api>::Device::texture_from_raw(
|
let hal_texture = <api::Dx12 as wgpu::hal::Api>::Device::texture_from_raw(
|
||||||
d3d12_resource,
|
d3d12_resource,
|
||||||
&wgpu::hal::TextureDescriptor {
|
format::cef_to_wgpu(self.format)?,
|
||||||
label: Some("CEF D3D11→D3D12 Shared Texture"),
|
wgpu::TextureDimension::D2,
|
||||||
size: wgpu::Extent3d {
|
wgpu::Extent3d {
|
||||||
width: self.width,
|
width: self.width,
|
||||||
height: self.height,
|
height: self.height,
|
||||||
depth_or_array_layers: 1,
|
depth_or_array_layers: 1,
|
||||||
},
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: format::cef_to_wgpu(self.format)?,
|
|
||||||
usage: wgpu::TextureUses::COPY_DST | wgpu::TextureUses::RESOURCE,
|
|
||||||
memory_flags: wgpu::hal::MemoryFlags::empty(),
|
|
||||||
view_formats: vec![],
|
|
||||||
},
|
},
|
||||||
None, // drop_callback
|
1, // mip_level_count
|
||||||
|
1, // sample_count
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(hal_texture)
|
Ok(hal_texture)
|
||||||
|
|
@ -286,7 +279,7 @@ impl D3D11Importer {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut shared_resource: Option<ID3D12Resource> = None;
|
let mut shared_resource: Option<ID3D12Resource> = None;
|
||||||
d3d12_device
|
d3d12_device
|
||||||
.OpenSharedHandle(windows::Win32::Foundation::HANDLE(self.handle as isize), &ID3D12Resource::IID, &mut shared_resource as *mut _ as *mut _)
|
.OpenSharedHandle(windows::Win32::Foundation::HANDLE(self.handle), &mut shared_resource)
|
||||||
.map_err(|e| TextureImportError::PlatformError {
|
.map_err(|e| TextureImportError::PlatformError {
|
||||||
message: format!("Failed to open D3D11 shared handle on D3D12: {:?}", e),
|
message: format!("Failed to open D3D11 shared handle on D3D12: {:?}", e),
|
||||||
})?;
|
})?;
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ use winit::event_loop::EventLoop;
|
||||||
pub(crate) mod consts;
|
pub(crate) mod consts;
|
||||||
|
|
||||||
mod cef;
|
mod cef;
|
||||||
use cef::Setup;
|
|
||||||
|
|
||||||
mod render;
|
mod render;
|
||||||
|
|
||||||
|
|
@ -29,21 +28,24 @@ pub(crate) enum CustomEvent {
|
||||||
fn main() {
|
fn main() {
|
||||||
tracing_subscriber::fmt().with_env_filter(EnvFilter::from_default_env()).init();
|
tracing_subscriber::fmt().with_env_filter(EnvFilter::from_default_env()).init();
|
||||||
|
|
||||||
let cef_context = match cef::Context::<Setup>::new() {
|
let cef_context_builder = cef::CefContextBuilder::new();
|
||||||
Ok(c) => c,
|
|
||||||
Err(cef::SetupError::Subprocess) => exit(0),
|
if cef_context_builder.is_sub_process() {
|
||||||
Err(cef::SetupError::SubprocessFailed(t)) => {
|
// We are in a CEF subprocess
|
||||||
tracing::error!("Subprocess of type {t} failed");
|
// This will block until the CEF subprocess quits
|
||||||
exit(1);
|
let error = cef_context_builder.execute_sub_process();
|
||||||
}
|
tracing::error!("Cef subprocess failed with error: {error}");
|
||||||
};
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let wgpu_context = futures::executor::block_on(WgpuContext::new()).unwrap();
|
||||||
|
|
||||||
let event_loop = EventLoop::<CustomEvent>::with_user_event().build().unwrap();
|
let event_loop = EventLoop::<CustomEvent>::with_user_event().build().unwrap();
|
||||||
|
|
||||||
let (window_size_sender, window_size_receiver) = std::sync::mpsc::channel();
|
let (window_size_sender, window_size_receiver) = std::sync::mpsc::channel();
|
||||||
|
|
||||||
let wgpu_context = futures::executor::block_on(WgpuContext::new()).unwrap();
|
let cef_handler = cef::CefHandler::new(window_size_receiver, event_loop.create_proxy(), wgpu_context.clone());
|
||||||
let cef_context = match cef_context.init(cef::CefHandler::new(window_size_receiver, event_loop.create_proxy(), wgpu_context.clone())) {
|
let cef_context = match cef_context_builder.initialize(cef_handler) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(cef::InitError::AlreadyRunning) => {
|
Err(cef::InitError::AlreadyRunning) => {
|
||||||
tracing::error!("Another instance is already running, Exiting.");
|
tracing::error!("Another instance is already running, Exiting.");
|
||||||
|
|
@ -53,11 +55,15 @@ fn main() {
|
||||||
tracing::error!("Cef initialization failed with code: {code}");
|
tracing::error!("Cef initialization failed with code: {code}");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
Err(cef::InitError::BrowserCreationFailed) => {
|
||||||
|
tracing::error!("Failed to create CEF browser");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
tracing::info!("Cef initialized successfully");
|
tracing::info!("CEF initialized successfully");
|
||||||
|
|
||||||
let mut winit_app = WinitApp::new(cef_context, window_size_sender, wgpu_context, event_loop.create_proxy());
|
let mut winit_app = WinitApp::new(Box::new(cef_context), window_size_sender, wgpu_context, event_loop.create_proxy());
|
||||||
|
|
||||||
event_loop.run_app(&mut winit_app).unwrap();
|
event_loop.run_app(&mut winit_app).unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue