Graphite/desktop/src/cef/context.rs

151 lines
4.3 KiB
Rust

use cef::sys::CEF_API_VERSION_LAST;
use cef::{App, BrowserSettings, Client, DictionaryValue, ImplBrowser, ImplBrowserHost, ImplCommandLine, RenderHandler, RequestContext, WindowInfo, browser_host_create_browser_sync, initialize};
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};
use super::input::InputState;
use super::scheme_handler::{FRONTEND_DOMAIN, GRAPHITE_SCHEME};
use super::{CefEventHandler, input};
use super::internal::{AppImpl, ClientImpl, NonBrowserAppImpl, RenderHandlerImpl};
pub(crate) struct Setup {}
pub(crate) struct Initialized {}
pub(crate) trait ContextState {}
impl ContextState for Setup {}
impl ContextState for Initialized {}
pub(crate) struct Context<S: ContextState> {
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 = NonBrowserAppImpl::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(AppImpl::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 {
return Err(InitError::InitializationFailed);
}
let render_handler = RenderHandlerImpl::new(event_handler.clone());
let mut client = Client::new(ClientImpl::new(RenderHandler::new(render_handler)));
let url = CefString::from(format!("{GRAPHITE_SCHEME}://{FRONTEND_DOMAIN}/").as_str());
let window_info = WindowInfo {
windowless_rendering_enabled: 1,
..Default::default()
};
let settings = BrowserSettings {
windowless_frame_rate: 60,
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();
}
}
}
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,
}