mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Execute editor natively
This commit is contained in:
@@ -9,17 +9,18 @@ edition = "2024"
|
||||
rust-version = "1.87"
|
||||
|
||||
[features]
|
||||
# default = ["gpu"]
|
||||
# gpu = ["graphite-editor/gpu"]
|
||||
default = ["gpu"]
|
||||
gpu = ["graphite-editor/gpu"]
|
||||
|
||||
[dependencies]
|
||||
# Local dependencies
|
||||
# graphite-editor = { path = "../editor", features = [
|
||||
# "gpu",
|
||||
# "ron",
|
||||
# "vello",
|
||||
# "decouple-execution",
|
||||
# ] }
|
||||
# # Local dependencies
|
||||
graphite-editor = { path = "../editor", features = [
|
||||
"gpu",
|
||||
"ron",
|
||||
"vello",
|
||||
"decouple-execution",
|
||||
] }
|
||||
|
||||
wgpu = { workspace = true }
|
||||
winit = { workspace = true, features = ["serde"] }
|
||||
thiserror = { workspace = true }
|
||||
@@ -29,3 +30,4 @@ include_dir = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
dirs = {workspace = true}
|
||||
serde_json = { workspace = true }
|
||||
|
||||
@@ -2,6 +2,10 @@ use crate::CustomEvent;
|
||||
use crate::WindowSize;
|
||||
use crate::render::GraphicsState;
|
||||
use crate::render::WgpuContext;
|
||||
use graphite_editor::application::Editor;
|
||||
use graphite_editor::dispatcher::Dispatcher;
|
||||
use graphite_editor::messages::prelude::Message;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Arc;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::time::Duration;
|
||||
@@ -21,11 +25,13 @@ pub(crate) struct WinitApp {
|
||||
pub(crate) cef_context: cef::Context<cef::Initialized>,
|
||||
pub(crate) window: Option<Arc<Window>>,
|
||||
cef_schedule: Option<Instant>,
|
||||
// Cached frame buffer from CEF, used to check if mouse is on a transparent pixel
|
||||
_ui_frame_buffer: Option<wgpu::Texture>,
|
||||
window_size_sender: Sender<WindowSize>,
|
||||
_viewport_frame_buffer: Option<wgpu::Texture>,
|
||||
graphics_state: Option<GraphicsState>,
|
||||
wgpu_context: WgpuContext,
|
||||
pub(crate) editor: Editor,
|
||||
}
|
||||
|
||||
impl WinitApp {
|
||||
@@ -39,6 +45,7 @@ impl WinitApp {
|
||||
graphics_state: None,
|
||||
window_size_sender,
|
||||
wgpu_context,
|
||||
editor: Editor::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,6 +104,16 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
|
||||
self.cef_schedule = Some(instant);
|
||||
}
|
||||
}
|
||||
CustomEvent::MessageReceived { message } => {
|
||||
let Ok(message) = serde_json::from_str::<Message>(&message) else {
|
||||
tracing::error!("Message could not be deserialized: {:?}", message);
|
||||
return;
|
||||
};
|
||||
println!("Message received: {message:?}");
|
||||
let responses = self.editor.handle_message(message);
|
||||
println!("responses: {:?}", responses);
|
||||
// Send response to CEF
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ pub(crate) trait CefEventHandler: Clone {
|
||||
/// 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.
|
||||
fn schedule_cef_message_loop_work(&self, scheduled_time: Instant);
|
||||
|
||||
fn send_message_to_editior(&self, message: String);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -116,4 +118,7 @@ impl CefEventHandler for CefHandler {
|
||||
fn schedule_cef_message_loop_work(&self, scheduled_time: std::time::Instant) {
|
||||
let _ = self.event_loop_proxy.send_event(CustomEvent::ScheduleBrowserWork(scheduled_time));
|
||||
}
|
||||
fn send_message_to_editior(&self, message: String) {
|
||||
let _ = self.event_loop_proxy.send_event(CustomEvent::MessageReceived { message });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +77,8 @@ impl Context<Setup> {
|
||||
return Err(InitError::InitializationFailed);
|
||||
}
|
||||
|
||||
let render_handler = RenderHandlerImpl::new(event_handler.clone());
|
||||
let mut client = Client::new(ClientImpl::new(RenderHandler::new(render_handler)));
|
||||
let render_handler = RenderHandler::new(RenderHandlerImpl::new(event_handler.clone()));
|
||||
let mut client = Client::new(ClientImpl::new(render_handler, event_handler.clone()));
|
||||
|
||||
let url = CefString::from(format!("{GRAPHITE_SCHEME}://{FRONTEND_DOMAIN}/").as_str());
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ mod app;
|
||||
mod browser_process_handler;
|
||||
mod client;
|
||||
mod non_browser_app;
|
||||
mod non_browser_render_process_handler;
|
||||
mod non_browser_v8_handler;
|
||||
mod render_handler;
|
||||
|
||||
pub(crate) use app::AppImpl;
|
||||
|
||||
@@ -3,6 +3,7 @@ use cef::sys::{_cef_app_t, cef_base_ref_counted_t};
|
||||
use cef::{BrowserProcessHandler, CefString, ImplApp, ImplCommandLine, SchemeRegistrar, WrapApp};
|
||||
|
||||
use crate::cef::CefEventHandler;
|
||||
|
||||
use crate::cef::scheme_handler::GraphiteSchemeHandlerFactory;
|
||||
|
||||
use super::browser_process_handler::BrowserProcessHandlerImpl;
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_client_t, cef_base_ref_counted_t};
|
||||
use cef::{ImplClient, RenderHandler, WrapClient};
|
||||
use cef::{ImplClient, ImplProcessMessage, RenderHandler, WrapClient};
|
||||
|
||||
pub(crate) struct ClientImpl {
|
||||
use crate::cef::CefEventHandler;
|
||||
|
||||
pub(crate) struct ClientImpl<H: CefEventHandler> {
|
||||
object: *mut RcImpl<_cef_client_t, Self>,
|
||||
render_handler: RenderHandler,
|
||||
event_handler: H,
|
||||
}
|
||||
impl ClientImpl {
|
||||
pub(crate) fn new(render_handler: RenderHandler) -> Self {
|
||||
impl<H: CefEventHandler> ClientImpl<H> {
|
||||
pub(crate) fn new(render_handler: RenderHandler, event_handler: H) -> Self {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
render_handler,
|
||||
event_handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplClient for ClientImpl {
|
||||
impl<H: CefEventHandler> ImplClient for ClientImpl<H> {
|
||||
fn render_handler(&self) -> Option<RenderHandler> {
|
||||
Some(self.render_handler.clone())
|
||||
}
|
||||
@@ -23,9 +27,33 @@ impl ImplClient for ClientImpl {
|
||||
fn get_raw(&self) -> *mut _cef_client_t {
|
||||
self.object.cast()
|
||||
}
|
||||
|
||||
fn on_process_message_received(
|
||||
&self,
|
||||
browser: Option<&mut cef::Browser>,
|
||||
frame: Option<&mut cef::Frame>,
|
||||
source_process: cef::ProcessId,
|
||||
message: Option<&mut cef::ProcessMessage>,
|
||||
) -> ::std::os::raw::c_int {
|
||||
let Some(message) = message else {
|
||||
tracing::event!(tracing::Level::ERROR, "No message in RenderProcessHandlerImpl::on_process_message_received");
|
||||
return 1;
|
||||
};
|
||||
|
||||
let pointer: *mut cef::sys::_cef_string_utf16_t = message.name().into();
|
||||
let message = unsafe {
|
||||
let str = (*pointer).str_;
|
||||
let len = (*pointer).length;
|
||||
let slice = std::slice::from_raw_parts(str, len as usize);
|
||||
String::from_utf16(slice).unwrap()
|
||||
};
|
||||
|
||||
let _ = self.event_handler.send_message_to_editior(message);
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ClientImpl {
|
||||
impl<H: CefEventHandler> Clone for ClientImpl<H> {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
@@ -34,10 +62,11 @@ impl Clone for ClientImpl {
|
||||
Self {
|
||||
object: self.object,
|
||||
render_handler: self.render_handler.clone(),
|
||||
event_handler: self.event_handler.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Rc for ClientImpl {
|
||||
impl<H: CefEventHandler> Rc for ClientImpl<H> {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
@@ -45,7 +74,7 @@ impl Rc for ClientImpl {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapClient for ClientImpl {
|
||||
impl<H: CefEventHandler> WrapClient for ClientImpl<H> {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_client_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ 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::internal::non_browser_render_process_handler::NonBrowserRenderProcessHandlerImpl;
|
||||
use crate::cef::scheme_handler::GraphiteSchemeHandlerFactory;
|
||||
|
||||
pub(crate) struct NonBrowserAppImpl {
|
||||
@@ -14,6 +15,10 @@ impl NonBrowserAppImpl {
|
||||
}
|
||||
|
||||
impl ImplApp for NonBrowserAppImpl {
|
||||
fn render_process_handler(&self) -> Option<cef::RenderProcessHandler> {
|
||||
Some(cef::RenderProcessHandler::new(NonBrowserRenderProcessHandlerImpl::new()))
|
||||
}
|
||||
|
||||
fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) {
|
||||
GraphiteSchemeHandlerFactory::register_schemes(registrar);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_browser_process_handler_t, _cef_render_process_handler_t, cef_base_ref_counted_t, cef_browser_process_handler_t, cef_v8_handler_t, cef_v8_propertyattribute_t};
|
||||
use cef::{
|
||||
CefString, ImplBrowserProcessHandler, ImplRenderProcessHandler, ImplV8Context, ImplV8Value, SchemeHandlerFactory, V8Handler, V8Propertyattribute, V8Value, WrapBrowserProcessHandler,
|
||||
WrapRenderProcessHandler, v8_value_create_function,
|
||||
};
|
||||
|
||||
use crate::cef::internal::non_browser_v8_handler::NonBrowserV8HandlerImpl;
|
||||
|
||||
pub(crate) struct NonBrowserRenderProcessHandlerImpl {
|
||||
object: *mut RcImpl<_cef_render_process_handler_t, Self>,
|
||||
}
|
||||
impl NonBrowserRenderProcessHandlerImpl {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { object: std::ptr::null_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplRenderProcessHandler for NonBrowserRenderProcessHandlerImpl {
|
||||
fn on_context_created(&self, browser: Option<&mut cef::Browser>, frame: Option<&mut cef::Frame>, context: Option<&mut cef::V8Context>) {
|
||||
let Some(context) = context else {
|
||||
tracing::event!(tracing::Level::ERROR, "No browser in RenderProcessHandlerImpl::on_context_created");
|
||||
return;
|
||||
};
|
||||
let mut v8_handler = V8Handler::new(NonBrowserV8HandlerImpl::new());
|
||||
let Some(mut function) = v8_value_create_function(Some(&CefString::from("sendMessageToCef")), Some(&mut v8_handler)) else {
|
||||
tracing::event!(tracing::Level::ERROR, "Failed to create V8 function");
|
||||
return;
|
||||
};
|
||||
let Some(global) = context.global() else {
|
||||
tracing::event!(tracing::Level::ERROR, "No global object in RenderProcessHandlerImpl::on_context_created");
|
||||
return;
|
||||
};
|
||||
|
||||
global.set_value_bykey(Some(&CefString::from("sendMessageToCef")), Some(&mut function), V8Propertyattribute::default());
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_render_process_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for NonBrowserRenderProcessHandlerImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self { object: self.object }
|
||||
}
|
||||
}
|
||||
impl Rc for NonBrowserRenderProcessHandlerImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapRenderProcessHandler for NonBrowserRenderProcessHandlerImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_render_process_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
85
desktop/src/cef/internal/non_browser_v8_handler.rs
Normal file
85
desktop/src/cef/internal/non_browser_v8_handler.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
use cef::{
|
||||
CefString, Frame, ImplFrame, ImplV8Context, ImplV8Handler, ImplV8Value, ProcessId, ProcessMessage, V8Value, WrapV8Handler, process_message_create,
|
||||
rc::Rc,
|
||||
string_userfree_utf16_free,
|
||||
sys::{cef_process_id_t, cef_string_userfree_utf16_free},
|
||||
v8_context_get_current_context,
|
||||
};
|
||||
use tracing::event;
|
||||
use winit::event_loop::EventLoopProxy;
|
||||
|
||||
pub struct NonBrowserV8HandlerImpl {
|
||||
object: *mut cef::rc::RcImpl<cef::sys::_cef_v8_handler_t, Self>,
|
||||
}
|
||||
|
||||
impl NonBrowserV8HandlerImpl {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { object: std::ptr::null_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplV8Handler for NonBrowserV8HandlerImpl {
|
||||
fn execute(
|
||||
&self,
|
||||
name: Option<&cef::CefString>,
|
||||
_object: Option<&mut V8Value>,
|
||||
arguments: Option<&[Option<V8Value>]>,
|
||||
_retval: Option<&mut Option<V8Value>>,
|
||||
_exception: Option<&mut cef::CefString>,
|
||||
) -> ::std::os::raw::c_int {
|
||||
if let Some(name) = name {
|
||||
if name.to_string() == "sendMessageToCef".to_string() {
|
||||
let string = arguments.unwrap().get(0).unwrap().as_ref().unwrap().string_value();
|
||||
|
||||
let pointer: *mut cef::sys::_cef_string_utf16_t = string.into();
|
||||
let message = unsafe {
|
||||
let str = (*pointer).str_;
|
||||
let len = (*pointer).length;
|
||||
let slice = std::slice::from_raw_parts(str, len as usize);
|
||||
String::from_utf16(slice).unwrap()
|
||||
};
|
||||
|
||||
let Some(mut process_message) = process_message_create(Some(&CefString::from(message.as_str()))) else {
|
||||
tracing::event!(tracing::Level::ERROR, "Failed to create process message");
|
||||
return 0;
|
||||
};
|
||||
|
||||
let Some(frame) = v8_context_get_current_context().and_then(|context| context.frame()) else {
|
||||
tracing::event!(tracing::Level::ERROR, "No current V8 context in V8HandlerImpl::execute");
|
||||
return 0;
|
||||
};
|
||||
frame.send_process_message(cef_process_id_t::PID_BROWSER.into(), Some(&mut process_message));
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut cef::sys::_cef_v8_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for NonBrowserV8HandlerImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self { object: self.object }
|
||||
}
|
||||
}
|
||||
|
||||
impl Rc for NonBrowserV8HandlerImpl {
|
||||
fn as_base(&self) -> &cef::sys::cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WrapV8Handler for NonBrowserV8HandlerImpl {
|
||||
fn wrap_rc(&mut self, object: *mut cef::rc::RcImpl<cef::sys::_cef_v8_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,9 @@ mod dirs;
|
||||
pub(crate) enum CustomEvent {
|
||||
UiUpdate(wgpu::Texture),
|
||||
ScheduleBrowserWork(Instant),
|
||||
MessageReceived { message: String },
|
||||
// // Called from the editor if the render node is evaluated and returns an UpdateViewport message
|
||||
// ViewportUpdate { texture: wgpu::TextureView },
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
@@ -99,10 +99,16 @@ pub(crate) struct GraphicsState {
|
||||
surface: wgpu::Surface<'static>,
|
||||
context: WgpuContext,
|
||||
config: wgpu::SurfaceConfiguration,
|
||||
texture: Option<wgpu::Texture>,
|
||||
bind_group: Option<wgpu::BindGroup>,
|
||||
render_pipeline: wgpu::RenderPipeline,
|
||||
sampler: wgpu::Sampler,
|
||||
|
||||
// Cached texture for UI rendering
|
||||
ui_texture: Option<wgpu::Texture>,
|
||||
ui_bind_group: Option<wgpu::BindGroup>,
|
||||
// Cached texture for node graph output
|
||||
// viewport_texture: Option<wgpu::Texture>,
|
||||
// // Returned from CEF js event callback
|
||||
pub viewport_top_left: (u32, u32),
|
||||
}
|
||||
|
||||
impl GraphicsState {
|
||||
@@ -211,10 +217,11 @@ impl GraphicsState {
|
||||
surface,
|
||||
context,
|
||||
config,
|
||||
texture: None,
|
||||
bind_group: None,
|
||||
render_pipeline,
|
||||
sampler,
|
||||
ui_texture: None,
|
||||
ui_bind_group: None,
|
||||
viewport_top_left: (0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,9 +235,9 @@ impl GraphicsState {
|
||||
|
||||
pub(crate) fn bind_texture(&mut self, texture: &wgpu::Texture) {
|
||||
let bind_group = self.create_bindgroup(texture);
|
||||
self.texture = Some(texture.clone());
|
||||
self.ui_texture = Some(texture.clone());
|
||||
|
||||
self.bind_group = Some(bind_group);
|
||||
self.ui_bind_group = Some(bind_group);
|
||||
}
|
||||
|
||||
fn create_bindgroup(&self, texture: &wgpu::Texture) -> wgpu::BindGroup {
|
||||
@@ -275,7 +282,7 @@ impl GraphicsState {
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&self.render_pipeline);
|
||||
if let Some(bind_group) = &self.bind_group {
|
||||
if let Some(bind_group) = &self.ui_bind_group {
|
||||
render_pass.set_bind_group(0, bind_group, &[]);
|
||||
render_pass.draw(0..6, 0..1); // Draw 3 vertices for fullscreen triangle
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user