mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 13:58:12 +08:00
Desktop: Make embedded resources optional (#3094)
* Make embedding resources optional * Move remaining cef rc to internal module * Move embedded resources to separate crate * Review fixup * Fix * Fix read * Add read error
This commit is contained in:
Generated
+8
-1
@@ -2087,8 +2087,8 @@ dependencies = [
|
|||||||
"dirs",
|
"dirs",
|
||||||
"futures",
|
"futures",
|
||||||
"glam",
|
"glam",
|
||||||
|
"graphite-desktop-embedded-resources",
|
||||||
"graphite-desktop-wrapper",
|
"graphite-desktop-wrapper",
|
||||||
"include_dir",
|
|
||||||
"libc",
|
"libc",
|
||||||
"objc2-io-surface",
|
"objc2-io-surface",
|
||||||
"objc2-metal 0.3.1",
|
"objc2-metal 0.3.1",
|
||||||
@@ -2104,6 +2104,13 @@ dependencies = [
|
|||||||
"winit",
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "graphite-desktop-embedded-resources"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"include_dir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "graphite-desktop-wrapper"
|
name = "graphite-desktop-wrapper"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ members = [
|
|||||||
"editor",
|
"editor",
|
||||||
"desktop",
|
"desktop",
|
||||||
"desktop/wrapper",
|
"desktop/wrapper",
|
||||||
|
"desktop/embedded-resources",
|
||||||
"proc-macros",
|
"proc-macros",
|
||||||
"frontend/wasm",
|
"frontend/wasm",
|
||||||
"node-graph/gapplication-io",
|
"node-graph/gapplication-io",
|
||||||
|
|||||||
+5
-3
@@ -9,7 +9,9 @@ edition = "2024"
|
|||||||
rust-version = "1.87"
|
rust-version = "1.87"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["gpu", "accelerated_paint"]
|
default = ["recommended", "embedded_resources"]
|
||||||
|
recommended = ["gpu", "accelerated_paint"]
|
||||||
|
embedded_resources = ["dep:graphite-desktop-embedded-resources"]
|
||||||
gpu = ["graphite-desktop-wrapper/gpu"]
|
gpu = ["graphite-desktop-wrapper/gpu"]
|
||||||
|
|
||||||
# Hardware acceleration features
|
# Hardware acceleration features
|
||||||
@@ -19,15 +21,15 @@ accelerated_paint_d3d11 = ["windows", "ash"]
|
|||||||
accelerated_paint_iosurface = ["objc2-io-surface", "objc2-metal", "core-foundation"]
|
accelerated_paint_iosurface = ["objc2-io-surface", "objc2-metal", "core-foundation"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# # Local dependencies
|
# Local dependencies
|
||||||
graphite-desktop-wrapper = { path = "wrapper" }
|
graphite-desktop-wrapper = { path = "wrapper" }
|
||||||
|
graphite-desktop-embedded-resources = { path = "embedded-resources", optional = true }
|
||||||
|
|
||||||
wgpu = { workspace = true }
|
wgpu = { workspace = true }
|
||||||
winit = { workspace = true, features = ["serde"] }
|
winit = { workspace = true, features = ["serde"] }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
cef = { workspace = true }
|
cef = { workspace = true }
|
||||||
include_dir = { workspace = true }
|
|
||||||
tracing-subscriber = { workspace = true }
|
tracing-subscriber = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
dirs = { workspace = true }
|
dirs = { workspace = true }
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "graphite-desktop-embedded-resources"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Graphite Desktop Embedded Resources"
|
||||||
|
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||||
|
license = "Apache-2.0"
|
||||||
|
repository = ""
|
||||||
|
edition = "2024"
|
||||||
|
rust-version = "1.87"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
include_dir = { workspace = true }
|
||||||
|
|
||||||
|
[lints.rust]
|
||||||
|
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(embedded_resources)'] }
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
const RESOURCES: &str = "../../frontend/dist";
|
||||||
|
|
||||||
|
// Check if the directory `RESOURCES` exists and sets the embedded_resources cfg accordingly
|
||||||
|
// Absolute path of `RESOURCES` available via the `EMBEDDED_RESOURCES` environment variable
|
||||||
|
fn main() {
|
||||||
|
let crate_dir = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed={RESOURCES}");
|
||||||
|
if let Ok(resources) = crate_dir.join(RESOURCES).canonicalize()
|
||||||
|
&& resources.exists()
|
||||||
|
{
|
||||||
|
println!("cargo:rustc-cfg=embedded_resources");
|
||||||
|
println!("cargo:rustc-env=EMBEDDED_RESOURCES={}", resources.to_string_lossy());
|
||||||
|
} else {
|
||||||
|
println!("cargo:warning=Resource directory does not exist. Resources will not be embedded. Did you forget to build the frontend?");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
//! This crate provides `EMBEDDED_RESOURCES` that can be included in the desktop application binary.
|
||||||
|
//! It is intended to be used by the `embedded_resources` feature of the `graphite-desktop` crate.
|
||||||
|
//! The build script checks if the specified resources directory exists and sets the `embedded_resources` cfg flag accordingly.
|
||||||
|
//! If the resources directory does not exist, resources will not be embedded and a warning will be reported during compilation.
|
||||||
|
|
||||||
|
#[cfg(embedded_resources)]
|
||||||
|
pub static EMBEDDED_RESOURCES: Option<include_dir::Dir> = Some(include_dir::include_dir!("$EMBEDDED_RESOURCES"));
|
||||||
|
|
||||||
|
#[cfg(not(embedded_resources))]
|
||||||
|
pub static EMBEDDED_RESOURCES: Option<include_dir::Dir> = None;
|
||||||
+94
-14
@@ -16,6 +16,9 @@
|
|||||||
use crate::CustomEvent;
|
use crate::CustomEvent;
|
||||||
use crate::render::FrameBufferRef;
|
use crate::render::FrameBufferRef;
|
||||||
use graphite_desktop_wrapper::{WgpuContext, deserialize_editor_message};
|
use graphite_desktop_wrapper::{WgpuContext, deserialize_editor_message};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Cursor, Read};
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::Receiver;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
@@ -27,7 +30,6 @@ mod input;
|
|||||||
mod internal;
|
mod internal;
|
||||||
mod ipc;
|
mod ipc;
|
||||||
mod platform;
|
mod platform;
|
||||||
mod scheme_handler;
|
|
||||||
mod utility;
|
mod utility;
|
||||||
|
|
||||||
#[cfg(feature = "accelerated_paint")]
|
#[cfg(feature = "accelerated_paint")]
|
||||||
@@ -38,11 +40,12 @@ use texture_import::SharedTextureHandle;
|
|||||||
pub(crate) use context::{CefContext, CefContextBuilder, InitError};
|
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 + Send + Sync + 'static {
|
||||||
fn window_size(&self) -> WindowSize;
|
fn window_size(&self) -> WindowSize;
|
||||||
fn draw<'a>(&self, frame_buffer: FrameBufferRef<'a>);
|
fn draw<'a>(&self, frame_buffer: FrameBufferRef<'a>);
|
||||||
#[cfg(feature = "accelerated_paint")]
|
#[cfg(feature = "accelerated_paint")]
|
||||||
fn draw_gpu(&self, shared_texture: SharedTextureHandle);
|
fn draw_gpu(&self, shared_texture: SharedTextureHandle);
|
||||||
|
fn load_resource(&self, path: PathBuf) -> Option<Resource>;
|
||||||
/// Scheudule the main event loop to run the cef event loop after the timeout
|
/// Scheudule the main event loop to run the cef event loop after the timeout
|
||||||
/// [`_cef_browser_process_handler_t::on_schedule_message_pump_work`] for more documentation.
|
/// [`_cef_browser_process_handler_t::on_schedule_message_pump_work`] for more documentation.
|
||||||
fn schedule_cef_message_loop_work(&self, scheduled_time: Instant);
|
fn schedule_cef_message_loop_work(&self, scheduled_time: Instant);
|
||||||
@@ -62,12 +65,34 @@ impl WindowSize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct Resource {
|
||||||
|
pub(crate) reader: ResourceReader,
|
||||||
|
pub(crate) mimetype: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) enum ResourceReader {
|
||||||
|
Embedded(Cursor<&'static [u8]>),
|
||||||
|
File(Arc<File>),
|
||||||
|
}
|
||||||
|
impl Read for ResourceReader {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||||
|
match self {
|
||||||
|
ResourceReader::Embedded(cursor) => cursor.read(buf),
|
||||||
|
ResourceReader::File(file) => file.as_ref().read(buf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct CefHandler {
|
pub(crate) struct CefHandler {
|
||||||
window_size_receiver: Arc<Mutex<WindowSizeReceiver>>,
|
window_size_receiver: Arc<Mutex<WindowSizeReceiver>>,
|
||||||
event_loop_proxy: EventLoopProxy<CustomEvent>,
|
event_loop_proxy: EventLoopProxy<CustomEvent>,
|
||||||
wgpu_context: WgpuContext,
|
wgpu_context: WgpuContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WindowSizeReceiver {
|
struct WindowSizeReceiver {
|
||||||
receiver: Receiver<WindowSize>,
|
receiver: Receiver<WindowSize>,
|
||||||
window_size: WindowSize,
|
window_size: WindowSize,
|
||||||
@@ -142,6 +167,73 @@ impl CefEventHandler for CefHandler {
|
|||||||
let _ = self.event_loop_proxy.send_event(CustomEvent::UiUpdate(texture));
|
let _ = self.event_loop_proxy.send_event(CustomEvent::UiUpdate(texture));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "accelerated_paint")]
|
||||||
|
fn draw_gpu(&self, shared_texture: SharedTextureHandle) {
|
||||||
|
match shared_texture.import_texture(&self.wgpu_context.device) {
|
||||||
|
Ok(texture) => {
|
||||||
|
let _ = self.event_loop_proxy.send_event(CustomEvent::UiUpdate(texture));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to import shared texture: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_resource(&self, path: PathBuf) -> Option<Resource> {
|
||||||
|
let path = if path.as_os_str().is_empty() { PathBuf::from("index.html") } else { path };
|
||||||
|
|
||||||
|
let mimetype = match path.extension().and_then(|s| s.to_str()).unwrap_or("") {
|
||||||
|
"html" => Some("text/html".to_string()),
|
||||||
|
"css" => Some("text/css".to_string()),
|
||||||
|
"txt" => Some("text/plain".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,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "embedded_resources")]
|
||||||
|
{
|
||||||
|
if let Some(resources) = &graphite_desktop_embedded_resources::EMBEDDED_RESOURCES
|
||||||
|
&& let Some(file) = resources.get_file(&path)
|
||||||
|
{
|
||||||
|
return Some(Resource {
|
||||||
|
reader: ResourceReader::Embedded(Cursor::new(file.contents())),
|
||||||
|
mimetype,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "embedded_resources"))]
|
||||||
|
{
|
||||||
|
use std::path::Path;
|
||||||
|
let asset_path_env = std::env::var("GRAPHITE_RESOURCES").ok()?;
|
||||||
|
let asset_path = Path::new(&asset_path_env);
|
||||||
|
let file_path = asset_path.join(path.strip_prefix("/").unwrap_or(&path));
|
||||||
|
if file_path.exists() && file_path.is_file() {
|
||||||
|
if let Ok(file) = std::fs::File::open(file_path) {
|
||||||
|
return Some(Resource {
|
||||||
|
reader: ResourceReader::File(file.into()),
|
||||||
|
mimetype,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn schedule_cef_message_loop_work(&self, scheduled_time: std::time::Instant) {
|
fn schedule_cef_message_loop_work(&self, scheduled_time: std::time::Instant) {
|
||||||
let _ = self.event_loop_proxy.send_event(CustomEvent::ScheduleBrowserWork(scheduled_time));
|
let _ = self.event_loop_proxy.send_event(CustomEvent::ScheduleBrowserWork(scheduled_time));
|
||||||
}
|
}
|
||||||
@@ -157,16 +249,4 @@ impl CefEventHandler for CefHandler {
|
|||||||
};
|
};
|
||||||
let _ = self.event_loop_proxy.send_event(CustomEvent::DesktopWrapperMessage(desktop_wrapper_message));
|
let _ = self.event_loop_proxy.send_event(CustomEvent::DesktopWrapperMessage(desktop_wrapper_message));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accelerated_paint")]
|
|
||||||
fn draw_gpu(&self, shared_texture: SharedTextureHandle) {
|
|
||||||
match shared_texture.import_texture(&self.wgpu_context.device) {
|
|
||||||
Ok(texture) => {
|
|
||||||
let _ = self.event_loop_proxy.send_event(CustomEvent::UiUpdate(texture));
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!("Failed to import shared texture: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
pub(crate) const GRAPHITE_SCHEME: &str = "graphite-static";
|
pub(crate) const RESOURCE_SCHEME: &str = "resources";
|
||||||
pub(crate) const FRONTEND_DOMAIN: &str = "frontend";
|
pub(crate) const RESOURCE_DOMAIN: &str = "resources";
|
||||||
|
|||||||
@@ -6,20 +6,21 @@ use cef::{
|
|||||||
|
|
||||||
use super::CefContext;
|
use super::CefContext;
|
||||||
use super::singlethreaded::SingleThreadedCefContext;
|
use super::singlethreaded::SingleThreadedCefContext;
|
||||||
use crate::cef::CefHandler;
|
use crate::cef::CefEventHandler;
|
||||||
use crate::cef::consts::{FRONTEND_DOMAIN, GRAPHITE_SCHEME};
|
use crate::cef::consts::{RESOURCE_DOMAIN, RESOURCE_SCHEME};
|
||||||
use crate::cef::dirs::{cef_cache_dir, cef_data_dir};
|
use crate::cef::dirs::{cef_cache_dir, cef_data_dir};
|
||||||
use crate::cef::input::InputState;
|
use crate::cef::input::InputState;
|
||||||
use crate::cef::internal::{BrowserProcessAppImpl, BrowserProcessClientImpl, RenderHandlerImpl, RenderProcessAppImpl};
|
use crate::cef::internal::{BrowserProcessAppImpl, BrowserProcessClientImpl, RenderHandlerImpl, RenderProcessAppImpl};
|
||||||
|
|
||||||
pub(crate) struct CefContextBuilder {
|
pub(crate) struct CefContextBuilder<H: CefEventHandler> {
|
||||||
pub(crate) args: Args,
|
pub(crate) args: Args,
|
||||||
pub(crate) is_sub_process: bool,
|
pub(crate) is_sub_process: bool,
|
||||||
|
_marker: std::marker::PhantomData<H>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for CefContextBuilder {}
|
unsafe impl<H: CefEventHandler> Send for CefContextBuilder<H> {}
|
||||||
|
|
||||||
impl CefContextBuilder {
|
impl<H: CefEventHandler> CefContextBuilder<H> {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
let _loader = {
|
let _loader = {
|
||||||
@@ -34,7 +35,11 @@ impl CefContextBuilder {
|
|||||||
let switch = CefString::from("type");
|
let switch = CefString::from("type");
|
||||||
let is_sub_process = cmd.has_switch(Some(&switch)) == 1;
|
let is_sub_process = cmd.has_switch(Some(&switch)) == 1;
|
||||||
|
|
||||||
Self { args, is_sub_process }
|
Self {
|
||||||
|
args,
|
||||||
|
is_sub_process,
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_sub_process(&self) -> bool {
|
pub(crate) fn is_sub_process(&self) -> bool {
|
||||||
@@ -45,7 +50,7 @@ impl CefContextBuilder {
|
|||||||
let cmd = self.args.as_cmd_line().unwrap();
|
let cmd = self.args.as_cmd_line().unwrap();
|
||||||
let switch = CefString::from("type");
|
let switch = CefString::from("type");
|
||||||
let process_type = CefString::from(&cmd.switch_value(Some(&switch)));
|
let process_type = CefString::from(&cmd.switch_value(Some(&switch)));
|
||||||
let mut app = RenderProcessAppImpl::app();
|
let mut app = RenderProcessAppImpl::<H>::app();
|
||||||
let ret = execute_process(Some(self.args.as_main_args()), Some(&mut app), std::ptr::null_mut());
|
let ret = execute_process(Some(self.args.as_main_args()), Some(&mut app), std::ptr::null_mut());
|
||||||
if ret >= 0 {
|
if ret >= 0 {
|
||||||
SetupError::SubprocessFailed(process_type.to_string())
|
SetupError::SubprocessFailed(process_type.to_string())
|
||||||
@@ -55,7 +60,7 @@ impl CefContextBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub(crate) fn initialize(self, event_handler: CefHandler) -> Result<impl CefContext, InitError> {
|
pub(crate) fn initialize(self, event_handler: H) -> Result<impl CefContext, InitError> {
|
||||||
let settings = Settings {
|
let settings = Settings {
|
||||||
windowless_rendering_enabled: 1,
|
windowless_rendering_enabled: 1,
|
||||||
multi_threaded_message_loop: 0,
|
multi_threaded_message_loop: 0,
|
||||||
@@ -71,7 +76,7 @@ impl CefContextBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
pub(crate) fn initialize(self, event_handler: CefHandler) -> Result<impl CefContext, InitError> {
|
pub(crate) fn initialize(self, event_handler: H) -> Result<impl CefContext, InitError> {
|
||||||
let settings = Settings {
|
let settings = Settings {
|
||||||
windowless_rendering_enabled: 1,
|
windowless_rendering_enabled: 1,
|
||||||
multi_threaded_message_loop: 1,
|
multi_threaded_message_loop: 1,
|
||||||
@@ -97,7 +102,7 @@ impl CefContextBuilder {
|
|||||||
Ok(super::multithreaded::MultiThreadedCefContextProxy)
|
Ok(super::multithreaded::MultiThreadedCefContextProxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_inner(self, event_handler: &CefHandler, settings: Settings) -> Result<(), InitError> {
|
fn initialize_inner(self, event_handler: &H, settings: Settings) -> Result<(), InitError> {
|
||||||
let mut cef_app = App::new(BrowserProcessAppImpl::new(event_handler.clone()));
|
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());
|
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
|
// Attention! Wrapping this in an extra App is necessary, otherwise the program still compiles but segfaults
|
||||||
@@ -113,11 +118,11 @@ impl CefContextBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_browser(event_handler: CefHandler) -> Result<SingleThreadedCefContext, InitError> {
|
fn create_browser<H: CefEventHandler>(event_handler: H) -> Result<SingleThreadedCefContext, InitError> {
|
||||||
let render_handler = RenderHandler::new(RenderHandlerImpl::new(event_handler.clone()));
|
let render_handler = RenderHandler::new(RenderHandlerImpl::new(event_handler.clone()));
|
||||||
let mut client = Client::new(BrowserProcessClientImpl::new(render_handler, 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(format!("{RESOURCE_SCHEME}://{RESOURCE_DOMAIN}/").as_str());
|
||||||
|
|
||||||
let window_info = WindowInfo {
|
let window_info = WindowInfo {
|
||||||
windowless_rendering_enabled: 1,
|
windowless_rendering_enabled: 1,
|
||||||
|
|||||||
@@ -2,10 +2,14 @@ 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;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
mod resource_handler;
|
||||||
|
mod scheme_handler_factory;
|
||||||
|
|
||||||
pub(super) mod render_handler;
|
pub(super) mod render_handler;
|
||||||
pub(super) mod task;
|
pub(super) mod task;
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,9 @@ use cef::rc::{Rc, RcImpl};
|
|||||||
use cef::sys::{_cef_app_t, cef_base_ref_counted_t};
|
use cef::sys::{_cef_app_t, cef_base_ref_counted_t};
|
||||||
use cef::{BrowserProcessHandler, CefString, ImplApp, ImplCommandLine, SchemeRegistrar, WrapApp};
|
use cef::{BrowserProcessHandler, CefString, ImplApp, ImplCommandLine, SchemeRegistrar, WrapApp};
|
||||||
|
|
||||||
use crate::cef::CefEventHandler;
|
|
||||||
|
|
||||||
use crate::cef::scheme_handler::GraphiteSchemeHandlerFactory;
|
|
||||||
|
|
||||||
use super::browser_process_handler::BrowserProcessHandlerImpl;
|
use super::browser_process_handler::BrowserProcessHandlerImpl;
|
||||||
|
use super::scheme_handler_factory::SchemeHandlerFactoryImpl;
|
||||||
|
use crate::cef::CefEventHandler;
|
||||||
|
|
||||||
pub(crate) struct BrowserProcessAppImpl<H: CefEventHandler> {
|
pub(crate) struct BrowserProcessAppImpl<H: CefEventHandler> {
|
||||||
object: *mut RcImpl<_cef_app_t, Self>,
|
object: *mut RcImpl<_cef_app_t, Self>,
|
||||||
@@ -30,7 +28,7 @@ impl<H: CefEventHandler + Clone> ImplApp for BrowserProcessAppImpl<H> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) {
|
fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) {
|
||||||
GraphiteSchemeHandlerFactory::register_schemes(registrar);
|
SchemeHandlerFactoryImpl::<H>::register_schemes(registrar);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_before_command_line_processing(&self, _process_type: Option<&cef::CefString>, command_line: Option<&mut cef::CommandLine>) {
|
fn on_before_command_line_processing(&self, _process_type: Option<&cef::CefString>, command_line: Option<&mut cef::CommandLine>) {
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ 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::sys::{_cef_browser_process_handler_t, cef_base_ref_counted_t, cef_browser_process_handler_t};
|
||||||
use cef::{CefString, ImplBrowserProcessHandler, SchemeHandlerFactory, WrapBrowserProcessHandler};
|
use cef::{CefString, ImplBrowserProcessHandler, SchemeHandlerFactory, WrapBrowserProcessHandler};
|
||||||
|
|
||||||
|
use super::scheme_handler_factory::SchemeHandlerFactoryImpl;
|
||||||
use crate::cef::CefEventHandler;
|
use crate::cef::CefEventHandler;
|
||||||
use crate::cef::consts::GRAPHITE_SCHEME;
|
use crate::cef::consts::RESOURCE_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>,
|
||||||
@@ -23,7 +23,11 @@ impl<H: CefEventHandler> BrowserProcessHandlerImpl<H> {
|
|||||||
|
|
||||||
impl<H: CefEventHandler + Clone> ImplBrowserProcessHandler for BrowserProcessHandlerImpl<H> {
|
impl<H: CefEventHandler + Clone> ImplBrowserProcessHandler for BrowserProcessHandlerImpl<H> {
|
||||||
fn on_context_initialized(&self) {
|
fn on_context_initialized(&self) {
|
||||||
cef::register_scheme_handler_factory(Some(&CefString::from(GRAPHITE_SCHEME)), None, Some(&mut SchemeHandlerFactory::new(GraphiteSchemeHandlerFactory::new())));
|
cef::register_scheme_handler_factory(
|
||||||
|
Some(&CefString::from(RESOURCE_SCHEME)),
|
||||||
|
None,
|
||||||
|
Some(&mut SchemeHandlerFactory::new(SchemeHandlerFactoryImpl::new(self.event_handler.clone()))),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_schedule_message_pump_work(&self, delay_ms: i64) {
|
fn on_schedule_message_pump_work(&self, delay_ms: i64) {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ pub(crate) struct RenderHandlerImpl<H: CefEventHandler> {
|
|||||||
object: *mut RcImpl<_cef_render_handler_t, Self>,
|
object: *mut RcImpl<_cef_render_handler_t, Self>,
|
||||||
event_handler: H,
|
event_handler: H,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H: CefEventHandler> RenderHandlerImpl<H> {
|
impl<H: CefEventHandler> RenderHandlerImpl<H> {
|
||||||
pub(crate) fn new(event_handler: H) -> Self {
|
pub(crate) fn new(event_handler: H) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -18,6 +17,7 @@ impl<H: CefEventHandler> RenderHandlerImpl<H> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H: CefEventHandler> ImplRenderHandler for RenderHandlerImpl<H> {
|
impl<H: CefEventHandler> ImplRenderHandler for RenderHandlerImpl<H> {
|
||||||
fn view_rect(&self, _browser: Option<&mut Browser>, rect: Option<&mut Rect>) {
|
fn view_rect(&self, _browser: Option<&mut Browser>, rect: Option<&mut Rect>) {
|
||||||
if let Some(rect) = rect {
|
if let Some(rect) = rect {
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ use cef::sys::{_cef_app_t, cef_base_ref_counted_t};
|
|||||||
use cef::{App, ImplApp, RenderProcessHandler, SchemeRegistrar, WrapApp};
|
use cef::{App, ImplApp, RenderProcessHandler, SchemeRegistrar, WrapApp};
|
||||||
|
|
||||||
use super::render_process_handler::RenderProcessHandlerImpl;
|
use super::render_process_handler::RenderProcessHandlerImpl;
|
||||||
use crate::cef::scheme_handler::GraphiteSchemeHandlerFactory;
|
use super::scheme_handler_factory::SchemeHandlerFactoryImpl;
|
||||||
|
use crate::cef::CefEventHandler;
|
||||||
|
|
||||||
pub(crate) struct RenderProcessAppImpl {
|
pub(crate) struct RenderProcessAppImpl<H: CefEventHandler> {
|
||||||
object: *mut RcImpl<_cef_app_t, Self>,
|
object: *mut RcImpl<_cef_app_t, Self>,
|
||||||
render_process_handler: RenderProcessHandler,
|
render_process_handler: RenderProcessHandler,
|
||||||
}
|
}
|
||||||
impl RenderProcessAppImpl {
|
impl<H: CefEventHandler> RenderProcessAppImpl<H> {
|
||||||
pub(crate) fn app() -> App {
|
pub(crate) fn app() -> App {
|
||||||
App::new(Self {
|
App::new(Self {
|
||||||
object: std::ptr::null_mut(),
|
object: std::ptr::null_mut(),
|
||||||
@@ -18,9 +19,9 @@ impl RenderProcessAppImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImplApp for RenderProcessAppImpl {
|
impl<H: CefEventHandler> ImplApp for RenderProcessAppImpl<H> {
|
||||||
fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) {
|
fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) {
|
||||||
GraphiteSchemeHandlerFactory::register_schemes(registrar);
|
SchemeHandlerFactoryImpl::<H>::register_schemes(registrar);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_process_handler(&self) -> Option<RenderProcessHandler> {
|
fn render_process_handler(&self) -> Option<RenderProcessHandler> {
|
||||||
@@ -32,7 +33,7 @@ impl ImplApp for RenderProcessAppImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for RenderProcessAppImpl {
|
impl<H: CefEventHandler> Clone for RenderProcessAppImpl<H> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
unsafe {
|
unsafe {
|
||||||
let rc_impl = &mut *self.object;
|
let rc_impl = &mut *self.object;
|
||||||
@@ -44,7 +45,7 @@ impl Clone for RenderProcessAppImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Rc for RenderProcessAppImpl {
|
impl<H: CefEventHandler> Rc for RenderProcessAppImpl<H> {
|
||||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||||
unsafe {
|
unsafe {
|
||||||
let base = &*self.object;
|
let base = &*self.object;
|
||||||
@@ -52,7 +53,7 @@ impl Rc for RenderProcessAppImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl WrapApp for RenderProcessAppImpl {
|
impl<H: CefEventHandler> WrapApp for RenderProcessAppImpl<H> {
|
||||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_app_t, Self>) {
|
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_app_t, Self>) {
|
||||||
self.object = object;
|
self.object = object;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use crate::cef::ipc::{MessageType, SendMessage};
|
|||||||
pub struct BrowserProcessV8HandlerImpl {
|
pub struct BrowserProcessV8HandlerImpl {
|
||||||
object: *mut cef::rc::RcImpl<cef::sys::_cef_v8_handler_t, Self>,
|
object: *mut cef::rc::RcImpl<cef::sys::_cef_v8_handler_t, Self>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BrowserProcessV8HandlerImpl {
|
impl BrowserProcessV8HandlerImpl {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
Self { object: std::ptr::null_mut() }
|
Self { object: std::ptr::null_mut() }
|
||||||
@@ -72,7 +71,6 @@ impl Clone for BrowserProcessV8HandlerImpl {
|
|||||||
Self { object: self.object }
|
Self { object: self.object }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rc for BrowserProcessV8HandlerImpl {
|
impl Rc for BrowserProcessV8HandlerImpl {
|
||||||
fn as_base(&self) -> &cef::sys::cef_base_ref_counted_t {
|
fn as_base(&self) -> &cef::sys::cef_base_ref_counted_t {
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -81,7 +79,6 @@ impl Rc for BrowserProcessV8HandlerImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WrapV8Handler for BrowserProcessV8HandlerImpl {
|
impl WrapV8Handler for BrowserProcessV8HandlerImpl {
|
||||||
fn wrap_rc(&mut self, object: *mut cef::rc::RcImpl<cef::sys::_cef_v8_handler_t, Self>) {
|
fn wrap_rc(&mut self, object: *mut cef::rc::RcImpl<cef::sys::_cef_v8_handler_t, Self>) {
|
||||||
self.object = object;
|
self.object = object;
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
use cef::rc::{Rc, RcImpl};
|
||||||
|
use cef::sys::{_cef_resource_handler_t, cef_base_ref_counted_t};
|
||||||
|
use cef::{Callback, CefString, ImplResourceHandler, ImplResponse, Request, ResourceReadCallback, Response, WrapResourceHandler};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::ffi::c_int;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
use crate::cef::{Resource, ResourceReader};
|
||||||
|
|
||||||
|
pub(crate) struct ResourceHandlerImpl {
|
||||||
|
object: *mut RcImpl<_cef_resource_handler_t, Self>,
|
||||||
|
reader: Option<RefCell<ResourceReader>>,
|
||||||
|
mimetype: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResourceHandlerImpl {
|
||||||
|
pub fn new(resource: Option<Resource>) -> Self {
|
||||||
|
if let Some(resource) = resource {
|
||||||
|
Self {
|
||||||
|
object: std::ptr::null_mut(),
|
||||||
|
reader: Some(resource.reader.into()),
|
||||||
|
mimetype: resource.mimetype,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self {
|
||||||
|
object: std::ptr::null_mut(),
|
||||||
|
reader: None,
|
||||||
|
mimetype: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImplResourceHandler for ResourceHandlerImpl {
|
||||||
|
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.reader.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 Some(bytes_read) = bytes_read else { unreachable!() };
|
||||||
|
let out = unsafe { std::slice::from_raw_parts_mut(data_out, bytes_to_read as usize) };
|
||||||
|
if let Some(reader) = &self.reader {
|
||||||
|
if let Ok(read) = reader.borrow_mut().read(out) {
|
||||||
|
*bytes_read = read as i32;
|
||||||
|
if read > 0 {
|
||||||
|
return 1; // Indicating that data was read
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*bytes_read = -2; // Indicating ERR_FAILED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0 // Indicating no data was read
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_raw(&self) -> *mut _cef_resource_handler_t {
|
||||||
|
self.object.cast()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for ResourceHandlerImpl {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
unsafe {
|
||||||
|
let rc_impl = &mut *self.object;
|
||||||
|
rc_impl.interface.add_ref();
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
object: self.object,
|
||||||
|
reader: self.reader.clone(),
|
||||||
|
mimetype: self.mimetype.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Rc for ResourceHandlerImpl {
|
||||||
|
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||||
|
unsafe {
|
||||||
|
let base = &*self.object;
|
||||||
|
std::mem::transmute(&base.cef_object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl WrapResourceHandler for ResourceHandlerImpl {
|
||||||
|
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_resource_handler_t, Self>) {
|
||||||
|
self.object = object;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
use cef::rc::{Rc, RcImpl};
|
||||||
|
use cef::sys::{_cef_scheme_handler_factory_t, cef_base_ref_counted_t, cef_scheme_options_t};
|
||||||
|
use cef::{Browser, CefString, Frame, ImplRequest, ImplSchemeHandlerFactory, ImplSchemeRegistrar, Request, ResourceHandler, SchemeRegistrar, WrapSchemeHandlerFactory};
|
||||||
|
|
||||||
|
use super::resource_handler::ResourceHandlerImpl;
|
||||||
|
use crate::cef::CefEventHandler;
|
||||||
|
use crate::cef::consts::{RESOURCE_DOMAIN, RESOURCE_SCHEME};
|
||||||
|
|
||||||
|
pub(crate) struct SchemeHandlerFactoryImpl<H: CefEventHandler> {
|
||||||
|
object: *mut RcImpl<_cef_scheme_handler_factory_t, Self>,
|
||||||
|
event_handler: H,
|
||||||
|
}
|
||||||
|
impl<H: CefEventHandler> SchemeHandlerFactoryImpl<H> {
|
||||||
|
pub(crate) fn new(event_handler: H) -> Self {
|
||||||
|
Self {
|
||||||
|
object: std::ptr::null_mut(),
|
||||||
|
event_handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(RESOURCE_SCHEME)), scheme_options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: CefEventHandler> ImplSchemeHandlerFactory for SchemeHandlerFactoryImpl<H> {
|
||||||
|
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() != RESOURCE_SCHEME {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if let Some(request) = request {
|
||||||
|
let url = CefString::from(&request.url()).to_string();
|
||||||
|
let path = url.strip_prefix(&format!("{RESOURCE_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 {
|
||||||
|
RESOURCE_DOMAIN => {
|
||||||
|
let resource = self.event_handler.load_resource(path.to_string().into());
|
||||||
|
Some(ResourceHandler::new(ResourceHandlerImpl::new(resource)))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn get_raw(&self) -> *mut _cef_scheme_handler_factory_t {
|
||||||
|
self.object.cast()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: CefEventHandler> Clone for SchemeHandlerFactoryImpl<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 SchemeHandlerFactoryImpl<H> {
|
||||||
|
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||||
|
unsafe {
|
||||||
|
let base = &*self.object;
|
||||||
|
std::mem::transmute(&base.cef_object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<H: CefEventHandler> WrapSchemeHandlerFactory for SchemeHandlerFactoryImpl<H> {
|
||||||
|
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_scheme_handler_factory_t, Self>) {
|
||||||
|
self.object = object;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,223 +0,0 @@
|
|||||||
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};
|
|
||||||
|
|
||||||
use super::consts::{FRONTEND_DOMAIN, GRAPHITE_SCHEME};
|
|
||||||
|
|
||||||
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()),
|
|
||||||
"txt" => Some("text/plain".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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use cef::CefHandler;
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
use winit::event_loop::EventLoop;
|
use winit::event_loop::EventLoop;
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ 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_builder = cef::CefContextBuilder::new();
|
let cef_context_builder = cef::CefContextBuilder::<CefHandler>::new();
|
||||||
|
|
||||||
if cef_context_builder.is_sub_process() {
|
if cef_context_builder.is_sub_process() {
|
||||||
// We are in a CEF subprocess
|
// We are in a CEF subprocess
|
||||||
|
|||||||
Reference in New Issue
Block a user