diff --git a/Cargo.lock b/Cargo.lock index 0116649cf..7d905cfd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1820,7 +1820,9 @@ dependencies = [ "cef", "dirs", "futures", + "graphite-editor", "include_dir", + "serde_json", "thiserror 2.0.12", "tracing", "tracing-subscriber", @@ -1887,6 +1889,7 @@ dependencies = [ "math-parser", "serde", "serde-wasm-bindgen", + "serde_json", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", diff --git a/desktop/Cargo.toml b/desktop/Cargo.toml index 581f0ed60..83c100385 100644 --- a/desktop/Cargo.toml +++ b/desktop/Cargo.toml @@ -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 } diff --git a/desktop/src/app.rs b/desktop/src/app.rs index eced6556c..9eebd77df 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -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, pub(crate) window: Option>, cef_schedule: Option, + // Cached frame buffer from CEF, used to check if mouse is on a transparent pixel _ui_frame_buffer: Option, window_size_sender: Sender, _viewport_frame_buffer: Option, graphics_state: Option, 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 for WinitApp { self.cef_schedule = Some(instant); } } + CustomEvent::MessageReceived { message } => { + let Ok(message) = serde_json::from_str::(&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 + } } } diff --git a/desktop/src/cef.rs b/desktop/src/cef.rs index d84e9ad67..64dc53d67 100644 --- a/desktop/src/cef.rs +++ b/desktop/src/cef.rs @@ -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 }); + } } diff --git a/desktop/src/cef/context.rs b/desktop/src/cef/context.rs index 04ebffb4a..90eec13e5 100644 --- a/desktop/src/cef/context.rs +++ b/desktop/src/cef/context.rs @@ -77,8 +77,8 @@ impl Context { 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()); diff --git a/desktop/src/cef/internal.rs b/desktop/src/cef/internal.rs index 8473884fd..37e140e4b 100644 --- a/desktop/src/cef/internal.rs +++ b/desktop/src/cef/internal.rs @@ -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; diff --git a/desktop/src/cef/internal/app.rs b/desktop/src/cef/internal/app.rs index 5da815d16..33404334d 100644 --- a/desktop/src/cef/internal/app.rs +++ b/desktop/src/cef/internal/app.rs @@ -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; diff --git a/desktop/src/cef/internal/client.rs b/desktop/src/cef/internal/client.rs index 543f9c590..91f28442b 100644 --- a/desktop/src/cef/internal/client.rs +++ b/desktop/src/cef/internal/client.rs @@ -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 { object: *mut RcImpl<_cef_client_t, Self>, render_handler: RenderHandler, + event_handler: H, } -impl ClientImpl { - pub(crate) fn new(render_handler: RenderHandler) -> Self { +impl ClientImpl { + 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 ImplClient for ClientImpl { fn render_handler(&self) -> Option { 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 Clone for ClientImpl { 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 Rc for ClientImpl { 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 WrapClient for ClientImpl { fn wrap_rc(&mut self, object: *mut RcImpl<_cef_client_t, Self>) { self.object = object; } diff --git a/desktop/src/cef/internal/non_browser_app.rs b/desktop/src/cef/internal/non_browser_app.rs index 04007d729..f460b6c0d 100644 --- a/desktop/src/cef/internal/non_browser_app.rs +++ b/desktop/src/cef/internal/non_browser_app.rs @@ -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 { + Some(cef::RenderProcessHandler::new(NonBrowserRenderProcessHandlerImpl::new())) + } + fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) { GraphiteSchemeHandlerFactory::register_schemes(registrar); } diff --git a/desktop/src/cef/internal/non_browser_render_process_handler.rs b/desktop/src/cef/internal/non_browser_render_process_handler.rs new file mode 100644 index 000000000..ad7d9786c --- /dev/null +++ b/desktop/src/cef/internal/non_browser_render_process_handler.rs @@ -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; + } +} diff --git a/desktop/src/cef/internal/non_browser_v8_handler.rs b/desktop/src/cef/internal/non_browser_v8_handler.rs new file mode 100644 index 000000000..2d8628f47 --- /dev/null +++ b/desktop/src/cef/internal/non_browser_v8_handler.rs @@ -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, +} + +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]>, + _retval: Option<&mut Option>, + _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) { + self.object = object; + } +} diff --git a/desktop/src/main.rs b/desktop/src/main.rs index c53848f8b..7e4c45431 100644 --- a/desktop/src/main.rs +++ b/desktop/src/main.rs @@ -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() { diff --git a/desktop/src/render.rs b/desktop/src/render.rs index 514154466..8b7a68a33 100644 --- a/desktop/src/render.rs +++ b/desktop/src/render.rs @@ -99,10 +99,16 @@ pub(crate) struct GraphicsState { surface: wgpu::Surface<'static>, context: WgpuContext, config: wgpu::SurfaceConfiguration, - texture: Option, - bind_group: Option, render_pipeline: wgpu::RenderPipeline, sampler: wgpu::Sampler, + + // Cached texture for UI rendering + ui_texture: Option, + ui_bind_group: Option, + // Cached texture for node graph output + // viewport_texture: Option, + // // 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 { diff --git a/editor/Cargo.toml b/editor/Cargo.toml index f54ba7947..d5124a93f 100644 --- a/editor/Cargo.toml +++ b/editor/Cargo.toml @@ -14,7 +14,6 @@ license = "Apache-2.0" default = ["wasm"] wasm = ["wasm-bindgen", "graphene-std/wasm", "wasm-bindgen-futures"] gpu = ["interpreted-executor/gpu", "wgpu-executor"] -tauri = ["ron", "decouple-execution"] decouple-execution = [] resvg = ["graphene-std/resvg"] vello = ["graphene-std/vello", "resvg"] diff --git a/frontend/package.json b/frontend/package.json index 07fc0460b..40d36d4c8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,6 +12,7 @@ "production": "npm run setup && npm run wasm:build-production && concurrently -k -n \"VITE,RUST\" \"vite\" \"npm run wasm:watch-production\"", "---------- BUILDS ----------": "", "build-dev": "npm run wasm:build-dev && vite build", + "build-native": "npm run native:build-dev && vite build", "build-profiling": "npm run wasm:build-profiling && vite build", "build": "npm run wasm:build-production && vite build", "---------- UTILITIES ----------": "", @@ -19,6 +20,7 @@ "lint-fix": "eslint . --fix && tsc --noEmit", "---------- INTERNAL ----------": "", "setup": "node package-installer.js", + "native:build-dev": "wasm-pack build ./wasm --dev --target=web --features native", "wasm:build-dev": "wasm-pack build ./wasm --dev --target=web", "wasm:build-profiling": "wasm-pack build ./wasm --profiling --target=web", "wasm:build-production": "wasm-pack build ./wasm --release --target=web", diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index a43fb1a6a..dc7f95bde 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -5,6 +5,8 @@ import Editor from "@graphite/components/Editor.svelte"; + import { send_message_to_cef } from "/wasm/pkg/graphite_wasm"; + let editor: GraphiteEditor | undefined = undefined; onMount(async () => { @@ -17,6 +19,9 @@ // Destroy the WASM editor handle editor?.handle.free(); }); + + console.log("Test from app.svelte javascript"); + sendMessageToCef("Test from app direct"); {#if editor !== undefined} diff --git a/frontend/src/components/Editor.svelte b/frontend/src/components/Editor.svelte index 148cd8e4b..fabc22658 100644 --- a/frontend/src/components/Editor.svelte +++ b/frontend/src/components/Editor.svelte @@ -48,6 +48,7 @@ onMount(() => { // Initialize certain setup tasks required by the editor backend to be ready for the user now that the frontend is ready + console.log("init after frontend ready from js"); editor.handle.initAfterFrontendReady(operatingSystem()); }); diff --git a/frontend/src/components/panels/Document.svelte b/frontend/src/components/panels/Document.svelte index bd36152f0..00958357c 100644 --- a/frontend/src/components/panels/Document.svelte +++ b/frontend/src/components/panels/Document.svelte @@ -171,7 +171,7 @@ function canvasPointerDown(e: PointerEvent) { const onEditbox = e.target instanceof HTMLDivElement && e.target.contentEditable; - + console.log("Canvas pointer down", e, onEditbox); if (!onEditbox) viewport?.setPointerCapture(e.pointerId); if (window.document.activeElement instanceof HTMLElement) { window.document.activeElement.blur(); diff --git a/frontend/wasm/Cargo.toml b/frontend/wasm/Cargo.toml index 13e45bd07..1ba3ae2de 100644 --- a/frontend/wasm/Cargo.toml +++ b/frontend/wasm/Cargo.toml @@ -13,7 +13,7 @@ license = "Apache-2.0" [features] default = ["gpu"] gpu = ["editor/gpu"] -tauri = ["editor/tauri"] +native = [] [lib] crate-type = ["cdylib", "rlib"] @@ -31,6 +31,7 @@ graphene-std = { workspace = true } graph-craft = { workspace = true } log = { workspace = true } serde = { workspace = true } +serde_json = { workspace = true } wasm-bindgen = { workspace = true } serde-wasm-bindgen = { workspace = true } js-sys = { workspace = true } diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index c649ee17a..0d5aecd6e 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -154,6 +154,7 @@ impl EditorHandle { } // Sends a message to the dispatcher in the Editor Backend + #[cfg(not(feature = "native"))] fn dispatch>(&self, message: T) { // Process no further messages after a crash to avoid spamming the console if EDITOR_HAS_CRASHED.load(Ordering::SeqCst) { @@ -169,6 +170,16 @@ impl EditorHandle { } } + #[cfg(feature = "native")] + fn dispatch>(&self, message: T) { + let message: Message = message.into(); + let Ok(serialized_message) = serde_json::to_string(&message) else { + log::error!("Failed to serialize message"); + return; + }; + crate::send_message_to_cef(serialized_message) + } + // Sends a FrontendMessage to JavaScript fn send_frontend_message_to_js(&self, mut message: FrontendMessage) { if let FrontendMessage::UpdateImageData { ref image_data } = message { @@ -202,11 +213,17 @@ impl EditorHandle { } } + #[cfg(feature = "native")] + #[wasm_bindgen(js_name = initAfterFrontendReady)] + pub fn init_after_frontend_ready(&self, platform: String) { + log::debug!("Init after frontend ready from rust"); + } // ======================================================================== // Add additional JS -> Rust wrapper functions below as needed for calling // the backend from the web frontend. // ======================================================================== + #[cfg(not(feature = "native"))] #[wasm_bindgen(js_name = initAfterFrontendReady)] pub fn init_after_frontend_ready(&self, platform: String) { // Send initialization messages diff --git a/frontend/wasm/src/lib.rs b/frontend/wasm/src/lib.rs index 730fb4215..6ef141719 100644 --- a/frontend/wasm/src/lib.rs +++ b/frontend/wasm/src/lib.rs @@ -27,6 +27,7 @@ thread_local! { #[wasm_bindgen(start)] pub fn init_graphite() { // Set up the panic hook + #[cfg(not(feature = "native"))] panic::set_hook(Box::new(panic_hook)); // Set up the logger with a default level of debug @@ -105,6 +106,24 @@ extern "C" { fn trace(msg: &str, format: &str); } +#[wasm_bindgen] +extern "C" { + fn sendMessageToCefFromWasm(message: String); +} + +#[wasm_bindgen] +pub fn send_message_to_cef(message: String) { + let global = js_sys::global(); + + // Get the function by name + let func = js_sys::Reflect::get(&global, &JsValue::from_str("sendMessageToCef")).expect("Function not found"); + + let func = func.dyn_into::().expect("Not a function"); + + // Call it with argument + func.call1(&JsValue::NULL, &JsValue::from_str(&message)).expect("Function call failed"); +} + #[derive(Default)] pub struct WasmLog;