mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-23 05:18:11 +08:00
Implement basic desktop app with chromium embeded framework (#2874)
* Remove tauri based desktop app * Allow bzip-1.0.6 license * Implement basic cef based desktop app * Cleanup build setup * Use wait until and execute cef loop more frequently * Remove custom do browser work event * Move WinitApp into its own module * Cleanup event handling * Cleanup + Scheudule cef message loop work * Fix cpu overheating on idle: https://xkcd.com/1172/ * Use tracing crate for logging instead of println * Rebase to main --------- Co-authored-by: Dennis Kobert <dennis@kobert.dev>
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
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 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,
|
||||
..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,
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
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 winit::dpi::PhysicalPosition;
|
||||
use winit::event::{ElementState, MouseButton, MouseScrollDelta, WindowEvent};
|
||||
|
||||
use super::context::{Context, Initialized};
|
||||
|
||||
mod keymap;
|
||||
use keymap::{ToDomBits, ToVKBits};
|
||||
|
||||
pub(crate) fn handle_window_event(context: &mut Context<Initialized>, event: WindowEvent) -> Option<WindowEvent> {
|
||||
match event {
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
if let Some(browser) = &context.browser {
|
||||
if let Some(host) = browser.host() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
WindowEvent::MouseInput { state, button, .. } => {
|
||||
if let Some(browser) = &context.browser {
|
||||
if let Some(host) = browser.host() {
|
||||
host.set_focus(1);
|
||||
|
||||
let mouse_up = match state {
|
||||
ElementState::Pressed => 0,
|
||||
ElementState::Released => 1,
|
||||
};
|
||||
|
||||
let cef_button = match button {
|
||||
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::Middle => Some(cef::MouseButtonType::from(cef_mouse_button_type_t::MBT_MIDDLE)),
|
||||
MouseButton::Forward => None, //TODO: Handle Forward button
|
||||
MouseButton::Back => None, //TODO: Handle Back button
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let mut mouse_state = context.input_state.mouse_state.clone();
|
||||
match button {
|
||||
MouseButton::Left => {
|
||||
mouse_state.left = match state {
|
||||
ElementState::Pressed => true,
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowEvent::MouseWheel { delta, phase: _, device_id: _, .. } => {
|
||||
if let Some(browser) = &context.browser {
|
||||
if let Some(host) = browser.host() {
|
||||
let mouse_event = (&context.input_state).into();
|
||||
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 (delta_x, delta_y) = match delta {
|
||||
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),
|
||||
};
|
||||
host.send_mouse_wheel_event(Some(&mouse_event), delta_x as i32, delta_y as i32);
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowEvent::ModifiersChanged(modifiers) => {
|
||||
context.input_state.update_modifiers(&modifiers.state());
|
||||
}
|
||||
WindowEvent::KeyboardInput { device_id: _, event, is_synthetic: _ } => {
|
||||
if let Some(browser) = &context.browser {
|
||||
if let Some(host) = browser.host() {
|
||||
host.set_focus(1);
|
||||
|
||||
let (named_key, character) = match &event.logical_key {
|
||||
winit::keyboard::Key::Named(named_key) => (
|
||||
Some(named_key),
|
||||
match named_key {
|
||||
winit::keyboard::NamedKey::Space => Some(' '),
|
||||
winit::keyboard::NamedKey::Enter => Some('\u{000d}'),
|
||||
_ => None,
|
||||
},
|
||||
),
|
||||
winit::keyboard::Key::Character(str) => {
|
||||
let char = str.chars().next().unwrap_or('\0');
|
||||
(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();
|
||||
}
|
||||
|
||||
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)]
|
||||
pub(crate) struct MouseState {
|
||||
left: bool,
|
||||
right: bool,
|
||||
middle: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub(crate) struct MousePosition {
|
||||
x: usize,
|
||||
y: usize,
|
||||
}
|
||||
|
||||
impl From<&PhysicalPosition<f64>> for MousePosition {
|
||||
fn from(position: &PhysicalPosition<f64>) -> Self {
|
||||
Self {
|
||||
x: position.x as usize,
|
||||
y: position.y as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub(crate) struct InputState {
|
||||
modifiers: winit::keyboard::ModifiersState,
|
||||
mouse_position: MousePosition,
|
||||
mouse_state: MouseState,
|
||||
}
|
||||
|
||||
impl InputState {
|
||||
fn update_modifiers(&mut self, modifiers: &winit::keyboard::ModifiersState) {
|
||||
self.modifiers = *modifiers;
|
||||
}
|
||||
|
||||
fn update_mouse_position(&mut self, position: &PhysicalPosition<f64>) {
|
||||
self.mouse_position = position.into();
|
||||
}
|
||||
|
||||
fn update_mouse_state(&mut self, state: MouseState) {
|
||||
self.mouse_state = state;
|
||||
}
|
||||
|
||||
fn cef_modifiers(&self, location: &winit::keyboard::KeyLocation, is_repeat: bool) -> CefModifiers {
|
||||
CefModifiers::new(self, location, is_repeat)
|
||||
}
|
||||
|
||||
fn cef_modifiers_mouse_event(&self) -> CefModifiers {
|
||||
self.cef_modifiers(&winit::keyboard::KeyLocation::Standard, false)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InputState> for CefModifiers {
|
||||
fn from(val: InputState) -> Self {
|
||||
CefModifiers::new(&val, &winit::keyboard::KeyLocation::Standard, false)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&InputState> for MouseEvent {
|
||||
fn from(val: &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);
|
||||
|
||||
impl CefModifiers {
|
||||
fn new(input_state: &InputState, location: &winit::keyboard::KeyLocation, is_repeat: bool) -> Self {
|
||||
let mut inner = 0;
|
||||
|
||||
if input_state.modifiers.shift_key() {
|
||||
inner |= cef_event_flags_t::EVENTFLAG_SHIFT_DOWN as u32;
|
||||
}
|
||||
if input_state.modifiers.control_key() {
|
||||
inner |= cef_event_flags_t::EVENTFLAG_CONTROL_DOWN as u32;
|
||||
}
|
||||
if input_state.modifiers.alt_key() {
|
||||
inner |= cef_event_flags_t::EVENTFLAG_ALT_DOWN as u32;
|
||||
}
|
||||
if input_state.modifiers.super_key() {
|
||||
inner |= cef_event_flags_t::EVENTFLAG_COMMAND_DOWN as u32;
|
||||
}
|
||||
|
||||
if input_state.mouse_state.left {
|
||||
inner |= cef_event_flags_t::EVENTFLAG_LEFT_MOUSE_BUTTON as u32;
|
||||
}
|
||||
if input_state.mouse_state.right {
|
||||
inner |= cef_event_flags_t::EVENTFLAG_RIGHT_MOUSE_BUTTON as u32;
|
||||
}
|
||||
if input_state.mouse_state.middle {
|
||||
inner |= cef_event_flags_t::EVENTFLAG_MIDDLE_MOUSE_BUTTON as u32;
|
||||
}
|
||||
|
||||
if is_repeat {
|
||||
inner |= cef_event_flags_t::EVENTFLAG_IS_REPEAT as u32;
|
||||
}
|
||||
|
||||
inner |= match location {
|
||||
winit::keyboard::KeyLocation::Left => cef_event_flags_t::EVENTFLAG_IS_LEFT as u32,
|
||||
winit::keyboard::KeyLocation::Right => cef_event_flags_t::EVENTFLAG_IS_RIGHT as u32,
|
||||
winit::keyboard::KeyLocation::Numpad => cef_event_flags_t::EVENTFLAG_IS_KEY_PAD as u32,
|
||||
winit::keyboard::KeyLocation::Standard => 0,
|
||||
};
|
||||
|
||||
Self(inner)
|
||||
}
|
||||
|
||||
fn raw(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
macro_rules! map_enum {
|
||||
($target:expr, $enum:ident, $( ($code:expr, $variant:ident), )+ ) => {
|
||||
match $target {
|
||||
$(
|
||||
$enum::$variant => $code,
|
||||
)+
|
||||
_ => 0,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! map {
|
||||
($target:expr, $( ($code:expr, $variant:literal), )+ ) => {
|
||||
match $target {
|
||||
$(
|
||||
$variant => $code,
|
||||
)+
|
||||
_ => 0,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Windows Virtual keyboard binary representation
|
||||
pub(crate) trait ToVKBits {
|
||||
fn to_vk_bits(&self) -> i32;
|
||||
}
|
||||
|
||||
impl ToVKBits for winit::keyboard::NamedKey {
|
||||
fn to_vk_bits(&self) -> i32 {
|
||||
use winit::keyboard::NamedKey;
|
||||
map_enum!(
|
||||
self,
|
||||
NamedKey,
|
||||
(0x12, Alt),
|
||||
(0xA5, AltGraph),
|
||||
(0x14, CapsLock),
|
||||
(0x11, Control),
|
||||
(0x90, NumLock),
|
||||
(0x91, ScrollLock),
|
||||
(0x10, Shift),
|
||||
(0x5B, Meta),
|
||||
(0x5C, Super),
|
||||
(0x0D, Enter),
|
||||
(0x09, Tab),
|
||||
(0x20, Space),
|
||||
(0x28, ArrowDown),
|
||||
(0x25, ArrowLeft),
|
||||
(0x27, ArrowRight),
|
||||
(0x26, ArrowUp),
|
||||
(0x23, End),
|
||||
(0x24, Home),
|
||||
(0x22, PageDown),
|
||||
(0x21, PageUp),
|
||||
(0x08, Backspace),
|
||||
(0x0C, Clear),
|
||||
(0xF7, CrSel),
|
||||
(0x2E, Delete),
|
||||
(0xF9, EraseEof),
|
||||
(0xF8, ExSel),
|
||||
(0x2D, Insert),
|
||||
(0x1E, Accept),
|
||||
(0xF6, Attn),
|
||||
(0x03, Cancel),
|
||||
(0x5D, ContextMenu),
|
||||
(0x1B, Escape),
|
||||
(0x2B, Execute),
|
||||
(0x2F, Help),
|
||||
(0x13, Pause),
|
||||
(0xFA, Play),
|
||||
(0x5D, Props),
|
||||
(0x29, Select),
|
||||
(0xFB, ZoomIn),
|
||||
(0xFB, ZoomOut),
|
||||
(0x2C, PrintScreen),
|
||||
(0x5F, Standby),
|
||||
(0x1C, Convert),
|
||||
(0x18, FinalMode),
|
||||
(0x1F, ModeChange),
|
||||
(0x1D, NonConvert),
|
||||
(0xE5, Process),
|
||||
(0x15, HangulMode),
|
||||
(0x19, HanjaMode),
|
||||
(0x17, JunjaMode),
|
||||
(0x15, KanaMode),
|
||||
(0x19, KanjiMode),
|
||||
(0xB0, MediaFastForward),
|
||||
(0xB3, MediaPause),
|
||||
(0xB3, MediaPlay),
|
||||
(0xB3, MediaPlayPause),
|
||||
(0xB1, MediaRewind),
|
||||
(0xB2, MediaStop),
|
||||
(0xB0, MediaTrackNext),
|
||||
(0xB1, MediaTrackPrevious),
|
||||
(0x2A, Print),
|
||||
(0xAE, AudioVolumeDown),
|
||||
(0xAF, AudioVolumeUp),
|
||||
(0xAD, AudioVolumeMute),
|
||||
(0xB6, LaunchApplication1),
|
||||
(0xB7, LaunchApplication2),
|
||||
(0xB4, LaunchMail),
|
||||
(0xB5, LaunchMediaPlayer),
|
||||
(0xB5, LaunchMusicPlayer),
|
||||
(0xA6, BrowserBack),
|
||||
(0xAB, BrowserFavorites),
|
||||
(0xA7, BrowserForward),
|
||||
(0xAC, BrowserHome),
|
||||
(0xA8, BrowserRefresh),
|
||||
(0xAA, BrowserSearch),
|
||||
(0xA9, BrowserStop),
|
||||
(0xFB, ZoomToggle),
|
||||
(0x70, F1),
|
||||
(0x71, F2),
|
||||
(0x72, F3),
|
||||
(0x73, F4),
|
||||
(0x74, F5),
|
||||
(0x75, F6),
|
||||
(0x76, F7),
|
||||
(0x77, F8),
|
||||
(0x78, F9),
|
||||
(0x79, F10),
|
||||
(0x7A, F11),
|
||||
(0x7B, F12),
|
||||
(0x7C, F13),
|
||||
(0x7D, F14),
|
||||
(0x7E, F15),
|
||||
(0x7F, F16),
|
||||
(0x80, F17),
|
||||
(0x81, F18),
|
||||
(0x82, F19),
|
||||
(0x83, F20),
|
||||
(0x84, F21),
|
||||
(0x85, F22),
|
||||
(0x86, F23),
|
||||
(0x87, F24),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToVKBits for char {
|
||||
fn to_vk_bits(&self) -> i32 {
|
||||
map!(
|
||||
self,
|
||||
(0x0041, 'a'),
|
||||
(0x0042, 'b'),
|
||||
(0x0043, 'c'),
|
||||
(0x0044, 'd'),
|
||||
(0x0045, 'e'),
|
||||
(0x0046, 'f'),
|
||||
(0x0047, 'g'),
|
||||
(0x0048, 'h'),
|
||||
(0x0049, 'i'),
|
||||
(0x004a, 'j'),
|
||||
(0x004b, 'k'),
|
||||
(0x004c, 'l'),
|
||||
(0x004d, 'm'),
|
||||
(0x004e, 'n'),
|
||||
(0x004f, 'o'),
|
||||
(0x0050, 'p'),
|
||||
(0x0051, 'q'),
|
||||
(0x0052, 'r'),
|
||||
(0x0053, 's'),
|
||||
(0x0054, 't'),
|
||||
(0x0055, 'u'),
|
||||
(0x0056, 'v'),
|
||||
(0x0057, 'w'),
|
||||
(0x0058, 'x'),
|
||||
(0x0059, 'y'),
|
||||
(0x005a, 'z'),
|
||||
(0x0041, 'A'),
|
||||
(0x0042, 'B'),
|
||||
(0x0043, 'C'),
|
||||
(0x0044, 'D'),
|
||||
(0x0045, 'E'),
|
||||
(0x0046, 'F'),
|
||||
(0x0047, 'G'),
|
||||
(0x0048, 'H'),
|
||||
(0x0049, 'I'),
|
||||
(0x004a, 'J'),
|
||||
(0x004b, 'K'),
|
||||
(0x004c, 'L'),
|
||||
(0x004d, 'M'),
|
||||
(0x004e, 'N'),
|
||||
(0x004f, 'O'),
|
||||
(0x0050, 'P'),
|
||||
(0x0051, 'Q'),
|
||||
(0x0052, 'R'),
|
||||
(0x0053, 'S'),
|
||||
(0x0054, 'T'),
|
||||
(0x0055, 'U'),
|
||||
(0x0056, 'V'),
|
||||
(0x0057, 'W'),
|
||||
(0x0058, 'X'),
|
||||
(0x0059, 'Y'),
|
||||
(0x005a, 'Z'),
|
||||
(0x0031, '1'),
|
||||
(0x0032, '2'),
|
||||
(0x0032, '3'),
|
||||
(0x0033, '4'),
|
||||
(0x0034, '5'),
|
||||
(0x0035, '6'),
|
||||
(0x0036, '7'),
|
||||
(0x0037, '8'),
|
||||
(0x0039, '9'),
|
||||
(0x0030, '0'),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Chromium dom key binary representation
|
||||
pub(crate) trait ToDomBits {
|
||||
fn to_dom_bits(&self) -> i32;
|
||||
}
|
||||
|
||||
impl ToDomBits for winit::keyboard::NamedKey {
|
||||
fn to_dom_bits(&self) -> i32 {
|
||||
use winit::keyboard::NamedKey;
|
||||
map_enum!(
|
||||
self,
|
||||
NamedKey,
|
||||
(0x0000, Hyper),
|
||||
(0x0085, Super),
|
||||
(0x0025, Control),
|
||||
(0x0032, Shift),
|
||||
(0x0040, Alt),
|
||||
(0x0000, Fn),
|
||||
(0x0000, FnLock),
|
||||
(0x0024, Enter),
|
||||
(0x0009, Escape),
|
||||
(0x0016, Backspace),
|
||||
(0x0017, Tab),
|
||||
(0x0041, Space),
|
||||
(0x0042, CapsLock),
|
||||
(0x0043, F1),
|
||||
(0x0044, F2),
|
||||
(0x0045, F3),
|
||||
(0x0046, F4),
|
||||
(0x0047, F5),
|
||||
(0x0048, F6),
|
||||
(0x0049, F7),
|
||||
(0x004a, F8),
|
||||
(0x004b, F9),
|
||||
(0x004c, F10),
|
||||
(0x005f, F11),
|
||||
(0x0060, F12),
|
||||
(0x006b, PrintScreen),
|
||||
(0x004e, ScrollLock),
|
||||
(0x007f, Pause),
|
||||
(0x0076, Insert),
|
||||
(0x006e, Home),
|
||||
(0x0070, PageUp),
|
||||
(0x0077, Delete),
|
||||
(0x0073, End),
|
||||
(0x0075, PageDown),
|
||||
(0x0072, ArrowRight),
|
||||
(0x0071, ArrowLeft),
|
||||
(0x0074, ArrowDown),
|
||||
(0x006f, ArrowUp),
|
||||
(0x004d, NumLock),
|
||||
(0x0087, ContextMenu),
|
||||
(0x007c, Power),
|
||||
(0x00bf, F13),
|
||||
(0x00c0, F14),
|
||||
(0x00c1, F15),
|
||||
(0x00c2, F16),
|
||||
(0x00c3, F17),
|
||||
(0x00c4, F18),
|
||||
(0x00c5, F19),
|
||||
(0x00c6, F20),
|
||||
(0x00c7, F21),
|
||||
(0x00c8, F22),
|
||||
(0x00c9, F23),
|
||||
(0x00ca, F24),
|
||||
(0x008e, Open),
|
||||
(0x0092, Help),
|
||||
(0x008c, Select),
|
||||
(0x0089, Again),
|
||||
(0x008b, Undo),
|
||||
(0x0091, Cut),
|
||||
(0x008d, Copy),
|
||||
(0x008f, Paste),
|
||||
(0x0090, Find),
|
||||
(0x0079, AudioVolumeMute),
|
||||
(0x007b, AudioVolumeUp),
|
||||
(0x007a, AudioVolumeDown),
|
||||
(0x0065, KanaMode),
|
||||
(0x0064, Convert),
|
||||
(0x0066, NonConvert),
|
||||
(0x0000, Props),
|
||||
(0x00e9, BrightnessUp),
|
||||
(0x00e8, BrightnessDown),
|
||||
(0x00d7, MediaPlay),
|
||||
(0x00d1, MediaPause),
|
||||
(0x00af, MediaRecord),
|
||||
(0x00d8, MediaFastForward),
|
||||
(0x00b0, MediaRewind),
|
||||
(0x00ab, MediaTrackNext),
|
||||
(0x00ad, MediaTrackPrevious),
|
||||
(0x00ae, MediaStop),
|
||||
(0x00a9, Eject),
|
||||
(0x00ac, MediaPlayPause),
|
||||
(0x00a3, LaunchMail),
|
||||
(0x024d, LaunchScreenSaver),
|
||||
(0x00e1, BrowserSearch),
|
||||
(0x00b4, BrowserHome),
|
||||
(0x00a6, BrowserBack),
|
||||
(0x00a7, BrowserForward),
|
||||
(0x0088, BrowserStop),
|
||||
(0x00b5, BrowserRefresh),
|
||||
(0x00a4, BrowserFavorites),
|
||||
(0x017c, ZoomToggle),
|
||||
(0x00f0, MailReply),
|
||||
(0x00f1, MailForward),
|
||||
(0x00ef, MailSend),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToDomBits for char {
|
||||
fn to_dom_bits(&self) -> i32 {
|
||||
map!(
|
||||
self,
|
||||
(0x0026, 'a'),
|
||||
(0x0038, 'b'),
|
||||
(0x0036, 'c'),
|
||||
(0x0028, 'd'),
|
||||
(0x001a, 'e'),
|
||||
(0x0029, 'f'),
|
||||
(0x002a, 'g'),
|
||||
(0x002b, 'h'),
|
||||
(0x001f, 'i'),
|
||||
(0x002c, 'j'),
|
||||
(0x002d, 'k'),
|
||||
(0x002e, 'l'),
|
||||
(0x003a, 'm'),
|
||||
(0x0039, 'n'),
|
||||
(0x0020, 'o'),
|
||||
(0x0021, 'p'),
|
||||
(0x0018, 'q'),
|
||||
(0x001b, 'r'),
|
||||
(0x0027, 's'),
|
||||
(0x001c, 't'),
|
||||
(0x001e, 'u'),
|
||||
(0x0037, 'v'),
|
||||
(0x0019, 'w'),
|
||||
(0x0035, 'x'),
|
||||
(0x001d, 'y'),
|
||||
(0x0034, 'z'),
|
||||
(0x0026, 'A'),
|
||||
(0x0038, 'B'),
|
||||
(0x0036, 'C'),
|
||||
(0x0028, 'D'),
|
||||
(0x001a, 'E'),
|
||||
(0x0029, 'F'),
|
||||
(0x002a, 'G'),
|
||||
(0x002b, 'H'),
|
||||
(0x001f, 'I'),
|
||||
(0x002c, 'J'),
|
||||
(0x002d, 'K'),
|
||||
(0x002e, 'L'),
|
||||
(0x003a, 'M'),
|
||||
(0x0039, 'N'),
|
||||
(0x0020, 'O'),
|
||||
(0x0021, 'P'),
|
||||
(0x0018, 'Q'),
|
||||
(0x001b, 'R'),
|
||||
(0x0027, 'S'),
|
||||
(0x001c, 'T'),
|
||||
(0x001e, 'U'),
|
||||
(0x0037, 'V'),
|
||||
(0x0019, 'W'),
|
||||
(0x0035, 'X'),
|
||||
(0x001d, 'Y'),
|
||||
(0x0034, 'Z'),
|
||||
(0x000a, '1'),
|
||||
(0x000b, '2'),
|
||||
(0x000c, '3'),
|
||||
(0x000d, '4'),
|
||||
(0x000e, '5'),
|
||||
(0x000f, '6'),
|
||||
(0x0010, '7'),
|
||||
(0x0011, '8'),
|
||||
(0x0012, '9'),
|
||||
(0x0013, '0'),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
mod app;
|
||||
mod browser_process_handler;
|
||||
mod client;
|
||||
mod non_browser_app;
|
||||
mod render_handler;
|
||||
|
||||
pub(crate) use app::AppImpl;
|
||||
pub(crate) use client::ClientImpl;
|
||||
pub(crate) use non_browser_app::NonBrowserAppImpl;
|
||||
pub(crate) use render_handler::RenderHandlerImpl;
|
||||
@@ -0,0 +1,61 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_app_t, cef_base_ref_counted_t};
|
||||
use cef::{BrowserProcessHandler, ImplApp, SchemeRegistrar, WrapApp};
|
||||
|
||||
use crate::cef::CefEventHandler;
|
||||
use crate::cef::scheme_handler::GraphiteSchemeHandlerFactory;
|
||||
|
||||
use super::browser_process_handler::BrowserProcessHandlerImpl;
|
||||
|
||||
pub(crate) struct AppImpl<H: CefEventHandler> {
|
||||
object: *mut RcImpl<_cef_app_t, Self>,
|
||||
event_handler: H,
|
||||
}
|
||||
impl<H: CefEventHandler> AppImpl<H> {
|
||||
pub(crate) fn new(event_handler: H) -> Self {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
event_handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: CefEventHandler> ImplApp for AppImpl<H> {
|
||||
fn browser_process_handler(&self) -> Option<BrowserProcessHandler> {
|
||||
Some(BrowserProcessHandler::new(BrowserProcessHandlerImpl::new(self.event_handler.clone())))
|
||||
}
|
||||
|
||||
fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) {
|
||||
GraphiteSchemeHandlerFactory::register_schemes(registrar);
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_app_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: CefEventHandler> Clone for AppImpl<H> {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self {
|
||||
object: self.object,
|
||||
event_handler: self.event_handler.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<H: CefEventHandler> Rc for AppImpl<H> {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<H: CefEventHandler> WrapApp for AppImpl<H> {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_app_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_browser_process_handler_t, cef_base_ref_counted_t, cef_browser_process_handler_t};
|
||||
use cef::{CefString, ImplBrowserProcessHandler, SchemeHandlerFactory, WrapBrowserProcessHandler};
|
||||
|
||||
use crate::cef::CefEventHandler;
|
||||
use crate::cef::scheme_handler::{GRAPHITE_SCHEME, GraphiteSchemeHandlerFactory};
|
||||
|
||||
pub(crate) struct BrowserProcessHandlerImpl<H: CefEventHandler> {
|
||||
object: *mut RcImpl<cef_browser_process_handler_t, Self>,
|
||||
event_handler: H,
|
||||
}
|
||||
impl<H: CefEventHandler> BrowserProcessHandlerImpl<H> {
|
||||
pub(crate) fn new(event_handler: H) -> Self {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
event_handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: CefEventHandler> ImplBrowserProcessHandler for BrowserProcessHandlerImpl<H> {
|
||||
fn on_context_initialized(&self) {
|
||||
cef::register_scheme_handler_factory(Some(&CefString::from(GRAPHITE_SCHEME)), None, Some(&mut SchemeHandlerFactory::new(GraphiteSchemeHandlerFactory::new())));
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_browser_process_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
|
||||
fn on_schedule_message_pump_work(&self, delay_ms: i64) {
|
||||
self.event_handler.schedule_cef_message_loop_work(Instant::now() + Duration::from_millis(delay_ms as u64));
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: CefEventHandler> Clone for BrowserProcessHandlerImpl<H> {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self {
|
||||
object: self.object,
|
||||
event_handler: self.event_handler.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<H: CefEventHandler> Rc for BrowserProcessHandlerImpl<H> {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<H: CefEventHandler> WrapBrowserProcessHandler for BrowserProcessHandlerImpl<H> {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_browser_process_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_client_t, cef_base_ref_counted_t};
|
||||
use cef::{ImplClient, RenderHandler, WrapClient};
|
||||
|
||||
pub(crate) struct ClientImpl {
|
||||
object: *mut RcImpl<_cef_client_t, Self>,
|
||||
render_handler: RenderHandler,
|
||||
}
|
||||
impl ClientImpl {
|
||||
pub(crate) fn new(render_handler: RenderHandler) -> Self {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
render_handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplClient for ClientImpl {
|
||||
fn render_handler(&self) -> Option<RenderHandler> {
|
||||
Some(self.render_handler.clone())
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_client_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ClientImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self {
|
||||
object: self.object,
|
||||
render_handler: self.render_handler.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Rc for ClientImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapClient for ClientImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_client_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_app_t, cef_base_ref_counted_t};
|
||||
use cef::{App, ImplApp, SchemeRegistrar, WrapApp};
|
||||
|
||||
use crate::cef::scheme_handler::GraphiteSchemeHandlerFactory;
|
||||
|
||||
pub(crate) struct NonBrowserAppImpl {
|
||||
object: *mut RcImpl<_cef_app_t, Self>,
|
||||
}
|
||||
impl NonBrowserAppImpl {
|
||||
pub(crate) fn app() -> App {
|
||||
App::new(Self { object: std::ptr::null_mut() })
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplApp for NonBrowserAppImpl {
|
||||
fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) {
|
||||
GraphiteSchemeHandlerFactory::register_schemes(registrar);
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_app_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for NonBrowserAppImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self { object: self.object }
|
||||
}
|
||||
}
|
||||
impl Rc for NonBrowserAppImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapApp for NonBrowserAppImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_app_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_render_handler_t, cef_base_ref_counted_t};
|
||||
use cef::{Browser, ImplBrowser, ImplBrowserHost, ImplRenderHandler, PaintElementType, Rect, WrapRenderHandler};
|
||||
|
||||
use crate::FrameBuffer;
|
||||
use crate::cef::CefEventHandler;
|
||||
|
||||
pub(crate) struct RenderHandlerImpl<H: CefEventHandler> {
|
||||
object: *mut RcImpl<_cef_render_handler_t, Self>,
|
||||
event_handler: H,
|
||||
}
|
||||
impl<H: CefEventHandler> RenderHandlerImpl<H> {
|
||||
pub(crate) fn new(event_handler: H) -> Self {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
event_handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<H: CefEventHandler> ImplRenderHandler for RenderHandlerImpl<H> {
|
||||
fn view_rect(&self, _browser: Option<&mut Browser>, rect: Option<&mut Rect>) {
|
||||
if let Some(rect) = rect {
|
||||
let view = self.event_handler.window_size();
|
||||
*rect = Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: view.width as i32,
|
||||
height: view.height as i32,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn on_paint(
|
||||
&self,
|
||||
browser: Option<&mut Browser>,
|
||||
_type_: PaintElementType,
|
||||
_dirty_rect_count: usize,
|
||||
_dirty_rects: Option<&Rect>,
|
||||
buffer: *const u8,
|
||||
width: ::std::os::raw::c_int,
|
||||
height: ::std::os::raw::c_int,
|
||||
) {
|
||||
let buffer_size = (width * height * 4) as usize;
|
||||
let buffer_slice = unsafe { std::slice::from_raw_parts(buffer, buffer_size) };
|
||||
let frame_buffer = FrameBuffer::new(buffer_slice.to_vec(), width as usize, height as usize).expect("Failed to create frame buffer");
|
||||
|
||||
let draw_successful = self.event_handler.draw(frame_buffer);
|
||||
if !draw_successful {
|
||||
if let Some(browser) = browser {
|
||||
browser.host().unwrap().was_resized();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_render_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: CefEventHandler> Clone for RenderHandlerImpl<H> {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self {
|
||||
object: self.object,
|
||||
event_handler: self.event_handler.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<H: CefEventHandler> Rc for RenderHandlerImpl<H> {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<H: CefEventHandler> WrapRenderHandler for RenderHandlerImpl<H> {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_render_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::c_int;
|
||||
use std::ops::DerefMut;
|
||||
use std::slice::Iter;
|
||||
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_resource_handler_t, _cef_scheme_handler_factory_t, cef_base_ref_counted_t, cef_scheme_options_t};
|
||||
use cef::{
|
||||
Browser, Callback, CefString, Frame, ImplRequest, ImplResourceHandler, ImplResponse, ImplSchemeHandlerFactory, ImplSchemeRegistrar, Request, ResourceHandler, ResourceReadCallback, Response,
|
||||
SchemeRegistrar, WrapResourceHandler, WrapSchemeHandlerFactory,
|
||||
};
|
||||
use include_dir::{Dir, include_dir};
|
||||
|
||||
pub(crate) const GRAPHITE_SCHEME: &str = "graphite-static";
|
||||
pub(crate) const FRONTEND_DOMAIN: &str = "frontend";
|
||||
|
||||
pub(crate) struct GraphiteSchemeHandlerFactory {
|
||||
object: *mut RcImpl<_cef_scheme_handler_factory_t, Self>,
|
||||
}
|
||||
impl GraphiteSchemeHandlerFactory {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { object: std::ptr::null_mut() }
|
||||
}
|
||||
|
||||
pub(crate) fn register_schemes(registrar: Option<&mut SchemeRegistrar>) {
|
||||
if let Some(registrar) = registrar {
|
||||
let mut scheme_options = 0;
|
||||
scheme_options |= cef_scheme_options_t::CEF_SCHEME_OPTION_STANDARD as i32;
|
||||
scheme_options |= cef_scheme_options_t::CEF_SCHEME_OPTION_FETCH_ENABLED as i32;
|
||||
scheme_options |= cef_scheme_options_t::CEF_SCHEME_OPTION_SECURE as i32;
|
||||
scheme_options |= cef_scheme_options_t::CEF_SCHEME_OPTION_CORS_ENABLED as i32;
|
||||
registrar.add_custom_scheme(Some(&CefString::from(GRAPHITE_SCHEME)), scheme_options);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ImplSchemeHandlerFactory for GraphiteSchemeHandlerFactory {
|
||||
fn create(&self, _browser: Option<&mut Browser>, _frame: Option<&mut Frame>, scheme_name: Option<&CefString>, request: Option<&mut Request>) -> Option<ResourceHandler> {
|
||||
if let Some(scheme_name) = scheme_name {
|
||||
if scheme_name.to_string() != GRAPHITE_SCHEME {
|
||||
return None;
|
||||
}
|
||||
if let Some(request) = request {
|
||||
let url = CefString::from(&request.url()).to_string();
|
||||
let path = url.strip_prefix(&format!("{GRAPHITE_SCHEME}://")).unwrap();
|
||||
let domain = path.split('/').next().unwrap_or("");
|
||||
let path = path.strip_prefix(domain).unwrap_or("");
|
||||
let path = path.trim_start_matches('/');
|
||||
return match domain {
|
||||
FRONTEND_DOMAIN => {
|
||||
if path.is_empty() {
|
||||
Some(ResourceHandler::new(GraphiteFrontendResourceHandler::new("index.html")))
|
||||
} else {
|
||||
Some(ResourceHandler::new(GraphiteFrontendResourceHandler::new(path)))
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
return None;
|
||||
}
|
||||
None
|
||||
}
|
||||
fn get_raw(&self) -> *mut _cef_scheme_handler_factory_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
static FRONTEND: Dir = include_dir!("$CARGO_MANIFEST_DIR/../frontend/dist");
|
||||
|
||||
struct GraphiteFrontendResourceHandler<'a> {
|
||||
object: *mut RcImpl<_cef_resource_handler_t, Self>,
|
||||
data: Option<RefCell<Iter<'a, u8>>>,
|
||||
mimetype: Option<String>,
|
||||
}
|
||||
impl<'a> GraphiteFrontendResourceHandler<'a> {
|
||||
pub fn new(path: &str) -> Self {
|
||||
let file = FRONTEND.get_file(path);
|
||||
let data = if let Some(file) = file {
|
||||
Some(RefCell::new(file.contents().iter()))
|
||||
} else {
|
||||
tracing::error!("Failed to find asset at path: {}", path);
|
||||
None
|
||||
};
|
||||
let mimetype = if let Some(file) = file {
|
||||
let ext = file.path().extension().and_then(|s| s.to_str()).unwrap_or("");
|
||||
|
||||
// We know what file types will be in the assets this should be fine
|
||||
match ext {
|
||||
"html" => Some("text/html".to_string()),
|
||||
"css" => Some("text/css".to_string()),
|
||||
"wasm" => Some("application/wasm".to_string()),
|
||||
"js" => Some("application/javascript".to_string()),
|
||||
"png" => Some("image/png".to_string()),
|
||||
"jpg" | "jpeg" => Some("image/jpeg".to_string()),
|
||||
"svg" => Some("image/svg+xml".to_string()),
|
||||
"xml" => Some("application/xml".to_string()),
|
||||
"json" => Some("application/json".to_string()),
|
||||
"ico" => Some("image/x-icon".to_string()),
|
||||
"woff" => Some("font/woff".to_string()),
|
||||
"woff2" => Some("font/woff2".to_string()),
|
||||
"ttf" => Some("font/ttf".to_string()),
|
||||
"otf" => Some("font/otf".to_string()),
|
||||
"webmanifest" => Some("application/manifest+json".to_string()),
|
||||
"graphite" => Some("application/graphite+json".to_string()),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
data,
|
||||
mimetype,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a> ImplResourceHandler for GraphiteFrontendResourceHandler<'a> {
|
||||
fn open(&self, _request: Option<&mut Request>, handle_request: Option<&mut c_int>, _callback: Option<&mut Callback>) -> c_int {
|
||||
if let Some(handle_request) = handle_request {
|
||||
*handle_request = 1;
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
fn response_headers(&self, response: Option<&mut Response>, response_length: Option<&mut i64>, _redirect_url: Option<&mut CefString>) {
|
||||
if let Some(response_length) = response_length {
|
||||
*response_length = -1; // Indicating that the length is unknown
|
||||
}
|
||||
if let Some(response) = response {
|
||||
if self.data.is_some() {
|
||||
if let Some(mimetype) = &self.mimetype {
|
||||
let cef_mime = CefString::from(mimetype.as_str());
|
||||
response.set_mime_type(Some(&cef_mime));
|
||||
} else {
|
||||
response.set_mime_type(None);
|
||||
}
|
||||
response.set_status(200);
|
||||
} else {
|
||||
response.set_status(404);
|
||||
response.set_mime_type(Some(&CefString::from("text/plain")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&self, data_out: *mut u8, bytes_to_read: c_int, bytes_read: Option<&mut c_int>, _callback: Option<&mut ResourceReadCallback>) -> c_int {
|
||||
let mut read = 0;
|
||||
|
||||
let out = unsafe { std::slice::from_raw_parts_mut(data_out, bytes_to_read as usize) };
|
||||
if let Some(data) = &self.data {
|
||||
let mut data = data.borrow_mut();
|
||||
|
||||
for (out, &data) in out.iter_mut().zip(data.deref_mut()) {
|
||||
*out = data;
|
||||
read += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(bytes_read) = bytes_read {
|
||||
*bytes_read = read;
|
||||
}
|
||||
|
||||
if read > 0 {
|
||||
1 // Indicating that data was read
|
||||
} else {
|
||||
0 // Indicating no data was read
|
||||
}
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_resource_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl WrapSchemeHandlerFactory for GraphiteSchemeHandlerFactory {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_scheme_handler_factory_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
impl<'a> WrapResourceHandler for GraphiteFrontendResourceHandler<'a> {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_resource_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for GraphiteSchemeHandlerFactory {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self { object: self.object }
|
||||
}
|
||||
}
|
||||
impl<'a> Clone for GraphiteFrontendResourceHandler<'a> {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self {
|
||||
object: self.object,
|
||||
data: self.data.clone(),
|
||||
mimetype: self.mimetype.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Rc for GraphiteSchemeHandlerFactory {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a> Rc for GraphiteFrontendResourceHandler<'a> {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user